blob: 683b074a862e45b91d59c74cee34d5424826b05e [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;
Anurag Chouhan306ba302018-04-17 11:30:04 +0530474 uint32_t fw_error_fatal_irq;
475 uint32_t fw_early_crash_irq;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800476} *penv;
477
Yuanyuan Liu68939762017-04-04 16:43:03 -0700478#ifdef CONFIG_ICNSS_DEBUG
479static void icnss_ignore_qmi_timeout(bool ignore)
480{
481 ignore_qmi_timeout = ignore;
482}
483#else
484static void icnss_ignore_qmi_timeout(bool ignore) { }
485#endif
486
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530487static int icnss_assign_msa_perm(struct icnss_mem_region_info
488 *mem_region, enum icnss_msa_perm new_perm)
489{
490 int ret = 0;
491 phys_addr_t addr;
492 u32 size;
493 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530494 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530495 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530496 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
497 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530498 u32 dest_nelems;
499 enum icnss_msa_perm cur_perm = mem_region->perm;
500 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
501
502 addr = mem_region->reg_addr;
503 size = mem_region->size;
504
505 if (mem_region->secure_flag) {
506 new_perm_list = &msa_perm_secure_list[new_perm];
507 old_perm_list = &msa_perm_secure_list[cur_perm];
508 } else {
509 new_perm_list = &msa_perm_list[new_perm];
510 old_perm_list = &msa_perm_list[cur_perm];
511 }
512
513 source_nelems = old_perm_list->nelems;
514 dest_nelems = new_perm_list->nelems;
515
516 for (i = 0; i < source_nelems; ++i)
517 source_vmids[i] = old_perm_list->vmids[i];
518
519 for (i = 0; i < dest_nelems; ++i) {
520 dest_vmids[i] = new_perm_list->vmids[i];
521 dest_perms[i] = new_perm_list->perms[i];
522 }
523
524 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
525 dest_vmids, dest_perms, dest_nelems);
526 if (ret) {
527 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
528 &addr, size, ret);
529 goto out;
530 }
531
532 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
533 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
534 source_nelems, source_vmids[0], source_vmids[1],
535 source_vmids[2], source_vmids[3], dest_nelems,
536 dest_vmids[0], dest_vmids[1], dest_vmids[2],
537 dest_vmids[3]);
538out:
539 return ret;
540}
541
542static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
543 enum icnss_msa_perm new_perm)
544{
545 int ret;
546 int i;
547 enum icnss_msa_perm old_perm;
548
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700549 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
550 icnss_pr_err("Invalid memory region len %d\n",
551 priv->nr_mem_region);
552 return -EINVAL;
553 }
554
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530555 for (i = 0; i < priv->nr_mem_region; i++) {
556 old_perm = priv->mem_region[i].perm;
557 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
558 if (ret)
559 goto err_unmap;
560 priv->mem_region[i].perm = new_perm;
561 }
562 return 0;
563
564err_unmap:
565 for (i--; i >= 0; i--) {
566 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
567 }
568 return ret;
569}
570
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800571static void icnss_pm_stay_awake(struct icnss_priv *priv)
572{
573 if (atomic_inc_return(&priv->pm_count) != 1)
574 return;
575
Yuanyuan Liu68939762017-04-04 16:43:03 -0700576 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800577 atomic_read(&priv->pm_count));
578
579 pm_stay_awake(&priv->pdev->dev);
580
581 priv->stats.pm_stay_awake++;
582}
583
584static void icnss_pm_relax(struct icnss_priv *priv)
585{
586 int r = atomic_dec_return(&priv->pm_count);
587
588 WARN_ON(r < 0);
589
590 if (r != 0)
591 return;
592
Yuanyuan Liu68939762017-04-04 16:43:03 -0700593 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800594 atomic_read(&priv->pm_count));
595
596 pm_relax(&priv->pdev->dev);
597 priv->stats.pm_relax++;
598}
599
600static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
601{
602 switch (type) {
603 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
604 return "SERVER_ARRIVE";
605 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
606 return "SERVER_EXIT";
607 case ICNSS_DRIVER_EVENT_FW_READY_IND:
608 return "FW_READY";
609 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
610 return "REGISTER_DRIVER";
611 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
612 return "UNREGISTER_DRIVER";
613 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
614 return "PD_SERVICE_DOWN";
Sameer Thalappil73eba352018-02-06 14:54:41 -0800615 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
616 return "FW_EARLY_CRASH_IND";
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800617 case ICNSS_DRIVER_EVENT_MAX:
618 return "EVENT_MAX";
619 }
620
621 return "UNKNOWN";
622};
623
624static int icnss_driver_event_post(enum icnss_driver_event_type type,
625 u32 flags, void *data)
626{
627 struct icnss_driver_event *event;
628 unsigned long irq_flags;
629 int gfp = GFP_KERNEL;
630 int ret = 0;
631
632 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
633 icnss_driver_event_to_str(type), type, current->comm,
634 flags, penv->state);
635
636 if (type >= ICNSS_DRIVER_EVENT_MAX) {
637 icnss_pr_err("Invalid Event type: %d, can't post", type);
638 return -EINVAL;
639 }
640
641 if (in_interrupt() || irqs_disabled())
642 gfp = GFP_ATOMIC;
643
644 event = kzalloc(sizeof(*event), gfp);
645 if (event == NULL)
646 return -ENOMEM;
647
648 icnss_pm_stay_awake(penv);
649
650 event->type = type;
651 event->data = data;
652 init_completion(&event->complete);
653 event->ret = ICNSS_EVENT_PENDING;
654 event->sync = !!(flags & ICNSS_EVENT_SYNC);
655
656 spin_lock_irqsave(&penv->event_lock, irq_flags);
657 list_add_tail(&event->list, &penv->event_list);
658 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
659
660 penv->stats.events[type].posted++;
661 queue_work(penv->event_wq, &penv->event_work);
662
663 if (!(flags & ICNSS_EVENT_SYNC))
664 goto out;
665
666 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
667 wait_for_completion(&event->complete);
668 else
669 ret = wait_for_completion_interruptible(&event->complete);
670
671 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
672 icnss_driver_event_to_str(type), type, penv->state, ret,
673 event->ret);
674
675 spin_lock_irqsave(&penv->event_lock, irq_flags);
676 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
677 event->sync = false;
678 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
679 ret = -EINTR;
680 goto out;
681 }
682 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
683
684 ret = event->ret;
685 kfree(event);
686
687out:
688 icnss_pm_relax(penv);
689 return ret;
690}
691
692static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
693 uint64_t voltage_uv)
694{
695 int ret;
696 struct wlfw_vbatt_req_msg_v01 req;
697 struct wlfw_vbatt_resp_msg_v01 resp;
698 struct msg_desc req_desc, resp_desc;
699
700 if (!priv->wlfw_clnt) {
701 ret = -ENODEV;
702 goto out;
703 }
704
705 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
706 penv->state);
707
708 memset(&req, 0, sizeof(req));
709 memset(&resp, 0, sizeof(resp));
710
711 req.voltage_uv = voltage_uv;
712
713 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
714 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
715 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
716
717 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
718 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
719 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
720
721 priv->stats.vbatt_req++;
722
723 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
724 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
725 if (ret < 0) {
726 icnss_pr_err("Send vbatt req failed %d\n", ret);
727 goto out;
728 }
729
730 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
731 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
732 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530733 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800734 goto out;
735 }
736 priv->stats.vbatt_resp++;
737
738out:
739 priv->stats.vbatt_req_err++;
740 return ret;
741}
742
743static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
744{
745 int ret = 0;
746 struct qpnp_vadc_result adc_result;
747
748 if (!priv->vadc_dev) {
749 icnss_pr_err("VADC dev doesn't exists\n");
750 ret = -EINVAL;
751 goto out;
752 }
753
754 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
755 if (ret) {
756 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
757 VADC_VPH_PWR, ret);
758 goto out;
759 }
760
761 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
762 adc_result.physical, adc_result.measurement);
763
764 *result_uv = adc_result.physical;
765out:
766 return ret;
767}
768
769static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
770{
771 struct icnss_priv *priv = ctx;
772 uint64_t vph_pwr = 0;
773 uint64_t vph_pwr_prev;
774 int ret = 0;
775 bool update = true;
776
777 if (!priv) {
778 icnss_pr_err("Priv pointer is NULL\n");
779 return;
780 }
781
782 vph_pwr_prev = priv->vph_pwr;
783
784 ret = icnss_get_phone_power(priv, &vph_pwr);
785 if (ret)
786 return;
787
788 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
789 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
790 update = false;
791 priv->vph_monitor_params.state_request =
792 ADC_TM_HIGH_THR_ENABLE;
793 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
794 ICNSS_THRESHOLD_GUARD;
795 priv->vph_monitor_params.low_thr = 0;
796 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
797 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
798 update = false;
799 priv->vph_monitor_params.state_request =
800 ADC_TM_LOW_THR_ENABLE;
801 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
802 ICNSS_THRESHOLD_GUARD;
803 priv->vph_monitor_params.high_thr = 0;
804 } else {
805 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
806 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
807 update = false;
808 priv->vph_monitor_params.state_request =
809 ADC_TM_HIGH_LOW_THR_ENABLE;
810 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
811 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
812 }
813
814 priv->vph_pwr = vph_pwr;
815
816 if (update)
817 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
818
819 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
820 priv->vph_monitor_params.low_thr,
821 priv->vph_monitor_params.high_thr);
822 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
823 &priv->vph_monitor_params);
824 if (ret)
825 icnss_pr_err("TM channel setup failed %d\n", ret);
826}
827
828static int icnss_setup_vph_monitor(struct icnss_priv *priv)
829{
830 int ret = 0;
831
832 if (!priv->adc_tm_dev) {
833 icnss_pr_err("ADC TM handler is NULL\n");
834 ret = -EINVAL;
835 goto out;
836 }
837
838 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
839 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
840 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
841 priv->vph_monitor_params.channel = VADC_VPH_PWR;
842 priv->vph_monitor_params.btm_ctx = priv;
843 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
844 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
845 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
846 priv->vph_monitor_params.low_thr,
847 priv->vph_monitor_params.high_thr);
848
849 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
850 &priv->vph_monitor_params);
851 if (ret)
852 icnss_pr_err("TM channel setup failed %d\n", ret);
853out:
854 return ret;
855}
856
857static int icnss_init_vph_monitor(struct icnss_priv *priv)
858{
859 int ret = 0;
860
861 if (test_bit(VBATT_DISABLE, &quirks))
862 goto out;
863
864 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
865 if (ret)
866 goto out;
867
868 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
869
870 ret = icnss_setup_vph_monitor(priv);
871 if (ret)
872 goto out;
873out:
874 return ret;
875}
876
877
878static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
879{
880 struct msg_desc ind_desc;
881 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
882 int ret = 0;
883
884 if (!penv || !penv->wlfw_clnt) {
885 ret = -ENODEV;
886 goto out;
887 }
888
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530889 memset(&ind_msg, 0, sizeof(ind_msg));
890
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800891 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
892 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
893 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
894
895 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
896 if (ret < 0) {
897 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
898 ret, msg_len);
899 goto out;
900 }
901
902 /* store pin result locally */
903 if (ind_msg.pwr_pin_result_valid)
904 penv->pwr_pin_result = ind_msg.pwr_pin_result;
905 if (ind_msg.phy_io_pin_result_valid)
906 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
907 if (ind_msg.rf_pin_result_valid)
908 penv->rf_pin_result = ind_msg.rf_pin_result;
909
910 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
911 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
912 ind_msg.rf_pin_result);
913
914 penv->stats.pin_connect_result++;
915out:
916 return ret;
917}
918
Yuanyuan Liu68939762017-04-04 16:43:03 -0700919static int icnss_vreg_on(struct icnss_priv *priv)
920{
921 int ret = 0;
922 struct icnss_vreg_info *vreg_info;
923 int i;
924
925 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
926 vreg_info = &priv->vreg_info[i];
927
928 if (!vreg_info->reg)
929 continue;
930
931 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
932
933 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
934 vreg_info->max_v);
935 if (ret) {
936 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
937 vreg_info->name, vreg_info->min_v,
938 vreg_info->max_v, ret);
939 break;
940 }
941
942 if (vreg_info->load_ua) {
943 ret = regulator_set_load(vreg_info->reg,
944 vreg_info->load_ua);
945 if (ret < 0) {
946 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
947 vreg_info->name,
948 vreg_info->load_ua, ret);
949 break;
950 }
951 }
952
953 ret = regulator_enable(vreg_info->reg);
954 if (ret) {
955 icnss_pr_err("Regulator %s, can't enable: %d\n",
956 vreg_info->name, ret);
957 break;
958 }
959
960 if (vreg_info->settle_delay)
961 udelay(vreg_info->settle_delay);
962 }
963
964 if (!ret)
965 return 0;
966
967 for (; i >= 0; i--) {
968 vreg_info = &priv->vreg_info[i];
969
970 if (!vreg_info->reg)
971 continue;
972
973 regulator_disable(vreg_info->reg);
974 regulator_set_load(vreg_info->reg, 0);
975 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
976 }
977
978 return ret;
979}
980
981static int icnss_vreg_off(struct icnss_priv *priv)
982{
983 int ret = 0;
984 struct icnss_vreg_info *vreg_info;
985 int i;
986
987 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
988 vreg_info = &priv->vreg_info[i];
989
990 if (!vreg_info->reg)
991 continue;
992
993 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
994
995 ret = regulator_disable(vreg_info->reg);
996 if (ret)
997 icnss_pr_err("Regulator %s, can't disable: %d\n",
998 vreg_info->name, ret);
999
1000 ret = regulator_set_load(vreg_info->reg, 0);
1001 if (ret < 0)
1002 icnss_pr_err("Regulator %s, can't set load: %d\n",
1003 vreg_info->name, ret);
1004
1005 ret = regulator_set_voltage(vreg_info->reg, 0,
1006 vreg_info->max_v);
1007 if (ret)
1008 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1009 vreg_info->name, ret);
1010 }
1011
1012 return ret;
1013}
1014
1015static int icnss_clk_init(struct icnss_priv *priv)
1016{
1017 struct icnss_clk_info *clk_info;
1018 int i;
1019 int ret = 0;
1020
1021 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1022 clk_info = &priv->clk_info[i];
1023
1024 if (!clk_info->handle)
1025 continue;
1026
1027 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1028
1029 if (clk_info->freq) {
1030 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1031
1032 if (ret) {
1033 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1034 clk_info->name, clk_info->freq,
1035 ret);
1036 break;
1037 }
1038 }
1039
1040 ret = clk_prepare_enable(clk_info->handle);
1041 if (ret) {
1042 icnss_pr_err("Clock %s, can't enable: %d\n",
1043 clk_info->name, ret);
1044 break;
1045 }
1046 }
1047
1048 if (ret == 0)
1049 return 0;
1050
1051 for (; i >= 0; i--) {
1052 clk_info = &priv->clk_info[i];
1053
1054 if (!clk_info->handle)
1055 continue;
1056
1057 clk_disable_unprepare(clk_info->handle);
1058 }
1059
1060 return ret;
1061}
1062
1063static int icnss_clk_deinit(struct icnss_priv *priv)
1064{
1065 struct icnss_clk_info *clk_info;
1066 int i;
1067
1068 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1069 clk_info = &priv->clk_info[i];
1070
1071 if (!clk_info->handle)
1072 continue;
1073
1074 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1075
1076 clk_disable_unprepare(clk_info->handle);
1077 }
1078
1079 return 0;
1080}
1081
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001082static int icnss_hw_power_on(struct icnss_priv *priv)
1083{
1084 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001085
1086 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1087
Yuanyuan Liu68939762017-04-04 16:43:03 -07001088 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001089 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001090 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001091 return ret;
1092 }
1093 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001094 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001095
Yuanyuan Liu68939762017-04-04 16:43:03 -07001096 ret = icnss_vreg_on(priv);
1097 if (ret)
1098 goto out;
1099
1100 ret = icnss_clk_init(priv);
1101 if (ret)
1102 goto vreg_off;
1103
1104 return ret;
1105
1106vreg_off:
1107 icnss_vreg_off(priv);
1108out:
1109 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001110 return ret;
1111}
1112
1113static int icnss_hw_power_off(struct icnss_priv *priv)
1114{
1115 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001116
1117 if (test_bit(HW_ALWAYS_ON, &quirks))
1118 return 0;
1119
Yuanyuan Liu16c89c32018-01-10 15:52:06 -08001120 if (test_bit(ICNSS_FW_DOWN, &priv->state))
1121 return 0;
1122
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001123 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1124
Yuanyuan Liu68939762017-04-04 16:43:03 -07001125 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001126 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001127 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001128 return ret;
1129 }
1130 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001131 spin_unlock(&priv->on_off_lock);
1132
1133 icnss_clk_deinit(priv);
1134
1135 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001136
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001137 return ret;
1138}
1139
1140int icnss_power_on(struct device *dev)
1141{
1142 struct icnss_priv *priv = dev_get_drvdata(dev);
1143
1144 if (!priv) {
1145 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1146 dev, priv);
1147 return -EINVAL;
1148 }
1149
1150 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1151
1152 return icnss_hw_power_on(priv);
1153}
1154EXPORT_SYMBOL(icnss_power_on);
1155
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001156bool icnss_is_fw_ready(void)
1157{
1158 if (!penv)
1159 return false;
1160 else
1161 return test_bit(ICNSS_FW_READY, &penv->state);
1162}
1163EXPORT_SYMBOL(icnss_is_fw_ready);
1164
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001165bool icnss_is_fw_down(void)
1166{
1167 if (!penv)
1168 return false;
Sameer Thalappil28c07752018-04-04 18:20:16 -07001169
1170 return test_bit(ICNSS_FW_DOWN, &penv->state) ||
1171 test_bit(ICNSS_PD_RESTART, &penv->state);
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001172}
1173EXPORT_SYMBOL(icnss_is_fw_down);
1174
1175
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001176int icnss_power_off(struct device *dev)
1177{
1178 struct icnss_priv *priv = dev_get_drvdata(dev);
1179
1180 if (!priv) {
1181 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1182 dev, priv);
1183 return -EINVAL;
1184 }
1185
1186 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1187
1188 return icnss_hw_power_off(priv);
1189}
1190EXPORT_SYMBOL(icnss_power_off);
1191
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001192static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
1193{
1194 struct icnss_priv *priv = ctx;
1195
1196 if (priv)
1197 priv->force_err_fatal = true;
1198
1199 icnss_pr_err("Received force error fatal request from FW\n");
1200
1201 return IRQ_HANDLED;
1202}
1203
Sameer Thalappil73eba352018-02-06 14:54:41 -08001204static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
1205{
1206 struct icnss_priv *priv = ctx;
1207
1208 icnss_pr_err("Received early crash indication from FW\n");
1209
1210 if (priv) {
1211 set_bit(ICNSS_FW_DOWN, &priv->state);
1212 icnss_ignore_qmi_timeout(true);
1213 }
1214
1215 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
1216 0, NULL);
1217
1218 return IRQ_HANDLED;
1219}
1220
Anurag Chouhan306ba302018-04-17 11:30:04 +05301221static void register_fw_error_notifications(struct device *dev)
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001222{
Anurag Chouhan306ba302018-04-17 11:30:04 +05301223 struct icnss_priv *priv = dev_get_drvdata(dev);
1224 int gpio = 0, irq = 0, ret = 0;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001225
Anurag Chouhan306ba302018-04-17 11:30:04 +05301226 if (!priv)
1227 return;
1228
1229 if (!of_find_property(dev->of_node, "qcom,gpio-force-fatal-error",
1230 NULL)) {
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001231 icnss_pr_dbg("Error fatal smp2p handler not registered\n");
1232 return;
1233 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301234 gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-force-fatal-error",
1235 0);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001236 if (!gpio_is_valid(gpio)) {
1237 icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
1238 return;
1239 }
1240 irq = gpio_to_irq(gpio);
1241 if (irq < 0) {
1242 icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
1243 return;
1244 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301245 ret = devm_request_irq(dev, irq, fw_error_fatal_handler,
1246 IRQF_TRIGGER_RISING, "wlanfw-err", priv);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001247 if (ret < 0) {
Sameer Thalappil73eba352018-02-06 14:54:41 -08001248 icnss_pr_err("Unable to register for error fatal IRQ handler %d",
Anurag Chouhan306ba302018-04-17 11:30:04 +05301249 irq);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001250 return;
1251 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301252 icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq);
1253 priv->fw_error_fatal_irq = irq;
1254}
Sameer Thalappil73eba352018-02-06 14:54:41 -08001255
Anurag Chouhan306ba302018-04-17 11:30:04 +05301256static void register_early_crash_notifications(struct device *dev)
1257{
1258 struct icnss_priv *priv = dev_get_drvdata(dev);
1259 int gpio = 0, irq = 0, ret = 0;
1260
1261 if (!priv)
1262 return;
1263
1264 if (!of_find_property(dev->of_node, "qcom,gpio-early-crash-ind",
1265 NULL)) {
Sameer Thalappil73eba352018-02-06 14:54:41 -08001266 icnss_pr_dbg("FW early crash indication handler not registered\n");
1267 return;
1268 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301269 gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-early-crash-ind", 0);
Sameer Thalappil73eba352018-02-06 14:54:41 -08001270 if (!gpio_is_valid(gpio)) {
1271 icnss_pr_err("Invalid GPIO for early crash indication %d\n",
1272 gpio);
1273 return;
1274 }
1275 irq = gpio_to_irq(gpio);
1276 if (irq < 0) {
1277 icnss_pr_err("Invalid IRQ for early crash indication %u\n",
1278 irq);
1279 return;
1280 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301281 ret = devm_request_irq(dev, irq, fw_crash_indication_handler,
1282 IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind",
1283 priv);
Sameer Thalappil73eba352018-02-06 14:54:41 -08001284 if (ret < 0) {
1285 icnss_pr_err("Unable to register for early crash indication IRQ handler %d",
1286 irq);
1287 return;
1288 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301289 icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq);
1290 priv->fw_early_crash_irq = irq;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001291}
1292
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001293static int wlfw_msa_mem_info_send_sync_msg(void)
1294{
1295 int ret;
1296 int i;
1297 struct wlfw_msa_info_req_msg_v01 req;
1298 struct wlfw_msa_info_resp_msg_v01 resp;
1299 struct msg_desc req_desc, resp_desc;
1300
1301 if (!penv || !penv->wlfw_clnt)
1302 return -ENODEV;
1303
1304 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1305
1306 memset(&req, 0, sizeof(req));
1307 memset(&resp, 0, sizeof(resp));
1308
1309 req.msa_addr = penv->msa_pa;
1310 req.size = penv->msa_mem_size;
1311
1312 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1313 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1314 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1315
1316 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1317 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1318 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1319
1320 penv->stats.msa_info_req++;
1321
1322 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1323 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1324 if (ret < 0) {
1325 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1326 goto out;
1327 }
1328
1329 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1330 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1331 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301332 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001333 goto out;
1334 }
1335
1336 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1337 resp.mem_region_info_len);
1338
Yuanyuan Liu68939762017-04-04 16:43:03 -07001339 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001340 icnss_pr_err("Invalid memory region length received: %d\n",
1341 resp.mem_region_info_len);
1342 ret = -EINVAL;
1343 goto out;
1344 }
1345
1346 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001347 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001348 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001349 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001350 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001351 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001352 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001353 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001354 resp.mem_region_info[i].secure_flag;
1355 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001356 i, penv->mem_region[i].reg_addr,
1357 penv->mem_region[i].size,
1358 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001359 }
1360
1361 return 0;
1362
1363out:
1364 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001365 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001366 return ret;
1367}
1368
1369static int wlfw_msa_ready_send_sync_msg(void)
1370{
1371 int ret;
1372 struct wlfw_msa_ready_req_msg_v01 req;
1373 struct wlfw_msa_ready_resp_msg_v01 resp;
1374 struct msg_desc req_desc, resp_desc;
1375
1376 if (!penv || !penv->wlfw_clnt)
1377 return -ENODEV;
1378
1379 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1380 penv->state);
1381
1382 memset(&req, 0, sizeof(req));
1383 memset(&resp, 0, sizeof(resp));
1384
1385 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1386 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1387 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1388
1389 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1390 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1391 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1392
1393 penv->stats.msa_ready_req++;
1394 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1395 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1396 if (ret < 0) {
1397 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1398 goto out;
1399 }
1400
1401 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1402 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1403 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301404 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001405 goto out;
1406 }
1407 penv->stats.msa_ready_resp++;
1408
1409 return 0;
1410
1411out:
1412 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001413 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001414 return ret;
1415}
1416
1417static int wlfw_ind_register_send_sync_msg(void)
1418{
1419 int ret;
1420 struct wlfw_ind_register_req_msg_v01 req;
1421 struct wlfw_ind_register_resp_msg_v01 resp;
1422 struct msg_desc req_desc, resp_desc;
1423
1424 if (!penv || !penv->wlfw_clnt)
1425 return -ENODEV;
1426
1427 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1428 penv->state);
1429
1430 memset(&req, 0, sizeof(req));
1431 memset(&resp, 0, sizeof(resp));
1432
1433 req.client_id_valid = 1;
1434 req.client_id = WLFW_CLIENT_ID;
1435 req.fw_ready_enable_valid = 1;
1436 req.fw_ready_enable = 1;
1437 req.msa_ready_enable_valid = 1;
1438 req.msa_ready_enable = 1;
1439 req.pin_connect_result_enable_valid = 1;
1440 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001441 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1442 req.rejuvenate_enable_valid = 1;
1443 req.rejuvenate_enable = 1;
1444 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001445
1446 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1447 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1448 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1449
1450 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1451 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1452 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1453
1454 penv->stats.ind_register_req++;
1455
1456 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1457 &resp_desc, &resp, sizeof(resp),
1458 WLFW_TIMEOUT_MS);
1459 if (ret < 0) {
1460 icnss_pr_err("Send indication register req failed %d\n", ret);
1461 goto out;
1462 }
1463
1464 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1465 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1466 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301467 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001468 goto out;
1469 }
1470 penv->stats.ind_register_resp++;
1471
1472 return 0;
1473
1474out:
1475 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001476 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001477 return ret;
1478}
1479
1480static int wlfw_cap_send_sync_msg(void)
1481{
1482 int ret;
1483 struct wlfw_cap_req_msg_v01 req;
1484 struct wlfw_cap_resp_msg_v01 resp;
1485 struct msg_desc req_desc, resp_desc;
1486
1487 if (!penv || !penv->wlfw_clnt)
1488 return -ENODEV;
1489
1490 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1491
1492 memset(&resp, 0, sizeof(resp));
1493
1494 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1495 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1496 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1497
1498 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1499 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1500 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1501
1502 penv->stats.cap_req++;
1503 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1504 &resp_desc, &resp, sizeof(resp),
1505 WLFW_TIMEOUT_MS);
1506 if (ret < 0) {
1507 icnss_pr_err("Send capability req failed %d\n", ret);
1508 goto out;
1509 }
1510
1511 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1512 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1513 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301514 ret = -resp.resp.result;
1515 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1516 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001517 goto out;
1518 }
1519
1520 penv->stats.cap_resp++;
1521 /* store cap locally */
1522 if (resp.chip_info_valid)
1523 penv->chip_info = resp.chip_info;
1524 if (resp.board_info_valid)
1525 penv->board_info = resp.board_info;
1526 else
1527 penv->board_info.board_id = 0xFF;
1528 if (resp.soc_info_valid)
1529 penv->soc_info = resp.soc_info;
1530 if (resp.fw_version_info_valid)
1531 penv->fw_version_info = resp.fw_version_info;
1532 if (resp.fw_build_id_valid)
1533 strlcpy(penv->fw_build_id, resp.fw_build_id,
1534 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1535
1536 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",
1537 penv->chip_info.chip_id, penv->chip_info.chip_family,
1538 penv->board_info.board_id, penv->soc_info.soc_id,
1539 penv->fw_version_info.fw_version,
1540 penv->fw_version_info.fw_build_timestamp,
1541 penv->fw_build_id);
1542
1543 return 0;
1544
1545out:
1546 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001547 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001548 return ret;
1549}
1550
1551static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1552{
1553 int ret;
1554 struct wlfw_wlan_mode_req_msg_v01 req;
1555 struct wlfw_wlan_mode_resp_msg_v01 resp;
1556 struct msg_desc req_desc, resp_desc;
1557
1558 if (!penv || !penv->wlfw_clnt)
1559 return -ENODEV;
1560
1561 /* During recovery do not send mode request for WLAN OFF as
1562 * FW not able to process it.
1563 */
1564 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1565 mode == QMI_WLFW_OFF_V01)
1566 return 0;
1567
1568 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1569 penv->state, mode);
1570
1571 memset(&req, 0, sizeof(req));
1572 memset(&resp, 0, sizeof(resp));
1573
1574 req.mode = mode;
1575 req.hw_debug_valid = 1;
1576 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1577
1578 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1579 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1580 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1581
1582 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1583 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1584 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1585
1586 penv->stats.mode_req++;
1587 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1588 &resp_desc, &resp, sizeof(resp),
1589 WLFW_TIMEOUT_MS);
1590 if (ret < 0) {
1591 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1592 mode, ret);
1593 goto out;
1594 }
1595
1596 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1597 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1598 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301599 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001600 goto out;
1601 }
1602 penv->stats.mode_resp++;
1603
1604 return 0;
1605
1606out:
1607 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001608 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001609 return ret;
1610}
1611
1612static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1613{
1614 int ret;
1615 struct wlfw_wlan_cfg_req_msg_v01 req;
1616 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1617 struct msg_desc req_desc, resp_desc;
1618
1619 if (!penv || !penv->wlfw_clnt)
1620 return -ENODEV;
1621
1622 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1623
1624 memset(&req, 0, sizeof(req));
1625 memset(&resp, 0, sizeof(resp));
1626
1627 memcpy(&req, data, sizeof(req));
1628
1629 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1630 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1631 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1632
1633 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1634 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1635 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1636
1637 penv->stats.cfg_req++;
1638 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1639 &resp_desc, &resp, sizeof(resp),
1640 WLFW_TIMEOUT_MS);
1641 if (ret < 0) {
1642 icnss_pr_err("Send config req failed %d\n", ret);
1643 goto out;
1644 }
1645
1646 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1647 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1648 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301649 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001650 goto out;
1651 }
1652 penv->stats.cfg_resp++;
1653
1654 return 0;
1655
1656out:
1657 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001658 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001659 return ret;
1660}
1661
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001662static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001663{
1664 int ret;
1665 struct wlfw_ini_req_msg_v01 req;
1666 struct wlfw_ini_resp_msg_v01 resp;
1667 struct msg_desc req_desc, resp_desc;
1668
1669 if (!penv || !penv->wlfw_clnt)
1670 return -ENODEV;
1671
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001672 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1673 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001674
1675 memset(&req, 0, sizeof(req));
1676 memset(&resp, 0, sizeof(resp));
1677
1678 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001679 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001680
1681 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1682 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1683 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1684
1685 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1686 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1687 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1688
1689 penv->stats.ini_req++;
1690
1691 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1692 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1693 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001694 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1695 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001696 goto out;
1697 }
1698
1699 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001700 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1701 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301702 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001703 goto out;
1704 }
1705 penv->stats.ini_resp++;
1706
1707 return 0;
1708
1709out:
1710 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001711 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001712 return ret;
1713}
1714
1715static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1716 uint32_t offset, uint32_t mem_type,
1717 uint32_t data_len, uint8_t *data)
1718{
1719 int ret;
1720 struct wlfw_athdiag_read_req_msg_v01 req;
1721 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1722 struct msg_desc req_desc, resp_desc;
1723
1724 if (!priv->wlfw_clnt) {
1725 ret = -ENODEV;
1726 goto out;
1727 }
1728
1729 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1730 priv->state, offset, mem_type, data_len);
1731
1732 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1733 if (!resp) {
1734 ret = -ENOMEM;
1735 goto out;
1736 }
1737 memset(&req, 0, sizeof(req));
1738
1739 req.offset = offset;
1740 req.mem_type = mem_type;
1741 req.data_len = data_len;
1742
1743 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1744 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1745 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1746
1747 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1748 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1749 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1750
1751 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1752 &resp_desc, resp, sizeof(*resp),
1753 WLFW_TIMEOUT_MS);
1754 if (ret < 0) {
1755 icnss_pr_err("send athdiag read req failed %d\n", ret);
1756 goto out;
1757 }
1758
1759 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1760 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1761 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301762 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001763 goto out;
1764 }
1765
Yuanyuan Liu68939762017-04-04 16:43:03 -07001766 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001767 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1768 resp->data_valid, resp->data_len);
1769 ret = -EINVAL;
1770 goto out;
1771 }
1772
1773 memcpy(data, resp->data, resp->data_len);
1774
1775out:
1776 kfree(resp);
1777 return ret;
1778}
1779
1780static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1781 uint32_t offset, uint32_t mem_type,
1782 uint32_t data_len, uint8_t *data)
1783{
1784 int ret;
1785 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1786 struct wlfw_athdiag_write_resp_msg_v01 resp;
1787 struct msg_desc req_desc, resp_desc;
1788
1789 if (!priv->wlfw_clnt) {
1790 ret = -ENODEV;
1791 goto out;
1792 }
1793
1794 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1795 priv->state, offset, mem_type, data_len, data);
1796
1797 req = kzalloc(sizeof(*req), GFP_KERNEL);
1798 if (!req) {
1799 ret = -ENOMEM;
1800 goto out;
1801 }
1802 memset(&resp, 0, sizeof(resp));
1803
1804 req->offset = offset;
1805 req->mem_type = mem_type;
1806 req->data_len = data_len;
1807 memcpy(req->data, data, data_len);
1808
1809 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1810 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1811 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1812
1813 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1814 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1815 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1816
1817 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1818 &resp_desc, &resp, sizeof(resp),
1819 WLFW_TIMEOUT_MS);
1820 if (ret < 0) {
1821 icnss_pr_err("send athdiag write req failed %d\n", ret);
1822 goto out;
1823 }
1824
1825 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1826 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1827 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301828 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001829 goto out;
1830 }
1831out:
1832 kfree(req);
1833 return ret;
1834}
1835
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001836static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1837{
1838 struct msg_desc ind_desc;
1839 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1840 int ret = 0;
1841
1842 if (!penv || !penv->wlfw_clnt) {
1843 ret = -ENODEV;
1844 goto out;
1845 }
1846
1847 memset(&ind_msg, 0, sizeof(ind_msg));
1848
1849 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1850 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1851 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1852
1853 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1854 if (ret < 0) {
1855 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1856 ret, msg_len);
1857 goto out;
1858 }
1859
1860 if (ind_msg.cause_for_rejuvenation_valid)
1861 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1862 else
1863 penv->cause_for_rejuvenation = 0;
1864 if (ind_msg.requesting_sub_system_valid)
1865 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1866 else
1867 penv->requesting_sub_system = 0;
1868 if (ind_msg.line_number_valid)
1869 penv->line_number = ind_msg.line_number;
1870 else
1871 penv->line_number = 0;
1872 if (ind_msg.function_name_valid)
1873 memcpy(penv->function_name, ind_msg.function_name,
1874 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1875 else
1876 memset(penv->function_name, 0,
1877 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1878
1879 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1880 penv->cause_for_rejuvenation,
1881 penv->requesting_sub_system,
1882 penv->line_number,
1883 penv->function_name);
1884
1885 penv->stats.rejuvenate_ind++;
1886out:
1887 return ret;
1888}
1889
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001890static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1891{
1892 int ret;
1893 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1894 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1895 struct msg_desc req_desc, resp_desc;
1896
1897 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1898 priv->state);
1899
1900 memset(&req, 0, sizeof(req));
1901 memset(&resp, 0, sizeof(resp));
1902
1903 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1904 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1905 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1906
1907 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1908 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1909 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1910
1911 priv->stats.rejuvenate_ack_req++;
1912 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1913 &resp_desc, &resp, sizeof(resp),
1914 WLFW_TIMEOUT_MS);
1915 if (ret < 0) {
1916 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1917 goto out;
1918 }
1919
1920 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1921 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1922 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301923 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001924 goto out;
1925 }
1926 priv->stats.rejuvenate_ack_resp++;
1927 return 0;
1928
1929out:
1930 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001931 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001932 return ret;
1933}
1934
1935static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1936 uint64_t dynamic_feature_mask)
1937{
1938 int ret;
1939 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1940 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1941 struct msg_desc req_desc, resp_desc;
1942
1943 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1944 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1945 priv->state);
1946 return -EINVAL;
1947 }
1948
1949 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1950 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1951 return 0;
1952 }
1953
1954 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1955 dynamic_feature_mask, priv->state);
1956
1957 memset(&req, 0, sizeof(req));
1958 memset(&resp, 0, sizeof(resp));
1959
1960 req.mask_valid = 1;
1961 req.mask = dynamic_feature_mask;
1962
1963 req_desc.max_msg_len =
1964 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1965 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1966 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1967
1968 resp_desc.max_msg_len =
1969 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1970 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1971 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1972
1973 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1974 &resp_desc, &resp, sizeof(resp),
1975 WLFW_TIMEOUT_MS);
1976 if (ret < 0) {
1977 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1978 goto out;
1979 }
1980
1981 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1982 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1983 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301984 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001985 goto out;
1986 }
1987
1988 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1989 resp.prev_mask_valid, resp.prev_mask,
1990 resp.curr_mask_valid, resp.curr_mask);
1991
1992 return 0;
1993
1994out:
1995 return ret;
1996}
1997
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001998static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1999{
2000 int ret;
2001
2002 if (!penv || !penv->wlfw_clnt)
2003 return;
2004
Yuanyuan Liu68939762017-04-04 16:43:03 -07002005 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002006
2007 do {
2008 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
2009
2010 if (ret != -ENOMSG)
2011 icnss_pr_err("Error receiving message: %d\n", ret);
2012
Yuanyuan Liu68939762017-04-04 16:43:03 -07002013 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002014}
2015
2016static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
2017 enum qmi_event_type event, void *notify_priv)
2018{
Yuanyuan Liu68939762017-04-04 16:43:03 -07002019 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002020
2021 if (!penv || !penv->wlfw_clnt)
2022 return;
2023
2024 switch (event) {
2025 case QMI_RECV_MSG:
2026 schedule_work(&penv->qmi_recv_msg_work);
2027 break;
2028 default:
2029 icnss_pr_dbg("Unknown Event: %d\n", event);
2030 break;
2031 }
2032}
2033
Yuanyuan Liu68939762017-04-04 16:43:03 -07002034static int icnss_call_driver_uevent(struct icnss_priv *priv,
2035 enum icnss_uevent uevent, void *data)
2036{
2037 struct icnss_uevent_data uevent_data;
2038
2039 if (!priv->ops || !priv->ops->uevent)
2040 return 0;
2041
2042 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
2043 priv->state, uevent);
2044
2045 uevent_data.uevent = uevent;
2046 uevent_data.data = data;
2047
2048 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
2049}
2050
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002051static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
2052 unsigned int msg_id, void *msg,
2053 unsigned int msg_len, void *ind_cb_priv)
2054{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002055 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002056 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002057
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002058 if (!penv)
2059 return;
2060
2061 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
2062
Sameer Thalappil93c00732017-08-18 13:02:32 -07002063 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2064 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
2065 msg_id, penv->state);
2066 return;
2067 }
2068
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002069 switch (msg_id) {
2070 case QMI_WLFW_FW_READY_IND_V01:
2071 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
2072 0, NULL);
2073 break;
2074 case QMI_WLFW_MSA_READY_IND_V01:
2075 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
2076 msg_id);
2077 penv->stats.msa_ready_ind++;
2078 break;
2079 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
2080 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
2081 msg_id);
2082 icnss_qmi_pin_connect_result_ind(msg, msg_len);
2083 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002084 case QMI_WLFW_REJUVENATE_IND_V01:
2085 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
2086 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002087
2088 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08002089 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002090 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
2091 if (event_data == NULL)
2092 return;
2093 event_data->crashed = true;
2094 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002095 fw_down_data.crashed = true;
2096 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
2097 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002098 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2099 0, event_data);
2100 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002101 default:
2102 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
2103 break;
2104 }
2105}
2106
2107static int icnss_driver_event_server_arrive(void *data)
2108{
2109 int ret = 0;
2110
2111 if (!penv)
2112 return -ENODEV;
2113
2114 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002115 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002116
2117 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2118 if (!penv->wlfw_clnt) {
2119 icnss_pr_err("QMI client handle create failed\n");
2120 ret = -ENOMEM;
2121 goto out;
2122 }
2123
2124 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2125 WLFW_SERVICE_VERS_V01,
2126 WLFW_SERVICE_INS_ID_V01);
2127 if (ret < 0) {
2128 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2129 goto fail;
2130 }
2131
2132 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2133 icnss_qmi_wlfw_clnt_ind, penv);
2134 if (ret < 0) {
2135 icnss_pr_err("Failed to register indication callback: %d\n",
2136 ret);
2137 goto fail;
2138 }
2139
2140 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2141
2142 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2143
2144 ret = icnss_hw_power_on(penv);
2145 if (ret)
2146 goto fail;
2147
2148 ret = wlfw_ind_register_send_sync_msg();
2149 if (ret < 0)
2150 goto err_power_on;
2151
2152 if (!penv->msa_va) {
2153 icnss_pr_err("Invalid MSA address\n");
2154 ret = -EINVAL;
2155 goto err_power_on;
2156 }
2157
2158 ret = wlfw_msa_mem_info_send_sync_msg();
2159 if (ret < 0)
2160 goto err_power_on;
2161
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302162 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2163 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2164 if (ret < 0)
2165 goto err_power_on;
2166 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2167 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002168
2169 ret = wlfw_msa_ready_send_sync_msg();
2170 if (ret < 0)
2171 goto err_setup_msa;
2172
2173 ret = wlfw_cap_send_sync_msg();
2174 if (ret < 0)
2175 goto err_setup_msa;
2176
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002177 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2178 dynamic_feature_mask);
2179
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002180 icnss_init_vph_monitor(penv);
2181
Anurag Chouhan306ba302018-04-17 11:30:04 +05302182 if (!penv->fw_error_fatal_irq)
2183 register_fw_error_notifications(&penv->pdev->dev);
2184
2185 if (!penv->fw_early_crash_irq)
2186 register_early_crash_notifications(&penv->pdev->dev);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002187
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002188 return ret;
2189
2190err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302191 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002192err_power_on:
2193 icnss_hw_power_off(penv);
2194fail:
2195 qmi_handle_destroy(penv->wlfw_clnt);
2196 penv->wlfw_clnt = NULL;
2197out:
2198 ICNSS_ASSERT(0);
2199 return ret;
2200}
2201
2202static int icnss_driver_event_server_exit(void *data)
2203{
2204 if (!penv || !penv->wlfw_clnt)
2205 return -ENODEV;
2206
2207 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2208
2209 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2210 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2211 &penv->vph_monitor_params);
2212
2213 qmi_handle_destroy(penv->wlfw_clnt);
2214
2215 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2216 penv->wlfw_clnt = NULL;
2217
2218 return 0;
2219}
2220
2221static int icnss_call_driver_probe(struct icnss_priv *priv)
2222{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002223 int ret = 0;
2224 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002225
2226 if (!priv->ops || !priv->ops->probe)
2227 return 0;
2228
Yuanyuan Liu68939762017-04-04 16:43:03 -07002229 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2230 return -EINVAL;
2231
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002232 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2233
2234 icnss_hw_power_on(priv);
2235
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002236 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2237 ret = priv->ops->probe(&priv->pdev->dev);
2238 probe_cnt++;
2239 if (ret != -EPROBE_DEFER)
2240 break;
2241 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002242 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002243 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2244 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002245 goto out;
2246 }
2247
2248 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2249
2250 return 0;
2251
2252out:
2253 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002254 return ret;
2255}
2256
Yuanyuan Liu68939762017-04-04 16:43:03 -07002257static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2258{
2259 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2260 goto out;
2261
2262 if (!priv->ops || !priv->ops->shutdown)
2263 goto out;
2264
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302265 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2266 goto out;
2267
Yuanyuan Liu68939762017-04-04 16:43:03 -07002268 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2269
2270 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302271 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002272
2273out:
2274 return 0;
2275}
2276
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002277static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002278{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002279 int ret;
2280
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002281 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002282
Anurag Chouhan9de21192017-08-08 15:30:02 +05302283 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002284
2285 clear_bit(ICNSS_PD_RESTART, &priv->state);
Sameer Thalappil73eba352018-02-06 14:54:41 -08002286 priv->early_crash_ind = false;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002287
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002288 if (!priv->ops || !priv->ops->reinit)
2289 goto out;
2290
Yuanyuan Liu48a51e42017-12-04 16:01:31 -08002291 if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
2292 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2293 priv->state);
2294 goto out;
2295 }
2296
Yuanyuan Liu68939762017-04-04 16:43:03 -07002297 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002298 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002299
2300 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2301
2302 icnss_hw_power_on(priv);
2303
2304 ret = priv->ops->reinit(&priv->pdev->dev);
2305 if (ret < 0) {
2306 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2307 ret, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002308 if (!priv->allow_recursive_recovery)
2309 ICNSS_ASSERT(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002310 goto out_power_off;
2311 }
2312
2313out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302314 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002315 return 0;
2316
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002317call_probe:
2318 return icnss_call_driver_probe(priv);
2319
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002320out_power_off:
2321 icnss_hw_power_off(priv);
2322
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002323 return ret;
2324}
2325
2326
2327static int icnss_driver_event_fw_ready_ind(void *data)
2328{
2329 int ret = 0;
2330
2331 if (!penv)
2332 return -ENODEV;
2333
2334 set_bit(ICNSS_FW_READY, &penv->state);
2335
2336 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2337
2338 icnss_hw_power_off(penv);
2339
2340 if (!penv->pdev) {
2341 icnss_pr_err("Device is not ready\n");
2342 ret = -ENODEV;
2343 goto out;
2344 }
2345
2346 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002347 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002348 else
2349 ret = icnss_call_driver_probe(penv);
2350
2351out:
2352 return ret;
2353}
2354
2355static int icnss_driver_event_register_driver(void *data)
2356{
2357 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002358 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002359
2360 if (penv->ops)
2361 return -EEXIST;
2362
2363 penv->ops = data;
2364
2365 if (test_bit(SKIP_QMI, &quirks))
2366 set_bit(ICNSS_FW_READY, &penv->state);
2367
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002368 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2369 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2370 penv->state);
2371 return -ENODEV;
2372 }
2373
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002374 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2375 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2376 penv->state);
2377 goto out;
2378 }
2379
2380 ret = icnss_hw_power_on(penv);
2381 if (ret)
2382 goto out;
2383
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002384 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2385 ret = penv->ops->probe(&penv->pdev->dev);
2386 probe_cnt++;
2387 if (ret != -EPROBE_DEFER)
2388 break;
2389 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002390 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002391 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2392 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002393 goto power_off;
2394 }
2395
2396 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2397
2398 return 0;
2399
2400power_off:
2401 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002402out:
2403 return ret;
2404}
2405
2406static int icnss_driver_event_unregister_driver(void *data)
2407{
2408 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2409 penv->ops = NULL;
2410 goto out;
2411 }
2412
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002413 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002414 if (penv->ops)
2415 penv->ops->remove(&penv->pdev->dev);
2416
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002417 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002418 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2419
2420 penv->ops = NULL;
2421
2422 icnss_hw_power_off(penv);
2423
2424out:
2425 return 0;
2426}
2427
Yuanyuan Liu68939762017-04-04 16:43:03 -07002428static int icnss_fw_crashed(struct icnss_priv *priv,
2429 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002430{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302431 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002432
2433 set_bit(ICNSS_PD_RESTART, &priv->state);
2434 clear_bit(ICNSS_FW_READY, &priv->state);
2435
2436 icnss_pm_stay_awake(priv);
2437
Yuanyuan Liu68939762017-04-04 16:43:03 -07002438 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2439 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002440
Sameer Thalappil73eba352018-02-06 14:54:41 -08002441 if (event_data && event_data->fw_rejuvenate)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002442 wlfw_rejuvenate_ack_send_sync_msg(priv);
2443
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002444 return 0;
2445}
2446
2447static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2448 void *data)
2449{
2450 int ret = 0;
2451 struct icnss_event_pd_service_down_data *event_data = data;
2452
2453 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002454 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002455
Sameer Thalappil73eba352018-02-06 14:54:41 -08002456 if (priv->early_crash_ind) {
2457 icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
2458 event_data->crashed, priv->state);
2459 goto out;
2460 }
2461
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002462 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002463 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2464 event_data->crashed, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002465 if (!priv->allow_recursive_recovery)
2466 ICNSS_ASSERT(0);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002467 goto out;
2468 }
2469
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002470 if (priv->force_err_fatal)
2471 ICNSS_ASSERT(0);
2472
Hardik Kantilal Patel3b934072018-01-12 18:02:10 +05302473 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002474
2475out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002476 kfree(data);
2477
Yuanyuan Liu68939762017-04-04 16:43:03 -07002478 icnss_ignore_qmi_timeout(false);
2479
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002480 return ret;
2481}
2482
Sameer Thalappil73eba352018-02-06 14:54:41 -08002483static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
2484 void *data)
2485{
2486 int ret = 0;
2487
2488 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
2489 goto out;
2490
2491 priv->early_crash_ind = true;
2492 icnss_fw_crashed(priv, NULL);
2493
2494out:
2495 kfree(data);
2496 icnss_ignore_qmi_timeout(false);
2497
2498 return ret;
2499}
2500
2501
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002502static void icnss_driver_event_work(struct work_struct *work)
2503{
2504 struct icnss_driver_event *event;
2505 unsigned long flags;
2506 int ret;
2507
2508 icnss_pm_stay_awake(penv);
2509
2510 spin_lock_irqsave(&penv->event_lock, flags);
2511
2512 while (!list_empty(&penv->event_list)) {
2513 event = list_first_entry(&penv->event_list,
2514 struct icnss_driver_event, list);
2515 list_del(&event->list);
2516 spin_unlock_irqrestore(&penv->event_lock, flags);
2517
2518 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2519 icnss_driver_event_to_str(event->type),
2520 event->sync ? "-sync" : "", event->type,
2521 penv->state);
2522
2523 switch (event->type) {
2524 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2525 ret = icnss_driver_event_server_arrive(event->data);
2526 break;
2527 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2528 ret = icnss_driver_event_server_exit(event->data);
2529 break;
2530 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2531 ret = icnss_driver_event_fw_ready_ind(event->data);
2532 break;
2533 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2534 ret = icnss_driver_event_register_driver(event->data);
2535 break;
2536 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2537 ret = icnss_driver_event_unregister_driver(event->data);
2538 break;
2539 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2540 ret = icnss_driver_event_pd_service_down(penv,
2541 event->data);
2542 break;
Sameer Thalappil73eba352018-02-06 14:54:41 -08002543 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
2544 ret = icnss_driver_event_early_crash_ind(penv,
2545 event->data);
2546 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002547 default:
2548 icnss_pr_err("Invalid Event type: %d", event->type);
2549 kfree(event);
2550 continue;
2551 }
2552
2553 penv->stats.events[event->type].processed++;
2554
2555 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2556 icnss_driver_event_to_str(event->type),
2557 event->sync ? "-sync" : "", event->type, ret,
2558 penv->state);
2559
2560 spin_lock_irqsave(&penv->event_lock, flags);
2561 if (event->sync) {
2562 event->ret = ret;
2563 complete(&event->complete);
2564 continue;
2565 }
2566 spin_unlock_irqrestore(&penv->event_lock, flags);
2567
2568 kfree(event);
2569
2570 spin_lock_irqsave(&penv->event_lock, flags);
2571 }
2572 spin_unlock_irqrestore(&penv->event_lock, flags);
2573
2574 icnss_pm_relax(penv);
2575}
2576
2577static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2578 unsigned long code,
2579 void *_cmd)
2580{
2581 int ret = 0;
2582
2583 if (!penv)
2584 return -ENODEV;
2585
2586 icnss_pr_dbg("Event Notify: code: %ld", code);
2587
2588 switch (code) {
2589 case QMI_SERVER_ARRIVE:
2590 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2591 0, NULL);
2592 break;
2593
2594 case QMI_SERVER_EXIT:
Anurag Chouhan7883c632018-04-18 19:21:20 +05302595 set_bit(ICNSS_FW_DOWN, &penv->state);
2596 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002597 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2598 0, NULL);
2599 break;
2600 default:
2601 icnss_pr_dbg("Invalid code: %ld", code);
2602 break;
2603 }
2604 return ret;
2605}
2606
2607static int icnss_msa0_ramdump(struct icnss_priv *priv)
2608{
2609 struct ramdump_segment segment;
2610
2611 memset(&segment, 0, sizeof(segment));
2612 segment.v_address = priv->msa_va;
2613 segment.size = priv->msa_mem_size;
2614 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2615}
2616
2617static struct notifier_block wlfw_clnt_nb = {
2618 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2619};
2620
2621static int icnss_modem_notifier_nb(struct notifier_block *nb,
2622 unsigned long code,
2623 void *data)
2624{
2625 struct icnss_event_pd_service_down_data *event_data;
2626 struct notif_data *notif = data;
2627 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2628 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002629 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302630 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002631
Yuanyuan Liu68939762017-04-04 16:43:03 -07002632 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002633
Sameer Thalappil765cb492017-10-04 17:57:07 -07002634 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002635 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302636 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002637 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302638 if (!ret) {
2639 icnss_pr_info("Collecting msa0 segment dump\n");
2640 icnss_msa0_ramdump(priv);
2641 icnss_assign_msa_perm_all(priv,
2642 ICNSS_MSA_PERM_WLAN_HW_RW);
2643 } else {
2644 icnss_pr_err("Not able to Collect msa0 segment dump"
2645 "Apps permissions not assigned %d\n", ret);
2646 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002647 return NOTIFY_OK;
2648 }
2649
2650 if (code != SUBSYS_BEFORE_SHUTDOWN)
2651 return NOTIFY_OK;
2652
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002653 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2654 set_bit(ICNSS_FW_DOWN, &priv->state);
2655 icnss_ignore_qmi_timeout(true);
2656
2657 fw_down_data.crashed = !!notif->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002658 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2659 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002660 icnss_call_driver_uevent(priv,
2661 ICNSS_UEVENT_FW_DOWN,
2662 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002663 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002664 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002665
Yuanyuan Liu68939762017-04-04 16:43:03 -07002666 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2667 priv->state, notif->crashed);
2668
Sameer Thalappil93c00732017-08-18 13:02:32 -07002669 set_bit(ICNSS_FW_DOWN, &priv->state);
2670
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002671 if (notif->crashed)
2672 priv->stats.recovery.root_pd_crash++;
2673 else
2674 priv->stats.recovery.root_pd_shutdown++;
2675
Yuanyuan Liu68939762017-04-04 16:43:03 -07002676 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002677
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002678 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002679
2680 if (event_data == NULL)
2681 return notifier_from_errno(-ENOMEM);
2682
2683 event_data->crashed = notif->crashed;
2684
Yuanyuan Liu68939762017-04-04 16:43:03 -07002685 fw_down_data.crashed = !!notif->crashed;
2686 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2687
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002688 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2689 ICNSS_EVENT_SYNC, event_data);
2690
2691 return NOTIFY_OK;
2692}
2693
2694static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2695{
2696 int ret = 0;
2697
2698 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2699
2700 priv->modem_notify_handler =
2701 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2702
2703 if (IS_ERR(priv->modem_notify_handler)) {
2704 ret = PTR_ERR(priv->modem_notify_handler);
2705 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2706 }
2707
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002708 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002709
2710 return ret;
2711}
2712
2713static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2714{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002715 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002716 return 0;
2717
2718 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2719 &priv->modem_ssr_nb);
2720 priv->modem_notify_handler = NULL;
2721
2722 return 0;
2723}
2724
2725static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2726{
2727 int i;
2728
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002729 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002730 return 0;
2731
2732 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002733 service_notif_unregister_notifier(
2734 priv->service_notifier[i].handle,
2735 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002736
2737 kfree(priv->service_notifier);
2738
2739 priv->service_notifier = NULL;
2740
2741 return 0;
2742}
2743
2744static int icnss_service_notifier_notify(struct notifier_block *nb,
2745 unsigned long notification, void *data)
2746{
2747 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2748 service_notifier_nb);
2749 enum pd_subsys_state *state = data;
2750 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002751 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002752 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002753
Yuanyuan Liu68939762017-04-04 16:43:03 -07002754 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2755 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002756
Yuanyuan Liu68939762017-04-04 16:43:03 -07002757 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2758 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002759
Yuanyuan Liu68939762017-04-04 16:43:03 -07002760 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002761
Yuanyuan Liu68939762017-04-04 16:43:03 -07002762 if (event_data == NULL)
2763 return notifier_from_errno(-ENOMEM);
2764
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002765 event_data->crashed = true;
2766
Yuanyuan Liu68939762017-04-04 16:43:03 -07002767 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002768 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002769 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002770 }
2771
Yuanyuan Liu68939762017-04-04 16:43:03 -07002772 switch (*state) {
2773 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002774 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002775 break;
2776 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002777 cause = ICNSS_ROOT_PD_SHUTDOWN;
2778 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002779 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002780 break;
2781 case USER_PD_STATE_CHANGE:
2782 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2783 cause = ICNSS_HOST_ERROR;
2784 priv->stats.recovery.pdr_host_error++;
2785 } else {
2786 cause = ICNSS_FW_CRASH;
2787 priv->stats.recovery.pdr_fw_crash++;
2788 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002789 break;
2790 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002791 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002792 break;
2793 }
2794
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002795 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2796 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002797event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002798 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2799 set_bit(ICNSS_FW_DOWN, &priv->state);
2800 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002801
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002802 fw_down_data.crashed = event_data->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002803 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2804 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002805 icnss_call_driver_uevent(priv,
2806 ICNSS_UEVENT_FW_DOWN,
2807 &fw_down_data);
2808 }
2809
2810 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002811 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2812 ICNSS_EVENT_SYNC, event_data);
2813done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002814 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2815 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002816 return NOTIFY_OK;
2817}
2818
2819static int icnss_get_service_location_notify(struct notifier_block *nb,
2820 unsigned long opcode, void *data)
2821{
2822 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2823 get_service_nb);
2824 struct pd_qmi_client_data *pd = data;
2825 int curr_state;
2826 int ret;
2827 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002828 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002829
2830 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2831 priv->state);
2832
2833 if (opcode != LOCATOR_UP)
2834 return NOTIFY_DONE;
2835
2836 if (pd->total_domains == 0) {
2837 icnss_pr_err("Did not find any domains\n");
2838 ret = -ENOENT;
2839 goto out;
2840 }
2841
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002842 notifier = kcalloc(pd->total_domains,
2843 sizeof(struct service_notifier_context),
2844 GFP_KERNEL);
2845 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002846 ret = -ENOMEM;
2847 goto out;
2848 }
2849
2850 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2851
2852 for (i = 0; i < pd->total_domains; i++) {
2853 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2854 pd->domain_list[i].name,
2855 pd->domain_list[i].instance_id);
2856
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002857 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002858 service_notif_register_notifier(pd->domain_list[i].name,
2859 pd->domain_list[i].instance_id,
2860 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002861 notifier[i].instance_id = pd->domain_list[i].instance_id;
2862 strlcpy(notifier[i].name, pd->domain_list[i].name,
2863 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002864
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002865 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002866 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2867 i, pd->domain_list->name,
2868 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002869 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002870 goto free_handle;
2871 }
2872 }
2873
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002874 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002875 priv->total_domains = pd->total_domains;
2876
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002877 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002878
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002879 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2880 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002881
2882 return NOTIFY_OK;
2883
2884free_handle:
2885 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002886 if (notifier[i].handle)
2887 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002888 &priv->service_notifier_nb);
2889 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002890 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002891
2892out:
2893 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2894 priv->state);
2895
2896 return NOTIFY_OK;
2897}
2898
2899
2900static int icnss_pd_restart_enable(struct icnss_priv *priv)
2901{
2902 int ret;
2903
2904 if (test_bit(SSR_ONLY, &quirks)) {
2905 icnss_pr_dbg("PDR disabled through module parameter\n");
2906 return 0;
2907 }
2908
2909 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2910
2911 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2912 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2913 ICNSS_WLAN_SERVICE_NAME,
2914 &priv->get_service_nb);
2915 if (ret) {
2916 icnss_pr_err("Get service location failed: %d\n", ret);
2917 goto out;
2918 }
2919
2920 return 0;
2921out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002922 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002923 return ret;
2924
2925}
2926
2927
2928static int icnss_enable_recovery(struct icnss_priv *priv)
2929{
2930 int ret;
2931
2932 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2933 icnss_pr_dbg("Recovery disabled through module parameter\n");
2934 return 0;
2935 }
2936
2937 if (test_bit(PDR_ONLY, &quirks)) {
2938 icnss_pr_dbg("SSR disabled through module parameter\n");
2939 goto enable_pdr;
2940 }
2941
2942 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2943 &priv->pdev->dev);
2944 if (!priv->msa0_dump_dev)
2945 return -ENOMEM;
2946
2947 icnss_modem_ssr_register_notifier(priv);
2948 if (test_bit(SSR_ONLY, &quirks)) {
2949 icnss_pr_dbg("PDR disabled through module parameter\n");
2950 return 0;
2951 }
2952
2953enable_pdr:
2954 ret = icnss_pd_restart_enable(priv);
2955
2956 if (ret)
2957 return ret;
2958
2959 return 0;
2960}
2961
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302962int __icnss_register_driver(struct icnss_driver_ops *ops,
2963 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002964{
2965 int ret = 0;
2966
2967 if (!penv || !penv->pdev) {
2968 ret = -ENODEV;
2969 goto out;
2970 }
2971
2972 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2973
2974 if (penv->ops) {
2975 icnss_pr_err("Driver already registered\n");
2976 ret = -EEXIST;
2977 goto out;
2978 }
2979
2980 if (!ops->probe || !ops->remove) {
2981 ret = -EINVAL;
2982 goto out;
2983 }
2984
2985 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302986 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002987
2988 if (ret == -EINTR)
2989 ret = 0;
2990
2991out:
2992 return ret;
2993}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302994EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002995
2996int icnss_unregister_driver(struct icnss_driver_ops *ops)
2997{
2998 int ret;
2999
3000 if (!penv || !penv->pdev) {
3001 ret = -ENODEV;
3002 goto out;
3003 }
3004
3005 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
3006
3007 if (!penv->ops) {
3008 icnss_pr_err("Driver not registered\n");
3009 ret = -ENOENT;
3010 goto out;
3011 }
3012
3013 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
3014 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
3015out:
3016 return ret;
3017}
3018EXPORT_SYMBOL(icnss_unregister_driver);
3019
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303020int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003021 irqreturn_t (*handler)(int, void *),
3022 unsigned long flags, const char *name, void *ctx)
3023{
3024 int ret = 0;
3025 unsigned int irq;
3026 struct ce_irq_list *irq_entry;
3027
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303028 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003029 ret = -ENODEV;
3030 goto out;
3031 }
3032
Yuanyuan Liu68939762017-04-04 16:43:03 -07003033 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003034
3035 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3036 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
3037 ret = -EINVAL;
3038 goto out;
3039 }
3040 irq = penv->ce_irqs[ce_id];
3041 irq_entry = &penv->ce_irq_list[ce_id];
3042
3043 if (irq_entry->handler || irq_entry->irq) {
3044 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
3045 irq, ce_id);
3046 ret = -EEXIST;
3047 goto out;
3048 }
3049
3050 ret = request_irq(irq, handler, flags, name, ctx);
3051 if (ret) {
3052 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
3053 irq, ce_id, ret);
3054 goto out;
3055 }
3056 irq_entry->irq = irq;
3057 irq_entry->handler = handler;
3058
Yuanyuan Liu68939762017-04-04 16:43:03 -07003059 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003060
3061 penv->stats.ce_irqs[ce_id].request++;
3062out:
3063 return ret;
3064}
3065EXPORT_SYMBOL(icnss_ce_request_irq);
3066
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303067int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003068{
3069 int ret = 0;
3070 unsigned int irq;
3071 struct ce_irq_list *irq_entry;
3072
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303073 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003074 ret = -ENODEV;
3075 goto out;
3076 }
3077
Yuanyuan Liu68939762017-04-04 16:43:03 -07003078 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003079
3080 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3081 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
3082 ret = -EINVAL;
3083 goto out;
3084 }
3085
3086 irq = penv->ce_irqs[ce_id];
3087 irq_entry = &penv->ce_irq_list[ce_id];
3088 if (!irq_entry->handler || !irq_entry->irq) {
3089 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
3090 ret = -EEXIST;
3091 goto out;
3092 }
3093 free_irq(irq, ctx);
3094 irq_entry->irq = 0;
3095 irq_entry->handler = NULL;
3096
3097 penv->stats.ce_irqs[ce_id].free++;
3098out:
3099 return ret;
3100}
3101EXPORT_SYMBOL(icnss_ce_free_irq);
3102
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303103void icnss_enable_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("Enable 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 enable IRQ, ce_id: %d\n", ce_id);
3117 return;
3118 }
3119
3120 penv->stats.ce_irqs[ce_id].enable++;
3121
3122 irq = penv->ce_irqs[ce_id];
3123 enable_irq(irq);
3124}
3125EXPORT_SYMBOL(icnss_enable_irq);
3126
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303127void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003128{
3129 unsigned int irq;
3130
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303131 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003132 icnss_pr_err("Platform driver not initialized\n");
3133 return;
3134 }
3135
Yuanyuan Liu68939762017-04-04 16:43:03 -07003136 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003137 penv->state);
3138
3139 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3140 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
3141 ce_id);
3142 return;
3143 }
3144
3145 irq = penv->ce_irqs[ce_id];
3146 disable_irq(irq);
3147
3148 penv->stats.ce_irqs[ce_id].disable++;
3149}
3150EXPORT_SYMBOL(icnss_disable_irq);
3151
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303152int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003153{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303154 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003155 icnss_pr_err("Platform driver not initialized\n");
3156 return -EINVAL;
3157 }
3158
3159 info->v_addr = penv->mem_base_va;
3160 info->p_addr = penv->mem_base_pa;
3161 info->chip_id = penv->chip_info.chip_id;
3162 info->chip_family = penv->chip_info.chip_family;
3163 info->board_id = penv->board_info.board_id;
3164 info->soc_id = penv->soc_info.soc_id;
3165 info->fw_version = penv->fw_version_info.fw_version;
3166 strlcpy(info->fw_build_timestamp,
3167 penv->fw_version_info.fw_build_timestamp,
3168 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3169
3170 return 0;
3171}
3172EXPORT_SYMBOL(icnss_get_soc_info);
3173
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303174int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003175{
3176 int ret;
3177
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303178 if (!dev)
3179 return -ENODEV;
3180
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303181 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3182 icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
3183 penv->state);
3184 return -EINVAL;
3185 }
3186
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003187 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003188
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003189 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003190 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003191 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3192 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003193 return ret;
3194}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003195EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003196
3197int icnss_athdiag_read(struct device *dev, uint32_t offset,
3198 uint32_t mem_type, uint32_t data_len,
3199 uint8_t *output)
3200{
3201 int ret = 0;
3202 struct icnss_priv *priv = dev_get_drvdata(dev);
3203
3204 if (priv->magic != ICNSS_MAGIC) {
3205 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3206 dev, priv, priv->magic);
3207 return -EINVAL;
3208 }
3209
3210 if (!output || data_len == 0
3211 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3212 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3213 output, data_len);
3214 ret = -EINVAL;
3215 goto out;
3216 }
3217
3218 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3219 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3220 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3221 priv->state);
3222 ret = -EINVAL;
3223 goto out;
3224 }
3225
3226 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3227 data_len, output);
3228out:
3229 return ret;
3230}
3231EXPORT_SYMBOL(icnss_athdiag_read);
3232
3233int icnss_athdiag_write(struct device *dev, uint32_t offset,
3234 uint32_t mem_type, uint32_t data_len,
3235 uint8_t *input)
3236{
3237 int ret = 0;
3238 struct icnss_priv *priv = dev_get_drvdata(dev);
3239
3240 if (priv->magic != ICNSS_MAGIC) {
3241 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3242 dev, priv, priv->magic);
3243 return -EINVAL;
3244 }
3245
3246 if (!input || data_len == 0
3247 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3248 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3249 input, data_len);
3250 ret = -EINVAL;
3251 goto out;
3252 }
3253
3254 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3255 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3256 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3257 priv->state);
3258 ret = -EINVAL;
3259 goto out;
3260 }
3261
3262 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3263 data_len, input);
3264out:
3265 return ret;
3266}
3267EXPORT_SYMBOL(icnss_athdiag_write);
3268
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303269int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003270 enum icnss_driver_mode mode,
3271 const char *host_version)
3272{
3273 struct wlfw_wlan_cfg_req_msg_v01 req;
3274 u32 i;
3275 int ret;
3276
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303277 if (!dev)
3278 return -ENODEV;
3279
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303280 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
3281 icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
3282 penv->state);
3283 return -EINVAL;
3284 }
3285
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003286 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3287 mode, config, host_version);
3288
3289 memset(&req, 0, sizeof(req));
3290
3291 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3292 goto skip;
3293
3294 if (!config || !host_version) {
3295 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3296 config, host_version);
3297 ret = -EINVAL;
3298 goto out;
3299 }
3300
3301 req.host_version_valid = 1;
3302 strlcpy(req.host_version, host_version,
3303 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3304
3305 req.tgt_cfg_valid = 1;
3306 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3307 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3308 else
3309 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3310 for (i = 0; i < req.tgt_cfg_len; i++) {
3311 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3312 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3313 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3314 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3315 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3316 }
3317
3318 req.svc_cfg_valid = 1;
3319 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3320 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3321 else
3322 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3323 for (i = 0; i < req.svc_cfg_len; i++) {
3324 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3325 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3326 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3327 }
3328
3329 req.shadow_reg_valid = 1;
3330 if (config->num_shadow_reg_cfg >
3331 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3332 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3333 else
3334 req.shadow_reg_len = config->num_shadow_reg_cfg;
3335
3336 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3337 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3338
3339 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3340 if (ret)
3341 goto out;
3342skip:
3343 ret = wlfw_wlan_mode_send_sync_msg(mode);
3344out:
3345 if (test_bit(SKIP_QMI, &quirks))
3346 ret = 0;
3347
3348 return ret;
3349}
3350EXPORT_SYMBOL(icnss_wlan_enable);
3351
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303352int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003353{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303354 if (!dev)
3355 return -ENODEV;
3356
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003357 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3358}
3359EXPORT_SYMBOL(icnss_wlan_disable);
3360
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303361bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003362{
3363 return test_bit(SKIP_QMI, &quirks) ? true : false;
3364}
3365EXPORT_SYMBOL(icnss_is_qmi_disable);
3366
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303367int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003368{
3369 int i;
3370
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303371 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003372 return -ENODEV;
3373
3374 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3375 if (penv->ce_irqs[i] == irq)
3376 return i;
3377 }
3378
3379 icnss_pr_err("No matching CE id for irq %d\n", irq);
3380
3381 return -EINVAL;
3382}
3383EXPORT_SYMBOL(icnss_get_ce_id);
3384
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303385int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003386{
3387 int irq;
3388
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303389 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003390 return -ENODEV;
3391
3392 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3393 return -EINVAL;
3394
3395 irq = penv->ce_irqs[ce_id];
3396
3397 return irq;
3398}
3399EXPORT_SYMBOL(icnss_get_irq);
3400
3401struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3402{
3403 struct icnss_priv *priv = dev_get_drvdata(dev);
3404
3405 if (!priv) {
3406 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3407 dev, priv);
3408 return NULL;
3409 }
3410
3411 return priv->smmu_mapping;
3412}
3413EXPORT_SYMBOL(icnss_smmu_get_mapping);
3414
3415int icnss_smmu_map(struct device *dev,
3416 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3417{
3418 struct icnss_priv *priv = dev_get_drvdata(dev);
3419 unsigned long iova;
3420 size_t len;
3421 int ret = 0;
3422
3423 if (!priv) {
3424 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3425 dev, priv);
3426 return -EINVAL;
3427 }
3428
3429 if (!iova_addr) {
3430 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3431 &paddr, size);
3432 return -EINVAL;
3433 }
3434
3435 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3436 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3437
3438 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3439 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3440 iova,
3441 &priv->smmu_iova_ipa_start,
3442 priv->smmu_iova_ipa_len);
3443 return -ENOMEM;
3444 }
3445
3446 ret = iommu_map(priv->smmu_mapping->domain, iova,
3447 rounddown(paddr, PAGE_SIZE), len,
3448 IOMMU_READ | IOMMU_WRITE);
3449 if (ret) {
3450 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3451 return ret;
3452 }
3453
3454 priv->smmu_iova_ipa_start = iova + len;
3455 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3456
3457 return 0;
3458}
3459EXPORT_SYMBOL(icnss_smmu_map);
3460
3461unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3462{
3463 return socinfo_get_serial_number();
3464}
3465EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3466
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003467int icnss_trigger_recovery(struct device *dev)
3468{
3469 int ret = 0;
3470 struct icnss_priv *priv = dev_get_drvdata(dev);
3471
3472 if (priv->magic != ICNSS_MAGIC) {
3473 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3474 ret = -EINVAL;
3475 goto out;
3476 }
3477
3478 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3479 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3480 priv->state);
3481 ret = -EPERM;
3482 goto out;
3483 }
3484
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003485 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003486 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3487 priv->state);
3488 ret = -EOPNOTSUPP;
3489 goto out;
3490 }
3491
3492 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3493 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3494 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003495 ret = -EINVAL;
3496 goto out;
3497 }
3498
Yuanyuan Liu68939762017-04-04 16:43:03 -07003499 WARN_ON(1);
3500 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3501 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003502
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003503 /*
3504 * Initiate PDR, required only for the first instance
3505 */
3506 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3507 priv->service_notifier[0].instance_id);
3508
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003509 if (!ret)
3510 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3511
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003512out:
3513 return ret;
3514}
3515EXPORT_SYMBOL(icnss_trigger_recovery);
3516
3517
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003518static int icnss_smmu_init(struct icnss_priv *priv)
3519{
3520 struct dma_iommu_mapping *mapping;
3521 int atomic_ctx = 1;
3522 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303523 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003524 int ret = 0;
3525
3526 icnss_pr_dbg("Initializing SMMU\n");
3527
3528 mapping = arm_iommu_create_mapping(&platform_bus_type,
3529 priv->smmu_iova_start,
3530 priv->smmu_iova_len);
3531 if (IS_ERR(mapping)) {
3532 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3533 ret = PTR_ERR(mapping);
3534 goto map_fail;
3535 }
3536
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303537 if (priv->bypass_s1_smmu) {
3538 ret = iommu_domain_set_attr(mapping->domain,
3539 DOMAIN_ATTR_S1_BYPASS,
3540 &s1_bypass);
3541 if (ret < 0) {
3542 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3543 ret);
3544 goto set_attr_fail;
3545 }
3546 icnss_pr_dbg("SMMU S1 BYPASS\n");
3547 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003548 ret = iommu_domain_set_attr(mapping->domain,
3549 DOMAIN_ATTR_ATOMIC,
3550 &atomic_ctx);
3551 if (ret < 0) {
3552 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3553 ret);
3554 goto set_attr_fail;
3555 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303556 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003557
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303558 ret = iommu_domain_set_attr(mapping->domain,
3559 DOMAIN_ATTR_FAST,
3560 &fast);
3561 if (ret < 0) {
3562 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3563 ret);
3564 goto set_attr_fail;
3565 }
3566 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003567 }
3568
3569 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3570 if (ret < 0) {
3571 icnss_pr_err("Attach device failed, err = %d\n", ret);
3572 goto attach_fail;
3573 }
3574
3575 priv->smmu_mapping = mapping;
3576
3577 return ret;
3578
3579attach_fail:
3580set_attr_fail:
3581 arm_iommu_release_mapping(mapping);
3582map_fail:
3583 return ret;
3584}
3585
3586static void icnss_smmu_deinit(struct icnss_priv *priv)
3587{
3588 if (!priv->smmu_mapping)
3589 return;
3590
3591 arm_iommu_detach_device(&priv->pdev->dev);
3592 arm_iommu_release_mapping(priv->smmu_mapping);
3593
3594 priv->smmu_mapping = NULL;
3595}
3596
Yuanyuan Liu68939762017-04-04 16:43:03 -07003597static int icnss_get_vreg_info(struct device *dev,
3598 struct icnss_vreg_info *vreg_info)
3599{
3600 int ret = 0;
3601 char prop_name[MAX_PROP_SIZE];
3602 struct regulator *reg;
3603 const __be32 *prop;
3604 int len = 0;
3605 int i;
3606
3607 reg = devm_regulator_get_optional(dev, vreg_info->name);
3608 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3609 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3610 vreg_info->name);
3611 ret = PTR_ERR(reg);
3612 goto out;
3613 }
3614
3615 if (IS_ERR(reg)) {
3616 ret = PTR_ERR(reg);
3617
3618 if (vreg_info->required) {
3619 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3620 vreg_info->name, ret);
3621 goto out;
3622 } else {
3623 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3624 vreg_info->name, ret);
3625 goto done;
3626 }
3627 }
3628
3629 vreg_info->reg = reg;
3630
3631 snprintf(prop_name, MAX_PROP_SIZE,
3632 "qcom,%s-config", vreg_info->name);
3633
3634 prop = of_get_property(dev->of_node, prop_name, &len);
3635
3636 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3637 prop_name, len);
3638
3639 if (!prop || len < (2 * sizeof(__be32))) {
3640 icnss_pr_dbg("Property %s %s\n", prop_name,
3641 prop ? "invalid format" : "doesn't exist");
3642 goto done;
3643 }
3644
3645 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3646 switch (i) {
3647 case 0:
3648 vreg_info->min_v = be32_to_cpup(&prop[0]);
3649 break;
3650 case 1:
3651 vreg_info->max_v = be32_to_cpup(&prop[1]);
3652 break;
3653 case 2:
3654 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3655 break;
3656 case 3:
3657 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3658 break;
3659 default:
3660 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3661 prop_name, i);
3662 break;
3663 }
3664 }
3665
3666done:
3667 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3668 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3669 vreg_info->load_ua, vreg_info->settle_delay);
3670
3671 return 0;
3672
3673out:
3674 return ret;
3675}
3676
3677static int icnss_get_clk_info(struct device *dev,
3678 struct icnss_clk_info *clk_info)
3679{
3680 struct clk *handle;
3681 int ret = 0;
3682
3683 handle = devm_clk_get(dev, clk_info->name);
3684 if (IS_ERR(handle)) {
3685 ret = PTR_ERR(handle);
3686 if (clk_info->required) {
3687 icnss_pr_err("Clock %s isn't available: %d\n",
3688 clk_info->name, ret);
3689 goto out;
3690 } else {
3691 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3692 ret);
3693 ret = 0;
3694 goto out;
3695 }
3696 }
3697
3698 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3699
3700 clk_info->handle = handle;
3701out:
3702 return ret;
3703}
3704
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003705static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003706{
3707 struct icnss_priv *priv = s->private;
3708
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003709 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003710
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003711 seq_puts(s, "\nCMD: test_mode\n");
3712 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3713 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3714 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003715 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003716
3717 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3718 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003719
3720 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003721 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003722 goto out;
3723 }
3724
3725 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003726 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003727 goto out;
3728 }
3729
3730 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003731 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003732 goto out;
3733 }
3734
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003735out:
3736 seq_puts(s, "\n");
3737 return 0;
3738}
3739
3740static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3741{
3742 int ret;
3743
3744 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3745 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3746 priv->state);
3747 ret = -ENODEV;
3748 goto out;
3749 }
3750
3751 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3752 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3753 priv->state);
3754 ret = -EINVAL;
3755 goto out;
3756 }
3757
3758 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3759 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3760 priv->state);
3761 ret = -EINVAL;
3762 goto out;
3763 }
3764
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303765 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003766
3767 ret = icnss_hw_power_off(priv);
3768
3769 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3770
3771out:
3772 return ret;
3773}
3774static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3775 enum icnss_driver_mode mode)
3776{
3777 int ret;
3778
3779 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3780 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3781 priv->state);
3782 ret = -ENODEV;
3783 goto out;
3784 }
3785
3786 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3787 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3788 priv->state);
3789 ret = -EINVAL;
3790 goto out;
3791 }
3792
3793 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3794 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3795 priv->state);
3796 ret = -EBUSY;
3797 goto out;
3798 }
3799
3800 ret = icnss_hw_power_on(priv);
3801 if (ret)
3802 goto out;
3803
3804 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3805
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303806 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003807 if (ret)
3808 goto power_off;
3809
3810 return 0;
3811
3812power_off:
3813 icnss_hw_power_off(priv);
3814 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3815
3816out:
3817 return ret;
3818}
3819
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003820static void icnss_allow_recursive_recovery(struct device *dev)
3821{
3822 struct icnss_priv *priv = dev_get_drvdata(dev);
3823
3824 priv->allow_recursive_recovery = true;
3825
3826 icnss_pr_info("Recursive recovery allowed for WLAN\n");
3827}
3828
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003829static ssize_t icnss_fw_debug_write(struct file *fp,
3830 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003831 size_t count, loff_t *off)
3832{
3833 struct icnss_priv *priv =
3834 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003835 char buf[64];
3836 char *sptr, *token;
3837 unsigned int len = 0;
3838 char *cmd;
3839 uint64_t val;
3840 const char *delim = " ";
3841 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003842
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003843 len = min(count, sizeof(buf) - 1);
3844 if (copy_from_user(buf, user_buf, len))
3845 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003846
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003847 buf[len] = '\0';
3848 sptr = buf;
3849
3850 token = strsep(&sptr, delim);
3851 if (!token)
3852 return -EINVAL;
3853 if (!sptr)
3854 return -EINVAL;
3855 cmd = token;
3856
3857 token = strsep(&sptr, delim);
3858 if (!token)
3859 return -EINVAL;
3860 if (kstrtou64(token, 0, &val))
3861 return -EINVAL;
3862
3863 if (strcmp(cmd, "test_mode") == 0) {
3864 switch (val) {
3865 case 0:
3866 ret = icnss_test_mode_fw_test_off(priv);
3867 break;
3868 case 1:
3869 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3870 break;
3871 case 2:
3872 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3873 break;
3874 case 3:
3875 ret = icnss_trigger_recovery(&priv->pdev->dev);
3876 break;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003877 case 4:
3878 icnss_allow_recursive_recovery(&priv->pdev->dev);
3879 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003880 default:
3881 return -EINVAL;
3882 }
3883 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3884 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3885 } else {
3886 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003887 }
3888
3889 if (ret)
3890 return ret;
3891
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003892 return count;
3893}
3894
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003895static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003896{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003897 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003898}
3899
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003900static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003901 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003902 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003903 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003904 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003905 .owner = THIS_MODULE,
3906 .llseek = seq_lseek,
3907};
3908
3909static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3910 size_t count, loff_t *off)
3911{
3912 struct icnss_priv *priv =
3913 ((struct seq_file *)fp->private_data)->private;
3914 int ret;
3915 u32 val;
3916
3917 ret = kstrtou32_from_user(buf, count, 0, &val);
3918 if (ret)
3919 return ret;
3920
3921 if (ret == 0)
3922 memset(&priv->stats, 0, sizeof(priv->stats));
3923
3924 return count;
3925}
3926
3927static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3928{
3929 enum icnss_driver_state i;
3930 int skip = 0;
3931 unsigned long state;
3932
3933 seq_printf(s, "\nState: 0x%lx(", priv->state);
3934 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3935
3936 if (!(state & 0x1))
3937 continue;
3938
3939 if (skip++)
3940 seq_puts(s, " | ");
3941
3942 switch (i) {
3943 case ICNSS_WLFW_QMI_CONNECTED:
3944 seq_puts(s, "QMI CONN");
3945 continue;
3946 case ICNSS_POWER_ON:
3947 seq_puts(s, "POWER ON");
3948 continue;
3949 case ICNSS_FW_READY:
3950 seq_puts(s, "FW READY");
3951 continue;
3952 case ICNSS_DRIVER_PROBED:
3953 seq_puts(s, "DRIVER PROBED");
3954 continue;
3955 case ICNSS_FW_TEST_MODE:
3956 seq_puts(s, "FW TEST MODE");
3957 continue;
3958 case ICNSS_PM_SUSPEND:
3959 seq_puts(s, "PM SUSPEND");
3960 continue;
3961 case ICNSS_PM_SUSPEND_NOIRQ:
3962 seq_puts(s, "PM SUSPEND NOIRQ");
3963 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003964 case ICNSS_SSR_REGISTERED:
3965 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003966 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003967 case ICNSS_PDR_REGISTERED:
3968 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003969 continue;
3970 case ICNSS_PD_RESTART:
3971 seq_puts(s, "PD RESTART");
3972 continue;
3973 case ICNSS_MSA0_ASSIGNED:
3974 seq_puts(s, "MSA0 ASSIGNED");
3975 continue;
3976 case ICNSS_WLFW_EXISTS:
3977 seq_puts(s, "WLAN FW EXISTS");
3978 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303979 case ICNSS_SHUTDOWN_DONE:
3980 seq_puts(s, "SHUTDOWN DONE");
3981 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003982 case ICNSS_HOST_TRIGGERED_PDR:
3983 seq_puts(s, "HOST TRIGGERED PDR");
3984 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003985 case ICNSS_FW_DOWN:
3986 seq_puts(s, "FW DOWN");
3987 continue;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08003988 case ICNSS_DRIVER_UNLOADING:
3989 seq_puts(s, "DRIVER UNLOADING");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003990 }
3991
3992 seq_printf(s, "UNKNOWN-%d", i);
3993 }
3994 seq_puts(s, ")\n");
3995
3996 return 0;
3997}
3998
3999static int icnss_stats_show_capability(struct seq_file *s,
4000 struct icnss_priv *priv)
4001{
4002 if (test_bit(ICNSS_FW_READY, &priv->state)) {
4003 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
4004 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
4005 seq_printf(s, "Chip family: 0x%x\n",
4006 priv->chip_info.chip_family);
4007 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
4008 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
4009 seq_printf(s, "Firmware Version: 0x%x\n",
4010 priv->fw_version_info.fw_version);
4011 seq_printf(s, "Firmware Build Timestamp: %s\n",
4012 priv->fw_version_info.fw_build_timestamp);
4013 seq_printf(s, "Firmware Build ID: %s\n",
4014 priv->fw_build_id);
4015 }
4016
4017 return 0;
4018}
4019
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004020static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
4021 struct icnss_priv *priv)
4022{
4023 if (priv->stats.rejuvenate_ind) {
4024 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
4025 seq_printf(s, "Number of Rejuvenations: %u\n",
4026 priv->stats.rejuvenate_ind);
4027 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
4028 priv->cause_for_rejuvenation);
4029 seq_printf(s, "Requesting Sub-System: 0x%x\n",
4030 priv->requesting_sub_system);
4031 seq_printf(s, "Line Number: %u\n",
4032 priv->line_number);
4033 seq_printf(s, "Function Name: %s\n",
4034 priv->function_name);
4035 }
4036
4037 return 0;
4038}
4039
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004040static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
4041{
4042 int i;
4043
4044 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
4045 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
4046 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
4047 seq_printf(s, "%24s %16u %16u\n",
4048 icnss_driver_event_to_str(i),
4049 priv->stats.events[i].posted,
4050 priv->stats.events[i].processed);
4051
4052 return 0;
4053}
4054
4055static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
4056{
4057 int i;
4058
4059 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
4060 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
4061 "Free", "Enable", "Disable");
4062 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
4063 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
4064 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
4065 priv->stats.ce_irqs[i].free,
4066 priv->stats.ce_irqs[i].enable,
4067 priv->stats.ce_irqs[i].disable);
4068
4069 return 0;
4070}
4071
4072static int icnss_stats_show(struct seq_file *s, void *data)
4073{
4074#define ICNSS_STATS_DUMP(_s, _priv, _x) \
4075 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
4076
4077 struct icnss_priv *priv = s->private;
4078
4079 ICNSS_STATS_DUMP(s, priv, ind_register_req);
4080 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
4081 ICNSS_STATS_DUMP(s, priv, ind_register_err);
4082 ICNSS_STATS_DUMP(s, priv, msa_info_req);
4083 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
4084 ICNSS_STATS_DUMP(s, priv, msa_info_err);
4085 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
4086 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
4087 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
4088 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
4089 ICNSS_STATS_DUMP(s, priv, cap_req);
4090 ICNSS_STATS_DUMP(s, priv, cap_resp);
4091 ICNSS_STATS_DUMP(s, priv, cap_err);
4092 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
4093 ICNSS_STATS_DUMP(s, priv, cfg_req);
4094 ICNSS_STATS_DUMP(s, priv, cfg_resp);
4095 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
4096 ICNSS_STATS_DUMP(s, priv, mode_req);
4097 ICNSS_STATS_DUMP(s, priv, mode_resp);
4098 ICNSS_STATS_DUMP(s, priv, mode_req_err);
4099 ICNSS_STATS_DUMP(s, priv, ini_req);
4100 ICNSS_STATS_DUMP(s, priv, ini_resp);
4101 ICNSS_STATS_DUMP(s, priv, ini_req_err);
4102 ICNSS_STATS_DUMP(s, priv, vbatt_req);
4103 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
4104 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004105 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004106 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
4107 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
4108 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004109 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
4110 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
4111 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
4112 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004113
4114 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
4115 ICNSS_STATS_DUMP(s, priv, pm_suspend);
4116 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
4117 ICNSS_STATS_DUMP(s, priv, pm_resume);
4118 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
4119 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
4120 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
4121 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
4122 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
4123 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
4124 ICNSS_STATS_DUMP(s, priv, pm_relax);
4125
4126 icnss_stats_show_irqs(s, priv);
4127
4128 icnss_stats_show_capability(s, priv);
4129
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004130 icnss_stats_show_rejuvenate_info(s, priv);
4131
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004132 icnss_stats_show_events(s, priv);
4133
4134 icnss_stats_show_state(s, priv);
4135
4136 return 0;
4137#undef ICNSS_STATS_DUMP
4138}
4139
4140static int icnss_stats_open(struct inode *inode, struct file *file)
4141{
4142 return single_open(file, icnss_stats_show, inode->i_private);
4143}
4144
4145static const struct file_operations icnss_stats_fops = {
4146 .read = seq_read,
4147 .write = icnss_stats_write,
4148 .release = single_release,
4149 .open = icnss_stats_open,
4150 .owner = THIS_MODULE,
4151 .llseek = seq_lseek,
4152};
4153
4154static int icnss_regwrite_show(struct seq_file *s, void *data)
4155{
4156 struct icnss_priv *priv = s->private;
4157
4158 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4159
4160 if (!test_bit(ICNSS_FW_READY, &priv->state))
4161 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4162
4163 return 0;
4164}
4165
4166static ssize_t icnss_regwrite_write(struct file *fp,
4167 const char __user *user_buf,
4168 size_t count, loff_t *off)
4169{
4170 struct icnss_priv *priv =
4171 ((struct seq_file *)fp->private_data)->private;
4172 char buf[64];
4173 char *sptr, *token;
4174 unsigned int len = 0;
4175 uint32_t reg_offset, mem_type, reg_val;
4176 const char *delim = " ";
4177 int ret = 0;
4178
4179 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4180 !test_bit(ICNSS_POWER_ON, &priv->state))
4181 return -EINVAL;
4182
4183 len = min(count, sizeof(buf) - 1);
4184 if (copy_from_user(buf, user_buf, len))
4185 return -EFAULT;
4186
4187 buf[len] = '\0';
4188 sptr = buf;
4189
4190 token = strsep(&sptr, delim);
4191 if (!token)
4192 return -EINVAL;
4193
4194 if (!sptr)
4195 return -EINVAL;
4196
4197 if (kstrtou32(token, 0, &mem_type))
4198 return -EINVAL;
4199
4200 token = strsep(&sptr, delim);
4201 if (!token)
4202 return -EINVAL;
4203
4204 if (!sptr)
4205 return -EINVAL;
4206
4207 if (kstrtou32(token, 0, &reg_offset))
4208 return -EINVAL;
4209
4210 token = strsep(&sptr, delim);
4211 if (!token)
4212 return -EINVAL;
4213
4214 if (kstrtou32(token, 0, &reg_val))
4215 return -EINVAL;
4216
4217 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4218 sizeof(uint32_t),
4219 (uint8_t *)&reg_val);
4220 if (ret)
4221 return ret;
4222
4223 return count;
4224}
4225
4226static int icnss_regwrite_open(struct inode *inode, struct file *file)
4227{
4228 return single_open(file, icnss_regwrite_show, inode->i_private);
4229}
4230
4231static const struct file_operations icnss_regwrite_fops = {
4232 .read = seq_read,
4233 .write = icnss_regwrite_write,
4234 .open = icnss_regwrite_open,
4235 .owner = THIS_MODULE,
4236 .llseek = seq_lseek,
4237};
4238
4239static int icnss_regread_show(struct seq_file *s, void *data)
4240{
4241 struct icnss_priv *priv = s->private;
4242
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304243 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004244 if (!priv->diag_reg_read_buf) {
4245 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4246
4247 if (!test_bit(ICNSS_FW_READY, &priv->state))
4248 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4249
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304250 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004251 return 0;
4252 }
4253
4254 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4255 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4256 priv->diag_reg_read_len);
4257
4258 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4259 priv->diag_reg_read_len, false);
4260
4261 priv->diag_reg_read_len = 0;
4262 kfree(priv->diag_reg_read_buf);
4263 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304264 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004265
4266 return 0;
4267}
4268
4269static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4270 size_t count, loff_t *off)
4271{
4272 struct icnss_priv *priv =
4273 ((struct seq_file *)fp->private_data)->private;
4274 char buf[64];
4275 char *sptr, *token;
4276 unsigned int len = 0;
4277 uint32_t reg_offset, mem_type;
4278 uint32_t data_len = 0;
4279 uint8_t *reg_buf = NULL;
4280 const char *delim = " ";
4281 int ret = 0;
4282
4283 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4284 !test_bit(ICNSS_POWER_ON, &priv->state))
4285 return -EINVAL;
4286
4287 len = min(count, sizeof(buf) - 1);
4288 if (copy_from_user(buf, user_buf, len))
4289 return -EFAULT;
4290
4291 buf[len] = '\0';
4292 sptr = buf;
4293
4294 token = strsep(&sptr, delim);
4295 if (!token)
4296 return -EINVAL;
4297
4298 if (!sptr)
4299 return -EINVAL;
4300
4301 if (kstrtou32(token, 0, &mem_type))
4302 return -EINVAL;
4303
4304 token = strsep(&sptr, delim);
4305 if (!token)
4306 return -EINVAL;
4307
4308 if (!sptr)
4309 return -EINVAL;
4310
4311 if (kstrtou32(token, 0, &reg_offset))
4312 return -EINVAL;
4313
4314 token = strsep(&sptr, delim);
4315 if (!token)
4316 return -EINVAL;
4317
4318 if (kstrtou32(token, 0, &data_len))
4319 return -EINVAL;
4320
4321 if (data_len == 0 ||
4322 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4323 return -EINVAL;
4324
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304325 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004326 kfree(priv->diag_reg_read_buf);
4327 priv->diag_reg_read_buf = NULL;
4328
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004329 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304330 if (!reg_buf) {
4331 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004332 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304333 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004334
4335 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4336 mem_type, data_len,
4337 reg_buf);
4338 if (ret) {
4339 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304340 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004341 return ret;
4342 }
4343
4344 priv->diag_reg_read_addr = reg_offset;
4345 priv->diag_reg_read_mem_type = mem_type;
4346 priv->diag_reg_read_len = data_len;
4347 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304348 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004349
4350 return count;
4351}
4352
4353static int icnss_regread_open(struct inode *inode, struct file *file)
4354{
4355 return single_open(file, icnss_regread_show, inode->i_private);
4356}
4357
4358static const struct file_operations icnss_regread_fops = {
4359 .read = seq_read,
4360 .write = icnss_regread_write,
4361 .open = icnss_regread_open,
4362 .owner = THIS_MODULE,
4363 .llseek = seq_lseek,
4364};
4365
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004366#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004367static int icnss_debugfs_create(struct icnss_priv *priv)
4368{
4369 int ret = 0;
4370 struct dentry *root_dentry;
4371
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004372 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004373
4374 if (IS_ERR(root_dentry)) {
4375 ret = PTR_ERR(root_dentry);
4376 icnss_pr_err("Unable to create debugfs %d\n", ret);
4377 goto out;
4378 }
4379
4380 priv->root_dentry = root_dentry;
4381
Yuanyuan Liu84132752017-05-24 12:07:12 -07004382 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004383 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004384
Yuanyuan Liu84132752017-05-24 12:07:12 -07004385 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004386 &icnss_stats_fops);
4387 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4388 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004389 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004390 &icnss_regwrite_fops);
4391
4392out:
4393 return ret;
4394}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004395#else
4396static int icnss_debugfs_create(struct icnss_priv *priv)
4397{
4398 int ret = 0;
4399 struct dentry *root_dentry;
4400
4401 root_dentry = debugfs_create_dir("icnss", NULL);
4402
4403 if (IS_ERR(root_dentry)) {
4404 ret = PTR_ERR(root_dentry);
4405 icnss_pr_err("Unable to create debugfs %d\n", ret);
4406 return ret;
4407 }
4408
4409 priv->root_dentry = root_dentry;
4410
4411 debugfs_create_file("stats", 0600, root_dentry, priv,
4412 &icnss_stats_fops);
4413 return 0;
4414}
4415#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004416
4417static void icnss_debugfs_destroy(struct icnss_priv *priv)
4418{
4419 debugfs_remove_recursive(priv->root_dentry);
4420}
4421
4422static int icnss_get_vbatt_info(struct icnss_priv *priv)
4423{
4424 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4425 struct qpnp_vadc_chip *vadc_dev = NULL;
4426 int ret = 0;
4427
4428 if (test_bit(VBATT_DISABLE, &quirks)) {
4429 icnss_pr_dbg("VBATT feature is disabled\n");
4430 return ret;
4431 }
4432
4433 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4434 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4435 icnss_pr_err("adc_tm_dev probe defer\n");
4436 return -EPROBE_DEFER;
4437 }
4438
4439 if (IS_ERR(adc_tm_dev)) {
4440 ret = PTR_ERR(adc_tm_dev);
4441 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4442 ret);
4443 return ret;
4444 }
4445
4446 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4447 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4448 icnss_pr_err("vadc_dev probe defer\n");
4449 return -EPROBE_DEFER;
4450 }
4451
4452 if (IS_ERR(vadc_dev)) {
4453 ret = PTR_ERR(vadc_dev);
4454 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4455 ret);
4456 return ret;
4457 }
4458
4459 priv->adc_tm_dev = adc_tm_dev;
4460 priv->vadc_dev = vadc_dev;
4461
4462 return 0;
4463}
4464
4465static int icnss_probe(struct platform_device *pdev)
4466{
4467 int ret = 0;
4468 struct resource *res;
4469 int i;
4470 struct device *dev = &pdev->dev;
4471 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304472 const __be32 *addrp;
4473 u64 prop_size = 0;
4474 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004475
4476 if (penv) {
4477 icnss_pr_err("Driver is already initialized\n");
4478 return -EEXIST;
4479 }
4480
4481 icnss_pr_dbg("Platform driver probe\n");
4482
4483 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4484 if (!priv)
4485 return -ENOMEM;
4486
4487 priv->magic = ICNSS_MAGIC;
4488 dev_set_drvdata(dev, priv);
4489
4490 priv->pdev = pdev;
4491
4492 ret = icnss_get_vbatt_info(priv);
4493 if (ret == -EPROBE_DEFER)
4494 goto out;
4495
Yuanyuan Liu68939762017-04-04 16:43:03 -07004496 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4497 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4498 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4499
4500 if (ret)
4501 goto out;
4502 }
4503
4504 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4505 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4506 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4507 if (ret)
4508 goto out;
4509 }
4510
4511 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4512 priv->bypass_s1_smmu = true;
4513
4514 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4515
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004516 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4517 if (!res) {
4518 icnss_pr_err("Memory base not found in DT\n");
4519 ret = -EINVAL;
4520 goto out;
4521 }
4522
4523 priv->mem_base_pa = res->start;
4524 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4525 resource_size(res));
4526 if (!priv->mem_base_va) {
4527 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4528 &priv->mem_base_pa);
4529 ret = -EINVAL;
4530 goto out;
4531 }
4532 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4533 priv->mem_base_va);
4534
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004535 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4536 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4537 if (!res) {
4538 icnss_pr_err("Fail to get IRQ-%d\n", i);
4539 ret = -ENODEV;
4540 goto out;
4541 } else {
4542 priv->ce_irqs[i] = res->start;
4543 }
4544 }
4545
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304546 np = of_parse_phandle(dev->of_node,
4547 "qcom,wlan-msa-fixed-region", 0);
4548 if (np) {
4549 addrp = of_get_address(np, 0, &prop_size, NULL);
4550 if (!addrp) {
4551 icnss_pr_err("Failed to get assigned-addresses or property\n");
4552 ret = -EINVAL;
4553 goto out;
4554 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004555
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304556 priv->msa_pa = of_translate_address(np, addrp);
4557 if (priv->msa_pa == OF_BAD_ADDR) {
4558 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4559 ret = -EINVAL;
4560 goto out;
4561 }
4562
4563 priv->msa_va = memremap(priv->msa_pa,
4564 (unsigned long)prop_size, MEMREMAP_WT);
4565 if (!priv->msa_va) {
4566 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4567 &priv->msa_pa);
4568 ret = -EINVAL;
4569 goto out;
4570 }
4571 priv->msa_mem_size = prop_size;
4572 } else {
4573 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4574 &priv->msa_mem_size);
4575 if (ret || priv->msa_mem_size == 0) {
4576 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4577 priv->msa_mem_size, ret);
4578 goto out;
4579 }
4580
4581 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4582 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4583
4584 if (!priv->msa_va) {
4585 icnss_pr_err("DMA alloc failed for MSA\n");
4586 ret = -ENOMEM;
4587 goto out;
4588 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004589 }
4590
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304591 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4592 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004593
4594 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4595 "smmu_iova_base");
4596 if (!res) {
4597 icnss_pr_err("SMMU IOVA base not found\n");
4598 } else {
4599 priv->smmu_iova_start = res->start;
4600 priv->smmu_iova_len = resource_size(res);
4601 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4602 &priv->smmu_iova_start, priv->smmu_iova_len);
4603
4604 res = platform_get_resource_byname(pdev,
4605 IORESOURCE_MEM,
4606 "smmu_iova_ipa");
4607 if (!res) {
4608 icnss_pr_err("SMMU IOVA IPA not found\n");
4609 } else {
4610 priv->smmu_iova_ipa_start = res->start;
4611 priv->smmu_iova_ipa_len = resource_size(res);
4612 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4613 &priv->smmu_iova_ipa_start,
4614 priv->smmu_iova_ipa_len);
4615 }
4616
4617 ret = icnss_smmu_init(priv);
4618 if (ret < 0) {
4619 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4620 ret, &priv->smmu_iova_start,
4621 priv->smmu_iova_len);
4622 goto out;
4623 }
4624 }
4625
4626 spin_lock_init(&priv->event_lock);
4627 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304628 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004629
4630 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4631 if (!priv->event_wq) {
4632 icnss_pr_err("Workqueue creation failed\n");
4633 ret = -EFAULT;
4634 goto out_smmu_deinit;
4635 }
4636
4637 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4638 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4639 INIT_LIST_HEAD(&priv->event_list);
4640
4641 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4642 WLFW_SERVICE_VERS_V01,
4643 WLFW_SERVICE_INS_ID_V01,
4644 &wlfw_clnt_nb);
4645 if (ret < 0) {
4646 icnss_pr_err("Notifier register failed: %d\n", ret);
4647 goto out_destroy_wq;
4648 }
4649
4650 icnss_enable_recovery(priv);
4651
4652 icnss_debugfs_create(priv);
4653
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004654 ret = device_init_wakeup(&priv->pdev->dev, true);
4655 if (ret)
4656 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4657 ret);
4658
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004659 penv = priv;
4660
4661 icnss_pr_info("Platform driver probed successfully\n");
4662
4663 return 0;
4664
4665out_destroy_wq:
4666 destroy_workqueue(priv->event_wq);
4667out_smmu_deinit:
4668 icnss_smmu_deinit(priv);
4669out:
4670 dev_set_drvdata(dev, NULL);
4671
4672 return ret;
4673}
4674
4675static int icnss_remove(struct platform_device *pdev)
4676{
4677 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4678
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004679 device_init_wakeup(&penv->pdev->dev, false);
4680
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004681 icnss_debugfs_destroy(penv);
4682
4683 icnss_modem_ssr_unregister_notifier(penv);
4684
4685 destroy_ramdump_device(penv->msa0_dump_dev);
4686
4687 icnss_pdr_unregister_notifier(penv);
4688
4689 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4690 WLFW_SERVICE_VERS_V01,
4691 WLFW_SERVICE_INS_ID_V01,
4692 &wlfw_clnt_nb);
4693 if (penv->event_wq)
4694 destroy_workqueue(penv->event_wq);
4695
4696 icnss_hw_power_off(penv);
4697
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304698 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4699 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004700
4701 dev_set_drvdata(&pdev->dev, NULL);
4702
4703 return 0;
4704}
4705
4706#ifdef CONFIG_PM_SLEEP
4707static int icnss_pm_suspend(struct device *dev)
4708{
4709 struct icnss_priv *priv = dev_get_drvdata(dev);
4710 int ret = 0;
4711
4712 if (priv->magic != ICNSS_MAGIC) {
4713 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4714 dev, priv, priv->magic);
4715 return -EINVAL;
4716 }
4717
Yuanyuan Liu68939762017-04-04 16:43:03 -07004718 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004719
4720 if (!priv->ops || !priv->ops->pm_suspend ||
4721 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4722 goto out;
4723
4724 ret = priv->ops->pm_suspend(dev);
4725
4726out:
4727 if (ret == 0) {
4728 priv->stats.pm_suspend++;
4729 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4730 } else {
4731 priv->stats.pm_suspend_err++;
4732 }
4733 return ret;
4734}
4735
4736static int icnss_pm_resume(struct device *dev)
4737{
4738 struct icnss_priv *priv = dev_get_drvdata(dev);
4739 int ret = 0;
4740
4741 if (priv->magic != ICNSS_MAGIC) {
4742 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4743 dev, priv, priv->magic);
4744 return -EINVAL;
4745 }
4746
Yuanyuan Liu68939762017-04-04 16:43:03 -07004747 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004748
4749 if (!priv->ops || !priv->ops->pm_resume ||
4750 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4751 goto out;
4752
4753 ret = priv->ops->pm_resume(dev);
4754
4755out:
4756 if (ret == 0) {
4757 priv->stats.pm_resume++;
4758 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4759 } else {
4760 priv->stats.pm_resume_err++;
4761 }
4762 return ret;
4763}
4764
4765static int icnss_pm_suspend_noirq(struct device *dev)
4766{
4767 struct icnss_priv *priv = dev_get_drvdata(dev);
4768 int ret = 0;
4769
4770 if (priv->magic != ICNSS_MAGIC) {
4771 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4772 dev, priv, priv->magic);
4773 return -EINVAL;
4774 }
4775
Yuanyuan Liu68939762017-04-04 16:43:03 -07004776 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004777
4778 if (!priv->ops || !priv->ops->suspend_noirq ||
4779 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4780 goto out;
4781
4782 ret = priv->ops->suspend_noirq(dev);
4783
4784out:
4785 if (ret == 0) {
4786 priv->stats.pm_suspend_noirq++;
4787 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4788 } else {
4789 priv->stats.pm_suspend_noirq_err++;
4790 }
4791 return ret;
4792}
4793
4794static int icnss_pm_resume_noirq(struct device *dev)
4795{
4796 struct icnss_priv *priv = dev_get_drvdata(dev);
4797 int ret = 0;
4798
4799 if (priv->magic != ICNSS_MAGIC) {
4800 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4801 dev, priv, priv->magic);
4802 return -EINVAL;
4803 }
4804
Yuanyuan Liu68939762017-04-04 16:43:03 -07004805 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004806
4807 if (!priv->ops || !priv->ops->resume_noirq ||
4808 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4809 goto out;
4810
4811 ret = priv->ops->resume_noirq(dev);
4812
4813out:
4814 if (ret == 0) {
4815 priv->stats.pm_resume_noirq++;
4816 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4817 } else {
4818 priv->stats.pm_resume_noirq_err++;
4819 }
4820 return ret;
4821}
4822#endif
4823
4824static const struct dev_pm_ops icnss_pm_ops = {
4825 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4826 icnss_pm_resume)
4827 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4828 icnss_pm_resume_noirq)
4829};
4830
4831static const struct of_device_id icnss_dt_match[] = {
4832 {.compatible = "qcom,icnss"},
4833 {}
4834};
4835
4836MODULE_DEVICE_TABLE(of, icnss_dt_match);
4837
4838static struct platform_driver icnss_driver = {
4839 .probe = icnss_probe,
4840 .remove = icnss_remove,
4841 .driver = {
4842 .name = "icnss",
4843 .pm = &icnss_pm_ops,
4844 .owner = THIS_MODULE,
4845 .of_match_table = icnss_dt_match,
4846 },
4847};
4848
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004849static int __init icnss_initialize(void)
4850{
4851 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4852 "icnss", 0);
4853 if (!icnss_ipc_log_context)
4854 icnss_pr_err("Unable to create log context\n");
4855
Yuanyuan Liu68939762017-04-04 16:43:03 -07004856 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4857 "icnss_long", 0);
4858 if (!icnss_ipc_log_long_context)
4859 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004860
4861 return platform_driver_register(&icnss_driver);
4862}
4863
4864static void __exit icnss_exit(void)
4865{
4866 platform_driver_unregister(&icnss_driver);
4867 ipc_log_context_destroy(icnss_ipc_log_context);
4868 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004869 ipc_log_context_destroy(icnss_ipc_log_long_context);
4870 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004871}
4872
4873
4874module_init(icnss_initialize);
4875module_exit(icnss_exit);
4876
4877MODULE_LICENSE("GPL v2");
4878MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");