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