blob: 39b79f66804e6ceb173ced2cd41f89180ce0dd0a [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,
Anurag Chouhanc4997342018-07-17 14:35:54 +0530287 ICNSS_REJUVENATE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800288};
289
290struct ce_irq_list {
291 int irq;
292 irqreturn_t (*handler)(int, void *);
293};
294
Yuanyuan Liu68939762017-04-04 16:43:03 -0700295struct icnss_vreg_info {
296 struct regulator *reg;
297 const char *name;
298 u32 min_v;
299 u32 max_v;
300 u32 load_ua;
301 unsigned long settle_delay;
302 bool required;
303};
304
305struct icnss_clk_info {
306 struct clk *handle;
307 const char *name;
308 u32 freq;
309 bool required;
310};
311
312static struct icnss_vreg_info icnss_vreg_info[] = {
313 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
314 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
315 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
316 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
317};
318
319#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
320
321static struct icnss_clk_info icnss_clk_info[] = {
322 {NULL, "cxo_ref_clk_pin", 0, false},
323};
324
325#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
326
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800327struct icnss_stats {
328 struct {
329 uint32_t posted;
330 uint32_t processed;
331 } events[ICNSS_DRIVER_EVENT_MAX];
332
333 struct {
334 uint32_t request;
335 uint32_t free;
336 uint32_t enable;
337 uint32_t disable;
338 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
339
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700340 struct {
341 uint32_t pdr_fw_crash;
342 uint32_t pdr_host_error;
343 uint32_t root_pd_crash;
344 uint32_t root_pd_shutdown;
345 } recovery;
346
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800347 uint32_t pm_suspend;
348 uint32_t pm_suspend_err;
349 uint32_t pm_resume;
350 uint32_t pm_resume_err;
351 uint32_t pm_suspend_noirq;
352 uint32_t pm_suspend_noirq_err;
353 uint32_t pm_resume_noirq;
354 uint32_t pm_resume_noirq_err;
355 uint32_t pm_stay_awake;
356 uint32_t pm_relax;
357
358 uint32_t ind_register_req;
359 uint32_t ind_register_resp;
360 uint32_t ind_register_err;
361 uint32_t msa_info_req;
362 uint32_t msa_info_resp;
363 uint32_t msa_info_err;
364 uint32_t msa_ready_req;
365 uint32_t msa_ready_resp;
366 uint32_t msa_ready_err;
367 uint32_t msa_ready_ind;
368 uint32_t cap_req;
369 uint32_t cap_resp;
370 uint32_t cap_err;
371 uint32_t pin_connect_result;
372 uint32_t cfg_req;
373 uint32_t cfg_resp;
374 uint32_t cfg_req_err;
375 uint32_t mode_req;
376 uint32_t mode_resp;
377 uint32_t mode_req_err;
378 uint32_t ini_req;
379 uint32_t ini_resp;
380 uint32_t ini_req_err;
381 uint32_t vbatt_req;
382 uint32_t vbatt_resp;
383 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800384 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800385 uint32_t rejuvenate_ack_req;
386 uint32_t rejuvenate_ack_resp;
387 uint32_t rejuvenate_ack_err;
388};
389
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700390enum icnss_pdr_cause_index {
391 ICNSS_FW_CRASH,
392 ICNSS_ROOT_PD_CRASH,
393 ICNSS_ROOT_PD_SHUTDOWN,
394 ICNSS_HOST_ERROR,
395};
396
397static const char * const icnss_pdr_cause[] = {
398 [ICNSS_FW_CRASH] = "FW crash",
399 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
400 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
401 [ICNSS_HOST_ERROR] = "Host error",
402};
403
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800404struct service_notifier_context {
405 void *handle;
406 uint32_t instance_id;
407 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800408};
409
410static struct icnss_priv {
411 uint32_t magic;
412 struct platform_device *pdev;
413 struct icnss_driver_ops *ops;
414 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700415 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
416 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800417 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
418 phys_addr_t mem_base_pa;
419 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800420 struct dma_iommu_mapping *smmu_mapping;
421 dma_addr_t smmu_iova_start;
422 size_t smmu_iova_len;
423 dma_addr_t smmu_iova_ipa_start;
424 size_t smmu_iova_ipa_len;
425 struct qmi_handle *wlfw_clnt;
426 struct list_head event_list;
427 spinlock_t event_lock;
428 struct work_struct event_work;
429 struct work_struct qmi_recv_msg_work;
430 struct workqueue_struct *event_wq;
431 phys_addr_t msa_pa;
432 uint32_t msa_mem_size;
433 void *msa_va;
434 unsigned long state;
435 struct wlfw_rf_chip_info_s_v01 chip_info;
436 struct wlfw_rf_board_info_s_v01 board_info;
437 struct wlfw_soc_info_s_v01 soc_info;
438 struct wlfw_fw_version_info_s_v01 fw_version_info;
439 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
440 u32 pwr_pin_result;
441 u32 phy_io_pin_result;
442 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700443 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800444 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700445 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800446 struct dentry *root_dentry;
447 spinlock_t on_off_lock;
448 struct icnss_stats stats;
449 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800450 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800451 struct notifier_block service_notifier_nb;
452 int total_domains;
453 struct notifier_block get_service_nb;
454 void *modem_notify_handler;
455 struct notifier_block modem_ssr_nb;
456 uint32_t diag_reg_read_addr;
457 uint32_t diag_reg_read_mem_type;
458 uint32_t diag_reg_read_len;
459 uint8_t *diag_reg_read_buf;
460 struct qpnp_adc_tm_btm_param vph_monitor_params;
461 struct qpnp_adc_tm_chip *adc_tm_dev;
462 struct qpnp_vadc_chip *vadc_dev;
463 uint64_t vph_pwr;
464 atomic_t pm_count;
465 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700466 bool bypass_s1_smmu;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -0800467 bool force_err_fatal;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -0800468 bool allow_recursive_recovery;
Sameer Thalappil73eba352018-02-06 14:54:41 -0800469 bool early_crash_ind;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800470 u8 cause_for_rejuvenation;
471 u8 requesting_sub_system;
472 u16 line_number;
473 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530474 struct mutex dev_lock;
Anurag Chouhan306ba302018-04-17 11:30:04 +0530475 uint32_t fw_error_fatal_irq;
476 uint32_t fw_early_crash_irq;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800477} *penv;
478
Yuanyuan Liu68939762017-04-04 16:43:03 -0700479#ifdef CONFIG_ICNSS_DEBUG
480static void icnss_ignore_qmi_timeout(bool ignore)
481{
482 ignore_qmi_timeout = ignore;
483}
484#else
485static void icnss_ignore_qmi_timeout(bool ignore) { }
486#endif
487
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530488static int icnss_assign_msa_perm(struct icnss_mem_region_info
489 *mem_region, enum icnss_msa_perm new_perm)
490{
491 int ret = 0;
492 phys_addr_t addr;
493 u32 size;
494 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530495 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530496 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530497 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
498 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530499 u32 dest_nelems;
500 enum icnss_msa_perm cur_perm = mem_region->perm;
501 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
502
503 addr = mem_region->reg_addr;
504 size = mem_region->size;
505
506 if (mem_region->secure_flag) {
507 new_perm_list = &msa_perm_secure_list[new_perm];
508 old_perm_list = &msa_perm_secure_list[cur_perm];
509 } else {
510 new_perm_list = &msa_perm_list[new_perm];
511 old_perm_list = &msa_perm_list[cur_perm];
512 }
513
514 source_nelems = old_perm_list->nelems;
515 dest_nelems = new_perm_list->nelems;
516
517 for (i = 0; i < source_nelems; ++i)
518 source_vmids[i] = old_perm_list->vmids[i];
519
520 for (i = 0; i < dest_nelems; ++i) {
521 dest_vmids[i] = new_perm_list->vmids[i];
522 dest_perms[i] = new_perm_list->perms[i];
523 }
524
525 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
526 dest_vmids, dest_perms, dest_nelems);
527 if (ret) {
528 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
529 &addr, size, ret);
530 goto out;
531 }
532
533 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
534 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
535 source_nelems, source_vmids[0], source_vmids[1],
536 source_vmids[2], source_vmids[3], dest_nelems,
537 dest_vmids[0], dest_vmids[1], dest_vmids[2],
538 dest_vmids[3]);
539out:
540 return ret;
541}
542
543static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
544 enum icnss_msa_perm new_perm)
545{
546 int ret;
547 int i;
548 enum icnss_msa_perm old_perm;
549
Yuanyuan Liu26ec2212017-09-01 10:34:25 -0700550 if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
551 icnss_pr_err("Invalid memory region len %d\n",
552 priv->nr_mem_region);
553 return -EINVAL;
554 }
555
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530556 for (i = 0; i < priv->nr_mem_region; i++) {
557 old_perm = priv->mem_region[i].perm;
558 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
559 if (ret)
560 goto err_unmap;
561 priv->mem_region[i].perm = new_perm;
562 }
563 return 0;
564
565err_unmap:
566 for (i--; i >= 0; i--) {
567 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
568 }
569 return ret;
570}
571
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800572static void icnss_pm_stay_awake(struct icnss_priv *priv)
573{
574 if (atomic_inc_return(&priv->pm_count) != 1)
575 return;
576
Yuanyuan Liu68939762017-04-04 16:43:03 -0700577 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800578 atomic_read(&priv->pm_count));
579
580 pm_stay_awake(&priv->pdev->dev);
581
582 priv->stats.pm_stay_awake++;
583}
584
585static void icnss_pm_relax(struct icnss_priv *priv)
586{
587 int r = atomic_dec_return(&priv->pm_count);
588
589 WARN_ON(r < 0);
590
591 if (r != 0)
592 return;
593
Yuanyuan Liu68939762017-04-04 16:43:03 -0700594 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800595 atomic_read(&priv->pm_count));
596
597 pm_relax(&priv->pdev->dev);
598 priv->stats.pm_relax++;
599}
600
601static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
602{
603 switch (type) {
604 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
605 return "SERVER_ARRIVE";
606 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
607 return "SERVER_EXIT";
608 case ICNSS_DRIVER_EVENT_FW_READY_IND:
609 return "FW_READY";
610 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
611 return "REGISTER_DRIVER";
612 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
613 return "UNREGISTER_DRIVER";
614 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
615 return "PD_SERVICE_DOWN";
Sameer Thalappil73eba352018-02-06 14:54:41 -0800616 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
617 return "FW_EARLY_CRASH_IND";
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800618 case ICNSS_DRIVER_EVENT_MAX:
619 return "EVENT_MAX";
620 }
621
622 return "UNKNOWN";
623};
624
625static int icnss_driver_event_post(enum icnss_driver_event_type type,
626 u32 flags, void *data)
627{
628 struct icnss_driver_event *event;
629 unsigned long irq_flags;
630 int gfp = GFP_KERNEL;
631 int ret = 0;
632
633 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
634 icnss_driver_event_to_str(type), type, current->comm,
635 flags, penv->state);
636
637 if (type >= ICNSS_DRIVER_EVENT_MAX) {
638 icnss_pr_err("Invalid Event type: %d, can't post", type);
639 return -EINVAL;
640 }
641
642 if (in_interrupt() || irqs_disabled())
643 gfp = GFP_ATOMIC;
644
645 event = kzalloc(sizeof(*event), gfp);
646 if (event == NULL)
647 return -ENOMEM;
648
649 icnss_pm_stay_awake(penv);
650
651 event->type = type;
652 event->data = data;
653 init_completion(&event->complete);
654 event->ret = ICNSS_EVENT_PENDING;
655 event->sync = !!(flags & ICNSS_EVENT_SYNC);
656
657 spin_lock_irqsave(&penv->event_lock, irq_flags);
658 list_add_tail(&event->list, &penv->event_list);
659 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
660
661 penv->stats.events[type].posted++;
662 queue_work(penv->event_wq, &penv->event_work);
663
664 if (!(flags & ICNSS_EVENT_SYNC))
665 goto out;
666
667 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
668 wait_for_completion(&event->complete);
669 else
670 ret = wait_for_completion_interruptible(&event->complete);
671
672 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
673 icnss_driver_event_to_str(type), type, penv->state, ret,
674 event->ret);
675
676 spin_lock_irqsave(&penv->event_lock, irq_flags);
677 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
678 event->sync = false;
679 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
680 ret = -EINTR;
681 goto out;
682 }
683 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
684
685 ret = event->ret;
686 kfree(event);
687
688out:
689 icnss_pm_relax(penv);
690 return ret;
691}
692
693static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
694 uint64_t voltage_uv)
695{
696 int ret;
697 struct wlfw_vbatt_req_msg_v01 req;
698 struct wlfw_vbatt_resp_msg_v01 resp;
699 struct msg_desc req_desc, resp_desc;
700
701 if (!priv->wlfw_clnt) {
702 ret = -ENODEV;
703 goto out;
704 }
705
706 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
707 penv->state);
708
709 memset(&req, 0, sizeof(req));
710 memset(&resp, 0, sizeof(resp));
711
712 req.voltage_uv = voltage_uv;
713
714 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
715 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
716 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
717
718 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
719 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
720 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
721
722 priv->stats.vbatt_req++;
723
724 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
725 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
726 if (ret < 0) {
727 icnss_pr_err("Send vbatt req failed %d\n", ret);
728 goto out;
729 }
730
731 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
732 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
733 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +0530734 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800735 goto out;
736 }
737 priv->stats.vbatt_resp++;
738
739out:
740 priv->stats.vbatt_req_err++;
741 return ret;
742}
743
744static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
745{
746 int ret = 0;
747 struct qpnp_vadc_result adc_result;
748
749 if (!priv->vadc_dev) {
750 icnss_pr_err("VADC dev doesn't exists\n");
751 ret = -EINVAL;
752 goto out;
753 }
754
755 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
756 if (ret) {
757 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
758 VADC_VPH_PWR, ret);
759 goto out;
760 }
761
762 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
763 adc_result.physical, adc_result.measurement);
764
765 *result_uv = adc_result.physical;
766out:
767 return ret;
768}
769
770static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
771{
772 struct icnss_priv *priv = ctx;
773 uint64_t vph_pwr = 0;
774 uint64_t vph_pwr_prev;
775 int ret = 0;
776 bool update = true;
777
778 if (!priv) {
779 icnss_pr_err("Priv pointer is NULL\n");
780 return;
781 }
782
783 vph_pwr_prev = priv->vph_pwr;
784
785 ret = icnss_get_phone_power(priv, &vph_pwr);
786 if (ret)
787 return;
788
789 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
790 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
791 update = false;
792 priv->vph_monitor_params.state_request =
793 ADC_TM_HIGH_THR_ENABLE;
794 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
795 ICNSS_THRESHOLD_GUARD;
796 priv->vph_monitor_params.low_thr = 0;
797 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
798 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
799 update = false;
800 priv->vph_monitor_params.state_request =
801 ADC_TM_LOW_THR_ENABLE;
802 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
803 ICNSS_THRESHOLD_GUARD;
804 priv->vph_monitor_params.high_thr = 0;
805 } else {
806 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
807 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
808 update = false;
809 priv->vph_monitor_params.state_request =
810 ADC_TM_HIGH_LOW_THR_ENABLE;
811 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
812 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
813 }
814
815 priv->vph_pwr = vph_pwr;
816
817 if (update)
818 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
819
820 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
821 priv->vph_monitor_params.low_thr,
822 priv->vph_monitor_params.high_thr);
823 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
824 &priv->vph_monitor_params);
825 if (ret)
826 icnss_pr_err("TM channel setup failed %d\n", ret);
827}
828
829static int icnss_setup_vph_monitor(struct icnss_priv *priv)
830{
831 int ret = 0;
832
833 if (!priv->adc_tm_dev) {
834 icnss_pr_err("ADC TM handler is NULL\n");
835 ret = -EINVAL;
836 goto out;
837 }
838
839 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
840 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
841 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
842 priv->vph_monitor_params.channel = VADC_VPH_PWR;
843 priv->vph_monitor_params.btm_ctx = priv;
844 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
845 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
846 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
847 priv->vph_monitor_params.low_thr,
848 priv->vph_monitor_params.high_thr);
849
850 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
851 &priv->vph_monitor_params);
852 if (ret)
853 icnss_pr_err("TM channel setup failed %d\n", ret);
854out:
855 return ret;
856}
857
858static int icnss_init_vph_monitor(struct icnss_priv *priv)
859{
860 int ret = 0;
861
862 if (test_bit(VBATT_DISABLE, &quirks))
863 goto out;
864
865 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
866 if (ret)
867 goto out;
868
869 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
870
871 ret = icnss_setup_vph_monitor(priv);
872 if (ret)
873 goto out;
874out:
875 return ret;
876}
877
878
879static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
880{
881 struct msg_desc ind_desc;
882 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
883 int ret = 0;
884
885 if (!penv || !penv->wlfw_clnt) {
886 ret = -ENODEV;
887 goto out;
888 }
889
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530890 memset(&ind_msg, 0, sizeof(ind_msg));
891
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800892 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
893 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
894 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
895
896 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
897 if (ret < 0) {
898 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
899 ret, msg_len);
900 goto out;
901 }
902
903 /* store pin result locally */
904 if (ind_msg.pwr_pin_result_valid)
905 penv->pwr_pin_result = ind_msg.pwr_pin_result;
906 if (ind_msg.phy_io_pin_result_valid)
907 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
908 if (ind_msg.rf_pin_result_valid)
909 penv->rf_pin_result = ind_msg.rf_pin_result;
910
911 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
912 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
913 ind_msg.rf_pin_result);
914
915 penv->stats.pin_connect_result++;
916out:
917 return ret;
918}
919
Yuanyuan Liu68939762017-04-04 16:43:03 -0700920static int icnss_vreg_on(struct icnss_priv *priv)
921{
922 int ret = 0;
923 struct icnss_vreg_info *vreg_info;
924 int i;
925
926 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
927 vreg_info = &priv->vreg_info[i];
928
929 if (!vreg_info->reg)
930 continue;
931
932 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
933
934 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
935 vreg_info->max_v);
936 if (ret) {
937 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
938 vreg_info->name, vreg_info->min_v,
939 vreg_info->max_v, ret);
940 break;
941 }
942
943 if (vreg_info->load_ua) {
944 ret = regulator_set_load(vreg_info->reg,
945 vreg_info->load_ua);
946 if (ret < 0) {
947 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
948 vreg_info->name,
949 vreg_info->load_ua, ret);
950 break;
951 }
952 }
953
954 ret = regulator_enable(vreg_info->reg);
955 if (ret) {
956 icnss_pr_err("Regulator %s, can't enable: %d\n",
957 vreg_info->name, ret);
958 break;
959 }
960
961 if (vreg_info->settle_delay)
962 udelay(vreg_info->settle_delay);
963 }
964
965 if (!ret)
966 return 0;
967
968 for (; i >= 0; i--) {
969 vreg_info = &priv->vreg_info[i];
970
971 if (!vreg_info->reg)
972 continue;
973
974 regulator_disable(vreg_info->reg);
975 regulator_set_load(vreg_info->reg, 0);
976 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
977 }
978
979 return ret;
980}
981
982static int icnss_vreg_off(struct icnss_priv *priv)
983{
984 int ret = 0;
985 struct icnss_vreg_info *vreg_info;
986 int i;
987
988 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
989 vreg_info = &priv->vreg_info[i];
990
991 if (!vreg_info->reg)
992 continue;
993
994 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
995
996 ret = regulator_disable(vreg_info->reg);
997 if (ret)
998 icnss_pr_err("Regulator %s, can't disable: %d\n",
999 vreg_info->name, ret);
1000
1001 ret = regulator_set_load(vreg_info->reg, 0);
1002 if (ret < 0)
1003 icnss_pr_err("Regulator %s, can't set load: %d\n",
1004 vreg_info->name, ret);
1005
1006 ret = regulator_set_voltage(vreg_info->reg, 0,
1007 vreg_info->max_v);
1008 if (ret)
1009 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1010 vreg_info->name, ret);
1011 }
1012
1013 return ret;
1014}
1015
1016static int icnss_clk_init(struct icnss_priv *priv)
1017{
1018 struct icnss_clk_info *clk_info;
1019 int i;
1020 int ret = 0;
1021
1022 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1023 clk_info = &priv->clk_info[i];
1024
1025 if (!clk_info->handle)
1026 continue;
1027
1028 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1029
1030 if (clk_info->freq) {
1031 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1032
1033 if (ret) {
1034 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1035 clk_info->name, clk_info->freq,
1036 ret);
1037 break;
1038 }
1039 }
1040
1041 ret = clk_prepare_enable(clk_info->handle);
1042 if (ret) {
1043 icnss_pr_err("Clock %s, can't enable: %d\n",
1044 clk_info->name, ret);
1045 break;
1046 }
1047 }
1048
1049 if (ret == 0)
1050 return 0;
1051
1052 for (; i >= 0; i--) {
1053 clk_info = &priv->clk_info[i];
1054
1055 if (!clk_info->handle)
1056 continue;
1057
1058 clk_disable_unprepare(clk_info->handle);
1059 }
1060
1061 return ret;
1062}
1063
1064static int icnss_clk_deinit(struct icnss_priv *priv)
1065{
1066 struct icnss_clk_info *clk_info;
1067 int i;
1068
1069 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1070 clk_info = &priv->clk_info[i];
1071
1072 if (!clk_info->handle)
1073 continue;
1074
1075 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1076
1077 clk_disable_unprepare(clk_info->handle);
1078 }
1079
1080 return 0;
1081}
1082
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001083static int icnss_hw_power_on(struct icnss_priv *priv)
1084{
1085 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001086
1087 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1088
Yuanyuan Liu68939762017-04-04 16:43:03 -07001089 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001090 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001091 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001092 return ret;
1093 }
1094 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001095 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001096
Yuanyuan Liu68939762017-04-04 16:43:03 -07001097 ret = icnss_vreg_on(priv);
1098 if (ret)
1099 goto out;
1100
1101 ret = icnss_clk_init(priv);
1102 if (ret)
1103 goto vreg_off;
1104
1105 return ret;
1106
1107vreg_off:
1108 icnss_vreg_off(priv);
1109out:
1110 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001111 return ret;
1112}
1113
1114static int icnss_hw_power_off(struct icnss_priv *priv)
1115{
1116 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001117
1118 if (test_bit(HW_ALWAYS_ON, &quirks))
1119 return 0;
1120
Yuanyuan Liu16c89c32018-01-10 15:52:06 -08001121 if (test_bit(ICNSS_FW_DOWN, &priv->state))
1122 return 0;
1123
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001124 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1125
Yuanyuan Liu68939762017-04-04 16:43:03 -07001126 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001127 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001128 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001129 return ret;
1130 }
1131 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001132 spin_unlock(&priv->on_off_lock);
1133
1134 icnss_clk_deinit(priv);
1135
1136 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001137
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001138 return ret;
1139}
1140
1141int icnss_power_on(struct device *dev)
1142{
1143 struct icnss_priv *priv = dev_get_drvdata(dev);
1144
1145 if (!priv) {
1146 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1147 dev, priv);
1148 return -EINVAL;
1149 }
1150
1151 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1152
1153 return icnss_hw_power_on(priv);
1154}
1155EXPORT_SYMBOL(icnss_power_on);
1156
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001157bool icnss_is_fw_ready(void)
1158{
1159 if (!penv)
1160 return false;
1161 else
1162 return test_bit(ICNSS_FW_READY, &penv->state);
1163}
1164EXPORT_SYMBOL(icnss_is_fw_ready);
1165
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001166bool icnss_is_fw_down(void)
1167{
1168 if (!penv)
1169 return false;
Sameer Thalappil28c07752018-04-04 18:20:16 -07001170
1171 return test_bit(ICNSS_FW_DOWN, &penv->state) ||
1172 test_bit(ICNSS_PD_RESTART, &penv->state);
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001173}
1174EXPORT_SYMBOL(icnss_is_fw_down);
1175
Anurag Chouhanc4997342018-07-17 14:35:54 +05301176bool icnss_is_rejuvenate(void)
1177{
1178 if (!penv)
1179 return false;
1180 else
1181 return test_bit(ICNSS_REJUVENATE, &penv->state);
1182}
1183EXPORT_SYMBOL(icnss_is_rejuvenate);
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08001184
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001185int icnss_power_off(struct device *dev)
1186{
1187 struct icnss_priv *priv = dev_get_drvdata(dev);
1188
1189 if (!priv) {
1190 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1191 dev, priv);
1192 return -EINVAL;
1193 }
1194
1195 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1196
1197 return icnss_hw_power_off(priv);
1198}
1199EXPORT_SYMBOL(icnss_power_off);
1200
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001201static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
1202{
1203 struct icnss_priv *priv = ctx;
1204
1205 if (priv)
1206 priv->force_err_fatal = true;
1207
1208 icnss_pr_err("Received force error fatal request from FW\n");
1209
1210 return IRQ_HANDLED;
1211}
1212
Sameer Thalappil73eba352018-02-06 14:54:41 -08001213static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
1214{
1215 struct icnss_priv *priv = ctx;
1216
1217 icnss_pr_err("Received early crash indication from FW\n");
1218
1219 if (priv) {
1220 set_bit(ICNSS_FW_DOWN, &priv->state);
1221 icnss_ignore_qmi_timeout(true);
1222 }
1223
1224 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
1225 0, NULL);
1226
1227 return IRQ_HANDLED;
1228}
1229
Anurag Chouhan306ba302018-04-17 11:30:04 +05301230static void register_fw_error_notifications(struct device *dev)
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001231{
Anurag Chouhan306ba302018-04-17 11:30:04 +05301232 struct icnss_priv *priv = dev_get_drvdata(dev);
1233 int gpio = 0, irq = 0, ret = 0;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001234
Anurag Chouhan306ba302018-04-17 11:30:04 +05301235 if (!priv)
1236 return;
1237
1238 if (!of_find_property(dev->of_node, "qcom,gpio-force-fatal-error",
1239 NULL)) {
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001240 icnss_pr_dbg("Error fatal smp2p handler not registered\n");
1241 return;
1242 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301243 gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-force-fatal-error",
1244 0);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001245 if (!gpio_is_valid(gpio)) {
1246 icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
1247 return;
1248 }
1249 irq = gpio_to_irq(gpio);
1250 if (irq < 0) {
1251 icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
1252 return;
1253 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301254 ret = devm_request_irq(dev, irq, fw_error_fatal_handler,
1255 IRQF_TRIGGER_RISING, "wlanfw-err", priv);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001256 if (ret < 0) {
Sameer Thalappil73eba352018-02-06 14:54:41 -08001257 icnss_pr_err("Unable to register for error fatal IRQ handler %d",
Anurag Chouhan306ba302018-04-17 11:30:04 +05301258 irq);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001259 return;
1260 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301261 icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq);
1262 priv->fw_error_fatal_irq = irq;
1263}
Sameer Thalappil73eba352018-02-06 14:54:41 -08001264
Anurag Chouhan306ba302018-04-17 11:30:04 +05301265static void register_early_crash_notifications(struct device *dev)
1266{
1267 struct icnss_priv *priv = dev_get_drvdata(dev);
1268 int gpio = 0, irq = 0, ret = 0;
1269
1270 if (!priv)
1271 return;
1272
1273 if (!of_find_property(dev->of_node, "qcom,gpio-early-crash-ind",
1274 NULL)) {
Sameer Thalappil73eba352018-02-06 14:54:41 -08001275 icnss_pr_dbg("FW early crash indication handler not registered\n");
1276 return;
1277 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301278 gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-early-crash-ind", 0);
Sameer Thalappil73eba352018-02-06 14:54:41 -08001279 if (!gpio_is_valid(gpio)) {
1280 icnss_pr_err("Invalid GPIO for early crash indication %d\n",
1281 gpio);
1282 return;
1283 }
1284 irq = gpio_to_irq(gpio);
1285 if (irq < 0) {
1286 icnss_pr_err("Invalid IRQ for early crash indication %u\n",
1287 irq);
1288 return;
1289 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301290 ret = devm_request_irq(dev, irq, fw_crash_indication_handler,
1291 IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind",
1292 priv);
Sameer Thalappil73eba352018-02-06 14:54:41 -08001293 if (ret < 0) {
1294 icnss_pr_err("Unable to register for early crash indication IRQ handler %d",
1295 irq);
1296 return;
1297 }
Anurag Chouhan306ba302018-04-17 11:30:04 +05301298 icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq);
1299 priv->fw_early_crash_irq = irq;
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08001300}
1301
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001302static int wlfw_msa_mem_info_send_sync_msg(void)
1303{
1304 int ret;
1305 int i;
1306 struct wlfw_msa_info_req_msg_v01 req;
1307 struct wlfw_msa_info_resp_msg_v01 resp;
1308 struct msg_desc req_desc, resp_desc;
1309
1310 if (!penv || !penv->wlfw_clnt)
1311 return -ENODEV;
1312
1313 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1314
1315 memset(&req, 0, sizeof(req));
1316 memset(&resp, 0, sizeof(resp));
1317
1318 req.msa_addr = penv->msa_pa;
1319 req.size = penv->msa_mem_size;
1320
1321 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1322 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1323 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1324
1325 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1326 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1327 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1328
1329 penv->stats.msa_info_req++;
1330
1331 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1332 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1333 if (ret < 0) {
1334 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1335 goto out;
1336 }
1337
1338 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1339 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1340 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301341 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001342 goto out;
1343 }
1344
1345 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1346 resp.mem_region_info_len);
1347
Yuanyuan Liu68939762017-04-04 16:43:03 -07001348 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001349 icnss_pr_err("Invalid memory region length received: %d\n",
1350 resp.mem_region_info_len);
1351 ret = -EINVAL;
1352 goto out;
1353 }
1354
1355 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001356 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001357 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001358 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001359 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001360 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001361 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001362 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001363 resp.mem_region_info[i].secure_flag;
1364 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001365 i, penv->mem_region[i].reg_addr,
1366 penv->mem_region[i].size,
1367 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001368 }
1369
1370 return 0;
1371
1372out:
1373 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001374 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001375 return ret;
1376}
1377
1378static int wlfw_msa_ready_send_sync_msg(void)
1379{
1380 int ret;
1381 struct wlfw_msa_ready_req_msg_v01 req;
1382 struct wlfw_msa_ready_resp_msg_v01 resp;
1383 struct msg_desc req_desc, resp_desc;
1384
1385 if (!penv || !penv->wlfw_clnt)
1386 return -ENODEV;
1387
1388 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1389 penv->state);
1390
1391 memset(&req, 0, sizeof(req));
1392 memset(&resp, 0, sizeof(resp));
1393
1394 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1395 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1396 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1397
1398 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1399 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1400 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1401
1402 penv->stats.msa_ready_req++;
1403 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1404 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1405 if (ret < 0) {
1406 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1407 goto out;
1408 }
1409
1410 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1411 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1412 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301413 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001414 goto out;
1415 }
1416 penv->stats.msa_ready_resp++;
1417
1418 return 0;
1419
1420out:
1421 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001422 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001423 return ret;
1424}
1425
1426static int wlfw_ind_register_send_sync_msg(void)
1427{
1428 int ret;
1429 struct wlfw_ind_register_req_msg_v01 req;
1430 struct wlfw_ind_register_resp_msg_v01 resp;
1431 struct msg_desc req_desc, resp_desc;
1432
1433 if (!penv || !penv->wlfw_clnt)
1434 return -ENODEV;
1435
1436 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1437 penv->state);
1438
1439 memset(&req, 0, sizeof(req));
1440 memset(&resp, 0, sizeof(resp));
1441
1442 req.client_id_valid = 1;
1443 req.client_id = WLFW_CLIENT_ID;
1444 req.fw_ready_enable_valid = 1;
1445 req.fw_ready_enable = 1;
1446 req.msa_ready_enable_valid = 1;
1447 req.msa_ready_enable = 1;
1448 req.pin_connect_result_enable_valid = 1;
1449 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001450 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1451 req.rejuvenate_enable_valid = 1;
1452 req.rejuvenate_enable = 1;
1453 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001454
1455 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1456 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1457 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1458
1459 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1460 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1461 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1462
1463 penv->stats.ind_register_req++;
1464
1465 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1466 &resp_desc, &resp, sizeof(resp),
1467 WLFW_TIMEOUT_MS);
1468 if (ret < 0) {
1469 icnss_pr_err("Send indication register req failed %d\n", ret);
1470 goto out;
1471 }
1472
1473 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1474 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1475 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301476 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001477 goto out;
1478 }
1479 penv->stats.ind_register_resp++;
1480
1481 return 0;
1482
1483out:
1484 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001485 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001486 return ret;
1487}
1488
1489static int wlfw_cap_send_sync_msg(void)
1490{
1491 int ret;
1492 struct wlfw_cap_req_msg_v01 req;
1493 struct wlfw_cap_resp_msg_v01 resp;
1494 struct msg_desc req_desc, resp_desc;
1495
1496 if (!penv || !penv->wlfw_clnt)
1497 return -ENODEV;
1498
1499 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1500
1501 memset(&resp, 0, sizeof(resp));
1502
1503 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1504 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1505 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1506
1507 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1508 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1509 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1510
1511 penv->stats.cap_req++;
1512 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1513 &resp_desc, &resp, sizeof(resp),
1514 WLFW_TIMEOUT_MS);
1515 if (ret < 0) {
1516 icnss_pr_err("Send capability req failed %d\n", ret);
1517 goto out;
1518 }
1519
1520 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1521 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1522 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301523 ret = -resp.resp.result;
1524 if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
1525 icnss_pr_err("RF card Not present");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001526 goto out;
1527 }
1528
1529 penv->stats.cap_resp++;
1530 /* store cap locally */
1531 if (resp.chip_info_valid)
1532 penv->chip_info = resp.chip_info;
1533 if (resp.board_info_valid)
1534 penv->board_info = resp.board_info;
1535 else
1536 penv->board_info.board_id = 0xFF;
1537 if (resp.soc_info_valid)
1538 penv->soc_info = resp.soc_info;
1539 if (resp.fw_version_info_valid)
1540 penv->fw_version_info = resp.fw_version_info;
1541 if (resp.fw_build_id_valid)
1542 strlcpy(penv->fw_build_id, resp.fw_build_id,
1543 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1544
1545 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",
1546 penv->chip_info.chip_id, penv->chip_info.chip_family,
1547 penv->board_info.board_id, penv->soc_info.soc_id,
1548 penv->fw_version_info.fw_version,
1549 penv->fw_version_info.fw_build_timestamp,
1550 penv->fw_build_id);
1551
1552 return 0;
1553
1554out:
1555 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001556 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001557 return ret;
1558}
1559
1560static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1561{
1562 int ret;
1563 struct wlfw_wlan_mode_req_msg_v01 req;
1564 struct wlfw_wlan_mode_resp_msg_v01 resp;
1565 struct msg_desc req_desc, resp_desc;
1566
1567 if (!penv || !penv->wlfw_clnt)
1568 return -ENODEV;
1569
1570 /* During recovery do not send mode request for WLAN OFF as
1571 * FW not able to process it.
1572 */
1573 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1574 mode == QMI_WLFW_OFF_V01)
1575 return 0;
1576
1577 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1578 penv->state, mode);
1579
1580 memset(&req, 0, sizeof(req));
1581 memset(&resp, 0, sizeof(resp));
1582
1583 req.mode = mode;
1584 req.hw_debug_valid = 1;
1585 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1586
1587 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1588 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1589 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1590
1591 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1592 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1593 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1594
1595 penv->stats.mode_req++;
1596 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1597 &resp_desc, &resp, sizeof(resp),
1598 WLFW_TIMEOUT_MS);
1599 if (ret < 0) {
1600 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1601 mode, ret);
1602 goto out;
1603 }
1604
1605 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1606 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1607 mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301608 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001609 goto out;
1610 }
1611 penv->stats.mode_resp++;
1612
1613 return 0;
1614
1615out:
1616 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001617 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001618 return ret;
1619}
1620
1621static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1622{
1623 int ret;
1624 struct wlfw_wlan_cfg_req_msg_v01 req;
1625 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1626 struct msg_desc req_desc, resp_desc;
1627
1628 if (!penv || !penv->wlfw_clnt)
1629 return -ENODEV;
1630
1631 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1632
1633 memset(&req, 0, sizeof(req));
1634 memset(&resp, 0, sizeof(resp));
1635
1636 memcpy(&req, data, sizeof(req));
1637
1638 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1639 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1640 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1641
1642 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1643 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1644 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1645
1646 penv->stats.cfg_req++;
1647 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1648 &resp_desc, &resp, sizeof(resp),
1649 WLFW_TIMEOUT_MS);
1650 if (ret < 0) {
1651 icnss_pr_err("Send config req failed %d\n", ret);
1652 goto out;
1653 }
1654
1655 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1656 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1657 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301658 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001659 goto out;
1660 }
1661 penv->stats.cfg_resp++;
1662
1663 return 0;
1664
1665out:
1666 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001667 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001668 return ret;
1669}
1670
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001671static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001672{
1673 int ret;
1674 struct wlfw_ini_req_msg_v01 req;
1675 struct wlfw_ini_resp_msg_v01 resp;
1676 struct msg_desc req_desc, resp_desc;
1677
1678 if (!penv || !penv->wlfw_clnt)
1679 return -ENODEV;
1680
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001681 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1682 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001683
1684 memset(&req, 0, sizeof(req));
1685 memset(&resp, 0, sizeof(resp));
1686
1687 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001688 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001689
1690 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1691 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1692 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1693
1694 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1695 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1696 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1697
1698 penv->stats.ini_req++;
1699
1700 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1701 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1702 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001703 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1704 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001705 goto out;
1706 }
1707
1708 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001709 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1710 fw_log_mode, resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301711 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001712 goto out;
1713 }
1714 penv->stats.ini_resp++;
1715
1716 return 0;
1717
1718out:
1719 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001720 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001721 return ret;
1722}
1723
1724static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1725 uint32_t offset, uint32_t mem_type,
1726 uint32_t data_len, uint8_t *data)
1727{
1728 int ret;
1729 struct wlfw_athdiag_read_req_msg_v01 req;
1730 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1731 struct msg_desc req_desc, resp_desc;
1732
1733 if (!priv->wlfw_clnt) {
1734 ret = -ENODEV;
1735 goto out;
1736 }
1737
1738 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1739 priv->state, offset, mem_type, data_len);
1740
1741 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1742 if (!resp) {
1743 ret = -ENOMEM;
1744 goto out;
1745 }
1746 memset(&req, 0, sizeof(req));
1747
1748 req.offset = offset;
1749 req.mem_type = mem_type;
1750 req.data_len = data_len;
1751
1752 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1753 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1754 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1755
1756 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1757 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1758 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1759
1760 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1761 &resp_desc, resp, sizeof(*resp),
1762 WLFW_TIMEOUT_MS);
1763 if (ret < 0) {
1764 icnss_pr_err("send athdiag read req failed %d\n", ret);
1765 goto out;
1766 }
1767
1768 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1769 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1770 resp->resp.result, resp->resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301771 ret = -resp->resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001772 goto out;
1773 }
1774
Yuanyuan Liu68939762017-04-04 16:43:03 -07001775 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001776 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1777 resp->data_valid, resp->data_len);
1778 ret = -EINVAL;
1779 goto out;
1780 }
1781
1782 memcpy(data, resp->data, resp->data_len);
1783
1784out:
1785 kfree(resp);
1786 return ret;
1787}
1788
1789static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1790 uint32_t offset, uint32_t mem_type,
1791 uint32_t data_len, uint8_t *data)
1792{
1793 int ret;
1794 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1795 struct wlfw_athdiag_write_resp_msg_v01 resp;
1796 struct msg_desc req_desc, resp_desc;
1797
1798 if (!priv->wlfw_clnt) {
1799 ret = -ENODEV;
1800 goto out;
1801 }
1802
1803 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1804 priv->state, offset, mem_type, data_len, data);
1805
1806 req = kzalloc(sizeof(*req), GFP_KERNEL);
1807 if (!req) {
1808 ret = -ENOMEM;
1809 goto out;
1810 }
1811 memset(&resp, 0, sizeof(resp));
1812
1813 req->offset = offset;
1814 req->mem_type = mem_type;
1815 req->data_len = data_len;
1816 memcpy(req->data, data, data_len);
1817
1818 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1819 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1820 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1821
1822 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1823 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1824 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1825
1826 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1827 &resp_desc, &resp, sizeof(resp),
1828 WLFW_TIMEOUT_MS);
1829 if (ret < 0) {
1830 icnss_pr_err("send athdiag write req failed %d\n", ret);
1831 goto out;
1832 }
1833
1834 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1835 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1836 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301837 ret = -resp.resp.result;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001838 goto out;
1839 }
1840out:
1841 kfree(req);
1842 return ret;
1843}
1844
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001845static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1846{
1847 struct msg_desc ind_desc;
1848 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1849 int ret = 0;
1850
1851 if (!penv || !penv->wlfw_clnt) {
1852 ret = -ENODEV;
1853 goto out;
1854 }
1855
1856 memset(&ind_msg, 0, sizeof(ind_msg));
1857
1858 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1859 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1860 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1861
1862 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1863 if (ret < 0) {
1864 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1865 ret, msg_len);
1866 goto out;
1867 }
1868
1869 if (ind_msg.cause_for_rejuvenation_valid)
1870 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1871 else
1872 penv->cause_for_rejuvenation = 0;
1873 if (ind_msg.requesting_sub_system_valid)
1874 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1875 else
1876 penv->requesting_sub_system = 0;
1877 if (ind_msg.line_number_valid)
1878 penv->line_number = ind_msg.line_number;
1879 else
1880 penv->line_number = 0;
1881 if (ind_msg.function_name_valid)
1882 memcpy(penv->function_name, ind_msg.function_name,
1883 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1884 else
1885 memset(penv->function_name, 0,
1886 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1887
1888 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1889 penv->cause_for_rejuvenation,
1890 penv->requesting_sub_system,
1891 penv->line_number,
1892 penv->function_name);
1893
1894 penv->stats.rejuvenate_ind++;
1895out:
1896 return ret;
1897}
1898
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001899static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1900{
1901 int ret;
1902 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1903 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1904 struct msg_desc req_desc, resp_desc;
1905
1906 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1907 priv->state);
1908
1909 memset(&req, 0, sizeof(req));
1910 memset(&resp, 0, sizeof(resp));
1911
1912 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1913 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1914 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1915
1916 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1917 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1918 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1919
1920 priv->stats.rejuvenate_ack_req++;
1921 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1922 &resp_desc, &resp, sizeof(resp),
1923 WLFW_TIMEOUT_MS);
1924 if (ret < 0) {
1925 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1926 goto out;
1927 }
1928
1929 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1930 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1931 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301932 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001933 goto out;
1934 }
1935 priv->stats.rejuvenate_ack_resp++;
1936 return 0;
1937
1938out:
1939 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001940 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001941 return ret;
1942}
1943
1944static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1945 uint64_t dynamic_feature_mask)
1946{
1947 int ret;
1948 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1949 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1950 struct msg_desc req_desc, resp_desc;
1951
1952 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1953 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1954 priv->state);
1955 return -EINVAL;
1956 }
1957
1958 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1959 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1960 return 0;
1961 }
1962
1963 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1964 dynamic_feature_mask, priv->state);
1965
1966 memset(&req, 0, sizeof(req));
1967 memset(&resp, 0, sizeof(resp));
1968
1969 req.mask_valid = 1;
1970 req.mask = dynamic_feature_mask;
1971
1972 req_desc.max_msg_len =
1973 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1974 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1975 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1976
1977 resp_desc.max_msg_len =
1978 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1979 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1980 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1981
1982 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1983 &resp_desc, &resp, sizeof(resp),
1984 WLFW_TIMEOUT_MS);
1985 if (ret < 0) {
1986 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1987 goto out;
1988 }
1989
1990 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1991 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1992 resp.resp.result, resp.resp.error);
Anurag Chouhan52a48a12017-09-08 18:40:14 +05301993 ret = -resp.resp.result;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001994 goto out;
1995 }
1996
1997 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1998 resp.prev_mask_valid, resp.prev_mask,
1999 resp.curr_mask_valid, resp.curr_mask);
2000
2001 return 0;
2002
2003out:
2004 return ret;
2005}
2006
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002007static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
2008{
2009 int ret;
2010
2011 if (!penv || !penv->wlfw_clnt)
2012 return;
2013
Yuanyuan Liu68939762017-04-04 16:43:03 -07002014 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002015
2016 do {
2017 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
2018
2019 if (ret != -ENOMSG)
2020 icnss_pr_err("Error receiving message: %d\n", ret);
2021
Yuanyuan Liu68939762017-04-04 16:43:03 -07002022 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002023}
2024
2025static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
2026 enum qmi_event_type event, void *notify_priv)
2027{
Yuanyuan Liu68939762017-04-04 16:43:03 -07002028 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002029
2030 if (!penv || !penv->wlfw_clnt)
2031 return;
2032
2033 switch (event) {
2034 case QMI_RECV_MSG:
2035 schedule_work(&penv->qmi_recv_msg_work);
2036 break;
2037 default:
2038 icnss_pr_dbg("Unknown Event: %d\n", event);
2039 break;
2040 }
2041}
2042
Yuanyuan Liu68939762017-04-04 16:43:03 -07002043static int icnss_call_driver_uevent(struct icnss_priv *priv,
2044 enum icnss_uevent uevent, void *data)
2045{
2046 struct icnss_uevent_data uevent_data;
2047
2048 if (!priv->ops || !priv->ops->uevent)
2049 return 0;
2050
2051 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
2052 priv->state, uevent);
2053
2054 uevent_data.uevent = uevent;
2055 uevent_data.data = data;
2056
2057 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
2058}
2059
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002060static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
2061 unsigned int msg_id, void *msg,
2062 unsigned int msg_len, void *ind_cb_priv)
2063{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002064 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002065 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002066
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002067 if (!penv)
2068 return;
2069
2070 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
2071
Sameer Thalappil93c00732017-08-18 13:02:32 -07002072 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2073 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
2074 msg_id, penv->state);
2075 return;
2076 }
2077
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002078 switch (msg_id) {
2079 case QMI_WLFW_FW_READY_IND_V01:
2080 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
2081 0, NULL);
2082 break;
2083 case QMI_WLFW_MSA_READY_IND_V01:
2084 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
2085 msg_id);
2086 penv->stats.msa_ready_ind++;
2087 break;
2088 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
2089 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
2090 msg_id);
2091 icnss_qmi_pin_connect_result_ind(msg, msg_len);
2092 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002093 case QMI_WLFW_REJUVENATE_IND_V01:
2094 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
2095 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002096
2097 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08002098 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002099 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
2100 if (event_data == NULL)
2101 return;
2102 event_data->crashed = true;
2103 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002104 fw_down_data.crashed = true;
Anurag Chouhanc4997342018-07-17 14:35:54 +05302105 set_bit(ICNSS_REJUVENATE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002106 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
2107 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002108 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2109 0, event_data);
2110 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002111 default:
2112 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
2113 break;
2114 }
2115}
2116
2117static int icnss_driver_event_server_arrive(void *data)
2118{
2119 int ret = 0;
2120
2121 if (!penv)
2122 return -ENODEV;
2123
2124 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002125 clear_bit(ICNSS_FW_DOWN, &penv->state);
Anurag Chouhan10c3e512018-05-22 18:56:16 +05302126 icnss_ignore_qmi_timeout(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002127
2128 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2129 if (!penv->wlfw_clnt) {
2130 icnss_pr_err("QMI client handle create failed\n");
2131 ret = -ENOMEM;
2132 goto out;
2133 }
2134
2135 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2136 WLFW_SERVICE_VERS_V01,
2137 WLFW_SERVICE_INS_ID_V01);
2138 if (ret < 0) {
2139 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2140 goto fail;
2141 }
2142
2143 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2144 icnss_qmi_wlfw_clnt_ind, penv);
2145 if (ret < 0) {
2146 icnss_pr_err("Failed to register indication callback: %d\n",
2147 ret);
2148 goto fail;
2149 }
2150
2151 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2152
2153 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2154
2155 ret = icnss_hw_power_on(penv);
2156 if (ret)
2157 goto fail;
2158
2159 ret = wlfw_ind_register_send_sync_msg();
2160 if (ret < 0)
2161 goto err_power_on;
2162
2163 if (!penv->msa_va) {
2164 icnss_pr_err("Invalid MSA address\n");
2165 ret = -EINVAL;
2166 goto err_power_on;
2167 }
2168
2169 ret = wlfw_msa_mem_info_send_sync_msg();
2170 if (ret < 0)
2171 goto err_power_on;
2172
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302173 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2174 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2175 if (ret < 0)
2176 goto err_power_on;
2177 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2178 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002179
2180 ret = wlfw_msa_ready_send_sync_msg();
2181 if (ret < 0)
2182 goto err_setup_msa;
2183
2184 ret = wlfw_cap_send_sync_msg();
2185 if (ret < 0)
2186 goto err_setup_msa;
2187
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002188 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2189 dynamic_feature_mask);
2190
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002191 icnss_init_vph_monitor(penv);
2192
Anurag Chouhan306ba302018-04-17 11:30:04 +05302193 if (!penv->fw_error_fatal_irq)
2194 register_fw_error_notifications(&penv->pdev->dev);
2195
2196 if (!penv->fw_early_crash_irq)
2197 register_early_crash_notifications(&penv->pdev->dev);
Sameer Thalappil3c2c7bf2017-12-15 15:00:29 -08002198
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002199 return ret;
2200
2201err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302202 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002203err_power_on:
2204 icnss_hw_power_off(penv);
2205fail:
2206 qmi_handle_destroy(penv->wlfw_clnt);
2207 penv->wlfw_clnt = NULL;
2208out:
2209 ICNSS_ASSERT(0);
2210 return ret;
2211}
2212
2213static int icnss_driver_event_server_exit(void *data)
2214{
2215 if (!penv || !penv->wlfw_clnt)
2216 return -ENODEV;
2217
2218 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2219
2220 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2221 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2222 &penv->vph_monitor_params);
2223
2224 qmi_handle_destroy(penv->wlfw_clnt);
2225
2226 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2227 penv->wlfw_clnt = NULL;
2228
2229 return 0;
2230}
2231
2232static int icnss_call_driver_probe(struct icnss_priv *priv)
2233{
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002234 int ret = 0;
2235 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002236
2237 if (!priv->ops || !priv->ops->probe)
2238 return 0;
2239
Yuanyuan Liu68939762017-04-04 16:43:03 -07002240 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2241 return -EINVAL;
2242
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002243 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2244
2245 icnss_hw_power_on(priv);
2246
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002247 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2248 ret = priv->ops->probe(&priv->pdev->dev);
2249 probe_cnt++;
2250 if (ret != -EPROBE_DEFER)
2251 break;
2252 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002253 if (ret < 0) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002254 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2255 ret, priv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002256 goto out;
2257 }
2258
2259 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2260
2261 return 0;
2262
2263out:
2264 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002265 return ret;
2266}
2267
Yuanyuan Liu68939762017-04-04 16:43:03 -07002268static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2269{
2270 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2271 goto out;
2272
2273 if (!priv->ops || !priv->ops->shutdown)
2274 goto out;
2275
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302276 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2277 goto out;
2278
Yuanyuan Liu68939762017-04-04 16:43:03 -07002279 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2280
2281 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302282 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002283
2284out:
2285 return 0;
2286}
2287
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002288static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002289{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002290 int ret;
2291
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002292 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002293
Anurag Chouhan9de21192017-08-08 15:30:02 +05302294 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002295
Anurag Chouhanc4997342018-07-17 14:35:54 +05302296 clear_bit(ICNSS_REJUVENATE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002297 clear_bit(ICNSS_PD_RESTART, &priv->state);
Sameer Thalappil73eba352018-02-06 14:54:41 -08002298 priv->early_crash_ind = false;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002299
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002300 if (!priv->ops || !priv->ops->reinit)
2301 goto out;
2302
Yuanyuan Liu48a51e42017-12-04 16:01:31 -08002303 if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
2304 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2305 priv->state);
2306 goto out;
2307 }
2308
Yuanyuan Liu68939762017-04-04 16:43:03 -07002309 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002310 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002311
2312 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2313
2314 icnss_hw_power_on(priv);
2315
2316 ret = priv->ops->reinit(&priv->pdev->dev);
2317 if (ret < 0) {
2318 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2319 ret, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002320 if (!priv->allow_recursive_recovery)
2321 ICNSS_ASSERT(false);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002322 goto out_power_off;
2323 }
2324
2325out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302326 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002327 return 0;
2328
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002329call_probe:
2330 return icnss_call_driver_probe(priv);
2331
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002332out_power_off:
2333 icnss_hw_power_off(priv);
2334
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002335 return ret;
2336}
2337
2338
2339static int icnss_driver_event_fw_ready_ind(void *data)
2340{
2341 int ret = 0;
2342
2343 if (!penv)
2344 return -ENODEV;
2345
2346 set_bit(ICNSS_FW_READY, &penv->state);
2347
2348 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2349
2350 icnss_hw_power_off(penv);
2351
2352 if (!penv->pdev) {
2353 icnss_pr_err("Device is not ready\n");
2354 ret = -ENODEV;
2355 goto out;
2356 }
2357
2358 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002359 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002360 else
2361 ret = icnss_call_driver_probe(penv);
2362
2363out:
2364 return ret;
2365}
2366
2367static int icnss_driver_event_register_driver(void *data)
2368{
2369 int ret = 0;
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002370 int probe_cnt = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002371
2372 if (penv->ops)
2373 return -EEXIST;
2374
2375 penv->ops = data;
2376
2377 if (test_bit(SKIP_QMI, &quirks))
2378 set_bit(ICNSS_FW_READY, &penv->state);
2379
Yuanyuan Liu5fa7ec52017-11-30 11:11:42 -08002380 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
2381 icnss_pr_err("FW is in bad state, state: 0x%lx\n",
2382 penv->state);
2383 return -ENODEV;
2384 }
2385
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002386 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2387 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2388 penv->state);
2389 goto out;
2390 }
2391
2392 ret = icnss_hw_power_on(penv);
2393 if (ret)
2394 goto out;
2395
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002396 while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
2397 ret = penv->ops->probe(&penv->pdev->dev);
2398 probe_cnt++;
2399 if (ret != -EPROBE_DEFER)
2400 break;
2401 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002402 if (ret) {
Yuanyuan Liu237c0ad2017-10-09 17:58:17 -07002403 icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
2404 ret, penv->state, probe_cnt);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002405 goto power_off;
2406 }
2407
2408 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2409
2410 return 0;
2411
2412power_off:
2413 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002414out:
2415 return ret;
2416}
2417
2418static int icnss_driver_event_unregister_driver(void *data)
2419{
2420 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2421 penv->ops = NULL;
2422 goto out;
2423 }
2424
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002425 set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002426 if (penv->ops)
2427 penv->ops->remove(&penv->pdev->dev);
2428
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002429 clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002430 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2431
2432 penv->ops = NULL;
2433
2434 icnss_hw_power_off(penv);
2435
2436out:
2437 return 0;
2438}
2439
Yuanyuan Liu68939762017-04-04 16:43:03 -07002440static int icnss_fw_crashed(struct icnss_priv *priv,
2441 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002442{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302443 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002444
2445 set_bit(ICNSS_PD_RESTART, &priv->state);
2446 clear_bit(ICNSS_FW_READY, &priv->state);
2447
2448 icnss_pm_stay_awake(priv);
2449
Yuanyuan Liu68939762017-04-04 16:43:03 -07002450 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2451 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002452
Sameer Thalappil73eba352018-02-06 14:54:41 -08002453 if (event_data && event_data->fw_rejuvenate)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002454 wlfw_rejuvenate_ack_send_sync_msg(priv);
2455
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002456 return 0;
2457}
2458
2459static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2460 void *data)
2461{
2462 int ret = 0;
2463 struct icnss_event_pd_service_down_data *event_data = data;
2464
Anurag Chouhan10c3e512018-05-22 18:56:16 +05302465 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
2466 icnss_ignore_qmi_timeout(false);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002467 goto out;
Anurag Chouhan10c3e512018-05-22 18:56:16 +05302468 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002469
Anurag Chouhan7a92d572018-05-03 16:59:40 +05302470 if (priv->force_err_fatal)
2471 ICNSS_ASSERT(0);
2472
Sameer Thalappil73eba352018-02-06 14:54:41 -08002473 if (priv->early_crash_ind) {
2474 icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
2475 event_data->crashed, priv->state);
2476 goto out;
2477 }
2478
Sameer Thalappil42ed2cc2017-10-30 11:17:01 -07002479 if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002480 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2481 event_data->crashed, priv->state);
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08002482 if (!priv->allow_recursive_recovery)
2483 ICNSS_ASSERT(0);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002484 goto out;
2485 }
2486
Hardik Kantilal Patel3b934072018-01-12 18:02:10 +05302487 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002488
2489out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002490 kfree(data);
2491
2492 return ret;
2493}
2494
Sameer Thalappil73eba352018-02-06 14:54:41 -08002495static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
2496 void *data)
2497{
2498 int ret = 0;
2499
Anurag Chouhan10c3e512018-05-22 18:56:16 +05302500 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
2501 icnss_ignore_qmi_timeout(false);
Sameer Thalappil73eba352018-02-06 14:54:41 -08002502 goto out;
Anurag Chouhan10c3e512018-05-22 18:56:16 +05302503 }
Sameer Thalappil73eba352018-02-06 14:54:41 -08002504
2505 priv->early_crash_ind = true;
2506 icnss_fw_crashed(priv, NULL);
2507
2508out:
2509 kfree(data);
Sameer Thalappil73eba352018-02-06 14:54:41 -08002510
2511 return ret;
2512}
2513
2514
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002515static void icnss_driver_event_work(struct work_struct *work)
2516{
2517 struct icnss_driver_event *event;
2518 unsigned long flags;
2519 int ret;
2520
2521 icnss_pm_stay_awake(penv);
2522
2523 spin_lock_irqsave(&penv->event_lock, flags);
2524
2525 while (!list_empty(&penv->event_list)) {
2526 event = list_first_entry(&penv->event_list,
2527 struct icnss_driver_event, list);
2528 list_del(&event->list);
2529 spin_unlock_irqrestore(&penv->event_lock, flags);
2530
2531 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2532 icnss_driver_event_to_str(event->type),
2533 event->sync ? "-sync" : "", event->type,
2534 penv->state);
2535
2536 switch (event->type) {
2537 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2538 ret = icnss_driver_event_server_arrive(event->data);
2539 break;
2540 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2541 ret = icnss_driver_event_server_exit(event->data);
2542 break;
2543 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2544 ret = icnss_driver_event_fw_ready_ind(event->data);
2545 break;
2546 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2547 ret = icnss_driver_event_register_driver(event->data);
2548 break;
2549 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2550 ret = icnss_driver_event_unregister_driver(event->data);
2551 break;
2552 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2553 ret = icnss_driver_event_pd_service_down(penv,
2554 event->data);
2555 break;
Sameer Thalappil73eba352018-02-06 14:54:41 -08002556 case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
2557 ret = icnss_driver_event_early_crash_ind(penv,
2558 event->data);
2559 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002560 default:
2561 icnss_pr_err("Invalid Event type: %d", event->type);
2562 kfree(event);
2563 continue;
2564 }
2565
2566 penv->stats.events[event->type].processed++;
2567
2568 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2569 icnss_driver_event_to_str(event->type),
2570 event->sync ? "-sync" : "", event->type, ret,
2571 penv->state);
2572
2573 spin_lock_irqsave(&penv->event_lock, flags);
2574 if (event->sync) {
2575 event->ret = ret;
2576 complete(&event->complete);
2577 continue;
2578 }
2579 spin_unlock_irqrestore(&penv->event_lock, flags);
2580
2581 kfree(event);
2582
2583 spin_lock_irqsave(&penv->event_lock, flags);
2584 }
2585 spin_unlock_irqrestore(&penv->event_lock, flags);
2586
2587 icnss_pm_relax(penv);
2588}
2589
2590static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2591 unsigned long code,
2592 void *_cmd)
2593{
2594 int ret = 0;
2595
2596 if (!penv)
2597 return -ENODEV;
2598
2599 icnss_pr_dbg("Event Notify: code: %ld", code);
2600
2601 switch (code) {
2602 case QMI_SERVER_ARRIVE:
2603 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2604 0, NULL);
2605 break;
2606
2607 case QMI_SERVER_EXIT:
Anurag Chouhan7883c632018-04-18 19:21:20 +05302608 set_bit(ICNSS_FW_DOWN, &penv->state);
2609 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002610 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2611 0, NULL);
2612 break;
2613 default:
2614 icnss_pr_dbg("Invalid code: %ld", code);
2615 break;
2616 }
2617 return ret;
2618}
2619
2620static int icnss_msa0_ramdump(struct icnss_priv *priv)
2621{
2622 struct ramdump_segment segment;
2623
2624 memset(&segment, 0, sizeof(segment));
2625 segment.v_address = priv->msa_va;
2626 segment.size = priv->msa_mem_size;
2627 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2628}
2629
2630static struct notifier_block wlfw_clnt_nb = {
2631 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2632};
2633
2634static int icnss_modem_notifier_nb(struct notifier_block *nb,
2635 unsigned long code,
2636 void *data)
2637{
2638 struct icnss_event_pd_service_down_data *event_data;
2639 struct notif_data *notif = data;
2640 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2641 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002642 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302643 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002644
Yuanyuan Liu68939762017-04-04 16:43:03 -07002645 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002646
Sameer Thalappil765cb492017-10-04 17:57:07 -07002647 if (code == SUBSYS_AFTER_SHUTDOWN &&
Yuanyuan Liu52a484b2017-11-15 11:23:45 -08002648 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302649 ret = icnss_assign_msa_perm_all(priv,
Sameer Thalappil765cb492017-10-04 17:57:07 -07002650 ICNSS_MSA_PERM_HLOS_ALL);
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302651 if (!ret) {
2652 icnss_pr_info("Collecting msa0 segment dump\n");
2653 icnss_msa0_ramdump(priv);
2654 icnss_assign_msa_perm_all(priv,
2655 ICNSS_MSA_PERM_WLAN_HW_RW);
2656 } else {
2657 icnss_pr_err("Not able to Collect msa0 segment dump"
2658 "Apps permissions not assigned %d\n", ret);
2659 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002660 return NOTIFY_OK;
2661 }
2662
2663 if (code != SUBSYS_BEFORE_SHUTDOWN)
2664 return NOTIFY_OK;
2665
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002666 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
2667 set_bit(ICNSS_FW_DOWN, &priv->state);
2668 icnss_ignore_qmi_timeout(true);
2669
2670 fw_down_data.crashed = !!notif->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002671 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2672 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002673 icnss_call_driver_uevent(priv,
2674 ICNSS_UEVENT_FW_DOWN,
2675 &fw_down_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002676 return NOTIFY_OK;
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002677 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002678
Yuanyuan Liu68939762017-04-04 16:43:03 -07002679 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2680 priv->state, notif->crashed);
2681
Sameer Thalappil93c00732017-08-18 13:02:32 -07002682 set_bit(ICNSS_FW_DOWN, &priv->state);
2683
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002684 if (notif->crashed)
2685 priv->stats.recovery.root_pd_crash++;
2686 else
2687 priv->stats.recovery.root_pd_shutdown++;
2688
Yuanyuan Liu68939762017-04-04 16:43:03 -07002689 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002690
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002691 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002692
2693 if (event_data == NULL)
2694 return notifier_from_errno(-ENOMEM);
2695
2696 event_data->crashed = notif->crashed;
2697
Yuanyuan Liu68939762017-04-04 16:43:03 -07002698 fw_down_data.crashed = !!notif->crashed;
2699 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2700
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002701 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2702 ICNSS_EVENT_SYNC, event_data);
2703
2704 return NOTIFY_OK;
2705}
2706
2707static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2708{
2709 int ret = 0;
2710
2711 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2712
2713 priv->modem_notify_handler =
2714 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2715
2716 if (IS_ERR(priv->modem_notify_handler)) {
2717 ret = PTR_ERR(priv->modem_notify_handler);
2718 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2719 }
2720
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002721 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002722
2723 return ret;
2724}
2725
2726static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2727{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002728 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002729 return 0;
2730
2731 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2732 &priv->modem_ssr_nb);
2733 priv->modem_notify_handler = NULL;
2734
2735 return 0;
2736}
2737
2738static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2739{
2740 int i;
2741
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002742 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002743 return 0;
2744
2745 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002746 service_notif_unregister_notifier(
2747 priv->service_notifier[i].handle,
2748 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002749
2750 kfree(priv->service_notifier);
2751
2752 priv->service_notifier = NULL;
2753
2754 return 0;
2755}
2756
2757static int icnss_service_notifier_notify(struct notifier_block *nb,
2758 unsigned long notification, void *data)
2759{
2760 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2761 service_notifier_nb);
2762 enum pd_subsys_state *state = data;
2763 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002764 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002765 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002766
Yuanyuan Liu68939762017-04-04 16:43:03 -07002767 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2768 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002769
Yuanyuan Liu68939762017-04-04 16:43:03 -07002770 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2771 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002772
Yuanyuan Liu68939762017-04-04 16:43:03 -07002773 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002774
Yuanyuan Liu68939762017-04-04 16:43:03 -07002775 if (event_data == NULL)
2776 return notifier_from_errno(-ENOMEM);
2777
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002778 event_data->crashed = true;
2779
Yuanyuan Liu68939762017-04-04 16:43:03 -07002780 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002781 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002782 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002783 }
2784
Yuanyuan Liu68939762017-04-04 16:43:03 -07002785 switch (*state) {
2786 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002787 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002788 break;
2789 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002790 cause = ICNSS_ROOT_PD_SHUTDOWN;
2791 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002792 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002793 break;
2794 case USER_PD_STATE_CHANGE:
2795 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2796 cause = ICNSS_HOST_ERROR;
2797 priv->stats.recovery.pdr_host_error++;
2798 } else {
2799 cause = ICNSS_FW_CRASH;
2800 priv->stats.recovery.pdr_fw_crash++;
2801 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002802 break;
2803 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002804 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002805 break;
2806 }
2807
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002808 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2809 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002810event_post:
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002811 if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
2812 set_bit(ICNSS_FW_DOWN, &priv->state);
2813 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002814
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002815 fw_down_data.crashed = event_data->crashed;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08002816 if (test_bit(ICNSS_FW_READY, &priv->state) &&
2817 !test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
Yuanyuan Liu08ab5692017-11-10 12:12:17 -08002818 icnss_call_driver_uevent(priv,
2819 ICNSS_UEVENT_FW_DOWN,
2820 &fw_down_data);
2821 }
2822
2823 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002824 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2825 ICNSS_EVENT_SYNC, event_data);
2826done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002827 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2828 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002829 return NOTIFY_OK;
2830}
2831
2832static int icnss_get_service_location_notify(struct notifier_block *nb,
2833 unsigned long opcode, void *data)
2834{
2835 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2836 get_service_nb);
2837 struct pd_qmi_client_data *pd = data;
2838 int curr_state;
2839 int ret;
2840 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002841 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002842
2843 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2844 priv->state);
2845
2846 if (opcode != LOCATOR_UP)
2847 return NOTIFY_DONE;
2848
2849 if (pd->total_domains == 0) {
2850 icnss_pr_err("Did not find any domains\n");
2851 ret = -ENOENT;
2852 goto out;
2853 }
2854
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002855 notifier = kcalloc(pd->total_domains,
2856 sizeof(struct service_notifier_context),
2857 GFP_KERNEL);
2858 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002859 ret = -ENOMEM;
2860 goto out;
2861 }
2862
2863 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2864
2865 for (i = 0; i < pd->total_domains; i++) {
2866 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2867 pd->domain_list[i].name,
2868 pd->domain_list[i].instance_id);
2869
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002870 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002871 service_notif_register_notifier(pd->domain_list[i].name,
2872 pd->domain_list[i].instance_id,
2873 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002874 notifier[i].instance_id = pd->domain_list[i].instance_id;
2875 strlcpy(notifier[i].name, pd->domain_list[i].name,
2876 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002877
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002878 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002879 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2880 i, pd->domain_list->name,
2881 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002882 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002883 goto free_handle;
2884 }
2885 }
2886
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002887 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002888 priv->total_domains = pd->total_domains;
2889
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002890 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002891
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002892 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2893 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002894
2895 return NOTIFY_OK;
2896
2897free_handle:
2898 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002899 if (notifier[i].handle)
2900 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002901 &priv->service_notifier_nb);
2902 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002903 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002904
2905out:
2906 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2907 priv->state);
2908
2909 return NOTIFY_OK;
2910}
2911
2912
2913static int icnss_pd_restart_enable(struct icnss_priv *priv)
2914{
2915 int ret;
2916
2917 if (test_bit(SSR_ONLY, &quirks)) {
2918 icnss_pr_dbg("PDR disabled through module parameter\n");
2919 return 0;
2920 }
2921
2922 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2923
2924 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2925 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2926 ICNSS_WLAN_SERVICE_NAME,
2927 &priv->get_service_nb);
2928 if (ret) {
2929 icnss_pr_err("Get service location failed: %d\n", ret);
2930 goto out;
2931 }
2932
2933 return 0;
2934out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002935 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002936 return ret;
2937
2938}
2939
2940
2941static int icnss_enable_recovery(struct icnss_priv *priv)
2942{
2943 int ret;
2944
2945 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2946 icnss_pr_dbg("Recovery disabled through module parameter\n");
2947 return 0;
2948 }
2949
2950 if (test_bit(PDR_ONLY, &quirks)) {
2951 icnss_pr_dbg("SSR disabled through module parameter\n");
2952 goto enable_pdr;
2953 }
2954
2955 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2956 &priv->pdev->dev);
2957 if (!priv->msa0_dump_dev)
2958 return -ENOMEM;
2959
2960 icnss_modem_ssr_register_notifier(priv);
2961 if (test_bit(SSR_ONLY, &quirks)) {
2962 icnss_pr_dbg("PDR disabled through module parameter\n");
2963 return 0;
2964 }
2965
2966enable_pdr:
2967 ret = icnss_pd_restart_enable(priv);
2968
2969 if (ret)
2970 return ret;
2971
2972 return 0;
2973}
2974
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05302975int __icnss_register_driver(struct icnss_driver_ops *ops,
2976 struct module *owner, const char *mod_name)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002977{
2978 int ret = 0;
2979
2980 if (!penv || !penv->pdev) {
2981 ret = -ENODEV;
2982 goto out;
2983 }
2984
2985 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2986
2987 if (penv->ops) {
2988 icnss_pr_err("Driver already registered\n");
2989 ret = -EEXIST;
2990 goto out;
2991 }
2992
2993 if (!ops->probe || !ops->remove) {
2994 ret = -EINVAL;
2995 goto out;
2996 }
2997
2998 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302999 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003000
3001 if (ret == -EINTR)
3002 ret = 0;
3003
3004out:
3005 return ret;
3006}
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303007EXPORT_SYMBOL(__icnss_register_driver);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003008
3009int icnss_unregister_driver(struct icnss_driver_ops *ops)
3010{
3011 int ret;
3012
3013 if (!penv || !penv->pdev) {
3014 ret = -ENODEV;
3015 goto out;
3016 }
3017
3018 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
3019
3020 if (!penv->ops) {
3021 icnss_pr_err("Driver not registered\n");
3022 ret = -ENOENT;
3023 goto out;
3024 }
3025
3026 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
3027 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
3028out:
3029 return ret;
3030}
3031EXPORT_SYMBOL(icnss_unregister_driver);
3032
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303033int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003034 irqreturn_t (*handler)(int, void *),
3035 unsigned long flags, const char *name, void *ctx)
3036{
3037 int ret = 0;
3038 unsigned int irq;
3039 struct ce_irq_list *irq_entry;
3040
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303041 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003042 ret = -ENODEV;
3043 goto out;
3044 }
3045
Yuanyuan Liu68939762017-04-04 16:43:03 -07003046 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003047
3048 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3049 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
3050 ret = -EINVAL;
3051 goto out;
3052 }
3053 irq = penv->ce_irqs[ce_id];
3054 irq_entry = &penv->ce_irq_list[ce_id];
3055
3056 if (irq_entry->handler || irq_entry->irq) {
3057 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
3058 irq, ce_id);
3059 ret = -EEXIST;
3060 goto out;
3061 }
3062
3063 ret = request_irq(irq, handler, flags, name, ctx);
3064 if (ret) {
3065 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
3066 irq, ce_id, ret);
3067 goto out;
3068 }
3069 irq_entry->irq = irq;
3070 irq_entry->handler = handler;
3071
Yuanyuan Liu68939762017-04-04 16:43:03 -07003072 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003073
3074 penv->stats.ce_irqs[ce_id].request++;
3075out:
3076 return ret;
3077}
3078EXPORT_SYMBOL(icnss_ce_request_irq);
3079
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303080int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003081{
3082 int ret = 0;
3083 unsigned int irq;
3084 struct ce_irq_list *irq_entry;
3085
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303086 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003087 ret = -ENODEV;
3088 goto out;
3089 }
3090
Yuanyuan Liu68939762017-04-04 16:43:03 -07003091 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003092
3093 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3094 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
3095 ret = -EINVAL;
3096 goto out;
3097 }
3098
3099 irq = penv->ce_irqs[ce_id];
3100 irq_entry = &penv->ce_irq_list[ce_id];
3101 if (!irq_entry->handler || !irq_entry->irq) {
3102 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
3103 ret = -EEXIST;
3104 goto out;
3105 }
3106 free_irq(irq, ctx);
3107 irq_entry->irq = 0;
3108 irq_entry->handler = NULL;
3109
3110 penv->stats.ce_irqs[ce_id].free++;
3111out:
3112 return ret;
3113}
3114EXPORT_SYMBOL(icnss_ce_free_irq);
3115
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303116void icnss_enable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003117{
3118 unsigned int irq;
3119
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303120 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003121 icnss_pr_err("Platform driver not initialized\n");
3122 return;
3123 }
3124
Yuanyuan Liu68939762017-04-04 16:43:03 -07003125 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003126 penv->state);
3127
3128 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3129 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
3130 return;
3131 }
3132
3133 penv->stats.ce_irqs[ce_id].enable++;
3134
3135 irq = penv->ce_irqs[ce_id];
3136 enable_irq(irq);
3137}
3138EXPORT_SYMBOL(icnss_enable_irq);
3139
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303140void icnss_disable_irq(struct device *dev, unsigned int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003141{
3142 unsigned int irq;
3143
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303144 if (!penv || !penv->pdev || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003145 icnss_pr_err("Platform driver not initialized\n");
3146 return;
3147 }
3148
Yuanyuan Liu68939762017-04-04 16:43:03 -07003149 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003150 penv->state);
3151
3152 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
3153 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
3154 ce_id);
3155 return;
3156 }
3157
3158 irq = penv->ce_irqs[ce_id];
3159 disable_irq(irq);
3160
3161 penv->stats.ce_irqs[ce_id].disable++;
3162}
3163EXPORT_SYMBOL(icnss_disable_irq);
3164
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303165int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003166{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303167 if (!penv || !dev) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003168 icnss_pr_err("Platform driver not initialized\n");
3169 return -EINVAL;
3170 }
3171
3172 info->v_addr = penv->mem_base_va;
3173 info->p_addr = penv->mem_base_pa;
3174 info->chip_id = penv->chip_info.chip_id;
3175 info->chip_family = penv->chip_info.chip_family;
3176 info->board_id = penv->board_info.board_id;
3177 info->soc_id = penv->soc_info.soc_id;
3178 info->fw_version = penv->fw_version_info.fw_version;
3179 strlcpy(info->fw_build_timestamp,
3180 penv->fw_version_info.fw_build_timestamp,
3181 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
3182
3183 return 0;
3184}
3185EXPORT_SYMBOL(icnss_get_soc_info);
3186
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303187int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003188{
3189 int ret;
3190
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303191 if (!dev)
3192 return -ENODEV;
3193
Anurag Chouhandca657f2018-05-22 19:07:01 +05303194 if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
3195 !test_bit(ICNSS_FW_READY, &penv->state)) {
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303196 icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
3197 penv->state);
3198 return -EINVAL;
3199 }
3200
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003201 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003202
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003203 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003204 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003205 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3206 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003207 return ret;
3208}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003209EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003210
3211int icnss_athdiag_read(struct device *dev, uint32_t offset,
3212 uint32_t mem_type, uint32_t data_len,
3213 uint8_t *output)
3214{
3215 int ret = 0;
3216 struct icnss_priv *priv = dev_get_drvdata(dev);
3217
3218 if (priv->magic != ICNSS_MAGIC) {
3219 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3220 dev, priv, priv->magic);
3221 return -EINVAL;
3222 }
3223
3224 if (!output || data_len == 0
3225 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3226 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3227 output, data_len);
3228 ret = -EINVAL;
3229 goto out;
3230 }
3231
3232 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3233 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3234 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3235 priv->state);
3236 ret = -EINVAL;
3237 goto out;
3238 }
3239
3240 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3241 data_len, output);
3242out:
3243 return ret;
3244}
3245EXPORT_SYMBOL(icnss_athdiag_read);
3246
3247int icnss_athdiag_write(struct device *dev, uint32_t offset,
3248 uint32_t mem_type, uint32_t data_len,
3249 uint8_t *input)
3250{
3251 int ret = 0;
3252 struct icnss_priv *priv = dev_get_drvdata(dev);
3253
3254 if (priv->magic != ICNSS_MAGIC) {
3255 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3256 dev, priv, priv->magic);
3257 return -EINVAL;
3258 }
3259
3260 if (!input || data_len == 0
3261 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3262 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3263 input, data_len);
3264 ret = -EINVAL;
3265 goto out;
3266 }
3267
3268 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3269 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3270 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3271 priv->state);
3272 ret = -EINVAL;
3273 goto out;
3274 }
3275
3276 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3277 data_len, input);
3278out:
3279 return ret;
3280}
3281EXPORT_SYMBOL(icnss_athdiag_write);
3282
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303283int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003284 enum icnss_driver_mode mode,
3285 const char *host_version)
3286{
3287 struct wlfw_wlan_cfg_req_msg_v01 req;
3288 u32 i;
3289 int ret;
3290
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303291 if (!dev)
3292 return -ENODEV;
3293
Anurag Chouhandca657f2018-05-22 19:07:01 +05303294 if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
3295 !test_bit(ICNSS_FW_READY, &penv->state)) {
Anurag Chouhand2ddbf42018-01-22 12:32:16 +05303296 icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
3297 penv->state);
3298 return -EINVAL;
3299 }
3300
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003301 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3302 mode, config, host_version);
3303
3304 memset(&req, 0, sizeof(req));
3305
3306 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3307 goto skip;
3308
3309 if (!config || !host_version) {
3310 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3311 config, host_version);
3312 ret = -EINVAL;
3313 goto out;
3314 }
3315
3316 req.host_version_valid = 1;
3317 strlcpy(req.host_version, host_version,
3318 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3319
3320 req.tgt_cfg_valid = 1;
3321 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3322 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3323 else
3324 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3325 for (i = 0; i < req.tgt_cfg_len; i++) {
3326 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3327 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3328 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3329 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3330 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3331 }
3332
3333 req.svc_cfg_valid = 1;
3334 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3335 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3336 else
3337 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3338 for (i = 0; i < req.svc_cfg_len; i++) {
3339 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3340 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3341 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3342 }
3343
3344 req.shadow_reg_valid = 1;
3345 if (config->num_shadow_reg_cfg >
3346 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3347 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3348 else
3349 req.shadow_reg_len = config->num_shadow_reg_cfg;
3350
3351 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3352 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3353
3354 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3355 if (ret)
3356 goto out;
3357skip:
3358 ret = wlfw_wlan_mode_send_sync_msg(mode);
3359out:
3360 if (test_bit(SKIP_QMI, &quirks))
3361 ret = 0;
3362
3363 return ret;
3364}
3365EXPORT_SYMBOL(icnss_wlan_enable);
3366
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303367int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003368{
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303369 if (!dev)
3370 return -ENODEV;
3371
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003372 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3373}
3374EXPORT_SYMBOL(icnss_wlan_disable);
3375
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303376bool icnss_is_qmi_disable(struct device *dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003377{
3378 return test_bit(SKIP_QMI, &quirks) ? true : false;
3379}
3380EXPORT_SYMBOL(icnss_is_qmi_disable);
3381
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303382int icnss_get_ce_id(struct device *dev, int irq)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003383{
3384 int i;
3385
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303386 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003387 return -ENODEV;
3388
3389 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3390 if (penv->ce_irqs[i] == irq)
3391 return i;
3392 }
3393
3394 icnss_pr_err("No matching CE id for irq %d\n", irq);
3395
3396 return -EINVAL;
3397}
3398EXPORT_SYMBOL(icnss_get_ce_id);
3399
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303400int icnss_get_irq(struct device *dev, int ce_id)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003401{
3402 int irq;
3403
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303404 if (!penv || !penv->pdev || !dev)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003405 return -ENODEV;
3406
3407 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3408 return -EINVAL;
3409
3410 irq = penv->ce_irqs[ce_id];
3411
3412 return irq;
3413}
3414EXPORT_SYMBOL(icnss_get_irq);
3415
3416struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3417{
3418 struct icnss_priv *priv = dev_get_drvdata(dev);
3419
3420 if (!priv) {
3421 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3422 dev, priv);
3423 return NULL;
3424 }
3425
3426 return priv->smmu_mapping;
3427}
3428EXPORT_SYMBOL(icnss_smmu_get_mapping);
3429
3430int icnss_smmu_map(struct device *dev,
3431 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3432{
3433 struct icnss_priv *priv = dev_get_drvdata(dev);
3434 unsigned long iova;
3435 size_t len;
3436 int ret = 0;
3437
3438 if (!priv) {
3439 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3440 dev, priv);
3441 return -EINVAL;
3442 }
3443
3444 if (!iova_addr) {
3445 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3446 &paddr, size);
3447 return -EINVAL;
3448 }
3449
3450 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3451 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3452
3453 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3454 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3455 iova,
3456 &priv->smmu_iova_ipa_start,
3457 priv->smmu_iova_ipa_len);
3458 return -ENOMEM;
3459 }
3460
3461 ret = iommu_map(priv->smmu_mapping->domain, iova,
3462 rounddown(paddr, PAGE_SIZE), len,
3463 IOMMU_READ | IOMMU_WRITE);
3464 if (ret) {
3465 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3466 return ret;
3467 }
3468
3469 priv->smmu_iova_ipa_start = iova + len;
3470 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3471
3472 return 0;
3473}
3474EXPORT_SYMBOL(icnss_smmu_map);
3475
3476unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3477{
3478 return socinfo_get_serial_number();
3479}
3480EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3481
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003482int icnss_trigger_recovery(struct device *dev)
3483{
3484 int ret = 0;
3485 struct icnss_priv *priv = dev_get_drvdata(dev);
3486
3487 if (priv->magic != ICNSS_MAGIC) {
3488 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3489 ret = -EINVAL;
3490 goto out;
3491 }
3492
3493 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3494 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3495 priv->state);
3496 ret = -EPERM;
3497 goto out;
3498 }
3499
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003500 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003501 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3502 priv->state);
3503 ret = -EOPNOTSUPP;
3504 goto out;
3505 }
3506
3507 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3508 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3509 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003510 ret = -EINVAL;
3511 goto out;
3512 }
3513
Yuanyuan Liu68939762017-04-04 16:43:03 -07003514 WARN_ON(1);
3515 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3516 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003517
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003518 /*
3519 * Initiate PDR, required only for the first instance
3520 */
3521 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3522 priv->service_notifier[0].instance_id);
3523
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003524 if (!ret)
3525 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3526
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003527out:
3528 return ret;
3529}
3530EXPORT_SYMBOL(icnss_trigger_recovery);
3531
3532
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003533static int icnss_smmu_init(struct icnss_priv *priv)
3534{
3535 struct dma_iommu_mapping *mapping;
3536 int atomic_ctx = 1;
3537 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303538 int fast = 1;
Anurag Chouhandfb92852018-05-31 12:15:21 +05303539 int stall_disable = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003540 int ret = 0;
3541
3542 icnss_pr_dbg("Initializing SMMU\n");
3543
3544 mapping = arm_iommu_create_mapping(&platform_bus_type,
3545 priv->smmu_iova_start,
3546 priv->smmu_iova_len);
3547 if (IS_ERR(mapping)) {
3548 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3549 ret = PTR_ERR(mapping);
3550 goto map_fail;
3551 }
3552
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303553 if (priv->bypass_s1_smmu) {
3554 ret = iommu_domain_set_attr(mapping->domain,
3555 DOMAIN_ATTR_S1_BYPASS,
3556 &s1_bypass);
3557 if (ret < 0) {
3558 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3559 ret);
3560 goto set_attr_fail;
3561 }
3562 icnss_pr_dbg("SMMU S1 BYPASS\n");
3563 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003564 ret = iommu_domain_set_attr(mapping->domain,
3565 DOMAIN_ATTR_ATOMIC,
3566 &atomic_ctx);
3567 if (ret < 0) {
3568 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3569 ret);
3570 goto set_attr_fail;
3571 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303572 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003573
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303574 ret = iommu_domain_set_attr(mapping->domain,
3575 DOMAIN_ATTR_FAST,
3576 &fast);
3577 if (ret < 0) {
3578 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3579 ret);
3580 goto set_attr_fail;
3581 }
3582 icnss_pr_dbg("SMMU FAST map set\n");
Anurag Chouhandfb92852018-05-31 12:15:21 +05303583
3584 ret = iommu_domain_set_attr(mapping->domain,
3585 DOMAIN_ATTR_CB_STALL_DISABLE,
3586 &stall_disable);
3587 if (ret < 0) {
3588 icnss_pr_err("Set stall disable map attribute failed, err = %d\n",
3589 ret);
3590 goto set_attr_fail;
3591 }
3592 icnss_pr_dbg("SMMU STALL DISABLE map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003593 }
3594
3595 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3596 if (ret < 0) {
3597 icnss_pr_err("Attach device failed, err = %d\n", ret);
3598 goto attach_fail;
3599 }
3600
3601 priv->smmu_mapping = mapping;
3602
3603 return ret;
3604
3605attach_fail:
3606set_attr_fail:
3607 arm_iommu_release_mapping(mapping);
3608map_fail:
3609 return ret;
3610}
3611
3612static void icnss_smmu_deinit(struct icnss_priv *priv)
3613{
3614 if (!priv->smmu_mapping)
3615 return;
3616
3617 arm_iommu_detach_device(&priv->pdev->dev);
3618 arm_iommu_release_mapping(priv->smmu_mapping);
3619
3620 priv->smmu_mapping = NULL;
3621}
3622
Yuanyuan Liu68939762017-04-04 16:43:03 -07003623static int icnss_get_vreg_info(struct device *dev,
3624 struct icnss_vreg_info *vreg_info)
3625{
3626 int ret = 0;
3627 char prop_name[MAX_PROP_SIZE];
3628 struct regulator *reg;
3629 const __be32 *prop;
3630 int len = 0;
3631 int i;
3632
3633 reg = devm_regulator_get_optional(dev, vreg_info->name);
3634 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3635 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3636 vreg_info->name);
3637 ret = PTR_ERR(reg);
3638 goto out;
3639 }
3640
3641 if (IS_ERR(reg)) {
3642 ret = PTR_ERR(reg);
3643
3644 if (vreg_info->required) {
3645 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3646 vreg_info->name, ret);
3647 goto out;
3648 } else {
3649 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3650 vreg_info->name, ret);
3651 goto done;
3652 }
3653 }
3654
3655 vreg_info->reg = reg;
3656
3657 snprintf(prop_name, MAX_PROP_SIZE,
3658 "qcom,%s-config", vreg_info->name);
3659
3660 prop = of_get_property(dev->of_node, prop_name, &len);
3661
3662 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3663 prop_name, len);
3664
3665 if (!prop || len < (2 * sizeof(__be32))) {
3666 icnss_pr_dbg("Property %s %s\n", prop_name,
3667 prop ? "invalid format" : "doesn't exist");
3668 goto done;
3669 }
3670
3671 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3672 switch (i) {
3673 case 0:
3674 vreg_info->min_v = be32_to_cpup(&prop[0]);
3675 break;
3676 case 1:
3677 vreg_info->max_v = be32_to_cpup(&prop[1]);
3678 break;
3679 case 2:
3680 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3681 break;
3682 case 3:
3683 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3684 break;
3685 default:
3686 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3687 prop_name, i);
3688 break;
3689 }
3690 }
3691
3692done:
3693 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3694 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3695 vreg_info->load_ua, vreg_info->settle_delay);
3696
3697 return 0;
3698
3699out:
3700 return ret;
3701}
3702
3703static int icnss_get_clk_info(struct device *dev,
3704 struct icnss_clk_info *clk_info)
3705{
3706 struct clk *handle;
3707 int ret = 0;
3708
3709 handle = devm_clk_get(dev, clk_info->name);
3710 if (IS_ERR(handle)) {
3711 ret = PTR_ERR(handle);
3712 if (clk_info->required) {
3713 icnss_pr_err("Clock %s isn't available: %d\n",
3714 clk_info->name, ret);
3715 goto out;
3716 } else {
3717 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3718 ret);
3719 ret = 0;
3720 goto out;
3721 }
3722 }
3723
3724 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3725
3726 clk_info->handle = handle;
3727out:
3728 return ret;
3729}
3730
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003731static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003732{
3733 struct icnss_priv *priv = s->private;
3734
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003735 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003736
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003737 seq_puts(s, "\nCMD: test_mode\n");
3738 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3739 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3740 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003741 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003742
3743 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3744 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003745
3746 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003747 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003748 goto out;
3749 }
3750
3751 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003752 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003753 goto out;
3754 }
3755
3756 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003757 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003758 goto out;
3759 }
3760
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003761out:
3762 seq_puts(s, "\n");
3763 return 0;
3764}
3765
3766static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3767{
3768 int ret;
3769
3770 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3771 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3772 priv->state);
3773 ret = -ENODEV;
3774 goto out;
3775 }
3776
3777 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3778 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3779 priv->state);
3780 ret = -EINVAL;
3781 goto out;
3782 }
3783
3784 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3785 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3786 priv->state);
3787 ret = -EINVAL;
3788 goto out;
3789 }
3790
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303791 icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003792
3793 ret = icnss_hw_power_off(priv);
3794
3795 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3796
3797out:
3798 return ret;
3799}
3800static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3801 enum icnss_driver_mode mode)
3802{
3803 int ret;
3804
3805 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3806 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3807 priv->state);
3808 ret = -ENODEV;
3809 goto out;
3810 }
3811
3812 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3813 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3814 priv->state);
3815 ret = -EINVAL;
3816 goto out;
3817 }
3818
3819 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3820 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3821 priv->state);
3822 ret = -EBUSY;
3823 goto out;
3824 }
3825
3826 ret = icnss_hw_power_on(priv);
3827 if (ret)
3828 goto out;
3829
3830 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3831
Sarada Prasanna Garnayak09e32072017-10-16 15:41:25 +05303832 ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003833 if (ret)
3834 goto power_off;
3835
3836 return 0;
3837
3838power_off:
3839 icnss_hw_power_off(priv);
3840 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3841
3842out:
3843 return ret;
3844}
3845
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003846static void icnss_allow_recursive_recovery(struct device *dev)
3847{
3848 struct icnss_priv *priv = dev_get_drvdata(dev);
3849
3850 priv->allow_recursive_recovery = true;
3851
3852 icnss_pr_info("Recursive recovery allowed for WLAN\n");
3853}
3854
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003855static ssize_t icnss_fw_debug_write(struct file *fp,
3856 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003857 size_t count, loff_t *off)
3858{
3859 struct icnss_priv *priv =
3860 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003861 char buf[64];
3862 char *sptr, *token;
3863 unsigned int len = 0;
3864 char *cmd;
3865 uint64_t val;
3866 const char *delim = " ";
3867 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003868
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003869 len = min(count, sizeof(buf) - 1);
3870 if (copy_from_user(buf, user_buf, len))
3871 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003872
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003873 buf[len] = '\0';
3874 sptr = buf;
3875
3876 token = strsep(&sptr, delim);
3877 if (!token)
3878 return -EINVAL;
3879 if (!sptr)
3880 return -EINVAL;
3881 cmd = token;
3882
3883 token = strsep(&sptr, delim);
3884 if (!token)
3885 return -EINVAL;
3886 if (kstrtou64(token, 0, &val))
3887 return -EINVAL;
3888
3889 if (strcmp(cmd, "test_mode") == 0) {
3890 switch (val) {
3891 case 0:
3892 ret = icnss_test_mode_fw_test_off(priv);
3893 break;
3894 case 1:
3895 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3896 break;
3897 case 2:
3898 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3899 break;
3900 case 3:
3901 ret = icnss_trigger_recovery(&priv->pdev->dev);
3902 break;
Sameer Thalappilcaab5b72018-01-11 15:01:55 -08003903 case 4:
3904 icnss_allow_recursive_recovery(&priv->pdev->dev);
3905 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003906 default:
3907 return -EINVAL;
3908 }
3909 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3910 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3911 } else {
3912 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003913 }
3914
3915 if (ret)
3916 return ret;
3917
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003918 return count;
3919}
3920
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003921static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003922{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003923 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003924}
3925
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003926static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003927 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003928 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003929 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003930 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003931 .owner = THIS_MODULE,
3932 .llseek = seq_lseek,
3933};
3934
3935static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3936 size_t count, loff_t *off)
3937{
3938 struct icnss_priv *priv =
3939 ((struct seq_file *)fp->private_data)->private;
3940 int ret;
3941 u32 val;
3942
3943 ret = kstrtou32_from_user(buf, count, 0, &val);
3944 if (ret)
3945 return ret;
3946
3947 if (ret == 0)
3948 memset(&priv->stats, 0, sizeof(priv->stats));
3949
3950 return count;
3951}
3952
3953static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3954{
3955 enum icnss_driver_state i;
3956 int skip = 0;
3957 unsigned long state;
3958
3959 seq_printf(s, "\nState: 0x%lx(", priv->state);
3960 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3961
3962 if (!(state & 0x1))
3963 continue;
3964
3965 if (skip++)
3966 seq_puts(s, " | ");
3967
3968 switch (i) {
3969 case ICNSS_WLFW_QMI_CONNECTED:
3970 seq_puts(s, "QMI CONN");
3971 continue;
3972 case ICNSS_POWER_ON:
3973 seq_puts(s, "POWER ON");
3974 continue;
3975 case ICNSS_FW_READY:
3976 seq_puts(s, "FW READY");
3977 continue;
3978 case ICNSS_DRIVER_PROBED:
3979 seq_puts(s, "DRIVER PROBED");
3980 continue;
3981 case ICNSS_FW_TEST_MODE:
3982 seq_puts(s, "FW TEST MODE");
3983 continue;
3984 case ICNSS_PM_SUSPEND:
3985 seq_puts(s, "PM SUSPEND");
3986 continue;
3987 case ICNSS_PM_SUSPEND_NOIRQ:
3988 seq_puts(s, "PM SUSPEND NOIRQ");
3989 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003990 case ICNSS_SSR_REGISTERED:
3991 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003992 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003993 case ICNSS_PDR_REGISTERED:
3994 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003995 continue;
3996 case ICNSS_PD_RESTART:
3997 seq_puts(s, "PD RESTART");
3998 continue;
3999 case ICNSS_MSA0_ASSIGNED:
4000 seq_puts(s, "MSA0 ASSIGNED");
4001 continue;
4002 case ICNSS_WLFW_EXISTS:
4003 seq_puts(s, "WLAN FW EXISTS");
4004 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05304005 case ICNSS_SHUTDOWN_DONE:
4006 seq_puts(s, "SHUTDOWN DONE");
4007 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004008 case ICNSS_HOST_TRIGGERED_PDR:
4009 seq_puts(s, "HOST TRIGGERED PDR");
4010 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07004011 case ICNSS_FW_DOWN:
4012 seq_puts(s, "FW DOWN");
4013 continue;
Anurag Chouhanc4997342018-07-17 14:35:54 +05304014 case ICNSS_REJUVENATE:
4015 seq_puts(s, "FW REJUVENATE");
4016 continue;
Yuanyuan Liu5718dfc2017-12-15 16:20:37 -08004017 case ICNSS_DRIVER_UNLOADING:
4018 seq_puts(s, "DRIVER UNLOADING");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004019 }
4020
4021 seq_printf(s, "UNKNOWN-%d", i);
4022 }
4023 seq_puts(s, ")\n");
4024
4025 return 0;
4026}
4027
4028static int icnss_stats_show_capability(struct seq_file *s,
4029 struct icnss_priv *priv)
4030{
4031 if (test_bit(ICNSS_FW_READY, &priv->state)) {
4032 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
4033 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
4034 seq_printf(s, "Chip family: 0x%x\n",
4035 priv->chip_info.chip_family);
4036 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
4037 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
4038 seq_printf(s, "Firmware Version: 0x%x\n",
4039 priv->fw_version_info.fw_version);
4040 seq_printf(s, "Firmware Build Timestamp: %s\n",
4041 priv->fw_version_info.fw_build_timestamp);
4042 seq_printf(s, "Firmware Build ID: %s\n",
4043 priv->fw_build_id);
4044 }
4045
4046 return 0;
4047}
4048
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004049static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
4050 struct icnss_priv *priv)
4051{
4052 if (priv->stats.rejuvenate_ind) {
4053 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
4054 seq_printf(s, "Number of Rejuvenations: %u\n",
4055 priv->stats.rejuvenate_ind);
4056 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
4057 priv->cause_for_rejuvenation);
4058 seq_printf(s, "Requesting Sub-System: 0x%x\n",
4059 priv->requesting_sub_system);
4060 seq_printf(s, "Line Number: %u\n",
4061 priv->line_number);
4062 seq_printf(s, "Function Name: %s\n",
4063 priv->function_name);
4064 }
4065
4066 return 0;
4067}
4068
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004069static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
4070{
4071 int i;
4072
4073 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
4074 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
4075 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
4076 seq_printf(s, "%24s %16u %16u\n",
4077 icnss_driver_event_to_str(i),
4078 priv->stats.events[i].posted,
4079 priv->stats.events[i].processed);
4080
4081 return 0;
4082}
4083
4084static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
4085{
4086 int i;
4087
4088 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
4089 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
4090 "Free", "Enable", "Disable");
4091 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
4092 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
4093 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
4094 priv->stats.ce_irqs[i].free,
4095 priv->stats.ce_irqs[i].enable,
4096 priv->stats.ce_irqs[i].disable);
4097
4098 return 0;
4099}
4100
4101static int icnss_stats_show(struct seq_file *s, void *data)
4102{
4103#define ICNSS_STATS_DUMP(_s, _priv, _x) \
4104 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
4105
4106 struct icnss_priv *priv = s->private;
4107
4108 ICNSS_STATS_DUMP(s, priv, ind_register_req);
4109 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
4110 ICNSS_STATS_DUMP(s, priv, ind_register_err);
4111 ICNSS_STATS_DUMP(s, priv, msa_info_req);
4112 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
4113 ICNSS_STATS_DUMP(s, priv, msa_info_err);
4114 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
4115 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
4116 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
4117 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
4118 ICNSS_STATS_DUMP(s, priv, cap_req);
4119 ICNSS_STATS_DUMP(s, priv, cap_resp);
4120 ICNSS_STATS_DUMP(s, priv, cap_err);
4121 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
4122 ICNSS_STATS_DUMP(s, priv, cfg_req);
4123 ICNSS_STATS_DUMP(s, priv, cfg_resp);
4124 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
4125 ICNSS_STATS_DUMP(s, priv, mode_req);
4126 ICNSS_STATS_DUMP(s, priv, mode_resp);
4127 ICNSS_STATS_DUMP(s, priv, mode_req_err);
4128 ICNSS_STATS_DUMP(s, priv, ini_req);
4129 ICNSS_STATS_DUMP(s, priv, ini_resp);
4130 ICNSS_STATS_DUMP(s, priv, ini_req_err);
4131 ICNSS_STATS_DUMP(s, priv, vbatt_req);
4132 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
4133 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004134 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004135 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
4136 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
4137 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07004138 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
4139 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
4140 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
4141 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004142
4143 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
4144 ICNSS_STATS_DUMP(s, priv, pm_suspend);
4145 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
4146 ICNSS_STATS_DUMP(s, priv, pm_resume);
4147 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
4148 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
4149 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
4150 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
4151 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
4152 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
4153 ICNSS_STATS_DUMP(s, priv, pm_relax);
4154
4155 icnss_stats_show_irqs(s, priv);
4156
4157 icnss_stats_show_capability(s, priv);
4158
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08004159 icnss_stats_show_rejuvenate_info(s, priv);
4160
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004161 icnss_stats_show_events(s, priv);
4162
4163 icnss_stats_show_state(s, priv);
4164
4165 return 0;
4166#undef ICNSS_STATS_DUMP
4167}
4168
4169static int icnss_stats_open(struct inode *inode, struct file *file)
4170{
4171 return single_open(file, icnss_stats_show, inode->i_private);
4172}
4173
4174static const struct file_operations icnss_stats_fops = {
4175 .read = seq_read,
4176 .write = icnss_stats_write,
4177 .release = single_release,
4178 .open = icnss_stats_open,
4179 .owner = THIS_MODULE,
4180 .llseek = seq_lseek,
4181};
4182
4183static int icnss_regwrite_show(struct seq_file *s, void *data)
4184{
4185 struct icnss_priv *priv = s->private;
4186
4187 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4188
4189 if (!test_bit(ICNSS_FW_READY, &priv->state))
4190 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4191
4192 return 0;
4193}
4194
4195static ssize_t icnss_regwrite_write(struct file *fp,
4196 const char __user *user_buf,
4197 size_t count, loff_t *off)
4198{
4199 struct icnss_priv *priv =
4200 ((struct seq_file *)fp->private_data)->private;
4201 char buf[64];
4202 char *sptr, *token;
4203 unsigned int len = 0;
4204 uint32_t reg_offset, mem_type, reg_val;
4205 const char *delim = " ";
4206 int ret = 0;
4207
4208 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4209 !test_bit(ICNSS_POWER_ON, &priv->state))
4210 return -EINVAL;
4211
4212 len = min(count, sizeof(buf) - 1);
4213 if (copy_from_user(buf, user_buf, len))
4214 return -EFAULT;
4215
4216 buf[len] = '\0';
4217 sptr = buf;
4218
4219 token = strsep(&sptr, delim);
4220 if (!token)
4221 return -EINVAL;
4222
4223 if (!sptr)
4224 return -EINVAL;
4225
4226 if (kstrtou32(token, 0, &mem_type))
4227 return -EINVAL;
4228
4229 token = strsep(&sptr, delim);
4230 if (!token)
4231 return -EINVAL;
4232
4233 if (!sptr)
4234 return -EINVAL;
4235
4236 if (kstrtou32(token, 0, &reg_offset))
4237 return -EINVAL;
4238
4239 token = strsep(&sptr, delim);
4240 if (!token)
4241 return -EINVAL;
4242
4243 if (kstrtou32(token, 0, &reg_val))
4244 return -EINVAL;
4245
4246 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4247 sizeof(uint32_t),
4248 (uint8_t *)&reg_val);
4249 if (ret)
4250 return ret;
4251
4252 return count;
4253}
4254
4255static int icnss_regwrite_open(struct inode *inode, struct file *file)
4256{
4257 return single_open(file, icnss_regwrite_show, inode->i_private);
4258}
4259
4260static const struct file_operations icnss_regwrite_fops = {
4261 .read = seq_read,
4262 .write = icnss_regwrite_write,
4263 .open = icnss_regwrite_open,
4264 .owner = THIS_MODULE,
4265 .llseek = seq_lseek,
4266};
4267
4268static int icnss_regread_show(struct seq_file *s, void *data)
4269{
4270 struct icnss_priv *priv = s->private;
4271
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304272 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004273 if (!priv->diag_reg_read_buf) {
4274 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4275
4276 if (!test_bit(ICNSS_FW_READY, &priv->state))
4277 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4278
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304279 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004280 return 0;
4281 }
4282
4283 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4284 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4285 priv->diag_reg_read_len);
4286
4287 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4288 priv->diag_reg_read_len, false);
4289
4290 priv->diag_reg_read_len = 0;
4291 kfree(priv->diag_reg_read_buf);
4292 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304293 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004294
4295 return 0;
4296}
4297
4298static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4299 size_t count, loff_t *off)
4300{
4301 struct icnss_priv *priv =
4302 ((struct seq_file *)fp->private_data)->private;
4303 char buf[64];
4304 char *sptr, *token;
4305 unsigned int len = 0;
4306 uint32_t reg_offset, mem_type;
4307 uint32_t data_len = 0;
4308 uint8_t *reg_buf = NULL;
4309 const char *delim = " ";
4310 int ret = 0;
4311
4312 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4313 !test_bit(ICNSS_POWER_ON, &priv->state))
4314 return -EINVAL;
4315
4316 len = min(count, sizeof(buf) - 1);
4317 if (copy_from_user(buf, user_buf, len))
4318 return -EFAULT;
4319
4320 buf[len] = '\0';
4321 sptr = buf;
4322
4323 token = strsep(&sptr, delim);
4324 if (!token)
4325 return -EINVAL;
4326
4327 if (!sptr)
4328 return -EINVAL;
4329
4330 if (kstrtou32(token, 0, &mem_type))
4331 return -EINVAL;
4332
4333 token = strsep(&sptr, delim);
4334 if (!token)
4335 return -EINVAL;
4336
4337 if (!sptr)
4338 return -EINVAL;
4339
4340 if (kstrtou32(token, 0, &reg_offset))
4341 return -EINVAL;
4342
4343 token = strsep(&sptr, delim);
4344 if (!token)
4345 return -EINVAL;
4346
4347 if (kstrtou32(token, 0, &data_len))
4348 return -EINVAL;
4349
4350 if (data_len == 0 ||
4351 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4352 return -EINVAL;
4353
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304354 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004355 kfree(priv->diag_reg_read_buf);
4356 priv->diag_reg_read_buf = NULL;
4357
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004358 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304359 if (!reg_buf) {
4360 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004361 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304362 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004363
4364 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4365 mem_type, data_len,
4366 reg_buf);
4367 if (ret) {
4368 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304369 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004370 return ret;
4371 }
4372
4373 priv->diag_reg_read_addr = reg_offset;
4374 priv->diag_reg_read_mem_type = mem_type;
4375 priv->diag_reg_read_len = data_len;
4376 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304377 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004378
4379 return count;
4380}
4381
4382static int icnss_regread_open(struct inode *inode, struct file *file)
4383{
4384 return single_open(file, icnss_regread_show, inode->i_private);
4385}
4386
4387static const struct file_operations icnss_regread_fops = {
4388 .read = seq_read,
4389 .write = icnss_regread_write,
4390 .open = icnss_regread_open,
4391 .owner = THIS_MODULE,
4392 .llseek = seq_lseek,
4393};
4394
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004395#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004396static int icnss_debugfs_create(struct icnss_priv *priv)
4397{
4398 int ret = 0;
4399 struct dentry *root_dentry;
4400
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004401 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004402
4403 if (IS_ERR(root_dentry)) {
4404 ret = PTR_ERR(root_dentry);
4405 icnss_pr_err("Unable to create debugfs %d\n", ret);
4406 goto out;
4407 }
4408
4409 priv->root_dentry = root_dentry;
4410
Yuanyuan Liu84132752017-05-24 12:07:12 -07004411 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004412 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004413
Yuanyuan Liu84132752017-05-24 12:07:12 -07004414 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004415 &icnss_stats_fops);
4416 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4417 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004418 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004419 &icnss_regwrite_fops);
4420
4421out:
4422 return ret;
4423}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004424#else
4425static int icnss_debugfs_create(struct icnss_priv *priv)
4426{
4427 int ret = 0;
4428 struct dentry *root_dentry;
4429
4430 root_dentry = debugfs_create_dir("icnss", NULL);
4431
4432 if (IS_ERR(root_dentry)) {
4433 ret = PTR_ERR(root_dentry);
4434 icnss_pr_err("Unable to create debugfs %d\n", ret);
4435 return ret;
4436 }
4437
4438 priv->root_dentry = root_dentry;
4439
4440 debugfs_create_file("stats", 0600, root_dentry, priv,
4441 &icnss_stats_fops);
4442 return 0;
4443}
4444#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004445
4446static void icnss_debugfs_destroy(struct icnss_priv *priv)
4447{
4448 debugfs_remove_recursive(priv->root_dentry);
4449}
4450
4451static int icnss_get_vbatt_info(struct icnss_priv *priv)
4452{
4453 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4454 struct qpnp_vadc_chip *vadc_dev = NULL;
4455 int ret = 0;
4456
4457 if (test_bit(VBATT_DISABLE, &quirks)) {
4458 icnss_pr_dbg("VBATT feature is disabled\n");
4459 return ret;
4460 }
4461
4462 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4463 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4464 icnss_pr_err("adc_tm_dev probe defer\n");
4465 return -EPROBE_DEFER;
4466 }
4467
4468 if (IS_ERR(adc_tm_dev)) {
4469 ret = PTR_ERR(adc_tm_dev);
4470 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4471 ret);
4472 return ret;
4473 }
4474
4475 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4476 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4477 icnss_pr_err("vadc_dev probe defer\n");
4478 return -EPROBE_DEFER;
4479 }
4480
4481 if (IS_ERR(vadc_dev)) {
4482 ret = PTR_ERR(vadc_dev);
4483 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4484 ret);
4485 return ret;
4486 }
4487
4488 priv->adc_tm_dev = adc_tm_dev;
4489 priv->vadc_dev = vadc_dev;
4490
4491 return 0;
4492}
4493
4494static int icnss_probe(struct platform_device *pdev)
4495{
4496 int ret = 0;
4497 struct resource *res;
4498 int i;
4499 struct device *dev = &pdev->dev;
4500 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304501 const __be32 *addrp;
4502 u64 prop_size = 0;
4503 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004504
4505 if (penv) {
4506 icnss_pr_err("Driver is already initialized\n");
4507 return -EEXIST;
4508 }
4509
4510 icnss_pr_dbg("Platform driver probe\n");
4511
4512 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4513 if (!priv)
4514 return -ENOMEM;
4515
4516 priv->magic = ICNSS_MAGIC;
4517 dev_set_drvdata(dev, priv);
4518
4519 priv->pdev = pdev;
4520
4521 ret = icnss_get_vbatt_info(priv);
4522 if (ret == -EPROBE_DEFER)
4523 goto out;
4524
Yuanyuan Liu68939762017-04-04 16:43:03 -07004525 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4526 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4527 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4528
4529 if (ret)
4530 goto out;
4531 }
4532
4533 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4534 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4535 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4536 if (ret)
4537 goto out;
4538 }
4539
4540 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4541 priv->bypass_s1_smmu = true;
4542
4543 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4544
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004545 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4546 if (!res) {
4547 icnss_pr_err("Memory base not found in DT\n");
4548 ret = -EINVAL;
4549 goto out;
4550 }
4551
4552 priv->mem_base_pa = res->start;
4553 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4554 resource_size(res));
4555 if (!priv->mem_base_va) {
4556 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4557 &priv->mem_base_pa);
4558 ret = -EINVAL;
4559 goto out;
4560 }
4561 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4562 priv->mem_base_va);
4563
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004564 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4565 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4566 if (!res) {
4567 icnss_pr_err("Fail to get IRQ-%d\n", i);
4568 ret = -ENODEV;
4569 goto out;
4570 } else {
4571 priv->ce_irqs[i] = res->start;
4572 }
4573 }
4574
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304575 np = of_parse_phandle(dev->of_node,
4576 "qcom,wlan-msa-fixed-region", 0);
4577 if (np) {
4578 addrp = of_get_address(np, 0, &prop_size, NULL);
4579 if (!addrp) {
4580 icnss_pr_err("Failed to get assigned-addresses or property\n");
4581 ret = -EINVAL;
4582 goto out;
4583 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004584
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304585 priv->msa_pa = of_translate_address(np, addrp);
4586 if (priv->msa_pa == OF_BAD_ADDR) {
4587 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4588 ret = -EINVAL;
4589 goto out;
4590 }
4591
4592 priv->msa_va = memremap(priv->msa_pa,
4593 (unsigned long)prop_size, MEMREMAP_WT);
4594 if (!priv->msa_va) {
4595 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4596 &priv->msa_pa);
4597 ret = -EINVAL;
4598 goto out;
4599 }
4600 priv->msa_mem_size = prop_size;
4601 } else {
4602 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4603 &priv->msa_mem_size);
4604 if (ret || priv->msa_mem_size == 0) {
4605 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4606 priv->msa_mem_size, ret);
4607 goto out;
4608 }
4609
4610 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4611 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4612
4613 if (!priv->msa_va) {
4614 icnss_pr_err("DMA alloc failed for MSA\n");
4615 ret = -ENOMEM;
4616 goto out;
4617 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004618 }
4619
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304620 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4621 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004622
4623 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4624 "smmu_iova_base");
4625 if (!res) {
4626 icnss_pr_err("SMMU IOVA base not found\n");
4627 } else {
4628 priv->smmu_iova_start = res->start;
4629 priv->smmu_iova_len = resource_size(res);
4630 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4631 &priv->smmu_iova_start, priv->smmu_iova_len);
4632
4633 res = platform_get_resource_byname(pdev,
4634 IORESOURCE_MEM,
4635 "smmu_iova_ipa");
4636 if (!res) {
4637 icnss_pr_err("SMMU IOVA IPA not found\n");
4638 } else {
4639 priv->smmu_iova_ipa_start = res->start;
4640 priv->smmu_iova_ipa_len = resource_size(res);
4641 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4642 &priv->smmu_iova_ipa_start,
4643 priv->smmu_iova_ipa_len);
4644 }
4645
4646 ret = icnss_smmu_init(priv);
4647 if (ret < 0) {
4648 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4649 ret, &priv->smmu_iova_start,
4650 priv->smmu_iova_len);
4651 goto out;
4652 }
4653 }
4654
4655 spin_lock_init(&priv->event_lock);
4656 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304657 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004658
4659 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4660 if (!priv->event_wq) {
4661 icnss_pr_err("Workqueue creation failed\n");
4662 ret = -EFAULT;
4663 goto out_smmu_deinit;
4664 }
4665
4666 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4667 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4668 INIT_LIST_HEAD(&priv->event_list);
4669
4670 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4671 WLFW_SERVICE_VERS_V01,
4672 WLFW_SERVICE_INS_ID_V01,
4673 &wlfw_clnt_nb);
4674 if (ret < 0) {
4675 icnss_pr_err("Notifier register failed: %d\n", ret);
4676 goto out_destroy_wq;
4677 }
4678
4679 icnss_enable_recovery(priv);
4680
4681 icnss_debugfs_create(priv);
4682
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004683 ret = device_init_wakeup(&priv->pdev->dev, true);
4684 if (ret)
4685 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4686 ret);
4687
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004688 penv = priv;
4689
4690 icnss_pr_info("Platform driver probed successfully\n");
4691
4692 return 0;
4693
4694out_destroy_wq:
4695 destroy_workqueue(priv->event_wq);
4696out_smmu_deinit:
4697 icnss_smmu_deinit(priv);
4698out:
4699 dev_set_drvdata(dev, NULL);
4700
4701 return ret;
4702}
4703
4704static int icnss_remove(struct platform_device *pdev)
4705{
4706 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4707
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004708 device_init_wakeup(&penv->pdev->dev, false);
4709
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004710 icnss_debugfs_destroy(penv);
4711
4712 icnss_modem_ssr_unregister_notifier(penv);
4713
4714 destroy_ramdump_device(penv->msa0_dump_dev);
4715
4716 icnss_pdr_unregister_notifier(penv);
4717
4718 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4719 WLFW_SERVICE_VERS_V01,
4720 WLFW_SERVICE_INS_ID_V01,
4721 &wlfw_clnt_nb);
4722 if (penv->event_wq)
4723 destroy_workqueue(penv->event_wq);
4724
4725 icnss_hw_power_off(penv);
4726
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304727 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4728 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004729
4730 dev_set_drvdata(&pdev->dev, NULL);
4731
4732 return 0;
4733}
4734
4735#ifdef CONFIG_PM_SLEEP
4736static int icnss_pm_suspend(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 suspend: 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 Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004748
4749 if (!priv->ops || !priv->ops->pm_suspend ||
4750 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4751 goto out;
4752
4753 ret = priv->ops->pm_suspend(dev);
4754
4755out:
4756 if (ret == 0) {
4757 priv->stats.pm_suspend++;
4758 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4759 } else {
4760 priv->stats.pm_suspend_err++;
4761 }
4762 return ret;
4763}
4764
4765static int icnss_pm_resume(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 resume: 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 resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004777
4778 if (!priv->ops || !priv->ops->pm_resume ||
4779 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4780 goto out;
4781
4782 ret = priv->ops->pm_resume(dev);
4783
4784out:
4785 if (ret == 0) {
4786 priv->stats.pm_resume++;
4787 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4788 } else {
4789 priv->stats.pm_resume_err++;
4790 }
4791 return ret;
4792}
4793
4794static int icnss_pm_suspend_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 suspend_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 suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004806
4807 if (!priv->ops || !priv->ops->suspend_noirq ||
4808 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4809 goto out;
4810
4811 ret = priv->ops->suspend_noirq(dev);
4812
4813out:
4814 if (ret == 0) {
4815 priv->stats.pm_suspend_noirq++;
4816 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4817 } else {
4818 priv->stats.pm_suspend_noirq_err++;
4819 }
4820 return ret;
4821}
4822
4823static int icnss_pm_resume_noirq(struct device *dev)
4824{
4825 struct icnss_priv *priv = dev_get_drvdata(dev);
4826 int ret = 0;
4827
4828 if (priv->magic != ICNSS_MAGIC) {
4829 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4830 dev, priv, priv->magic);
4831 return -EINVAL;
4832 }
4833
Yuanyuan Liu68939762017-04-04 16:43:03 -07004834 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004835
4836 if (!priv->ops || !priv->ops->resume_noirq ||
4837 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4838 goto out;
4839
4840 ret = priv->ops->resume_noirq(dev);
4841
4842out:
4843 if (ret == 0) {
4844 priv->stats.pm_resume_noirq++;
4845 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4846 } else {
4847 priv->stats.pm_resume_noirq_err++;
4848 }
4849 return ret;
4850}
4851#endif
4852
4853static const struct dev_pm_ops icnss_pm_ops = {
4854 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4855 icnss_pm_resume)
4856 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4857 icnss_pm_resume_noirq)
4858};
4859
4860static const struct of_device_id icnss_dt_match[] = {
4861 {.compatible = "qcom,icnss"},
4862 {}
4863};
4864
4865MODULE_DEVICE_TABLE(of, icnss_dt_match);
4866
4867static struct platform_driver icnss_driver = {
4868 .probe = icnss_probe,
4869 .remove = icnss_remove,
4870 .driver = {
4871 .name = "icnss",
4872 .pm = &icnss_pm_ops,
4873 .owner = THIS_MODULE,
4874 .of_match_table = icnss_dt_match,
4875 },
4876};
4877
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004878static int __init icnss_initialize(void)
4879{
4880 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4881 "icnss", 0);
4882 if (!icnss_ipc_log_context)
4883 icnss_pr_err("Unable to create log context\n");
4884
Yuanyuan Liu68939762017-04-04 16:43:03 -07004885 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4886 "icnss_long", 0);
4887 if (!icnss_ipc_log_long_context)
4888 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004889
4890 return platform_driver_register(&icnss_driver);
4891}
4892
4893static void __exit icnss_exit(void)
4894{
4895 platform_driver_unregister(&icnss_driver);
4896 ipc_log_context_destroy(icnss_ipc_log_context);
4897 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004898 ipc_log_context_destroy(icnss_ipc_log_long_context);
4899 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004900}
4901
4902
4903module_init(icnss_initialize);
4904module_exit(icnss_exit);
4905
4906MODULE_LICENSE("GPL v2");
4907MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");