blob: d92b495e2f0c7e6a3d00cd4d31bf1239d7652fc9 [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,
Sameer Thalappil73eba352018-02-06 14:54:41 -0800198 ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800199 ICNSS_DRIVER_EVENT_MAX,
200};
201
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530202enum icnss_msa_perm {
203 ICNSS_MSA_PERM_HLOS_ALL = 0,
204 ICNSS_MSA_PERM_WLAN_HW_RW = 1,
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530205 ICNSS_MSA_PERM_MAX,
206};
207
208#define ICNSS_MAX_VMIDS 4
209
210struct icnss_mem_region_info {
211 uint64_t reg_addr;
212 uint32_t size;
213 uint8_t secure_flag;
214 enum icnss_msa_perm perm;
215};
216
217struct icnss_msa_perm_list_t {
218 int vmids[ICNSS_MAX_VMIDS];
219 int perms[ICNSS_MAX_VMIDS];
220 int nelems;
221};
222
223struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
224 [ICNSS_MSA_PERM_HLOS_ALL] = {
225 .vmids = {VMID_HLOS},
226 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
227 .nelems = 1,
228 },
229
230 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
231 .vmids = {VMID_MSS_MSA, VMID_WLAN},
232 .perms = {PERM_READ | PERM_WRITE,
233 PERM_READ | PERM_WRITE},
234 .nelems = 2,
235 },
236
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530237};
238
239struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
240 [ICNSS_MSA_PERM_HLOS_ALL] = {
241 .vmids = {VMID_HLOS},
242 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
243 .nelems = 1,
244 },
245
246 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
247 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
248 .perms = {PERM_READ | PERM_WRITE,
249 PERM_READ | PERM_WRITE,
250 PERM_READ | PERM_WRITE},
251 .nelems = 3,
252 },
253
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530254};
255
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800256struct icnss_event_pd_service_down_data {
257 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800258 bool fw_rejuvenate;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800259};
260
261struct icnss_driver_event {
262 struct list_head list;
263 enum icnss_driver_event_type type;
264 bool sync;
265 struct completion complete;
266 int ret;
267 void *data;
268};
269
270enum icnss_driver_state {
271 ICNSS_WLFW_QMI_CONNECTED,
272 ICNSS_POWER_ON,
273 ICNSS_FW_READY,
274 ICNSS_DRIVER_PROBED,
275 ICNSS_FW_TEST_MODE,
276 ICNSS_PM_SUSPEND,
277 ICNSS_PM_SUSPEND_NOIRQ,
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -0700278 ICNSS_SSR_REGISTERED,
279 ICNSS_PDR_REGISTERED,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800280 ICNSS_PD_RESTART,
281 ICNSS_MSA0_ASSIGNED,
282 ICNSS_WLFW_EXISTS,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530283 ICNSS_SHUTDOWN_DONE,
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700284 ICNSS_HOST_TRIGGERED_PDR,
Sameer Thalappil93c00732017-08-18 13:02:32 -0700285 ICNSS_FW_DOWN,
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -0800286 ICNSS_DRIVER_UNLOADING,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800287};
288
289struct ce_irq_list {
290 int irq;
291 irqreturn_t (*handler)(int, void *);
292};
293
Yuanyuan Liu68939762017-04-04 16:43:03 -0700294struct icnss_vreg_info {
295 struct regulator *reg;
296 const char *name;
297 u32 min_v;
298 u32 max_v;
299 u32 load_ua;
300 unsigned long settle_delay;
301 bool required;
302};
303
304struct icnss_clk_info {
305 struct clk *handle;
306 const char *name;
307 u32 freq;
308 bool required;
309};
310
311static struct icnss_vreg_info icnss_vreg_info[] = {
312 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
313 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
314 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
315 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
316};
317
318#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
319
320static struct icnss_clk_info icnss_clk_info[] = {
321 {NULL, "cxo_ref_clk_pin", 0, false},
322};
323
324#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
325
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800326struct icnss_stats {
327 struct {
328 uint32_t posted;
329 uint32_t processed;
330 } events[ICNSS_DRIVER_EVENT_MAX];
331
332 struct {
333 uint32_t request;
334 uint32_t free;
335 uint32_t enable;
336 uint32_t disable;
337 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
338
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700339 struct {
340 uint32_t pdr_fw_crash;
341 uint32_t pdr_host_error;
342 uint32_t root_pd_crash;
343 uint32_t root_pd_shutdown;
344 } recovery;
345
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800346 uint32_t pm_suspend;
347 uint32_t pm_suspend_err;
348 uint32_t pm_resume;
349 uint32_t pm_resume_err;
350 uint32_t pm_suspend_noirq;
351 uint32_t pm_suspend_noirq_err;
352 uint32_t pm_resume_noirq;
353 uint32_t pm_resume_noirq_err;
354 uint32_t pm_stay_awake;
355 uint32_t pm_relax;
356
357 uint32_t ind_register_req;
358 uint32_t ind_register_resp;
359 uint32_t ind_register_err;
360 uint32_t msa_info_req;
361 uint32_t msa_info_resp;
362 uint32_t msa_info_err;
363 uint32_t msa_ready_req;
364 uint32_t msa_ready_resp;
365 uint32_t msa_ready_err;
366 uint32_t msa_ready_ind;
367 uint32_t cap_req;
368 uint32_t cap_resp;
369 uint32_t cap_err;
370 uint32_t pin_connect_result;
371 uint32_t cfg_req;
372 uint32_t cfg_resp;
373 uint32_t cfg_req_err;
374 uint32_t mode_req;
375 uint32_t mode_resp;
376 uint32_t mode_req_err;
377 uint32_t ini_req;
378 uint32_t ini_resp;
379 uint32_t ini_req_err;
380 uint32_t vbatt_req;
381 uint32_t vbatt_resp;
382 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800383 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800384 uint32_t rejuvenate_ack_req;
385 uint32_t rejuvenate_ack_resp;
386 uint32_t rejuvenate_ack_err;
387};
388
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700389enum icnss_pdr_cause_index {
390 ICNSS_FW_CRASH,
391 ICNSS_ROOT_PD_CRASH,
392 ICNSS_ROOT_PD_SHUTDOWN,
393 ICNSS_HOST_ERROR,
394};
395
396static const char * const icnss_pdr_cause[] = {
397 [ICNSS_FW_CRASH] = "FW crash",
398 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
399 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
400 [ICNSS_HOST_ERROR] = "Host error",
401};
402
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800403struct service_notifier_context {
404 void *handle;
405 uint32_t instance_id;
406 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800407};
408
409static struct icnss_priv {
410 uint32_t magic;
411 struct platform_device *pdev;
412 struct icnss_driver_ops *ops;
413 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700414 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
415 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800416 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
417 phys_addr_t mem_base_pa;
418 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800419 struct dma_iommu_mapping *smmu_mapping;
420 dma_addr_t smmu_iova_start;
421 size_t smmu_iova_len;
422 dma_addr_t smmu_iova_ipa_start;
423 size_t smmu_iova_ipa_len;
424 struct qmi_handle *wlfw_clnt;
425 struct list_head event_list;
426 spinlock_t event_lock;
427 struct work_struct event_work;
428 struct work_struct qmi_recv_msg_work;
429 struct workqueue_struct *event_wq;
430 phys_addr_t msa_pa;
431 uint32_t msa_mem_size;
432 void *msa_va;
433 unsigned long state;
434 struct wlfw_rf_chip_info_s_v01 chip_info;
435 struct wlfw_rf_board_info_s_v01 board_info;
436 struct wlfw_soc_info_s_v01 soc_info;
437 struct wlfw_fw_version_info_s_v01 fw_version_info;
438 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
439 u32 pwr_pin_result;
440 u32 phy_io_pin_result;
441 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700442 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800443 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700444 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800445 struct dentry *root_dentry;
446 spinlock_t on_off_lock;
447 struct icnss_stats stats;
448 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800449 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800450 struct notifier_block service_notifier_nb;
451 int total_domains;
452 struct notifier_block get_service_nb;
453 void *modem_notify_handler;
454 struct notifier_block modem_ssr_nb;
455 uint32_t diag_reg_read_addr;
456 uint32_t diag_reg_read_mem_type;
457 uint32_t diag_reg_read_len;
458 uint8_t *diag_reg_read_buf;
459 struct qpnp_adc_tm_btm_param vph_monitor_params;
460 struct qpnp_adc_tm_chip *adc_tm_dev;
461 struct qpnp_vadc_chip *vadc_dev;
462 uint64_t vph_pwr;
463 atomic_t pm_count;
464 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700465 bool bypass_s1_smmu;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -0800466 bool force_err_fatal;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -0800467 bool allow_recursive_recovery;
Sameer Thalappil73eba352018-02-06 14:54:41 -0800468 bool early_crash_ind;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800469 u8 cause_for_rejuvenation;
470 u8 requesting_sub_system;
471 u16 line_number;
472 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530473 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800474} *penv;
475
Yuanyuan Liu68939762017-04-04 16:43:03 -0700476#ifdef CONFIG_ICNSS_DEBUG
477static void icnss_ignore_qmi_timeout(bool ignore)
478{
479 ignore_qmi_timeout = ignore;
480}
481#else
482static void icnss_ignore_qmi_timeout(bool ignore) { }
483#endif
484
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530485static int icnss_assign_msa_perm(struct icnss_mem_region_info
486 *mem_region, enum icnss_msa_perm new_perm)
487{
488 int ret = 0;
489 phys_addr_t addr;
490 u32 size;
491 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530492 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530493 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530494 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
495 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530496 u32 dest_nelems;
497 enum icnss_msa_perm cur_perm = mem_region->perm;
498 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
499
500 addr = mem_region->reg_addr;
501 size = mem_region->size;
502
503 if (mem_region->secure_flag) {
504 new_perm_list = &msa_perm_secure_list[new_perm];
505 old_perm_list = &msa_perm_secure_list[cur_perm];
506 } else {
507 new_perm_list = &msa_perm_list[new_perm];
508 old_perm_list = &msa_perm_list[cur_perm];
509 }
510
511 source_nelems = old_perm_list->nelems;
512 dest_nelems = new_perm_list->nelems;
513
514 for (i = 0; i < source_nelems; ++i)
515 source_vmids[i] = old_perm_list->vmids[i];
516
517 for (i = 0; i < dest_nelems; ++i) {
518 dest_vmids[i] = new_perm_list->vmids[i];
519 dest_perms[i] = new_perm_list->perms[i];
520 }
521
522 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
523 dest_vmids, dest_perms, dest_nelems);
524 if (ret) {
525 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
526 &addr, size, ret);
527 goto out;
528 }
529
530 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
531 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
532 source_nelems, source_vmids[0], source_vmids[1],
533 source_vmids[2], source_vmids[3], dest_nelems,
534 dest_vmids[0], dest_vmids[1], dest_vmids[2],
535 dest_vmids[3]);
536out:
537 return ret;
538}
539
540static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
541 enum icnss_msa_perm new_perm)
542{
543 int ret;
544 int i;
545 enum icnss_msa_perm old_perm;
546
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700547 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
548 icnss_pr_err("Invalid memory region len %d\n",
549 priv->nr_mem_region);
550 return -EINVAL;
551 }
552
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530553 for (i = 0; i < priv->nr_mem_region; i++) {
554 old_perm = priv->mem_region[i].perm;
555 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
556 if (ret)
557 goto err_unmap;
558 priv->mem_region[i].perm = new_perm;
559 }
560 return 0;
561
562err_unmap:
563 for (i--; i >= 0; i--) {
564 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
565 }
566 return ret;
567}
568
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800569static void icnss_pm_stay_awake(struct icnss_priv *priv)
570{
571 if (atomic_inc_return(&priv->pm_count) != 1)
572 return;
573
Yuanyuan Liu68939762017-04-04 16:43:03 -0700574 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800575 atomic_read(&priv->pm_count));
576
577 pm_stay_awake(&priv->pdev->dev);
578
579 priv->stats.pm_stay_awake++;
580}
581
582static void icnss_pm_relax(struct icnss_priv *priv)
583{
584 int r = atomic_dec_return(&priv->pm_count);
585
586 WARN_ON(r < 0);
587
588 if (r != 0)
589 return;
590
Yuanyuan Liu68939762017-04-04 16:43:03 -0700591 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800592 atomic_read(&priv->pm_count));
593
594 pm_relax(&priv->pdev->dev);
595 priv->stats.pm_relax++;
596}
597
598static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
599{
600 switch (type) {
601 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
602 return "SERVER_ARRIVE";
603 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
604 return "SERVER_EXIT";
605 case ICNSS_DRIVER_EVENT_FW_READY_IND:
606 return "FW_READY";
607 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
608 return "REGISTER_DRIVER";
609 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
610 return "UNREGISTER_DRIVER";
611 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
612 return "PD_SERVICE_DOWN";
Sameer Thalappil73eba352018-02-06 14:54:41 -0800613 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
614 return "FW_EARLY_CRASH_IND";
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800615 case ICNSS_DRIVER_EVENT_MAX:
616 return "EVENT_MAX";
617 }
618
619 return "UNKNOWN";
620};
621
622static int icnss_driver_event_post(enum icnss_driver_event_type type,
623 u32 flags, void *data)
624{
625 struct icnss_driver_event *event;
626 unsigned long irq_flags;
627 int gfp = GFP_KERNEL;
628 int ret = 0;
629
630 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
631 icnss_driver_event_to_str(type), type, current->comm,
632 flags, penv->state);
633
634 if (type >= ICNSS_DRIVER_EVENT_MAX) {
635 icnss_pr_err("Invalid Event type: %d, can't post", type);
636 return -EINVAL;
637 }
638
639 if (in_interrupt() || irqs_disabled())
640 gfp = GFP_ATOMIC;
641
642 event = kzalloc(sizeof(*event), gfp);
643 if (event == NULL)
644 return -ENOMEM;
645
646 icnss_pm_stay_awake(penv);
647
648 event->type = type;
649 event->data = data;
650 init_completion(&event->complete);
651 event->ret = ICNSS_EVENT_PENDING;
652 event->sync = !!(flags & ICNSS_EVENT_SYNC);
653
654 spin_lock_irqsave(&penv->event_lock, irq_flags);
655 list_add_tail(&event->list, &penv->event_list);
656 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
657
658 penv->stats.events[type].posted++;
659 queue_work(penv->event_wq, &penv->event_work);
660
661 if (!(flags & ICNSS_EVENT_SYNC))
662 goto out;
663
664 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
665 wait_for_completion(&event->complete);
666 else
667 ret = wait_for_completion_interruptible(&event->complete);
668
669 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
670 icnss_driver_event_to_str(type), type, penv->state, ret,
671 event->ret);
672
673 spin_lock_irqsave(&penv->event_lock, irq_flags);
674 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
675 event->sync = false;
676 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
677 ret = -EINTR;
678 goto out;
679 }
680 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
681
682 ret = event->ret;
683 kfree(event);
684
685out:
686 icnss_pm_relax(penv);
687 return ret;
688}
689
690static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
691 uint64_t voltage_uv)
692{
693 int ret;
694 struct wlfw_vbatt_req_msg_v01 req;
695 struct wlfw_vbatt_resp_msg_v01 resp;
696 struct msg_desc req_desc, resp_desc;
697
698 if (!priv->wlfw_clnt) {
699 ret = -ENODEV;
700 goto out;
701 }
702
703 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
704 penv->state);
705
706 memset(&req, 0, sizeof(req));
707 memset(&resp, 0, sizeof(resp));
708
709 req.voltage_uv = voltage_uv;
710
711 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
712 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
713 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
714
715 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
716 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
717 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
718
719 priv->stats.vbatt_req++;
720
721 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
722 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
723 if (ret < 0) {
724 icnss_pr_err("Send vbatt req failed %d\n", ret);
725 goto out;
726 }
727
728 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
729 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
730 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530731 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800732 goto out;
733 }
734 priv->stats.vbatt_resp++;
735
736out:
737 priv->stats.vbatt_req_err++;
738 return ret;
739}
740
741static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
742{
743 int ret = 0;
744 struct qpnp_vadc_result adc_result;
745
746 if (!priv->vadc_dev) {
747 icnss_pr_err("VADC dev doesn't exists\n");
748 ret = -EINVAL;
749 goto out;
750 }
751
752 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
753 if (ret) {
754 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
755 VADC_VPH_PWR, ret);
756 goto out;
757 }
758
759 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
760 adc_result.physical, adc_result.measurement);
761
762 *result_uv = adc_result.physical;
763out:
764 return ret;
765}
766
767static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
768{
769 struct icnss_priv *priv = ctx;
770 uint64_t vph_pwr = 0;
771 uint64_t vph_pwr_prev;
772 int ret = 0;
773 bool update = true;
774
775 if (!priv) {
776 icnss_pr_err("Priv pointer is NULL\n");
777 return;
778 }
779
780 vph_pwr_prev = priv->vph_pwr;
781
782 ret = icnss_get_phone_power(priv, &vph_pwr);
783 if (ret)
784 return;
785
786 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
787 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
788 update = false;
789 priv->vph_monitor_params.state_request =
790 ADC_TM_HIGH_THR_ENABLE;
791 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
792 ICNSS_THRESHOLD_GUARD;
793 priv->vph_monitor_params.low_thr = 0;
794 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
795 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
796 update = false;
797 priv->vph_monitor_params.state_request =
798 ADC_TM_LOW_THR_ENABLE;
799 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
800 ICNSS_THRESHOLD_GUARD;
801 priv->vph_monitor_params.high_thr = 0;
802 } else {
803 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
804 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
805 update = false;
806 priv->vph_monitor_params.state_request =
807 ADC_TM_HIGH_LOW_THR_ENABLE;
808 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
809 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
810 }
811
812 priv->vph_pwr = vph_pwr;
813
814 if (update)
815 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
816
817 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
818 priv->vph_monitor_params.low_thr,
819 priv->vph_monitor_params.high_thr);
820 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
821 &priv->vph_monitor_params);
822 if (ret)
823 icnss_pr_err("TM channel setup failed %d\n", ret);
824}
825
826static int icnss_setup_vph_monitor(struct icnss_priv *priv)
827{
828 int ret = 0;
829
830 if (!priv->adc_tm_dev) {
831 icnss_pr_err("ADC TM handler is NULL\n");
832 ret = -EINVAL;
833 goto out;
834 }
835
836 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
837 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
838 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
839 priv->vph_monitor_params.channel = VADC_VPH_PWR;
840 priv->vph_monitor_params.btm_ctx = priv;
841 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
842 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
843 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
844 priv->vph_monitor_params.low_thr,
845 priv->vph_monitor_params.high_thr);
846
847 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
848 &priv->vph_monitor_params);
849 if (ret)
850 icnss_pr_err("TM channel setup failed %d\n", ret);
851out:
852 return ret;
853}
854
855static int icnss_init_vph_monitor(struct icnss_priv *priv)
856{
857 int ret = 0;
858
859 if (test_bit(VBATT_DISABLE, &quirks))
860 goto out;
861
862 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
863 if (ret)
864 goto out;
865
866 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
867
868 ret = icnss_setup_vph_monitor(priv);
869 if (ret)
870 goto out;
871out:
872 return ret;
873}
874
875
876static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
877{
878 struct msg_desc ind_desc;
879 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
880 int ret = 0;
881
882 if (!penv || !penv->wlfw_clnt) {
883 ret = -ENODEV;
884 goto out;
885 }
886
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530887 memset(&ind_msg, 0, sizeof(ind_msg));
888
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800889 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
890 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
891 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
892
893 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
894 if (ret < 0) {
895 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
896 ret, msg_len);
897 goto out;
898 }
899
900 /* store pin result locally */
901 if (ind_msg.pwr_pin_result_valid)
902 penv->pwr_pin_result = ind_msg.pwr_pin_result;
903 if (ind_msg.phy_io_pin_result_valid)
904 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
905 if (ind_msg.rf_pin_result_valid)
906 penv->rf_pin_result = ind_msg.rf_pin_result;
907
908 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
909 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
910 ind_msg.rf_pin_result);
911
912 penv->stats.pin_connect_result++;
913out:
914 return ret;
915}
916
Yuanyuan Liu68939762017-04-04 16:43:03 -0700917static int icnss_vreg_on(struct icnss_priv *priv)
918{
919 int ret = 0;
920 struct icnss_vreg_info *vreg_info;
921 int i;
922
923 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
924 vreg_info = &priv->vreg_info[i];
925
926 if (!vreg_info->reg)
927 continue;
928
929 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
930
931 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
932 vreg_info->max_v);
933 if (ret) {
934 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
935 vreg_info->name, vreg_info->min_v,
936 vreg_info->max_v, ret);
937 break;
938 }
939
940 if (vreg_info->load_ua) {
941 ret = regulator_set_load(vreg_info->reg,
942 vreg_info->load_ua);
943 if (ret < 0) {
944 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
945 vreg_info->name,
946 vreg_info->load_ua, ret);
947 break;
948 }
949 }
950
951 ret = regulator_enable(vreg_info->reg);
952 if (ret) {
953 icnss_pr_err("Regulator %s, can't enable: %d\n",
954 vreg_info->name, ret);
955 break;
956 }
957
958 if (vreg_info->settle_delay)
959 udelay(vreg_info->settle_delay);
960 }
961
962 if (!ret)
963 return 0;
964
965 for (; i >= 0; i--) {
966 vreg_info = &priv->vreg_info[i];
967
968 if (!vreg_info->reg)
969 continue;
970
971 regulator_disable(vreg_info->reg);
972 regulator_set_load(vreg_info->reg, 0);
973 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
974 }
975
976 return ret;
977}
978
979static int icnss_vreg_off(struct icnss_priv *priv)
980{
981 int ret = 0;
982 struct icnss_vreg_info *vreg_info;
983 int i;
984
985 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
986 vreg_info = &priv->vreg_info[i];
987
988 if (!vreg_info->reg)
989 continue;
990
991 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
992
993 ret = regulator_disable(vreg_info->reg);
994 if (ret)
995 icnss_pr_err("Regulator %s, can't disable: %d\n",
996 vreg_info->name, ret);
997
998 ret = regulator_set_load(vreg_info->reg, 0);
999 if (ret < 0)
1000 icnss_pr_err("Regulator %s, can't set load: %d\n",
1001 vreg_info->name, ret);
1002
1003 ret = regulator_set_voltage(vreg_info->reg, 0,
1004 vreg_info->max_v);
1005 if (ret)
1006 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1007 vreg_info->name, ret);
1008 }
1009
1010 return ret;
1011}
1012
1013static int icnss_clk_init(struct icnss_priv *priv)
1014{
1015 struct icnss_clk_info *clk_info;
1016 int i;
1017 int ret = 0;
1018
1019 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1020 clk_info = &priv->clk_info[i];
1021
1022 if (!clk_info->handle)
1023 continue;
1024
1025 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1026
1027 if (clk_info->freq) {
1028 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1029
1030 if (ret) {
1031 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1032 clk_info->name, clk_info->freq,
1033 ret);
1034 break;
1035 }
1036 }
1037
1038 ret = clk_prepare_enable(clk_info->handle);
1039 if (ret) {
1040 icnss_pr_err("Clock %s, can't enable: %d\n",
1041 clk_info->name, ret);
1042 break;
1043 }
1044 }
1045
1046 if (ret == 0)
1047 return 0;
1048
1049 for (; i >= 0; i--) {
1050 clk_info = &priv->clk_info[i];
1051
1052 if (!clk_info->handle)
1053 continue;
1054
1055 clk_disable_unprepare(clk_info->handle);
1056 }
1057
1058 return ret;
1059}
1060
1061static int icnss_clk_deinit(struct icnss_priv *priv)
1062{
1063 struct icnss_clk_info *clk_info;
1064 int i;
1065
1066 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1067 clk_info = &priv->clk_info[i];
1068
1069 if (!clk_info->handle)
1070 continue;
1071
1072 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1073
1074 clk_disable_unprepare(clk_info->handle);
1075 }
1076
1077 return 0;
1078}
1079
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001080static int icnss_hw_power_on(struct icnss_priv *priv)
1081{
1082 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001083
1084 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1085
Yuanyuan Liu68939762017-04-04 16:43:03 -07001086 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001087 if (test_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 return ret;
1090 }
1091 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001092 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001093
Yuanyuan Liu68939762017-04-04 16:43:03 -07001094 ret = icnss_vreg_on(priv);
1095 if (ret)
1096 goto out;
1097
1098 ret = icnss_clk_init(priv);
1099 if (ret)
1100 goto vreg_off;
1101
1102 return ret;
1103
1104vreg_off:
1105 icnss_vreg_off(priv);
1106out:
1107 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001108 return ret;
1109}
1110
1111static int icnss_hw_power_off(struct icnss_priv *priv)
1112{
1113 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001114
1115 if (test_bit(HW_ALWAYS_ON, &quirks))
1116 return 0;
1117
Yuanyuan Liu16c89c32018-01-10 15:52:06 -08001118 if (test_bit(ICNSS_FW_DOWN, &priv->state))
1119 return 0;
1120
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001121 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1122
Yuanyuan Liu68939762017-04-04 16:43:03 -07001123 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001124 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001125 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001126 return ret;
1127 }
1128 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001129 spin_unlock(&priv->on_off_lock);
1130
1131 icnss_clk_deinit(priv);
1132
1133 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001134
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001135 return ret;
1136}
1137
1138int icnss_power_on(struct device *dev)
1139{
1140 struct icnss_priv *priv = dev_get_drvdata(dev);
1141
1142 if (!priv) {
1143 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1144 dev, priv);
1145 return -EINVAL;
1146 }
1147
1148 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1149
1150 return icnss_hw_power_on(priv);
1151}
1152EXPORT_SYMBOL(icnss_power_on);
1153
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001154bool icnss_is_fw_ready(void)
1155{
1156 if (!penv)
1157 return false;
1158 else
1159 return test_bit(ICNSS_FW_READY, &penv->state);
1160}
1161EXPORT_SYMBOL(icnss_is_fw_ready);
1162
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001163bool icnss_is_fw_down(void)
1164{
1165 if (!penv)
1166 return false;
1167 else
1168 return test_bit(ICNSS_FW_DOWN, &penv->state);
1169}
1170EXPORT_SYMBOL(icnss_is_fw_down);
1171
1172
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001173int icnss_power_off(struct device *dev)
1174{
1175 struct icnss_priv *priv = dev_get_drvdata(dev);
1176
1177 if (!priv) {
1178 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1179 dev, priv);
1180 return -EINVAL;
1181 }
1182
1183 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1184
1185 return icnss_hw_power_off(priv);
1186}
1187EXPORT_SYMBOL(icnss_power_off);
1188
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001189static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
1190{
1191 struct icnss_priv *priv = ctx;
1192
1193 if (priv)
1194 priv->force_err_fatal = true;
1195
1196 icnss_pr_err("Received force error fatal request from FW\n");
1197
1198 return IRQ_HANDLED;
1199}
1200
Sameer Thalappil73eba352018-02-06 14:54:41 -08001201static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
1202{
1203 struct icnss_priv *priv = ctx;
1204
1205 icnss_pr_err("Received early crash indication from FW\n");
1206
1207 if (priv) {
1208 set_bit(ICNSS_FW_DOWN, &priv->state);
1209 icnss_ignore_qmi_timeout(true);
1210 }
1211
1212 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
1213 0, NULL);
1214
1215 return IRQ_HANDLED;
1216}
1217
1218static void register_fw_error_notifications(struct icnss_priv *priv)
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001219{
1220 int gpio, irq, ret;
1221
1222 if (!of_find_property(priv->pdev->dev.of_node,
1223 "qcom,gpio-force-fatal-error", NULL)) {
1224 icnss_pr_dbg("Error fatal smp2p handler not registered\n");
1225 return;
1226 }
1227 gpio = of_get_named_gpio(priv->pdev->dev.of_node,
1228 "qcom,gpio-force-fatal-error", 0);
1229 if (!gpio_is_valid(gpio)) {
1230 icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
1231 return;
1232 }
1233 irq = gpio_to_irq(gpio);
1234 if (irq < 0) {
1235 icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
1236 return;
1237 }
1238 ret = request_irq(irq, fw_error_fatal_handler,
1239 IRQF_TRIGGER_RISING, "wlanfw-err", priv);
1240 if (ret < 0) {
Sameer Thalappil73eba352018-02-06 14:54:41 -08001241 icnss_pr_err("Unable to register for error fatal IRQ handler %d",
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001242 irq);
1243 return;
1244 }
1245 icnss_pr_dbg("FW force error fatal handler registered\n");
Sameer Thalappil73eba352018-02-06 14:54:41 -08001246
1247 if (!of_find_property(priv->pdev->dev.of_node,
1248 "qcom,gpio-early-crash-ind", NULL)) {
1249 icnss_pr_dbg("FW early crash indication handler not registered\n");
1250 return;
1251 }
1252 gpio = of_get_named_gpio(priv->pdev->dev.of_node,
1253 "qcom,gpio-early-crash-ind", 0);
1254 if (!gpio_is_valid(gpio)) {
1255 icnss_pr_err("Invalid GPIO for early crash indication %d\n",
1256 gpio);
1257 return;
1258 }
1259 irq = gpio_to_irq(gpio);
1260 if (irq < 0) {
1261 icnss_pr_err("Invalid IRQ for early crash indication %u\n",
1262 irq);
1263 return;
1264 }
1265 ret = request_irq(irq, fw_crash_indication_handler,
1266 IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv);
1267 if (ret < 0) {
1268 icnss_pr_err("Unable to register for early crash indication IRQ handler %d",
1269 irq);
1270 return;
1271 }
1272 icnss_pr_dbg("FW crash indication handler registered\n");
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001273}
1274
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001275static int wlfw_msa_mem_info_send_sync_msg(void)
1276{
1277 int ret;
1278 int i;
1279 struct wlfw_msa_info_req_msg_v01 req;
1280 struct wlfw_msa_info_resp_msg_v01 resp;
1281 struct msg_desc req_desc, resp_desc;
1282
1283 if (!penv || !penv->wlfw_clnt)
1284 return -ENODEV;
1285
1286 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1287
1288 memset(&req, 0, sizeof(req));
1289 memset(&resp, 0, sizeof(resp));
1290
1291 req.msa_addr = penv->msa_pa;
1292 req.size = penv->msa_mem_size;
1293
1294 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1295 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1296 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1297
1298 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1299 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1300 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1301
1302 penv->stats.msa_info_req++;
1303
1304 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1305 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1306 if (ret < 0) {
1307 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1308 goto out;
1309 }
1310
1311 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1312 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1313 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301314 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001315 goto out;
1316 }
1317
1318 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1319 resp.mem_region_info_len);
1320
Yuanyuan Liu68939762017-04-04 16:43:03 -07001321 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001322 icnss_pr_err("Invalid memory region length received: %d\n",
1323 resp.mem_region_info_len);
1324 ret = -EINVAL;
1325 goto out;
1326 }
1327
1328 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001329 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001330 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001331 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001332 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001333 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001334 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001335 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001336 resp.mem_region_info[i].secure_flag;
1337 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001338 i, penv->mem_region[i].reg_addr,
1339 penv->mem_region[i].size,
1340 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001341 }
1342
1343 return 0;
1344
1345out:
1346 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001347 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001348 return ret;
1349}
1350
1351static int wlfw_msa_ready_send_sync_msg(void)
1352{
1353 int ret;
1354 struct wlfw_msa_ready_req_msg_v01 req;
1355 struct wlfw_msa_ready_resp_msg_v01 resp;
1356 struct msg_desc req_desc, resp_desc;
1357
1358 if (!penv || !penv->wlfw_clnt)
1359 return -ENODEV;
1360
1361 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1362 penv->state);
1363
1364 memset(&req, 0, sizeof(req));
1365 memset(&resp, 0, sizeof(resp));
1366
1367 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1368 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1369 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1370
1371 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1372 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1373 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1374
1375 penv->stats.msa_ready_req++;
1376 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1377 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1378 if (ret < 0) {
1379 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1380 goto out;
1381 }
1382
1383 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1384 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1385 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301386 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001387 goto out;
1388 }
1389 penv->stats.msa_ready_resp++;
1390
1391 return 0;
1392
1393out:
1394 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001395 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001396 return ret;
1397}
1398
1399static int wlfw_ind_register_send_sync_msg(void)
1400{
1401 int ret;
1402 struct wlfw_ind_register_req_msg_v01 req;
1403 struct wlfw_ind_register_resp_msg_v01 resp;
1404 struct msg_desc req_desc, resp_desc;
1405
1406 if (!penv || !penv->wlfw_clnt)
1407 return -ENODEV;
1408
1409 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1410 penv->state);
1411
1412 memset(&req, 0, sizeof(req));
1413 memset(&resp, 0, sizeof(resp));
1414
1415 req.client_id_valid = 1;
1416 req.client_id = WLFW_CLIENT_ID;
1417 req.fw_ready_enable_valid = 1;
1418 req.fw_ready_enable = 1;
1419 req.msa_ready_enable_valid = 1;
1420 req.msa_ready_enable = 1;
1421 req.pin_connect_result_enable_valid = 1;
1422 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001423 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1424 req.rejuvenate_enable_valid = 1;
1425 req.rejuvenate_enable = 1;
1426 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001427
1428 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1429 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1430 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1431
1432 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1433 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1434 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1435
1436 penv->stats.ind_register_req++;
1437
1438 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1439 &resp_desc, &resp, sizeof(resp),
1440 WLFW_TIMEOUT_MS);
1441 if (ret < 0) {
1442 icnss_pr_err("Send indication register req failed %d\n", ret);
1443 goto out;
1444 }
1445
1446 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1447 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1448 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301449 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001450 goto out;
1451 }
1452 penv->stats.ind_register_resp++;
1453
1454 return 0;
1455
1456out:
1457 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001458 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001459 return ret;
1460}
1461
1462static int wlfw_cap_send_sync_msg(void)
1463{
1464 int ret;
1465 struct wlfw_cap_req_msg_v01 req;
1466 struct wlfw_cap_resp_msg_v01 resp;
1467 struct msg_desc req_desc, resp_desc;
1468
1469 if (!penv || !penv->wlfw_clnt)
1470 return -ENODEV;
1471
1472 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1473
1474 memset(&resp, 0, sizeof(resp));
1475
1476 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1477 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1478 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1479
1480 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1481 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1482 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1483
1484 penv->stats.cap_req++;
1485 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1486 &resp_desc, &resp, sizeof(resp),
1487 WLFW_TIMEOUT_MS);
1488 if (ret < 0) {
1489 icnss_pr_err("Send capability req failed %d\n", ret);
1490 goto out;
1491 }
1492
1493 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1494 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1495 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301496 ret = -resp.resp.result;
1497 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1498 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001499 goto out;
1500 }
1501
1502 penv->stats.cap_resp++;
1503 /* store cap locally */
1504 if (resp.chip_info_valid)
1505 penv->chip_info = resp.chip_info;
1506 if (resp.board_info_valid)
1507 penv->board_info = resp.board_info;
1508 else
1509 penv->board_info.board_id = 0xFF;
1510 if (resp.soc_info_valid)
1511 penv->soc_info = resp.soc_info;
1512 if (resp.fw_version_info_valid)
1513 penv->fw_version_info = resp.fw_version_info;
1514 if (resp.fw_build_id_valid)
1515 strlcpy(penv->fw_build_id, resp.fw_build_id,
1516 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1517
1518 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",
1519 penv->chip_info.chip_id, penv->chip_info.chip_family,
1520 penv->board_info.board_id, penv->soc_info.soc_id,
1521 penv->fw_version_info.fw_version,
1522 penv->fw_version_info.fw_build_timestamp,
1523 penv->fw_build_id);
1524
1525 return 0;
1526
1527out:
1528 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001529 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001530 return ret;
1531}
1532
1533static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1534{
1535 int ret;
1536 struct wlfw_wlan_mode_req_msg_v01 req;
1537 struct wlfw_wlan_mode_resp_msg_v01 resp;
1538 struct msg_desc req_desc, resp_desc;
1539
1540 if (!penv || !penv->wlfw_clnt)
1541 return -ENODEV;
1542
1543 /* During recovery do not send mode request for WLAN OFF as
1544 * FW not able to process it.
1545 */
1546 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1547 mode == QMI_WLFW_OFF_V01)
1548 return 0;
1549
1550 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1551 penv->state, mode);
1552
1553 memset(&req, 0, sizeof(req));
1554 memset(&resp, 0, sizeof(resp));
1555
1556 req.mode = mode;
1557 req.hw_debug_valid = 1;
1558 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1559
1560 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1561 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1562 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1563
1564 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1565 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1566 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1567
1568 penv->stats.mode_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 mode req failed, mode: %d ret: %d\n",
1574 mode, ret);
1575 goto out;
1576 }
1577
1578 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1579 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1580 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301581 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001582 goto out;
1583 }
1584 penv->stats.mode_resp++;
1585
1586 return 0;
1587
1588out:
1589 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001590 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001591 return ret;
1592}
1593
1594static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1595{
1596 int ret;
1597 struct wlfw_wlan_cfg_req_msg_v01 req;
1598 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1599 struct msg_desc req_desc, resp_desc;
1600
1601 if (!penv || !penv->wlfw_clnt)
1602 return -ENODEV;
1603
1604 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1605
1606 memset(&req, 0, sizeof(req));
1607 memset(&resp, 0, sizeof(resp));
1608
1609 memcpy(&req, data, sizeof(req));
1610
1611 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1612 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1613 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1614
1615 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1616 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1617 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1618
1619 penv->stats.cfg_req++;
1620 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1621 &resp_desc, &resp, sizeof(resp),
1622 WLFW_TIMEOUT_MS);
1623 if (ret < 0) {
1624 icnss_pr_err("Send config req failed %d\n", ret);
1625 goto out;
1626 }
1627
1628 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1629 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1630 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301631 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001632 goto out;
1633 }
1634 penv->stats.cfg_resp++;
1635
1636 return 0;
1637
1638out:
1639 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001640 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001641 return ret;
1642}
1643
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001644static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001645{
1646 int ret;
1647 struct wlfw_ini_req_msg_v01 req;
1648 struct wlfw_ini_resp_msg_v01 resp;
1649 struct msg_desc req_desc, resp_desc;
1650
1651 if (!penv || !penv->wlfw_clnt)
1652 return -ENODEV;
1653
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001654 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1655 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001656
1657 memset(&req, 0, sizeof(req));
1658 memset(&resp, 0, sizeof(resp));
1659
1660 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001661 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001662
1663 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1664 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1665 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1666
1667 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1668 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1669 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1670
1671 penv->stats.ini_req++;
1672
1673 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1674 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1675 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001676 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1677 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001678 goto out;
1679 }
1680
1681 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001682 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1683 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301684 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001685 goto out;
1686 }
1687 penv->stats.ini_resp++;
1688
1689 return 0;
1690
1691out:
1692 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001693 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001694 return ret;
1695}
1696
1697static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1698 uint32_t offset, uint32_t mem_type,
1699 uint32_t data_len, uint8_t *data)
1700{
1701 int ret;
1702 struct wlfw_athdiag_read_req_msg_v01 req;
1703 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1704 struct msg_desc req_desc, resp_desc;
1705
1706 if (!priv->wlfw_clnt) {
1707 ret = -ENODEV;
1708 goto out;
1709 }
1710
1711 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1712 priv->state, offset, mem_type, data_len);
1713
1714 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1715 if (!resp) {
1716 ret = -ENOMEM;
1717 goto out;
1718 }
1719 memset(&req, 0, sizeof(req));
1720
1721 req.offset = offset;
1722 req.mem_type = mem_type;
1723 req.data_len = data_len;
1724
1725 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1726 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1727 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1728
1729 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1730 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1731 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1732
1733 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1734 &resp_desc, resp, sizeof(*resp),
1735 WLFW_TIMEOUT_MS);
1736 if (ret < 0) {
1737 icnss_pr_err("send athdiag read req failed %d\n", ret);
1738 goto out;
1739 }
1740
1741 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1742 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1743 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301744 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001745 goto out;
1746 }
1747
Yuanyuan Liu68939762017-04-04 16:43:03 -07001748 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001749 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1750 resp->data_valid, resp->data_len);
1751 ret = -EINVAL;
1752 goto out;
1753 }
1754
1755 memcpy(data, resp->data, resp->data_len);
1756
1757out:
1758 kfree(resp);
1759 return ret;
1760}
1761
1762static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1763 uint32_t offset, uint32_t mem_type,
1764 uint32_t data_len, uint8_t *data)
1765{
1766 int ret;
1767 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1768 struct wlfw_athdiag_write_resp_msg_v01 resp;
1769 struct msg_desc req_desc, resp_desc;
1770
1771 if (!priv->wlfw_clnt) {
1772 ret = -ENODEV;
1773 goto out;
1774 }
1775
1776 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1777 priv->state, offset, mem_type, data_len, data);
1778
1779 req = kzalloc(sizeof(*req), GFP_KERNEL);
1780 if (!req) {
1781 ret = -ENOMEM;
1782 goto out;
1783 }
1784 memset(&resp, 0, sizeof(resp));
1785
1786 req->offset = offset;
1787 req->mem_type = mem_type;
1788 req->data_len = data_len;
1789 memcpy(req->data, data, data_len);
1790
1791 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1792 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1793 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1794
1795 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1796 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1797 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1798
1799 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1800 &resp_desc, &resp, sizeof(resp),
1801 WLFW_TIMEOUT_MS);
1802 if (ret < 0) {
1803 icnss_pr_err("send athdiag write req failed %d\n", ret);
1804 goto out;
1805 }
1806
1807 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1808 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1809 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301810 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001811 goto out;
1812 }
1813out:
1814 kfree(req);
1815 return ret;
1816}
1817
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001818static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1819{
1820 struct msg_desc ind_desc;
1821 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1822 int ret = 0;
1823
1824 if (!penv || !penv->wlfw_clnt) {
1825 ret = -ENODEV;
1826 goto out;
1827 }
1828
1829 memset(&ind_msg, 0, sizeof(ind_msg));
1830
1831 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1832 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1833 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1834
1835 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1836 if (ret < 0) {
1837 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1838 ret, msg_len);
1839 goto out;
1840 }
1841
1842 if (ind_msg.cause_for_rejuvenation_valid)
1843 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1844 else
1845 penv->cause_for_rejuvenation = 0;
1846 if (ind_msg.requesting_sub_system_valid)
1847 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1848 else
1849 penv->requesting_sub_system = 0;
1850 if (ind_msg.line_number_valid)
1851 penv->line_number = ind_msg.line_number;
1852 else
1853 penv->line_number = 0;
1854 if (ind_msg.function_name_valid)
1855 memcpy(penv->function_name, ind_msg.function_name,
1856 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1857 else
1858 memset(penv->function_name, 0,
1859 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1860
1861 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1862 penv->cause_for_rejuvenation,
1863 penv->requesting_sub_system,
1864 penv->line_number,
1865 penv->function_name);
1866
1867 penv->stats.rejuvenate_ind++;
1868out:
1869 return ret;
1870}
1871
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001872static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1873{
1874 int ret;
1875 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1876 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1877 struct msg_desc req_desc, resp_desc;
1878
1879 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1880 priv->state);
1881
1882 memset(&req, 0, sizeof(req));
1883 memset(&resp, 0, sizeof(resp));
1884
1885 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1886 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1887 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1888
1889 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1890 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1891 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1892
1893 priv->stats.rejuvenate_ack_req++;
1894 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1895 &resp_desc, &resp, sizeof(resp),
1896 WLFW_TIMEOUT_MS);
1897 if (ret < 0) {
1898 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1899 goto out;
1900 }
1901
1902 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1903 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1904 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301905 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001906 goto out;
1907 }
1908 priv->stats.rejuvenate_ack_resp++;
1909 return 0;
1910
1911out:
1912 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001913 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001914 return ret;
1915}
1916
1917static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1918 uint64_t dynamic_feature_mask)
1919{
1920 int ret;
1921 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1922 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1923 struct msg_desc req_desc, resp_desc;
1924
1925 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1926 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1927 priv->state);
1928 return -EINVAL;
1929 }
1930
1931 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1932 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1933 return 0;
1934 }
1935
1936 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1937 dynamic_feature_mask, priv->state);
1938
1939 memset(&req, 0, sizeof(req));
1940 memset(&resp, 0, sizeof(resp));
1941
1942 req.mask_valid = 1;
1943 req.mask = dynamic_feature_mask;
1944
1945 req_desc.max_msg_len =
1946 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1947 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1948 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1949
1950 resp_desc.max_msg_len =
1951 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1952 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1953 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1954
1955 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1956 &resp_desc, &resp, sizeof(resp),
1957 WLFW_TIMEOUT_MS);
1958 if (ret < 0) {
1959 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1960 goto out;
1961 }
1962
1963 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1964 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1965 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301966 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001967 goto out;
1968 }
1969
1970 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1971 resp.prev_mask_valid, resp.prev_mask,
1972 resp.curr_mask_valid, resp.curr_mask);
1973
1974 return 0;
1975
1976out:
1977 return ret;
1978}
1979
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001980static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1981{
1982 int ret;
1983
1984 if (!penv || !penv->wlfw_clnt)
1985 return;
1986
Yuanyuan Liu68939762017-04-04 16:43:03 -07001987 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001988
1989 do {
1990 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1991
1992 if (ret != -ENOMSG)
1993 icnss_pr_err("Error receiving message: %d\n", ret);
1994
Yuanyuan Liu68939762017-04-04 16:43:03 -07001995 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001996}
1997
1998static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1999 enum qmi_event_type event, void *notify_priv)
2000{
Yuanyuan Liu68939762017-04-04 16:43:03 -07002001 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002002
2003 if (!penv || !penv->wlfw_clnt)
2004 return;
2005
2006 switch (event) {
2007 case QMI_RECV_MSG:
2008 schedule_work(&penv->qmi_recv_msg_work);
2009 break;
2010 default:
2011 icnss_pr_dbg("Unknown Event: %d\n", event);
2012 break;
2013 }
2014}
2015
Yuanyuan Liu68939762017-04-04 16:43:03 -07002016static int icnss_call_driver_uevent(struct icnss_priv *priv,
2017 enum icnss_uevent uevent, void *data)
2018{
2019 struct icnss_uevent_data uevent_data;
2020
2021 if (!priv->ops || !priv->ops->uevent)
2022 return 0;
2023
2024 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
2025 priv->state, uevent);
2026
2027 uevent_data.uevent = uevent;
2028 uevent_data.data = data;
2029
2030 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
2031}
2032
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002033static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
2034 unsigned int msg_id, void *msg,
2035 unsigned int msg_len, void *ind_cb_priv)
2036{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002037 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002038 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002039
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002040 if (!penv)
2041 return;
2042
2043 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
2044
Sameer Thalappil93c00732017-08-18 13:02:32 -07002045 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2046 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
2047 msg_id, penv->state);
2048 return;
2049 }
2050
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002051 switch (msg_id) {
2052 case QMI_WLFW_FW_READY_IND_V01:
2053 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
2054 0, NULL);
2055 break;
2056 case QMI_WLFW_MSA_READY_IND_V01:
2057 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
2058 msg_id);
2059 penv->stats.msa_ready_ind++;
2060 break;
2061 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
2062 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
2063 msg_id);
2064 icnss_qmi_pin_connect_result_ind(msg, msg_len);
2065 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002066 case QMI_WLFW_REJUVENATE_IND_V01:
2067 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
2068 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002069
2070 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08002071 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002072 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
2073 if (event_data == NULL)
2074 return;
2075 event_data->crashed = true;
2076 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002077 fw_down_data.crashed = true;
2078 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
2079 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002080 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2081 0, event_data);
2082 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002083 default:
2084 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
2085 break;
2086 }
2087}
2088
2089static int icnss_driver_event_server_arrive(void *data)
2090{
2091 int ret = 0;
2092
2093 if (!penv)
2094 return -ENODEV;
2095
2096 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002097 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002098
2099 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2100 if (!penv->wlfw_clnt) {
2101 icnss_pr_err("QMI client handle create failed\n");
2102 ret = -ENOMEM;
2103 goto out;
2104 }
2105
2106 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2107 WLFW_SERVICE_VERS_V01,
2108 WLFW_SERVICE_INS_ID_V01);
2109 if (ret < 0) {
2110 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2111 goto fail;
2112 }
2113
2114 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2115 icnss_qmi_wlfw_clnt_ind, penv);
2116 if (ret < 0) {
2117 icnss_pr_err("Failed to register indication callback: %d\n",
2118 ret);
2119 goto fail;
2120 }
2121
2122 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2123
2124 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2125
2126 ret = icnss_hw_power_on(penv);
2127 if (ret)
2128 goto fail;
2129
2130 ret = wlfw_ind_register_send_sync_msg();
2131 if (ret < 0)
2132 goto err_power_on;
2133
2134 if (!penv->msa_va) {
2135 icnss_pr_err("Invalid MSA address\n");
2136 ret = -EINVAL;
2137 goto err_power_on;
2138 }
2139
2140 ret = wlfw_msa_mem_info_send_sync_msg();
2141 if (ret < 0)
2142 goto err_power_on;
2143
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302144 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2145 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2146 if (ret < 0)
2147 goto err_power_on;
2148 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2149 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002150
2151 ret = wlfw_msa_ready_send_sync_msg();
2152 if (ret < 0)
2153 goto err_setup_msa;
2154
2155 ret = wlfw_cap_send_sync_msg();
2156 if (ret < 0)
2157 goto err_setup_msa;
2158
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002159 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2160 dynamic_feature_mask);
2161
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002162 icnss_init_vph_monitor(penv);
2163
Sameer Thalappil73eba352018-02-06 14:54:41 -08002164 register_fw_error_notifications(penv);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002165
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002166 return ret;
2167
2168err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302169 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002170err_power_on:
2171 icnss_hw_power_off(penv);
2172fail:
2173 qmi_handle_destroy(penv->wlfw_clnt);
2174 penv->wlfw_clnt = NULL;
2175out:
2176 ICNSS_ASSERT(0);
2177 return ret;
2178}
2179
2180static int icnss_driver_event_server_exit(void *data)
2181{
2182 if (!penv || !penv->wlfw_clnt)
2183 return -ENODEV;
2184
2185 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2186
2187 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2188 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2189 &penv->vph_monitor_params);
2190
2191 qmi_handle_destroy(penv->wlfw_clnt);
2192
2193 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2194 penv->wlfw_clnt = NULL;
2195
2196 return 0;
2197}
2198
2199static int icnss_call_driver_probe(struct icnss_priv *priv)
2200{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002201 int ret = 0;
2202 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002203
2204 if (!priv->ops || !priv->ops->probe)
2205 return 0;
2206
Yuanyuan Liu68939762017-04-04 16:43:03 -07002207 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2208 return -EINVAL;
2209
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002210 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2211
2212 icnss_hw_power_on(priv);
2213
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002214 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2215 ret = priv->ops->probe(&priv->pdev->dev);
2216 probe_cnt++;
2217 if (ret != -EPROBE_DEFER)
2218 break;
2219 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002220 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002221 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2222 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002223 goto out;
2224 }
2225
2226 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2227
2228 return 0;
2229
2230out:
2231 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002232 return ret;
2233}
2234
Yuanyuan Liu68939762017-04-04 16:43:03 -07002235static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2236{
2237 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2238 goto out;
2239
2240 if (!priv->ops || !priv->ops->shutdown)
2241 goto out;
2242
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302243 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2244 goto out;
2245
Yuanyuan Liu68939762017-04-04 16:43:03 -07002246 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2247
2248 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302249 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002250
2251out:
2252 return 0;
2253}
2254
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002255static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002256{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002257 int ret;
2258
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002259 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002260
Anurag Chouhan9de21192017-08-08 15:30:02 +05302261 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002262
2263 clear_bit(ICNSS_PD_RESTART, &priv->state);
Sameer Thalappil73eba352018-02-06 14:54:41 -08002264 priv->early_crash_ind = false;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002265
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002266 if (!priv->ops || !priv->ops->reinit)
2267 goto out;
2268
Yuanyuan Liu48a51e42017-12-04 16:01:31 -08002269 if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
2270 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2271 priv->state);
2272 goto out;
2273 }
2274
Yuanyuan Liu68939762017-04-04 16:43:03 -07002275 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002276 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002277
2278 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2279
2280 icnss_hw_power_on(priv);
2281
2282 ret = priv->ops->reinit(&priv->pdev->dev);
2283 if (ret < 0) {
2284 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2285 ret, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002286 if (!priv->allow_recursive_recovery)
2287 ICNSS_ASSERT(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002288 goto out_power_off;
2289 }
2290
2291out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302292 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002293 return 0;
2294
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002295call_probe:
2296 return icnss_call_driver_probe(priv);
2297
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002298out_power_off:
2299 icnss_hw_power_off(priv);
2300
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002301 return ret;
2302}
2303
2304
2305static int icnss_driver_event_fw_ready_ind(void *data)
2306{
2307 int ret = 0;
2308
2309 if (!penv)
2310 return -ENODEV;
2311
2312 set_bit(ICNSS_FW_READY, &penv->state);
2313
2314 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2315
2316 icnss_hw_power_off(penv);
2317
2318 if (!penv->pdev) {
2319 icnss_pr_err("Device is not ready\n");
2320 ret = -ENODEV;
2321 goto out;
2322 }
2323
2324 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002325 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002326 else
2327 ret = icnss_call_driver_probe(penv);
2328
2329out:
2330 return ret;
2331}
2332
2333static int icnss_driver_event_register_driver(void *data)
2334{
2335 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002336 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002337
2338 if (penv->ops)
2339 return -EEXIST;
2340
2341 penv->ops = data;
2342
2343 if (test_bit(SKIP_QMI, &quirks))
2344 set_bit(ICNSS_FW_READY, &penv->state);
2345
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002346 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2347 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2348 penv->state);
2349 return -ENODEV;
2350 }
2351
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002352 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2353 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2354 penv->state);
2355 goto out;
2356 }
2357
2358 ret = icnss_hw_power_on(penv);
2359 if (ret)
2360 goto out;
2361
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002362 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2363 ret = penv->ops->probe(&penv->pdev->dev);
2364 probe_cnt++;
2365 if (ret != -EPROBE_DEFER)
2366 break;
2367 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002368 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002369 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2370 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002371 goto power_off;
2372 }
2373
2374 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2375
2376 return 0;
2377
2378power_off:
2379 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002380out:
2381 return ret;
2382}
2383
2384static int icnss_driver_event_unregister_driver(void *data)
2385{
2386 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2387 penv->ops = NULL;
2388 goto out;
2389 }
2390
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002391 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002392 if (penv->ops)
2393 penv->ops->remove(&penv->pdev->dev);
2394
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002395 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002396 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2397
2398 penv->ops = NULL;
2399
2400 icnss_hw_power_off(penv);
2401
2402out:
2403 return 0;
2404}
2405
Yuanyuan Liu68939762017-04-04 16:43:03 -07002406static int icnss_fw_crashed(struct icnss_priv *priv,
2407 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002408{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302409 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002410
2411 set_bit(ICNSS_PD_RESTART, &priv->state);
2412 clear_bit(ICNSS_FW_READY, &priv->state);
2413
2414 icnss_pm_stay_awake(priv);
2415
Yuanyuan Liu68939762017-04-04 16:43:03 -07002416 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2417 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002418
Sameer Thalappil73eba352018-02-06 14:54:41 -08002419 if (event_data && event_data->fw_rejuvenate)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002420 wlfw_rejuvenate_ack_send_sync_msg(priv);
2421
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002422 return 0;
2423}
2424
2425static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2426 void *data)
2427{
2428 int ret = 0;
2429 struct icnss_event_pd_service_down_data *event_data = data;
2430
2431 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002432 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002433
Sameer Thalappil73eba352018-02-06 14:54:41 -08002434 if (priv->early_crash_ind) {
2435 icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
2436 event_data->crashed, priv->state);
2437 goto out;
2438 }
2439
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002440 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002441 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2442 event_data->crashed, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002443 if (!priv->allow_recursive_recovery)
2444 ICNSS_ASSERT(0);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002445 goto out;
2446 }
2447
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002448 if (priv->force_err_fatal)
2449 ICNSS_ASSERT(0);
2450
Hardik Kantilal Patel3b934072018-01-12 18:02:10 +05302451 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002452
2453out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002454 kfree(data);
2455
Yuanyuan Liu68939762017-04-04 16:43:03 -07002456 icnss_ignore_qmi_timeout(false);
2457
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002458 return ret;
2459}
2460
Sameer Thalappil73eba352018-02-06 14:54:41 -08002461static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
2462 void *data)
2463{
2464 int ret = 0;
2465
2466 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
2467 goto out;
2468
2469 priv->early_crash_ind = true;
2470 icnss_fw_crashed(priv, NULL);
2471
2472out:
2473 kfree(data);
2474 icnss_ignore_qmi_timeout(false);
2475
2476 return ret;
2477}
2478
2479
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002480static void icnss_driver_event_work(struct work_struct *work)
2481{
2482 struct icnss_driver_event *event;
2483 unsigned long flags;
2484 int ret;
2485
2486 icnss_pm_stay_awake(penv);
2487
2488 spin_lock_irqsave(&penv->event_lock, flags);
2489
2490 while (!list_empty(&penv->event_list)) {
2491 event = list_first_entry(&penv->event_list,
2492 struct icnss_driver_event, list);
2493 list_del(&event->list);
2494 spin_unlock_irqrestore(&penv->event_lock, flags);
2495
2496 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2497 icnss_driver_event_to_str(event->type),
2498 event->sync ? "-sync" : "", event->type,
2499 penv->state);
2500
2501 switch (event->type) {
2502 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2503 ret = icnss_driver_event_server_arrive(event->data);
2504 break;
2505 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2506 ret = icnss_driver_event_server_exit(event->data);
2507 break;
2508 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2509 ret = icnss_driver_event_fw_ready_ind(event->data);
2510 break;
2511 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2512 ret = icnss_driver_event_register_driver(event->data);
2513 break;
2514 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2515 ret = icnss_driver_event_unregister_driver(event->data);
2516 break;
2517 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2518 ret = icnss_driver_event_pd_service_down(penv,
2519 event->data);
2520 break;
Sameer Thalappil73eba352018-02-06 14:54:41 -08002521 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
2522 ret = icnss_driver_event_early_crash_ind(penv,
2523 event->data);
2524 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002525 default:
2526 icnss_pr_err("Invalid Event type: %d", event->type);
2527 kfree(event);
2528 continue;
2529 }
2530
2531 penv->stats.events[event->type].processed++;
2532
2533 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2534 icnss_driver_event_to_str(event->type),
2535 event->sync ? "-sync" : "", event->type, ret,
2536 penv->state);
2537
2538 spin_lock_irqsave(&penv->event_lock, flags);
2539 if (event->sync) {
2540 event->ret = ret;
2541 complete(&event->complete);
2542 continue;
2543 }
2544 spin_unlock_irqrestore(&penv->event_lock, flags);
2545
2546 kfree(event);
2547
2548 spin_lock_irqsave(&penv->event_lock, flags);
2549 }
2550 spin_unlock_irqrestore(&penv->event_lock, flags);
2551
2552 icnss_pm_relax(penv);
2553}
2554
2555static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2556 unsigned long code,
2557 void *_cmd)
2558{
2559 int ret = 0;
2560
2561 if (!penv)
2562 return -ENODEV;
2563
2564 icnss_pr_dbg("Event Notify: code: %ld", code);
2565
2566 switch (code) {
2567 case QMI_SERVER_ARRIVE:
2568 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2569 0, NULL);
2570 break;
2571
2572 case QMI_SERVER_EXIT:
2573 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2574 0, NULL);
2575 break;
2576 default:
2577 icnss_pr_dbg("Invalid code: %ld", code);
2578 break;
2579 }
2580 return ret;
2581}
2582
2583static int icnss_msa0_ramdump(struct icnss_priv *priv)
2584{
2585 struct ramdump_segment segment;
2586
2587 memset(&segment, 0, sizeof(segment));
2588 segment.v_address = priv->msa_va;
2589 segment.size = priv->msa_mem_size;
2590 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2591}
2592
2593static struct notifier_block wlfw_clnt_nb = {
2594 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2595};
2596
2597static int icnss_modem_notifier_nb(struct notifier_block *nb,
2598 unsigned long code,
2599 void *data)
2600{
2601 struct icnss_event_pd_service_down_data *event_data;
2602 struct notif_data *notif = data;
2603 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2604 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002605 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302606 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002607
Yuanyuan Liu68939762017-04-04 16:43:03 -07002608 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002609
Sameer Thalappil765cb492017-10-04 17:57:07 -07002610 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002611 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302612 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002613 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302614 if (!ret) {
2615 icnss_pr_info("Collecting msa0 segment dump\n");
2616 icnss_msa0_ramdump(priv);
2617 icnss_assign_msa_perm_all(priv,
2618 ICNSS_MSA_PERM_WLAN_HW_RW);
2619 } else {
2620 icnss_pr_err("Not able to Collect msa0 segment dump"
2621 "Apps permissions not assigned %d\n", ret);
2622 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002623 return NOTIFY_OK;
2624 }
2625
2626 if (code != SUBSYS_BEFORE_SHUTDOWN)
2627 return NOTIFY_OK;
2628
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002629 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2630 set_bit(ICNSS_FW_DOWN, &priv->state);
2631 icnss_ignore_qmi_timeout(true);
2632
2633 fw_down_data.crashed = !!notif->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002634 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2635 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002636 icnss_call_driver_uevent(priv,
2637 ICNSS_UEVENT_FW_DOWN,
2638 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002639 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002640 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002641
Yuanyuan Liu68939762017-04-04 16:43:03 -07002642 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2643 priv->state, notif->crashed);
2644
Sameer Thalappil93c00732017-08-18 13:02:32 -07002645 set_bit(ICNSS_FW_DOWN, &priv->state);
2646
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002647 if (notif->crashed)
2648 priv->stats.recovery.root_pd_crash++;
2649 else
2650 priv->stats.recovery.root_pd_shutdown++;
2651
Yuanyuan Liu68939762017-04-04 16:43:03 -07002652 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002653
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002654 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002655
2656 if (event_data == NULL)
2657 return notifier_from_errno(-ENOMEM);
2658
2659 event_data->crashed = notif->crashed;
2660
Yuanyuan Liu68939762017-04-04 16:43:03 -07002661 fw_down_data.crashed = !!notif->crashed;
2662 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2663
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002664 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2665 ICNSS_EVENT_SYNC, event_data);
2666
2667 return NOTIFY_OK;
2668}
2669
2670static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2671{
2672 int ret = 0;
2673
2674 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2675
2676 priv->modem_notify_handler =
2677 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2678
2679 if (IS_ERR(priv->modem_notify_handler)) {
2680 ret = PTR_ERR(priv->modem_notify_handler);
2681 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2682 }
2683
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002684 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002685
2686 return ret;
2687}
2688
2689static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2690{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002691 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002692 return 0;
2693
2694 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2695 &priv->modem_ssr_nb);
2696 priv->modem_notify_handler = NULL;
2697
2698 return 0;
2699}
2700
2701static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2702{
2703 int i;
2704
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002705 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002706 return 0;
2707
2708 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002709 service_notif_unregister_notifier(
2710 priv->service_notifier[i].handle,
2711 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002712
2713 kfree(priv->service_notifier);
2714
2715 priv->service_notifier = NULL;
2716
2717 return 0;
2718}
2719
2720static int icnss_service_notifier_notify(struct notifier_block *nb,
2721 unsigned long notification, void *data)
2722{
2723 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2724 service_notifier_nb);
2725 enum pd_subsys_state *state = data;
2726 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002727 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002728 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002729
Yuanyuan Liu68939762017-04-04 16:43:03 -07002730 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2731 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002732
Yuanyuan Liu68939762017-04-04 16:43:03 -07002733 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2734 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002735
Yuanyuan Liu68939762017-04-04 16:43:03 -07002736 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002737
Yuanyuan Liu68939762017-04-04 16:43:03 -07002738 if (event_data == NULL)
2739 return notifier_from_errno(-ENOMEM);
2740
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002741 event_data->crashed = true;
2742
Yuanyuan Liu68939762017-04-04 16:43:03 -07002743 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002744 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002745 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002746 }
2747
Yuanyuan Liu68939762017-04-04 16:43:03 -07002748 switch (*state) {
2749 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002750 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002751 break;
2752 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002753 cause = ICNSS_ROOT_PD_SHUTDOWN;
2754 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002755 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002756 break;
2757 case USER_PD_STATE_CHANGE:
2758 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2759 cause = ICNSS_HOST_ERROR;
2760 priv->stats.recovery.pdr_host_error++;
2761 } else {
2762 cause = ICNSS_FW_CRASH;
2763 priv->stats.recovery.pdr_fw_crash++;
2764 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002765 break;
2766 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002767 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002768 break;
2769 }
2770
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002771 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2772 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002773event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002774 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2775 set_bit(ICNSS_FW_DOWN, &priv->state);
2776 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002777
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002778 fw_down_data.crashed = event_data->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002779 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2780 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002781 icnss_call_driver_uevent(priv,
2782 ICNSS_UEVENT_FW_DOWN,
2783 &fw_down_data);
2784 }
2785
2786 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002787 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2788 ICNSS_EVENT_SYNC, event_data);
2789done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002790 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2791 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002792 return NOTIFY_OK;
2793}
2794
2795static int icnss_get_service_location_notify(struct notifier_block *nb,
2796 unsigned long opcode, void *data)
2797{
2798 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2799 get_service_nb);
2800 struct pd_qmi_client_data *pd = data;
2801 int curr_state;
2802 int ret;
2803 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002804 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002805
2806 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2807 priv->state);
2808
2809 if (opcode != LOCATOR_UP)
2810 return NOTIFY_DONE;
2811
2812 if (pd->total_domains == 0) {
2813 icnss_pr_err("Did not find any domains\n");
2814 ret = -ENOENT;
2815 goto out;
2816 }
2817
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002818 notifier = kcalloc(pd->total_domains,
2819 sizeof(struct service_notifier_context),
2820 GFP_KERNEL);
2821 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002822 ret = -ENOMEM;
2823 goto out;
2824 }
2825
2826 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2827
2828 for (i = 0; i < pd->total_domains; i++) {
2829 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2830 pd->domain_list[i].name,
2831 pd->domain_list[i].instance_id);
2832
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002833 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002834 service_notif_register_notifier(pd->domain_list[i].name,
2835 pd->domain_list[i].instance_id,
2836 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002837 notifier[i].instance_id = pd->domain_list[i].instance_id;
2838 strlcpy(notifier[i].name, pd->domain_list[i].name,
2839 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002840
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002841 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002842 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2843 i, pd->domain_list->name,
2844 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002845 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002846 goto free_handle;
2847 }
2848 }
2849
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002850 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002851 priv->total_domains = pd->total_domains;
2852
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002853 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002854
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002855 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2856 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002857
2858 return NOTIFY_OK;
2859
2860free_handle:
2861 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002862 if (notifier[i].handle)
2863 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002864 &priv->service_notifier_nb);
2865 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002866 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002867
2868out:
2869 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2870 priv->state);
2871
2872 return NOTIFY_OK;
2873}
2874
2875
2876static int icnss_pd_restart_enable(struct icnss_priv *priv)
2877{
2878 int ret;
2879
2880 if (test_bit(SSR_ONLY, &quirks)) {
2881 icnss_pr_dbg("PDR disabled through module parameter\n");
2882 return 0;
2883 }
2884
2885 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2886
2887 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2888 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2889 ICNSS_WLAN_SERVICE_NAME,
2890 &priv->get_service_nb);
2891 if (ret) {
2892 icnss_pr_err("Get service location failed: %d\n", ret);
2893 goto out;
2894 }
2895
2896 return 0;
2897out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002898 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002899 return ret;
2900
2901}
2902
2903
2904static int icnss_enable_recovery(struct icnss_priv *priv)
2905{
2906 int ret;
2907
2908 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2909 icnss_pr_dbg("Recovery disabled through module parameter\n");
2910 return 0;
2911 }
2912
2913 if (test_bit(PDR_ONLY, &quirks)) {
2914 icnss_pr_dbg("SSR disabled through module parameter\n");
2915 goto enable_pdr;
2916 }
2917
2918 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2919 &priv->pdev->dev);
2920 if (!priv->msa0_dump_dev)
2921 return -ENOMEM;
2922
2923 icnss_modem_ssr_register_notifier(priv);
2924 if (test_bit(SSR_ONLY, &quirks)) {
2925 icnss_pr_dbg("PDR disabled through module parameter\n");
2926 return 0;
2927 }
2928
2929enable_pdr:
2930 ret = icnss_pd_restart_enable(priv);
2931
2932 if (ret)
2933 return ret;
2934
2935 return 0;
2936}
2937
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302938int __icnss_register_driver(struct icnss_driver_ops *ops,
2939 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002940{
2941 int ret = 0;
2942
2943 if (!penv || !penv->pdev) {
2944 ret = -ENODEV;
2945 goto out;
2946 }
2947
2948 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2949
2950 if (penv->ops) {
2951 icnss_pr_err("Driver already registered\n");
2952 ret = -EEXIST;
2953 goto out;
2954 }
2955
2956 if (!ops->probe || !ops->remove) {
2957 ret = -EINVAL;
2958 goto out;
2959 }
2960
2961 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302962 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002963
2964 if (ret == -EINTR)
2965 ret = 0;
2966
2967out:
2968 return ret;
2969}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302970EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002971
2972int icnss_unregister_driver(struct icnss_driver_ops *ops)
2973{
2974 int ret;
2975
2976 if (!penv || !penv->pdev) {
2977 ret = -ENODEV;
2978 goto out;
2979 }
2980
2981 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2982
2983 if (!penv->ops) {
2984 icnss_pr_err("Driver not registered\n");
2985 ret = -ENOENT;
2986 goto out;
2987 }
2988
2989 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2990 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2991out:
2992 return ret;
2993}
2994EXPORT_SYMBOL(icnss_unregister_driver);
2995
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302996int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002997 irqreturn_t (*handler)(int, void *),
2998 unsigned long flags, const char *name, void *ctx)
2999{
3000 int ret = 0;
3001 unsigned int irq;
3002 struct ce_irq_list *irq_entry;
3003
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303004 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003005 ret = -ENODEV;
3006 goto out;
3007 }
3008
Yuanyuan Liu68939762017-04-04 16:43:03 -07003009 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003010
3011 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3012 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
3013 ret = -EINVAL;
3014 goto out;
3015 }
3016 irq = penv->ce_irqs[ce_id];
3017 irq_entry = &penv->ce_irq_list[ce_id];
3018
3019 if (irq_entry->handler || irq_entry->irq) {
3020 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
3021 irq, ce_id);
3022 ret = -EEXIST;
3023 goto out;
3024 }
3025
3026 ret = request_irq(irq, handler, flags, name, ctx);
3027 if (ret) {
3028 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
3029 irq, ce_id, ret);
3030 goto out;
3031 }
3032 irq_entry->irq = irq;
3033 irq_entry->handler = handler;
3034
Yuanyuan Liu68939762017-04-04 16:43:03 -07003035 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003036
3037 penv->stats.ce_irqs[ce_id].request++;
3038out:
3039 return ret;
3040}
3041EXPORT_SYMBOL(icnss_ce_request_irq);
3042
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303043int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003044{
3045 int ret = 0;
3046 unsigned int irq;
3047 struct ce_irq_list *irq_entry;
3048
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303049 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003050 ret = -ENODEV;
3051 goto out;
3052 }
3053
Yuanyuan Liu68939762017-04-04 16:43:03 -07003054 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003055
3056 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3057 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
3058 ret = -EINVAL;
3059 goto out;
3060 }
3061
3062 irq = penv->ce_irqs[ce_id];
3063 irq_entry = &penv->ce_irq_list[ce_id];
3064 if (!irq_entry->handler || !irq_entry->irq) {
3065 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
3066 ret = -EEXIST;
3067 goto out;
3068 }
3069 free_irq(irq, ctx);
3070 irq_entry->irq = 0;
3071 irq_entry->handler = NULL;
3072
3073 penv->stats.ce_irqs[ce_id].free++;
3074out:
3075 return ret;
3076}
3077EXPORT_SYMBOL(icnss_ce_free_irq);
3078
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303079void icnss_enable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003080{
3081 unsigned int irq;
3082
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303083 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003084 icnss_pr_err("Platform driver not initialized\n");
3085 return;
3086 }
3087
Yuanyuan Liu68939762017-04-04 16:43:03 -07003088 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003089 penv->state);
3090
3091 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3092 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
3093 return;
3094 }
3095
3096 penv->stats.ce_irqs[ce_id].enable++;
3097
3098 irq = penv->ce_irqs[ce_id];
3099 enable_irq(irq);
3100}
3101EXPORT_SYMBOL(icnss_enable_irq);
3102
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303103void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003104{
3105 unsigned int irq;
3106
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303107 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003108 icnss_pr_err("Platform driver not initialized\n");
3109 return;
3110 }
3111
Yuanyuan Liu68939762017-04-04 16:43:03 -07003112 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003113 penv->state);
3114
3115 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3116 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
3117 ce_id);
3118 return;
3119 }
3120
3121 irq = penv->ce_irqs[ce_id];
3122 disable_irq(irq);
3123
3124 penv->stats.ce_irqs[ce_id].disable++;
3125}
3126EXPORT_SYMBOL(icnss_disable_irq);
3127
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303128int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003129{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303130 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003131 icnss_pr_err("Platform driver not initialized\n");
3132 return -EINVAL;
3133 }
3134
3135 info->v_addr = penv->mem_base_va;
3136 info->p_addr = penv->mem_base_pa;
3137 info->chip_id = penv->chip_info.chip_id;
3138 info->chip_family = penv->chip_info.chip_family;
3139 info->board_id = penv->board_info.board_id;
3140 info->soc_id = penv->soc_info.soc_id;
3141 info->fw_version = penv->fw_version_info.fw_version;
3142 strlcpy(info->fw_build_timestamp,
3143 penv->fw_version_info.fw_build_timestamp,
3144 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3145
3146 return 0;
3147}
3148EXPORT_SYMBOL(icnss_get_soc_info);
3149
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303150int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003151{
3152 int ret;
3153
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303154 if (!dev)
3155 return -ENODEV;
3156
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303157 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3158 icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
3159 penv->state);
3160 return -EINVAL;
3161 }
3162
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003163 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003164
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003165 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003166 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003167 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3168 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003169 return ret;
3170}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003171EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003172
3173int icnss_athdiag_read(struct device *dev, uint32_t offset,
3174 uint32_t mem_type, uint32_t data_len,
3175 uint8_t *output)
3176{
3177 int ret = 0;
3178 struct icnss_priv *priv = dev_get_drvdata(dev);
3179
3180 if (priv->magic != ICNSS_MAGIC) {
3181 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3182 dev, priv, priv->magic);
3183 return -EINVAL;
3184 }
3185
3186 if (!output || data_len == 0
3187 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3188 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3189 output, data_len);
3190 ret = -EINVAL;
3191 goto out;
3192 }
3193
3194 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3195 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3196 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3197 priv->state);
3198 ret = -EINVAL;
3199 goto out;
3200 }
3201
3202 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3203 data_len, output);
3204out:
3205 return ret;
3206}
3207EXPORT_SYMBOL(icnss_athdiag_read);
3208
3209int icnss_athdiag_write(struct device *dev, uint32_t offset,
3210 uint32_t mem_type, uint32_t data_len,
3211 uint8_t *input)
3212{
3213 int ret = 0;
3214 struct icnss_priv *priv = dev_get_drvdata(dev);
3215
3216 if (priv->magic != ICNSS_MAGIC) {
3217 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3218 dev, priv, priv->magic);
3219 return -EINVAL;
3220 }
3221
3222 if (!input || data_len == 0
3223 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3224 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3225 input, data_len);
3226 ret = -EINVAL;
3227 goto out;
3228 }
3229
3230 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3231 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3232 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3233 priv->state);
3234 ret = -EINVAL;
3235 goto out;
3236 }
3237
3238 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3239 data_len, input);
3240out:
3241 return ret;
3242}
3243EXPORT_SYMBOL(icnss_athdiag_write);
3244
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303245int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003246 enum icnss_driver_mode mode,
3247 const char *host_version)
3248{
3249 struct wlfw_wlan_cfg_req_msg_v01 req;
3250 u32 i;
3251 int ret;
3252
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303253 if (!dev)
3254 return -ENODEV;
3255
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303256 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3257 icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
3258 penv->state);
3259 return -EINVAL;
3260 }
3261
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003262 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3263 mode, config, host_version);
3264
3265 memset(&req, 0, sizeof(req));
3266
3267 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3268 goto skip;
3269
3270 if (!config || !host_version) {
3271 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3272 config, host_version);
3273 ret = -EINVAL;
3274 goto out;
3275 }
3276
3277 req.host_version_valid = 1;
3278 strlcpy(req.host_version, host_version,
3279 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3280
3281 req.tgt_cfg_valid = 1;
3282 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3283 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3284 else
3285 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3286 for (i = 0; i < req.tgt_cfg_len; i++) {
3287 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3288 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3289 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3290 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3291 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3292 }
3293
3294 req.svc_cfg_valid = 1;
3295 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3296 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3297 else
3298 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3299 for (i = 0; i < req.svc_cfg_len; i++) {
3300 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3301 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3302 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3303 }
3304
3305 req.shadow_reg_valid = 1;
3306 if (config->num_shadow_reg_cfg >
3307 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3308 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3309 else
3310 req.shadow_reg_len = config->num_shadow_reg_cfg;
3311
3312 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3313 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3314
3315 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3316 if (ret)
3317 goto out;
3318skip:
3319 ret = wlfw_wlan_mode_send_sync_msg(mode);
3320out:
3321 if (test_bit(SKIP_QMI, &quirks))
3322 ret = 0;
3323
3324 return ret;
3325}
3326EXPORT_SYMBOL(icnss_wlan_enable);
3327
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303328int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003329{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303330 if (!dev)
3331 return -ENODEV;
3332
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003333 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3334}
3335EXPORT_SYMBOL(icnss_wlan_disable);
3336
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303337bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003338{
3339 return test_bit(SKIP_QMI, &quirks) ? true : false;
3340}
3341EXPORT_SYMBOL(icnss_is_qmi_disable);
3342
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303343int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003344{
3345 int i;
3346
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303347 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003348 return -ENODEV;
3349
3350 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3351 if (penv->ce_irqs[i] == irq)
3352 return i;
3353 }
3354
3355 icnss_pr_err("No matching CE id for irq %d\n", irq);
3356
3357 return -EINVAL;
3358}
3359EXPORT_SYMBOL(icnss_get_ce_id);
3360
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303361int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003362{
3363 int irq;
3364
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303365 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003366 return -ENODEV;
3367
3368 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3369 return -EINVAL;
3370
3371 irq = penv->ce_irqs[ce_id];
3372
3373 return irq;
3374}
3375EXPORT_SYMBOL(icnss_get_irq);
3376
3377struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3378{
3379 struct icnss_priv *priv = dev_get_drvdata(dev);
3380
3381 if (!priv) {
3382 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3383 dev, priv);
3384 return NULL;
3385 }
3386
3387 return priv->smmu_mapping;
3388}
3389EXPORT_SYMBOL(icnss_smmu_get_mapping);
3390
3391int icnss_smmu_map(struct device *dev,
3392 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3393{
3394 struct icnss_priv *priv = dev_get_drvdata(dev);
3395 unsigned long iova;
3396 size_t len;
3397 int ret = 0;
3398
3399 if (!priv) {
3400 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3401 dev, priv);
3402 return -EINVAL;
3403 }
3404
3405 if (!iova_addr) {
3406 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3407 &paddr, size);
3408 return -EINVAL;
3409 }
3410
3411 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3412 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3413
3414 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3415 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3416 iova,
3417 &priv->smmu_iova_ipa_start,
3418 priv->smmu_iova_ipa_len);
3419 return -ENOMEM;
3420 }
3421
3422 ret = iommu_map(priv->smmu_mapping->domain, iova,
3423 rounddown(paddr, PAGE_SIZE), len,
3424 IOMMU_READ | IOMMU_WRITE);
3425 if (ret) {
3426 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3427 return ret;
3428 }
3429
3430 priv->smmu_iova_ipa_start = iova + len;
3431 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3432
3433 return 0;
3434}
3435EXPORT_SYMBOL(icnss_smmu_map);
3436
3437unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3438{
3439 return socinfo_get_serial_number();
3440}
3441EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3442
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003443int icnss_trigger_recovery(struct device *dev)
3444{
3445 int ret = 0;
3446 struct icnss_priv *priv = dev_get_drvdata(dev);
3447
3448 if (priv->magic != ICNSS_MAGIC) {
3449 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3450 ret = -EINVAL;
3451 goto out;
3452 }
3453
3454 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3455 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3456 priv->state);
3457 ret = -EPERM;
3458 goto out;
3459 }
3460
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003461 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003462 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3463 priv->state);
3464 ret = -EOPNOTSUPP;
3465 goto out;
3466 }
3467
3468 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3469 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3470 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003471 ret = -EINVAL;
3472 goto out;
3473 }
3474
Yuanyuan Liu68939762017-04-04 16:43:03 -07003475 WARN_ON(1);
3476 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3477 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003478
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003479 /*
3480 * Initiate PDR, required only for the first instance
3481 */
3482 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3483 priv->service_notifier[0].instance_id);
3484
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003485 if (!ret)
3486 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3487
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003488out:
3489 return ret;
3490}
3491EXPORT_SYMBOL(icnss_trigger_recovery);
3492
3493
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003494static int icnss_smmu_init(struct icnss_priv *priv)
3495{
3496 struct dma_iommu_mapping *mapping;
3497 int atomic_ctx = 1;
3498 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303499 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003500 int ret = 0;
3501
3502 icnss_pr_dbg("Initializing SMMU\n");
3503
3504 mapping = arm_iommu_create_mapping(&platform_bus_type,
3505 priv->smmu_iova_start,
3506 priv->smmu_iova_len);
3507 if (IS_ERR(mapping)) {
3508 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3509 ret = PTR_ERR(mapping);
3510 goto map_fail;
3511 }
3512
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303513 if (priv->bypass_s1_smmu) {
3514 ret = iommu_domain_set_attr(mapping->domain,
3515 DOMAIN_ATTR_S1_BYPASS,
3516 &s1_bypass);
3517 if (ret < 0) {
3518 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3519 ret);
3520 goto set_attr_fail;
3521 }
3522 icnss_pr_dbg("SMMU S1 BYPASS\n");
3523 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003524 ret = iommu_domain_set_attr(mapping->domain,
3525 DOMAIN_ATTR_ATOMIC,
3526 &atomic_ctx);
3527 if (ret < 0) {
3528 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3529 ret);
3530 goto set_attr_fail;
3531 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303532 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003533
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303534 ret = iommu_domain_set_attr(mapping->domain,
3535 DOMAIN_ATTR_FAST,
3536 &fast);
3537 if (ret < 0) {
3538 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3539 ret);
3540 goto set_attr_fail;
3541 }
3542 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003543 }
3544
3545 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3546 if (ret < 0) {
3547 icnss_pr_err("Attach device failed, err = %d\n", ret);
3548 goto attach_fail;
3549 }
3550
3551 priv->smmu_mapping = mapping;
3552
3553 return ret;
3554
3555attach_fail:
3556set_attr_fail:
3557 arm_iommu_release_mapping(mapping);
3558map_fail:
3559 return ret;
3560}
3561
3562static void icnss_smmu_deinit(struct icnss_priv *priv)
3563{
3564 if (!priv->smmu_mapping)
3565 return;
3566
3567 arm_iommu_detach_device(&priv->pdev->dev);
3568 arm_iommu_release_mapping(priv->smmu_mapping);
3569
3570 priv->smmu_mapping = NULL;
3571}
3572
Yuanyuan Liu68939762017-04-04 16:43:03 -07003573static int icnss_get_vreg_info(struct device *dev,
3574 struct icnss_vreg_info *vreg_info)
3575{
3576 int ret = 0;
3577 char prop_name[MAX_PROP_SIZE];
3578 struct regulator *reg;
3579 const __be32 *prop;
3580 int len = 0;
3581 int i;
3582
3583 reg = devm_regulator_get_optional(dev, vreg_info->name);
3584 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3585 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3586 vreg_info->name);
3587 ret = PTR_ERR(reg);
3588 goto out;
3589 }
3590
3591 if (IS_ERR(reg)) {
3592 ret = PTR_ERR(reg);
3593
3594 if (vreg_info->required) {
3595 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3596 vreg_info->name, ret);
3597 goto out;
3598 } else {
3599 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3600 vreg_info->name, ret);
3601 goto done;
3602 }
3603 }
3604
3605 vreg_info->reg = reg;
3606
3607 snprintf(prop_name, MAX_PROP_SIZE,
3608 "qcom,%s-config", vreg_info->name);
3609
3610 prop = of_get_property(dev->of_node, prop_name, &len);
3611
3612 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3613 prop_name, len);
3614
3615 if (!prop || len < (2 * sizeof(__be32))) {
3616 icnss_pr_dbg("Property %s %s\n", prop_name,
3617 prop ? "invalid format" : "doesn't exist");
3618 goto done;
3619 }
3620
3621 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3622 switch (i) {
3623 case 0:
3624 vreg_info->min_v = be32_to_cpup(&prop[0]);
3625 break;
3626 case 1:
3627 vreg_info->max_v = be32_to_cpup(&prop[1]);
3628 break;
3629 case 2:
3630 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3631 break;
3632 case 3:
3633 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3634 break;
3635 default:
3636 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3637 prop_name, i);
3638 break;
3639 }
3640 }
3641
3642done:
3643 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3644 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3645 vreg_info->load_ua, vreg_info->settle_delay);
3646
3647 return 0;
3648
3649out:
3650 return ret;
3651}
3652
3653static int icnss_get_clk_info(struct device *dev,
3654 struct icnss_clk_info *clk_info)
3655{
3656 struct clk *handle;
3657 int ret = 0;
3658
3659 handle = devm_clk_get(dev, clk_info->name);
3660 if (IS_ERR(handle)) {
3661 ret = PTR_ERR(handle);
3662 if (clk_info->required) {
3663 icnss_pr_err("Clock %s isn't available: %d\n",
3664 clk_info->name, ret);
3665 goto out;
3666 } else {
3667 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3668 ret);
3669 ret = 0;
3670 goto out;
3671 }
3672 }
3673
3674 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3675
3676 clk_info->handle = handle;
3677out:
3678 return ret;
3679}
3680
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003681static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003682{
3683 struct icnss_priv *priv = s->private;
3684
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003685 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003686
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003687 seq_puts(s, "\nCMD: test_mode\n");
3688 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3689 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3690 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003691 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003692
3693 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3694 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003695
3696 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003697 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003698 goto out;
3699 }
3700
3701 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003702 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003703 goto out;
3704 }
3705
3706 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003707 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003708 goto out;
3709 }
3710
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003711out:
3712 seq_puts(s, "\n");
3713 return 0;
3714}
3715
3716static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3717{
3718 int ret;
3719
3720 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3721 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3722 priv->state);
3723 ret = -ENODEV;
3724 goto out;
3725 }
3726
3727 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3728 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3729 priv->state);
3730 ret = -EINVAL;
3731 goto out;
3732 }
3733
3734 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3735 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3736 priv->state);
3737 ret = -EINVAL;
3738 goto out;
3739 }
3740
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303741 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003742
3743 ret = icnss_hw_power_off(priv);
3744
3745 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3746
3747out:
3748 return ret;
3749}
3750static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3751 enum icnss_driver_mode mode)
3752{
3753 int ret;
3754
3755 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3756 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3757 priv->state);
3758 ret = -ENODEV;
3759 goto out;
3760 }
3761
3762 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3763 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3764 priv->state);
3765 ret = -EINVAL;
3766 goto out;
3767 }
3768
3769 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3770 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3771 priv->state);
3772 ret = -EBUSY;
3773 goto out;
3774 }
3775
3776 ret = icnss_hw_power_on(priv);
3777 if (ret)
3778 goto out;
3779
3780 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3781
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303782 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003783 if (ret)
3784 goto power_off;
3785
3786 return 0;
3787
3788power_off:
3789 icnss_hw_power_off(priv);
3790 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3791
3792out:
3793 return ret;
3794}
3795
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003796static void icnss_allow_recursive_recovery(struct device *dev)
3797{
3798 struct icnss_priv *priv = dev_get_drvdata(dev);
3799
3800 priv->allow_recursive_recovery = true;
3801
3802 icnss_pr_info("Recursive recovery allowed for WLAN\n");
3803}
3804
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003805static ssize_t icnss_fw_debug_write(struct file *fp,
3806 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003807 size_t count, loff_t *off)
3808{
3809 struct icnss_priv *priv =
3810 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003811 char buf[64];
3812 char *sptr, *token;
3813 unsigned int len = 0;
3814 char *cmd;
3815 uint64_t val;
3816 const char *delim = " ";
3817 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003818
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003819 len = min(count, sizeof(buf) - 1);
3820 if (copy_from_user(buf, user_buf, len))
3821 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003822
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003823 buf[len] = '\0';
3824 sptr = buf;
3825
3826 token = strsep(&sptr, delim);
3827 if (!token)
3828 return -EINVAL;
3829 if (!sptr)
3830 return -EINVAL;
3831 cmd = token;
3832
3833 token = strsep(&sptr, delim);
3834 if (!token)
3835 return -EINVAL;
3836 if (kstrtou64(token, 0, &val))
3837 return -EINVAL;
3838
3839 if (strcmp(cmd, "test_mode") == 0) {
3840 switch (val) {
3841 case 0:
3842 ret = icnss_test_mode_fw_test_off(priv);
3843 break;
3844 case 1:
3845 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3846 break;
3847 case 2:
3848 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3849 break;
3850 case 3:
3851 ret = icnss_trigger_recovery(&priv->pdev->dev);
3852 break;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003853 case 4:
3854 icnss_allow_recursive_recovery(&priv->pdev->dev);
3855 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003856 default:
3857 return -EINVAL;
3858 }
3859 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3860 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3861 } else {
3862 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003863 }
3864
3865 if (ret)
3866 return ret;
3867
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003868 return count;
3869}
3870
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003871static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003872{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003873 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003874}
3875
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003876static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003877 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003878 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003879 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003880 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003881 .owner = THIS_MODULE,
3882 .llseek = seq_lseek,
3883};
3884
3885static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3886 size_t count, loff_t *off)
3887{
3888 struct icnss_priv *priv =
3889 ((struct seq_file *)fp->private_data)->private;
3890 int ret;
3891 u32 val;
3892
3893 ret = kstrtou32_from_user(buf, count, 0, &val);
3894 if (ret)
3895 return ret;
3896
3897 if (ret == 0)
3898 memset(&priv->stats, 0, sizeof(priv->stats));
3899
3900 return count;
3901}
3902
3903static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3904{
3905 enum icnss_driver_state i;
3906 int skip = 0;
3907 unsigned long state;
3908
3909 seq_printf(s, "\nState: 0x%lx(", priv->state);
3910 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3911
3912 if (!(state & 0x1))
3913 continue;
3914
3915 if (skip++)
3916 seq_puts(s, " | ");
3917
3918 switch (i) {
3919 case ICNSS_WLFW_QMI_CONNECTED:
3920 seq_puts(s, "QMI CONN");
3921 continue;
3922 case ICNSS_POWER_ON:
3923 seq_puts(s, "POWER ON");
3924 continue;
3925 case ICNSS_FW_READY:
3926 seq_puts(s, "FW READY");
3927 continue;
3928 case ICNSS_DRIVER_PROBED:
3929 seq_puts(s, "DRIVER PROBED");
3930 continue;
3931 case ICNSS_FW_TEST_MODE:
3932 seq_puts(s, "FW TEST MODE");
3933 continue;
3934 case ICNSS_PM_SUSPEND:
3935 seq_puts(s, "PM SUSPEND");
3936 continue;
3937 case ICNSS_PM_SUSPEND_NOIRQ:
3938 seq_puts(s, "PM SUSPEND NOIRQ");
3939 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003940 case ICNSS_SSR_REGISTERED:
3941 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003942 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003943 case ICNSS_PDR_REGISTERED:
3944 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003945 continue;
3946 case ICNSS_PD_RESTART:
3947 seq_puts(s, "PD RESTART");
3948 continue;
3949 case ICNSS_MSA0_ASSIGNED:
3950 seq_puts(s, "MSA0 ASSIGNED");
3951 continue;
3952 case ICNSS_WLFW_EXISTS:
3953 seq_puts(s, "WLAN FW EXISTS");
3954 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303955 case ICNSS_SHUTDOWN_DONE:
3956 seq_puts(s, "SHUTDOWN DONE");
3957 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003958 case ICNSS_HOST_TRIGGERED_PDR:
3959 seq_puts(s, "HOST TRIGGERED PDR");
3960 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003961 case ICNSS_FW_DOWN:
3962 seq_puts(s, "FW DOWN");
3963 continue;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08003964 case ICNSS_DRIVER_UNLOADING:
3965 seq_puts(s, "DRIVER UNLOADING");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003966 }
3967
3968 seq_printf(s, "UNKNOWN-%d", i);
3969 }
3970 seq_puts(s, ")\n");
3971
3972 return 0;
3973}
3974
3975static int icnss_stats_show_capability(struct seq_file *s,
3976 struct icnss_priv *priv)
3977{
3978 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3979 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3980 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3981 seq_printf(s, "Chip family: 0x%x\n",
3982 priv->chip_info.chip_family);
3983 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3984 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3985 seq_printf(s, "Firmware Version: 0x%x\n",
3986 priv->fw_version_info.fw_version);
3987 seq_printf(s, "Firmware Build Timestamp: %s\n",
3988 priv->fw_version_info.fw_build_timestamp);
3989 seq_printf(s, "Firmware Build ID: %s\n",
3990 priv->fw_build_id);
3991 }
3992
3993 return 0;
3994}
3995
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003996static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3997 struct icnss_priv *priv)
3998{
3999 if (priv->stats.rejuvenate_ind) {
4000 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
4001 seq_printf(s, "Number of Rejuvenations: %u\n",
4002 priv->stats.rejuvenate_ind);
4003 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
4004 priv->cause_for_rejuvenation);
4005 seq_printf(s, "Requesting Sub-System: 0x%x\n",
4006 priv->requesting_sub_system);
4007 seq_printf(s, "Line Number: %u\n",
4008 priv->line_number);
4009 seq_printf(s, "Function Name: %s\n",
4010 priv->function_name);
4011 }
4012
4013 return 0;
4014}
4015
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004016static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
4017{
4018 int i;
4019
4020 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
4021 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
4022 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
4023 seq_printf(s, "%24s %16u %16u\n",
4024 icnss_driver_event_to_str(i),
4025 priv->stats.events[i].posted,
4026 priv->stats.events[i].processed);
4027
4028 return 0;
4029}
4030
4031static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
4032{
4033 int i;
4034
4035 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
4036 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
4037 "Free", "Enable", "Disable");
4038 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
4039 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
4040 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
4041 priv->stats.ce_irqs[i].free,
4042 priv->stats.ce_irqs[i].enable,
4043 priv->stats.ce_irqs[i].disable);
4044
4045 return 0;
4046}
4047
4048static int icnss_stats_show(struct seq_file *s, void *data)
4049{
4050#define ICNSS_STATS_DUMP(_s, _priv, _x) \
4051 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
4052
4053 struct icnss_priv *priv = s->private;
4054
4055 ICNSS_STATS_DUMP(s, priv, ind_register_req);
4056 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
4057 ICNSS_STATS_DUMP(s, priv, ind_register_err);
4058 ICNSS_STATS_DUMP(s, priv, msa_info_req);
4059 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
4060 ICNSS_STATS_DUMP(s, priv, msa_info_err);
4061 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
4062 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
4063 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
4064 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
4065 ICNSS_STATS_DUMP(s, priv, cap_req);
4066 ICNSS_STATS_DUMP(s, priv, cap_resp);
4067 ICNSS_STATS_DUMP(s, priv, cap_err);
4068 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
4069 ICNSS_STATS_DUMP(s, priv, cfg_req);
4070 ICNSS_STATS_DUMP(s, priv, cfg_resp);
4071 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
4072 ICNSS_STATS_DUMP(s, priv, mode_req);
4073 ICNSS_STATS_DUMP(s, priv, mode_resp);
4074 ICNSS_STATS_DUMP(s, priv, mode_req_err);
4075 ICNSS_STATS_DUMP(s, priv, ini_req);
4076 ICNSS_STATS_DUMP(s, priv, ini_resp);
4077 ICNSS_STATS_DUMP(s, priv, ini_req_err);
4078 ICNSS_STATS_DUMP(s, priv, vbatt_req);
4079 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
4080 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004081 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004082 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
4083 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
4084 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004085 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
4086 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
4087 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
4088 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004089
4090 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
4091 ICNSS_STATS_DUMP(s, priv, pm_suspend);
4092 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
4093 ICNSS_STATS_DUMP(s, priv, pm_resume);
4094 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
4095 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
4096 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
4097 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
4098 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
4099 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
4100 ICNSS_STATS_DUMP(s, priv, pm_relax);
4101
4102 icnss_stats_show_irqs(s, priv);
4103
4104 icnss_stats_show_capability(s, priv);
4105
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004106 icnss_stats_show_rejuvenate_info(s, priv);
4107
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004108 icnss_stats_show_events(s, priv);
4109
4110 icnss_stats_show_state(s, priv);
4111
4112 return 0;
4113#undef ICNSS_STATS_DUMP
4114}
4115
4116static int icnss_stats_open(struct inode *inode, struct file *file)
4117{
4118 return single_open(file, icnss_stats_show, inode->i_private);
4119}
4120
4121static const struct file_operations icnss_stats_fops = {
4122 .read = seq_read,
4123 .write = icnss_stats_write,
4124 .release = single_release,
4125 .open = icnss_stats_open,
4126 .owner = THIS_MODULE,
4127 .llseek = seq_lseek,
4128};
4129
4130static int icnss_regwrite_show(struct seq_file *s, void *data)
4131{
4132 struct icnss_priv *priv = s->private;
4133
4134 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4135
4136 if (!test_bit(ICNSS_FW_READY, &priv->state))
4137 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4138
4139 return 0;
4140}
4141
4142static ssize_t icnss_regwrite_write(struct file *fp,
4143 const char __user *user_buf,
4144 size_t count, loff_t *off)
4145{
4146 struct icnss_priv *priv =
4147 ((struct seq_file *)fp->private_data)->private;
4148 char buf[64];
4149 char *sptr, *token;
4150 unsigned int len = 0;
4151 uint32_t reg_offset, mem_type, reg_val;
4152 const char *delim = " ";
4153 int ret = 0;
4154
4155 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4156 !test_bit(ICNSS_POWER_ON, &priv->state))
4157 return -EINVAL;
4158
4159 len = min(count, sizeof(buf) - 1);
4160 if (copy_from_user(buf, user_buf, len))
4161 return -EFAULT;
4162
4163 buf[len] = '\0';
4164 sptr = buf;
4165
4166 token = strsep(&sptr, delim);
4167 if (!token)
4168 return -EINVAL;
4169
4170 if (!sptr)
4171 return -EINVAL;
4172
4173 if (kstrtou32(token, 0, &mem_type))
4174 return -EINVAL;
4175
4176 token = strsep(&sptr, delim);
4177 if (!token)
4178 return -EINVAL;
4179
4180 if (!sptr)
4181 return -EINVAL;
4182
4183 if (kstrtou32(token, 0, &reg_offset))
4184 return -EINVAL;
4185
4186 token = strsep(&sptr, delim);
4187 if (!token)
4188 return -EINVAL;
4189
4190 if (kstrtou32(token, 0, &reg_val))
4191 return -EINVAL;
4192
4193 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4194 sizeof(uint32_t),
4195 (uint8_t *)&reg_val);
4196 if (ret)
4197 return ret;
4198
4199 return count;
4200}
4201
4202static int icnss_regwrite_open(struct inode *inode, struct file *file)
4203{
4204 return single_open(file, icnss_regwrite_show, inode->i_private);
4205}
4206
4207static const struct file_operations icnss_regwrite_fops = {
4208 .read = seq_read,
4209 .write = icnss_regwrite_write,
4210 .open = icnss_regwrite_open,
4211 .owner = THIS_MODULE,
4212 .llseek = seq_lseek,
4213};
4214
4215static int icnss_regread_show(struct seq_file *s, void *data)
4216{
4217 struct icnss_priv *priv = s->private;
4218
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304219 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004220 if (!priv->diag_reg_read_buf) {
4221 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4222
4223 if (!test_bit(ICNSS_FW_READY, &priv->state))
4224 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4225
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304226 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004227 return 0;
4228 }
4229
4230 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4231 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4232 priv->diag_reg_read_len);
4233
4234 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4235 priv->diag_reg_read_len, false);
4236
4237 priv->diag_reg_read_len = 0;
4238 kfree(priv->diag_reg_read_buf);
4239 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304240 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004241
4242 return 0;
4243}
4244
4245static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4246 size_t count, loff_t *off)
4247{
4248 struct icnss_priv *priv =
4249 ((struct seq_file *)fp->private_data)->private;
4250 char buf[64];
4251 char *sptr, *token;
4252 unsigned int len = 0;
4253 uint32_t reg_offset, mem_type;
4254 uint32_t data_len = 0;
4255 uint8_t *reg_buf = NULL;
4256 const char *delim = " ";
4257 int ret = 0;
4258
4259 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4260 !test_bit(ICNSS_POWER_ON, &priv->state))
4261 return -EINVAL;
4262
4263 len = min(count, sizeof(buf) - 1);
4264 if (copy_from_user(buf, user_buf, len))
4265 return -EFAULT;
4266
4267 buf[len] = '\0';
4268 sptr = buf;
4269
4270 token = strsep(&sptr, delim);
4271 if (!token)
4272 return -EINVAL;
4273
4274 if (!sptr)
4275 return -EINVAL;
4276
4277 if (kstrtou32(token, 0, &mem_type))
4278 return -EINVAL;
4279
4280 token = strsep(&sptr, delim);
4281 if (!token)
4282 return -EINVAL;
4283
4284 if (!sptr)
4285 return -EINVAL;
4286
4287 if (kstrtou32(token, 0, &reg_offset))
4288 return -EINVAL;
4289
4290 token = strsep(&sptr, delim);
4291 if (!token)
4292 return -EINVAL;
4293
4294 if (kstrtou32(token, 0, &data_len))
4295 return -EINVAL;
4296
4297 if (data_len == 0 ||
4298 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4299 return -EINVAL;
4300
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304301 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004302 kfree(priv->diag_reg_read_buf);
4303 priv->diag_reg_read_buf = NULL;
4304
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004305 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304306 if (!reg_buf) {
4307 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004308 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304309 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004310
4311 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4312 mem_type, data_len,
4313 reg_buf);
4314 if (ret) {
4315 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304316 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004317 return ret;
4318 }
4319
4320 priv->diag_reg_read_addr = reg_offset;
4321 priv->diag_reg_read_mem_type = mem_type;
4322 priv->diag_reg_read_len = data_len;
4323 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304324 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004325
4326 return count;
4327}
4328
4329static int icnss_regread_open(struct inode *inode, struct file *file)
4330{
4331 return single_open(file, icnss_regread_show, inode->i_private);
4332}
4333
4334static const struct file_operations icnss_regread_fops = {
4335 .read = seq_read,
4336 .write = icnss_regread_write,
4337 .open = icnss_regread_open,
4338 .owner = THIS_MODULE,
4339 .llseek = seq_lseek,
4340};
4341
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004342#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004343static int icnss_debugfs_create(struct icnss_priv *priv)
4344{
4345 int ret = 0;
4346 struct dentry *root_dentry;
4347
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004348 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004349
4350 if (IS_ERR(root_dentry)) {
4351 ret = PTR_ERR(root_dentry);
4352 icnss_pr_err("Unable to create debugfs %d\n", ret);
4353 goto out;
4354 }
4355
4356 priv->root_dentry = root_dentry;
4357
Yuanyuan Liu84132752017-05-24 12:07:12 -07004358 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004359 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004360
Yuanyuan Liu84132752017-05-24 12:07:12 -07004361 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004362 &icnss_stats_fops);
4363 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4364 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004365 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004366 &icnss_regwrite_fops);
4367
4368out:
4369 return ret;
4370}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004371#else
4372static int icnss_debugfs_create(struct icnss_priv *priv)
4373{
4374 int ret = 0;
4375 struct dentry *root_dentry;
4376
4377 root_dentry = debugfs_create_dir("icnss", NULL);
4378
4379 if (IS_ERR(root_dentry)) {
4380 ret = PTR_ERR(root_dentry);
4381 icnss_pr_err("Unable to create debugfs %d\n", ret);
4382 return ret;
4383 }
4384
4385 priv->root_dentry = root_dentry;
4386
4387 debugfs_create_file("stats", 0600, root_dentry, priv,
4388 &icnss_stats_fops);
4389 return 0;
4390}
4391#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004392
4393static void icnss_debugfs_destroy(struct icnss_priv *priv)
4394{
4395 debugfs_remove_recursive(priv->root_dentry);
4396}
4397
4398static int icnss_get_vbatt_info(struct icnss_priv *priv)
4399{
4400 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4401 struct qpnp_vadc_chip *vadc_dev = NULL;
4402 int ret = 0;
4403
4404 if (test_bit(VBATT_DISABLE, &quirks)) {
4405 icnss_pr_dbg("VBATT feature is disabled\n");
4406 return ret;
4407 }
4408
4409 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4410 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4411 icnss_pr_err("adc_tm_dev probe defer\n");
4412 return -EPROBE_DEFER;
4413 }
4414
4415 if (IS_ERR(adc_tm_dev)) {
4416 ret = PTR_ERR(adc_tm_dev);
4417 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4418 ret);
4419 return ret;
4420 }
4421
4422 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4423 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4424 icnss_pr_err("vadc_dev probe defer\n");
4425 return -EPROBE_DEFER;
4426 }
4427
4428 if (IS_ERR(vadc_dev)) {
4429 ret = PTR_ERR(vadc_dev);
4430 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4431 ret);
4432 return ret;
4433 }
4434
4435 priv->adc_tm_dev = adc_tm_dev;
4436 priv->vadc_dev = vadc_dev;
4437
4438 return 0;
4439}
4440
4441static int icnss_probe(struct platform_device *pdev)
4442{
4443 int ret = 0;
4444 struct resource *res;
4445 int i;
4446 struct device *dev = &pdev->dev;
4447 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304448 const __be32 *addrp;
4449 u64 prop_size = 0;
4450 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004451
4452 if (penv) {
4453 icnss_pr_err("Driver is already initialized\n");
4454 return -EEXIST;
4455 }
4456
4457 icnss_pr_dbg("Platform driver probe\n");
4458
4459 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4460 if (!priv)
4461 return -ENOMEM;
4462
4463 priv->magic = ICNSS_MAGIC;
4464 dev_set_drvdata(dev, priv);
4465
4466 priv->pdev = pdev;
4467
4468 ret = icnss_get_vbatt_info(priv);
4469 if (ret == -EPROBE_DEFER)
4470 goto out;
4471
Yuanyuan Liu68939762017-04-04 16:43:03 -07004472 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4473 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4474 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4475
4476 if (ret)
4477 goto out;
4478 }
4479
4480 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4481 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4482 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4483 if (ret)
4484 goto out;
4485 }
4486
4487 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4488 priv->bypass_s1_smmu = true;
4489
4490 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4491
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004492 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4493 if (!res) {
4494 icnss_pr_err("Memory base not found in DT\n");
4495 ret = -EINVAL;
4496 goto out;
4497 }
4498
4499 priv->mem_base_pa = res->start;
4500 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4501 resource_size(res));
4502 if (!priv->mem_base_va) {
4503 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4504 &priv->mem_base_pa);
4505 ret = -EINVAL;
4506 goto out;
4507 }
4508 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4509 priv->mem_base_va);
4510
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004511 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4512 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4513 if (!res) {
4514 icnss_pr_err("Fail to get IRQ-%d\n", i);
4515 ret = -ENODEV;
4516 goto out;
4517 } else {
4518 priv->ce_irqs[i] = res->start;
4519 }
4520 }
4521
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304522 np = of_parse_phandle(dev->of_node,
4523 "qcom,wlan-msa-fixed-region", 0);
4524 if (np) {
4525 addrp = of_get_address(np, 0, &prop_size, NULL);
4526 if (!addrp) {
4527 icnss_pr_err("Failed to get assigned-addresses or property\n");
4528 ret = -EINVAL;
4529 goto out;
4530 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004531
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304532 priv->msa_pa = of_translate_address(np, addrp);
4533 if (priv->msa_pa == OF_BAD_ADDR) {
4534 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4535 ret = -EINVAL;
4536 goto out;
4537 }
4538
4539 priv->msa_va = memremap(priv->msa_pa,
4540 (unsigned long)prop_size, MEMREMAP_WT);
4541 if (!priv->msa_va) {
4542 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4543 &priv->msa_pa);
4544 ret = -EINVAL;
4545 goto out;
4546 }
4547 priv->msa_mem_size = prop_size;
4548 } else {
4549 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4550 &priv->msa_mem_size);
4551 if (ret || priv->msa_mem_size == 0) {
4552 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4553 priv->msa_mem_size, ret);
4554 goto out;
4555 }
4556
4557 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4558 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4559
4560 if (!priv->msa_va) {
4561 icnss_pr_err("DMA alloc failed for MSA\n");
4562 ret = -ENOMEM;
4563 goto out;
4564 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004565 }
4566
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304567 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4568 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004569
4570 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4571 "smmu_iova_base");
4572 if (!res) {
4573 icnss_pr_err("SMMU IOVA base not found\n");
4574 } else {
4575 priv->smmu_iova_start = res->start;
4576 priv->smmu_iova_len = resource_size(res);
4577 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4578 &priv->smmu_iova_start, priv->smmu_iova_len);
4579
4580 res = platform_get_resource_byname(pdev,
4581 IORESOURCE_MEM,
4582 "smmu_iova_ipa");
4583 if (!res) {
4584 icnss_pr_err("SMMU IOVA IPA not found\n");
4585 } else {
4586 priv->smmu_iova_ipa_start = res->start;
4587 priv->smmu_iova_ipa_len = resource_size(res);
4588 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4589 &priv->smmu_iova_ipa_start,
4590 priv->smmu_iova_ipa_len);
4591 }
4592
4593 ret = icnss_smmu_init(priv);
4594 if (ret < 0) {
4595 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4596 ret, &priv->smmu_iova_start,
4597 priv->smmu_iova_len);
4598 goto out;
4599 }
4600 }
4601
4602 spin_lock_init(&priv->event_lock);
4603 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304604 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004605
4606 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4607 if (!priv->event_wq) {
4608 icnss_pr_err("Workqueue creation failed\n");
4609 ret = -EFAULT;
4610 goto out_smmu_deinit;
4611 }
4612
4613 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4614 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4615 INIT_LIST_HEAD(&priv->event_list);
4616
4617 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4618 WLFW_SERVICE_VERS_V01,
4619 WLFW_SERVICE_INS_ID_V01,
4620 &wlfw_clnt_nb);
4621 if (ret < 0) {
4622 icnss_pr_err("Notifier register failed: %d\n", ret);
4623 goto out_destroy_wq;
4624 }
4625
4626 icnss_enable_recovery(priv);
4627
4628 icnss_debugfs_create(priv);
4629
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004630 ret = device_init_wakeup(&priv->pdev->dev, true);
4631 if (ret)
4632 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4633 ret);
4634
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004635 penv = priv;
4636
4637 icnss_pr_info("Platform driver probed successfully\n");
4638
4639 return 0;
4640
4641out_destroy_wq:
4642 destroy_workqueue(priv->event_wq);
4643out_smmu_deinit:
4644 icnss_smmu_deinit(priv);
4645out:
4646 dev_set_drvdata(dev, NULL);
4647
4648 return ret;
4649}
4650
4651static int icnss_remove(struct platform_device *pdev)
4652{
4653 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4654
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004655 device_init_wakeup(&penv->pdev->dev, false);
4656
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004657 icnss_debugfs_destroy(penv);
4658
4659 icnss_modem_ssr_unregister_notifier(penv);
4660
4661 destroy_ramdump_device(penv->msa0_dump_dev);
4662
4663 icnss_pdr_unregister_notifier(penv);
4664
4665 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4666 WLFW_SERVICE_VERS_V01,
4667 WLFW_SERVICE_INS_ID_V01,
4668 &wlfw_clnt_nb);
4669 if (penv->event_wq)
4670 destroy_workqueue(penv->event_wq);
4671
4672 icnss_hw_power_off(penv);
4673
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304674 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4675 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004676
4677 dev_set_drvdata(&pdev->dev, NULL);
4678
4679 return 0;
4680}
4681
4682#ifdef CONFIG_PM_SLEEP
4683static int icnss_pm_suspend(struct device *dev)
4684{
4685 struct icnss_priv *priv = dev_get_drvdata(dev);
4686 int ret = 0;
4687
4688 if (priv->magic != ICNSS_MAGIC) {
4689 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4690 dev, priv, priv->magic);
4691 return -EINVAL;
4692 }
4693
Yuanyuan Liu68939762017-04-04 16:43:03 -07004694 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004695
4696 if (!priv->ops || !priv->ops->pm_suspend ||
4697 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4698 goto out;
4699
4700 ret = priv->ops->pm_suspend(dev);
4701
4702out:
4703 if (ret == 0) {
4704 priv->stats.pm_suspend++;
4705 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4706 } else {
4707 priv->stats.pm_suspend_err++;
4708 }
4709 return ret;
4710}
4711
4712static int icnss_pm_resume(struct device *dev)
4713{
4714 struct icnss_priv *priv = dev_get_drvdata(dev);
4715 int ret = 0;
4716
4717 if (priv->magic != ICNSS_MAGIC) {
4718 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4719 dev, priv, priv->magic);
4720 return -EINVAL;
4721 }
4722
Yuanyuan Liu68939762017-04-04 16:43:03 -07004723 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004724
4725 if (!priv->ops || !priv->ops->pm_resume ||
4726 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4727 goto out;
4728
4729 ret = priv->ops->pm_resume(dev);
4730
4731out:
4732 if (ret == 0) {
4733 priv->stats.pm_resume++;
4734 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4735 } else {
4736 priv->stats.pm_resume_err++;
4737 }
4738 return ret;
4739}
4740
4741static int icnss_pm_suspend_noirq(struct device *dev)
4742{
4743 struct icnss_priv *priv = dev_get_drvdata(dev);
4744 int ret = 0;
4745
4746 if (priv->magic != ICNSS_MAGIC) {
4747 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4748 dev, priv, priv->magic);
4749 return -EINVAL;
4750 }
4751
Yuanyuan Liu68939762017-04-04 16:43:03 -07004752 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004753
4754 if (!priv->ops || !priv->ops->suspend_noirq ||
4755 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4756 goto out;
4757
4758 ret = priv->ops->suspend_noirq(dev);
4759
4760out:
4761 if (ret == 0) {
4762 priv->stats.pm_suspend_noirq++;
4763 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4764 } else {
4765 priv->stats.pm_suspend_noirq_err++;
4766 }
4767 return ret;
4768}
4769
4770static int icnss_pm_resume_noirq(struct device *dev)
4771{
4772 struct icnss_priv *priv = dev_get_drvdata(dev);
4773 int ret = 0;
4774
4775 if (priv->magic != ICNSS_MAGIC) {
4776 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4777 dev, priv, priv->magic);
4778 return -EINVAL;
4779 }
4780
Yuanyuan Liu68939762017-04-04 16:43:03 -07004781 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004782
4783 if (!priv->ops || !priv->ops->resume_noirq ||
4784 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4785 goto out;
4786
4787 ret = priv->ops->resume_noirq(dev);
4788
4789out:
4790 if (ret == 0) {
4791 priv->stats.pm_resume_noirq++;
4792 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4793 } else {
4794 priv->stats.pm_resume_noirq_err++;
4795 }
4796 return ret;
4797}
4798#endif
4799
4800static const struct dev_pm_ops icnss_pm_ops = {
4801 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4802 icnss_pm_resume)
4803 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4804 icnss_pm_resume_noirq)
4805};
4806
4807static const struct of_device_id icnss_dt_match[] = {
4808 {.compatible = "qcom,icnss"},
4809 {}
4810};
4811
4812MODULE_DEVICE_TABLE(of, icnss_dt_match);
4813
4814static struct platform_driver icnss_driver = {
4815 .probe = icnss_probe,
4816 .remove = icnss_remove,
4817 .driver = {
4818 .name = "icnss",
4819 .pm = &icnss_pm_ops,
4820 .owner = THIS_MODULE,
4821 .of_match_table = icnss_dt_match,
4822 },
4823};
4824
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004825static int __init icnss_initialize(void)
4826{
4827 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4828 "icnss", 0);
4829 if (!icnss_ipc_log_context)
4830 icnss_pr_err("Unable to create log context\n");
4831
Yuanyuan Liu68939762017-04-04 16:43:03 -07004832 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4833 "icnss_long", 0);
4834 if (!icnss_ipc_log_long_context)
4835 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004836
4837 return platform_driver_register(&icnss_driver);
4838}
4839
4840static void __exit icnss_exit(void)
4841{
4842 platform_driver_unregister(&icnss_driver);
4843 ipc_log_context_destroy(icnss_ipc_log_context);
4844 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004845 ipc_log_context_destroy(icnss_ipc_log_long_context);
4846 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004847}
4848
4849
4850module_init(icnss_initialize);
4851module_exit(icnss_exit);
4852
4853MODULE_LICENSE("GPL v2");
4854MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");