blob: 38099e9d3aef3fad6198b0eef4fcfb62fcd58f0c [file] [log] [blame]
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001/* Copyright (c) 2015-2017, 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>
Yuanyuan Liu607051c2016-11-28 17:04:13 -080041#include <soc/qcom/memory_dump.h>
42#include <soc/qcom/icnss.h>
43#include <soc/qcom/msm_qmi_interface.h>
44#include <soc/qcom/secure_buffer.h>
45#include <soc/qcom/subsystem_notif.h>
46#include <soc/qcom/subsystem_restart.h>
47#include <soc/qcom/service-locator.h>
48#include <soc/qcom/service-notifier.h>
49#include <soc/qcom/socinfo.h>
50#include <soc/qcom/ramdump.h>
51
52#include "wlan_firmware_service_v01.h"
53
54#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080055unsigned long qmi_timeout = 10000;
Yuanyuan Liu607051c2016-11-28 17:04:13 -080056module_param(qmi_timeout, ulong, 0600);
57
58#define WLFW_TIMEOUT_MS qmi_timeout
59#else
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080060#define WLFW_TIMEOUT_MS 10000
Yuanyuan Liu607051c2016-11-28 17:04:13 -080061#endif
62#define WLFW_SERVICE_INS_ID_V01 0
63#define WLFW_CLIENT_ID 0x4b4e454c
64#define MAX_PROP_SIZE 32
65#define NUM_LOG_PAGES 10
Yuanyuan Liu68939762017-04-04 16:43:03 -070066#define NUM_LOG_LONG_PAGES 4
Yuanyuan Liu607051c2016-11-28 17:04:13 -080067#define ICNSS_MAGIC 0x5abc5abc
68
Yuanyuan Liu607051c2016-11-28 17:04:13 -080069#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
70#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
71
72#define ICNSS_THRESHOLD_HIGH 3600000
73#define ICNSS_THRESHOLD_LOW 3450000
74#define ICNSS_THRESHOLD_GUARD 20000
75
76#define icnss_ipc_log_string(_x...) do { \
77 if (icnss_ipc_log_context) \
78 ipc_log_string(icnss_ipc_log_context, _x); \
79 } while (0)
80
Yuanyuan Liu607051c2016-11-28 17:04:13 -080081#define icnss_ipc_log_long_string(_x...) do { \
82 if (icnss_ipc_log_long_context) \
83 ipc_log_string(icnss_ipc_log_long_context, _x); \
84 } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -080085
86#define icnss_pr_err(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070087 printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
88 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
89 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080090 } while (0)
91
92#define icnss_pr_warn(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070093 printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
94 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
95 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -080096 } while (0)
97
98#define icnss_pr_info(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -070099 printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
100 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
101 ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800102 } while (0)
103
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700104#if defined(CONFIG_DYNAMIC_DEBUG)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800105#define icnss_pr_dbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700106 pr_debug(_fmt, ##__VA_ARGS__); \
107 icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800108 } while (0)
109
Yuanyuan Liu68939762017-04-04 16:43:03 -0700110#define icnss_pr_vdbg(_fmt, ...) do { \
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700111 pr_debug(_fmt, ##__VA_ARGS__); \
112 icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800113 } while (0)
Yuanyuan Liu795d6c52017-07-11 16:53:14 -0700114#elif defined(DEBUG)
115#define icnss_pr_dbg(_fmt, ...) do { \
116 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
117 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
118 ##__VA_ARGS__); \
119 } while (0)
120
121#define icnss_pr_vdbg(_fmt, ...) do { \
122 printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
123 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
124 ##__VA_ARGS__); \
125 } while (0)
126#else
127#define icnss_pr_dbg(_fmt, ...) do { \
128 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
129 icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
130 ##__VA_ARGS__); \
131 } while (0)
132
133#define icnss_pr_vdbg(_fmt, ...) do { \
134 no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
135 icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
136 ##__VA_ARGS__); \
137 } while (0)
138#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800139
140#ifdef CONFIG_ICNSS_DEBUG
141#define ICNSS_ASSERT(_condition) do { \
142 if (!(_condition)) { \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700143 icnss_pr_err("ASSERT at line %d\n", __LINE__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800144 BUG_ON(1); \
145 } \
146 } while (0)
Yuanyuan Liu68939762017-04-04 16:43:03 -0700147
148bool ignore_qmi_timeout;
149#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800150#else
Yuanyuan Liu68939762017-04-04 16:43:03 -0700151#define ICNSS_ASSERT(_condition) do { } while (0)
152#define ICNSS_QMI_ASSERT() do { } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800153#endif
154
155enum icnss_debug_quirks {
156 HW_ALWAYS_ON,
157 HW_DEBUG_ENABLE,
158 SKIP_QMI,
159 HW_ONLY_TOP_LEVEL_RESET,
160 RECOVERY_DISABLE,
161 SSR_ONLY,
162 PDR_ONLY,
163 VBATT_DISABLE,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800164 FW_REJUVENATE_ENABLE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800165};
166
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800167#define ICNSS_QUIRKS_DEFAULT (BIT(VBATT_DISABLE) | \
168 BIT(FW_REJUVENATE_ENABLE))
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800169
170unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
171module_param(quirks, ulong, 0600);
172
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800173uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
174module_param(dynamic_feature_mask, ullong, 0600);
175
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800176void *icnss_ipc_log_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800177void *icnss_ipc_log_long_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800178
179#define ICNSS_EVENT_PENDING 2989
180
181#define ICNSS_EVENT_SYNC BIT(0)
182#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
183#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
184 ICNSS_EVENT_SYNC)
185
186enum icnss_driver_event_type {
187 ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
188 ICNSS_DRIVER_EVENT_SERVER_EXIT,
189 ICNSS_DRIVER_EVENT_FW_READY_IND,
190 ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
191 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
192 ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
193 ICNSS_DRIVER_EVENT_MAX,
194};
195
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530196enum icnss_msa_perm {
197 ICNSS_MSA_PERM_HLOS_ALL = 0,
198 ICNSS_MSA_PERM_WLAN_HW_RW = 1,
199 ICNSS_MSA_PERM_DUMP_COLLECT = 2,
200 ICNSS_MSA_PERM_MAX,
201};
202
203#define ICNSS_MAX_VMIDS 4
204
205struct icnss_mem_region_info {
206 uint64_t reg_addr;
207 uint32_t size;
208 uint8_t secure_flag;
209 enum icnss_msa_perm perm;
210};
211
212struct icnss_msa_perm_list_t {
213 int vmids[ICNSS_MAX_VMIDS];
214 int perms[ICNSS_MAX_VMIDS];
215 int nelems;
216};
217
218struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
219 [ICNSS_MSA_PERM_HLOS_ALL] = {
220 .vmids = {VMID_HLOS},
221 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
222 .nelems = 1,
223 },
224
225 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
226 .vmids = {VMID_MSS_MSA, VMID_WLAN},
227 .perms = {PERM_READ | PERM_WRITE,
228 PERM_READ | PERM_WRITE},
229 .nelems = 2,
230 },
231
232 [ICNSS_MSA_PERM_DUMP_COLLECT] = {
233 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
234 .perms = {PERM_READ | PERM_WRITE,
235 PERM_READ | PERM_WRITE,
236 PERM_READ},
237 .nelems = 3,
238 },
239};
240
241struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
242 [ICNSS_MSA_PERM_HLOS_ALL] = {
243 .vmids = {VMID_HLOS},
244 .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
245 .nelems = 1,
246 },
247
248 [ICNSS_MSA_PERM_WLAN_HW_RW] = {
249 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
250 .perms = {PERM_READ | PERM_WRITE,
251 PERM_READ | PERM_WRITE,
252 PERM_READ | PERM_WRITE},
253 .nelems = 3,
254 },
255
256 [ICNSS_MSA_PERM_DUMP_COLLECT] = {
257 .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
258 .perms = {PERM_READ | PERM_WRITE,
259 PERM_READ | PERM_WRITE,
260 PERM_READ | PERM_WRITE,
261 PERM_READ},
262 .nelems = 4,
263 },
264};
265
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800266struct icnss_event_pd_service_down_data {
267 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800268 bool fw_rejuvenate;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800269};
270
271struct icnss_driver_event {
272 struct list_head list;
273 enum icnss_driver_event_type type;
274 bool sync;
275 struct completion complete;
276 int ret;
277 void *data;
278};
279
280enum icnss_driver_state {
281 ICNSS_WLFW_QMI_CONNECTED,
282 ICNSS_POWER_ON,
283 ICNSS_FW_READY,
284 ICNSS_DRIVER_PROBED,
285 ICNSS_FW_TEST_MODE,
286 ICNSS_PM_SUSPEND,
287 ICNSS_PM_SUSPEND_NOIRQ,
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -0700288 ICNSS_SSR_REGISTERED,
289 ICNSS_PDR_REGISTERED,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800290 ICNSS_PD_RESTART,
291 ICNSS_MSA0_ASSIGNED,
292 ICNSS_WLFW_EXISTS,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530293 ICNSS_SHUTDOWN_DONE,
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700294 ICNSS_HOST_TRIGGERED_PDR,
Sameer Thalappil93c00732017-08-18 13:02:32 -0700295 ICNSS_FW_DOWN,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800296};
297
298struct ce_irq_list {
299 int irq;
300 irqreturn_t (*handler)(int, void *);
301};
302
Yuanyuan Liu68939762017-04-04 16:43:03 -0700303struct icnss_vreg_info {
304 struct regulator *reg;
305 const char *name;
306 u32 min_v;
307 u32 max_v;
308 u32 load_ua;
309 unsigned long settle_delay;
310 bool required;
311};
312
313struct icnss_clk_info {
314 struct clk *handle;
315 const char *name;
316 u32 freq;
317 bool required;
318};
319
320static struct icnss_vreg_info icnss_vreg_info[] = {
321 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
322 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
323 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
324 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
325};
326
327#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
328
329static struct icnss_clk_info icnss_clk_info[] = {
330 {NULL, "cxo_ref_clk_pin", 0, false},
331};
332
333#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
334
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800335struct icnss_stats {
336 struct {
337 uint32_t posted;
338 uint32_t processed;
339 } events[ICNSS_DRIVER_EVENT_MAX];
340
341 struct {
342 uint32_t request;
343 uint32_t free;
344 uint32_t enable;
345 uint32_t disable;
346 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
347
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700348 struct {
349 uint32_t pdr_fw_crash;
350 uint32_t pdr_host_error;
351 uint32_t root_pd_crash;
352 uint32_t root_pd_shutdown;
353 } recovery;
354
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800355 uint32_t pm_suspend;
356 uint32_t pm_suspend_err;
357 uint32_t pm_resume;
358 uint32_t pm_resume_err;
359 uint32_t pm_suspend_noirq;
360 uint32_t pm_suspend_noirq_err;
361 uint32_t pm_resume_noirq;
362 uint32_t pm_resume_noirq_err;
363 uint32_t pm_stay_awake;
364 uint32_t pm_relax;
365
366 uint32_t ind_register_req;
367 uint32_t ind_register_resp;
368 uint32_t ind_register_err;
369 uint32_t msa_info_req;
370 uint32_t msa_info_resp;
371 uint32_t msa_info_err;
372 uint32_t msa_ready_req;
373 uint32_t msa_ready_resp;
374 uint32_t msa_ready_err;
375 uint32_t msa_ready_ind;
376 uint32_t cap_req;
377 uint32_t cap_resp;
378 uint32_t cap_err;
379 uint32_t pin_connect_result;
380 uint32_t cfg_req;
381 uint32_t cfg_resp;
382 uint32_t cfg_req_err;
383 uint32_t mode_req;
384 uint32_t mode_resp;
385 uint32_t mode_req_err;
386 uint32_t ini_req;
387 uint32_t ini_resp;
388 uint32_t ini_req_err;
389 uint32_t vbatt_req;
390 uint32_t vbatt_resp;
391 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800392 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800393 uint32_t rejuvenate_ack_req;
394 uint32_t rejuvenate_ack_resp;
395 uint32_t rejuvenate_ack_err;
396};
397
398#define MAX_NO_OF_MAC_ADDR 4
399struct icnss_wlan_mac_addr {
400 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
401 uint32_t no_of_mac_addr_set;
402};
403
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700404enum icnss_pdr_cause_index {
405 ICNSS_FW_CRASH,
406 ICNSS_ROOT_PD_CRASH,
407 ICNSS_ROOT_PD_SHUTDOWN,
408 ICNSS_HOST_ERROR,
409};
410
411static const char * const icnss_pdr_cause[] = {
412 [ICNSS_FW_CRASH] = "FW crash",
413 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
414 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
415 [ICNSS_HOST_ERROR] = "Host error",
416};
417
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800418struct service_notifier_context {
419 void *handle;
420 uint32_t instance_id;
421 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800422};
423
424static struct icnss_priv {
425 uint32_t magic;
426 struct platform_device *pdev;
427 struct icnss_driver_ops *ops;
428 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700429 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
430 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800431 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
432 phys_addr_t mem_base_pa;
433 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800434 struct dma_iommu_mapping *smmu_mapping;
435 dma_addr_t smmu_iova_start;
436 size_t smmu_iova_len;
437 dma_addr_t smmu_iova_ipa_start;
438 size_t smmu_iova_ipa_len;
439 struct qmi_handle *wlfw_clnt;
440 struct list_head event_list;
441 spinlock_t event_lock;
442 struct work_struct event_work;
443 struct work_struct qmi_recv_msg_work;
444 struct workqueue_struct *event_wq;
445 phys_addr_t msa_pa;
446 uint32_t msa_mem_size;
447 void *msa_va;
448 unsigned long state;
449 struct wlfw_rf_chip_info_s_v01 chip_info;
450 struct wlfw_rf_board_info_s_v01 board_info;
451 struct wlfw_soc_info_s_v01 soc_info;
452 struct wlfw_fw_version_info_s_v01 fw_version_info;
453 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
454 u32 pwr_pin_result;
455 u32 phy_io_pin_result;
456 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700457 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800458 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700459 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800460 struct dentry *root_dentry;
461 spinlock_t on_off_lock;
462 struct icnss_stats stats;
463 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800464 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800465 struct notifier_block service_notifier_nb;
466 int total_domains;
467 struct notifier_block get_service_nb;
468 void *modem_notify_handler;
469 struct notifier_block modem_ssr_nb;
470 uint32_t diag_reg_read_addr;
471 uint32_t diag_reg_read_mem_type;
472 uint32_t diag_reg_read_len;
473 uint8_t *diag_reg_read_buf;
474 struct qpnp_adc_tm_btm_param vph_monitor_params;
475 struct qpnp_adc_tm_chip *adc_tm_dev;
476 struct qpnp_vadc_chip *vadc_dev;
477 uint64_t vph_pwr;
478 atomic_t pm_count;
479 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800480 bool is_wlan_mac_set;
481 struct icnss_wlan_mac_addr wlan_mac_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700482 bool bypass_s1_smmu;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800483 u8 cause_for_rejuvenation;
484 u8 requesting_sub_system;
485 u16 line_number;
486 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530487 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800488} *penv;
489
Yuanyuan Liu68939762017-04-04 16:43:03 -0700490#ifdef CONFIG_ICNSS_DEBUG
491static void icnss_ignore_qmi_timeout(bool ignore)
492{
493 ignore_qmi_timeout = ignore;
494}
495#else
496static void icnss_ignore_qmi_timeout(bool ignore) { }
497#endif
498
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530499static int icnss_assign_msa_perm(struct icnss_mem_region_info
500 *mem_region, enum icnss_msa_perm new_perm)
501{
502 int ret = 0;
503 phys_addr_t addr;
504 u32 size;
505 u32 i = 0;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530506 u32 source_vmids[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530507 u32 source_nelems;
Anurag Chouhana8b56832017-08-31 16:54:48 +0530508 u32 dest_vmids[ICNSS_MAX_VMIDS] = {0};
509 u32 dest_perms[ICNSS_MAX_VMIDS] = {0};
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530510 u32 dest_nelems;
511 enum icnss_msa_perm cur_perm = mem_region->perm;
512 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
513
514 addr = mem_region->reg_addr;
515 size = mem_region->size;
516
517 if (mem_region->secure_flag) {
518 new_perm_list = &msa_perm_secure_list[new_perm];
519 old_perm_list = &msa_perm_secure_list[cur_perm];
520 } else {
521 new_perm_list = &msa_perm_list[new_perm];
522 old_perm_list = &msa_perm_list[cur_perm];
523 }
524
525 source_nelems = old_perm_list->nelems;
526 dest_nelems = new_perm_list->nelems;
527
528 for (i = 0; i < source_nelems; ++i)
529 source_vmids[i] = old_perm_list->vmids[i];
530
531 for (i = 0; i < dest_nelems; ++i) {
532 dest_vmids[i] = new_perm_list->vmids[i];
533 dest_perms[i] = new_perm_list->perms[i];
534 }
535
536 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
537 dest_vmids, dest_perms, dest_nelems);
538 if (ret) {
539 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
540 &addr, size, ret);
541 goto out;
542 }
543
544 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
545 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
546 source_nelems, source_vmids[0], source_vmids[1],
547 source_vmids[2], source_vmids[3], dest_nelems,
548 dest_vmids[0], dest_vmids[1], dest_vmids[2],
549 dest_vmids[3]);
550out:
551 return ret;
552}
553
554static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
555 enum icnss_msa_perm new_perm)
556{
557 int ret;
558 int i;
559 enum icnss_msa_perm old_perm;
560
561 for (i = 0; i < priv->nr_mem_region; i++) {
562 old_perm = priv->mem_region[i].perm;
563 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
564 if (ret)
565 goto err_unmap;
566 priv->mem_region[i].perm = new_perm;
567 }
568 return 0;
569
570err_unmap:
571 for (i--; i >= 0; i--) {
572 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
573 }
574 return ret;
575}
576
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800577static void icnss_pm_stay_awake(struct icnss_priv *priv)
578{
579 if (atomic_inc_return(&priv->pm_count) != 1)
580 return;
581
Yuanyuan Liu68939762017-04-04 16:43:03 -0700582 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800583 atomic_read(&priv->pm_count));
584
585 pm_stay_awake(&priv->pdev->dev);
586
587 priv->stats.pm_stay_awake++;
588}
589
590static void icnss_pm_relax(struct icnss_priv *priv)
591{
592 int r = atomic_dec_return(&priv->pm_count);
593
594 WARN_ON(r < 0);
595
596 if (r != 0)
597 return;
598
Yuanyuan Liu68939762017-04-04 16:43:03 -0700599 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800600 atomic_read(&priv->pm_count));
601
602 pm_relax(&priv->pdev->dev);
603 priv->stats.pm_relax++;
604}
605
606static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
607{
608 switch (type) {
609 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
610 return "SERVER_ARRIVE";
611 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
612 return "SERVER_EXIT";
613 case ICNSS_DRIVER_EVENT_FW_READY_IND:
614 return "FW_READY";
615 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
616 return "REGISTER_DRIVER";
617 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
618 return "UNREGISTER_DRIVER";
619 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
620 return "PD_SERVICE_DOWN";
621 case ICNSS_DRIVER_EVENT_MAX:
622 return "EVENT_MAX";
623 }
624
625 return "UNKNOWN";
626};
627
628static int icnss_driver_event_post(enum icnss_driver_event_type type,
629 u32 flags, void *data)
630{
631 struct icnss_driver_event *event;
632 unsigned long irq_flags;
633 int gfp = GFP_KERNEL;
634 int ret = 0;
635
636 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
637 icnss_driver_event_to_str(type), type, current->comm,
638 flags, penv->state);
639
640 if (type >= ICNSS_DRIVER_EVENT_MAX) {
641 icnss_pr_err("Invalid Event type: %d, can't post", type);
642 return -EINVAL;
643 }
644
645 if (in_interrupt() || irqs_disabled())
646 gfp = GFP_ATOMIC;
647
648 event = kzalloc(sizeof(*event), gfp);
649 if (event == NULL)
650 return -ENOMEM;
651
652 icnss_pm_stay_awake(penv);
653
654 event->type = type;
655 event->data = data;
656 init_completion(&event->complete);
657 event->ret = ICNSS_EVENT_PENDING;
658 event->sync = !!(flags & ICNSS_EVENT_SYNC);
659
660 spin_lock_irqsave(&penv->event_lock, irq_flags);
661 list_add_tail(&event->list, &penv->event_list);
662 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
663
664 penv->stats.events[type].posted++;
665 queue_work(penv->event_wq, &penv->event_work);
666
667 if (!(flags & ICNSS_EVENT_SYNC))
668 goto out;
669
670 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
671 wait_for_completion(&event->complete);
672 else
673 ret = wait_for_completion_interruptible(&event->complete);
674
675 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
676 icnss_driver_event_to_str(type), type, penv->state, ret,
677 event->ret);
678
679 spin_lock_irqsave(&penv->event_lock, irq_flags);
680 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
681 event->sync = false;
682 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
683 ret = -EINTR;
684 goto out;
685 }
686 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
687
688 ret = event->ret;
689 kfree(event);
690
691out:
692 icnss_pm_relax(penv);
693 return ret;
694}
695
696static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
697 uint64_t voltage_uv)
698{
699 int ret;
700 struct wlfw_vbatt_req_msg_v01 req;
701 struct wlfw_vbatt_resp_msg_v01 resp;
702 struct msg_desc req_desc, resp_desc;
703
704 if (!priv->wlfw_clnt) {
705 ret = -ENODEV;
706 goto out;
707 }
708
709 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
710 penv->state);
711
712 memset(&req, 0, sizeof(req));
713 memset(&resp, 0, sizeof(resp));
714
715 req.voltage_uv = voltage_uv;
716
717 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
718 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
719 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
720
721 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
722 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
723 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
724
725 priv->stats.vbatt_req++;
726
727 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
728 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
729 if (ret < 0) {
730 icnss_pr_err("Send vbatt req failed %d\n", ret);
731 goto out;
732 }
733
734 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
735 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
736 resp.resp.result, resp.resp.error);
737 ret = resp.resp.result;
738 goto out;
739 }
740 priv->stats.vbatt_resp++;
741
742out:
743 priv->stats.vbatt_req_err++;
744 return ret;
745}
746
747static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
748{
749 int ret = 0;
750 struct qpnp_vadc_result adc_result;
751
752 if (!priv->vadc_dev) {
753 icnss_pr_err("VADC dev doesn't exists\n");
754 ret = -EINVAL;
755 goto out;
756 }
757
758 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
759 if (ret) {
760 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
761 VADC_VPH_PWR, ret);
762 goto out;
763 }
764
765 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
766 adc_result.physical, adc_result.measurement);
767
768 *result_uv = adc_result.physical;
769out:
770 return ret;
771}
772
773static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
774{
775 struct icnss_priv *priv = ctx;
776 uint64_t vph_pwr = 0;
777 uint64_t vph_pwr_prev;
778 int ret = 0;
779 bool update = true;
780
781 if (!priv) {
782 icnss_pr_err("Priv pointer is NULL\n");
783 return;
784 }
785
786 vph_pwr_prev = priv->vph_pwr;
787
788 ret = icnss_get_phone_power(priv, &vph_pwr);
789 if (ret)
790 return;
791
792 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
793 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
794 update = false;
795 priv->vph_monitor_params.state_request =
796 ADC_TM_HIGH_THR_ENABLE;
797 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
798 ICNSS_THRESHOLD_GUARD;
799 priv->vph_monitor_params.low_thr = 0;
800 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
801 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
802 update = false;
803 priv->vph_monitor_params.state_request =
804 ADC_TM_LOW_THR_ENABLE;
805 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
806 ICNSS_THRESHOLD_GUARD;
807 priv->vph_monitor_params.high_thr = 0;
808 } else {
809 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
810 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
811 update = false;
812 priv->vph_monitor_params.state_request =
813 ADC_TM_HIGH_LOW_THR_ENABLE;
814 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
815 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
816 }
817
818 priv->vph_pwr = vph_pwr;
819
820 if (update)
821 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
822
823 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
824 priv->vph_monitor_params.low_thr,
825 priv->vph_monitor_params.high_thr);
826 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
827 &priv->vph_monitor_params);
828 if (ret)
829 icnss_pr_err("TM channel setup failed %d\n", ret);
830}
831
832static int icnss_setup_vph_monitor(struct icnss_priv *priv)
833{
834 int ret = 0;
835
836 if (!priv->adc_tm_dev) {
837 icnss_pr_err("ADC TM handler is NULL\n");
838 ret = -EINVAL;
839 goto out;
840 }
841
842 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
843 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
844 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
845 priv->vph_monitor_params.channel = VADC_VPH_PWR;
846 priv->vph_monitor_params.btm_ctx = priv;
847 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
848 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
849 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
850 priv->vph_monitor_params.low_thr,
851 priv->vph_monitor_params.high_thr);
852
853 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
854 &priv->vph_monitor_params);
855 if (ret)
856 icnss_pr_err("TM channel setup failed %d\n", ret);
857out:
858 return ret;
859}
860
861static int icnss_init_vph_monitor(struct icnss_priv *priv)
862{
863 int ret = 0;
864
865 if (test_bit(VBATT_DISABLE, &quirks))
866 goto out;
867
868 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
869 if (ret)
870 goto out;
871
872 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
873
874 ret = icnss_setup_vph_monitor(priv);
875 if (ret)
876 goto out;
877out:
878 return ret;
879}
880
881
882static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
883{
884 struct msg_desc ind_desc;
885 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
886 int ret = 0;
887
888 if (!penv || !penv->wlfw_clnt) {
889 ret = -ENODEV;
890 goto out;
891 }
892
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530893 memset(&ind_msg, 0, sizeof(ind_msg));
894
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800895 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
896 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
897 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
898
899 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
900 if (ret < 0) {
901 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
902 ret, msg_len);
903 goto out;
904 }
905
906 /* store pin result locally */
907 if (ind_msg.pwr_pin_result_valid)
908 penv->pwr_pin_result = ind_msg.pwr_pin_result;
909 if (ind_msg.phy_io_pin_result_valid)
910 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
911 if (ind_msg.rf_pin_result_valid)
912 penv->rf_pin_result = ind_msg.rf_pin_result;
913
914 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
915 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
916 ind_msg.rf_pin_result);
917
918 penv->stats.pin_connect_result++;
919out:
920 return ret;
921}
922
Yuanyuan Liu68939762017-04-04 16:43:03 -0700923static int icnss_vreg_on(struct icnss_priv *priv)
924{
925 int ret = 0;
926 struct icnss_vreg_info *vreg_info;
927 int i;
928
929 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
930 vreg_info = &priv->vreg_info[i];
931
932 if (!vreg_info->reg)
933 continue;
934
935 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
936
937 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
938 vreg_info->max_v);
939 if (ret) {
940 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
941 vreg_info->name, vreg_info->min_v,
942 vreg_info->max_v, ret);
943 break;
944 }
945
946 if (vreg_info->load_ua) {
947 ret = regulator_set_load(vreg_info->reg,
948 vreg_info->load_ua);
949 if (ret < 0) {
950 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
951 vreg_info->name,
952 vreg_info->load_ua, ret);
953 break;
954 }
955 }
956
957 ret = regulator_enable(vreg_info->reg);
958 if (ret) {
959 icnss_pr_err("Regulator %s, can't enable: %d\n",
960 vreg_info->name, ret);
961 break;
962 }
963
964 if (vreg_info->settle_delay)
965 udelay(vreg_info->settle_delay);
966 }
967
968 if (!ret)
969 return 0;
970
971 for (; i >= 0; i--) {
972 vreg_info = &priv->vreg_info[i];
973
974 if (!vreg_info->reg)
975 continue;
976
977 regulator_disable(vreg_info->reg);
978 regulator_set_load(vreg_info->reg, 0);
979 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
980 }
981
982 return ret;
983}
984
985static int icnss_vreg_off(struct icnss_priv *priv)
986{
987 int ret = 0;
988 struct icnss_vreg_info *vreg_info;
989 int i;
990
991 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
992 vreg_info = &priv->vreg_info[i];
993
994 if (!vreg_info->reg)
995 continue;
996
997 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
998
999 ret = regulator_disable(vreg_info->reg);
1000 if (ret)
1001 icnss_pr_err("Regulator %s, can't disable: %d\n",
1002 vreg_info->name, ret);
1003
1004 ret = regulator_set_load(vreg_info->reg, 0);
1005 if (ret < 0)
1006 icnss_pr_err("Regulator %s, can't set load: %d\n",
1007 vreg_info->name, ret);
1008
1009 ret = regulator_set_voltage(vreg_info->reg, 0,
1010 vreg_info->max_v);
1011 if (ret)
1012 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1013 vreg_info->name, ret);
1014 }
1015
1016 return ret;
1017}
1018
1019static int icnss_clk_init(struct icnss_priv *priv)
1020{
1021 struct icnss_clk_info *clk_info;
1022 int i;
1023 int ret = 0;
1024
1025 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1026 clk_info = &priv->clk_info[i];
1027
1028 if (!clk_info->handle)
1029 continue;
1030
1031 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1032
1033 if (clk_info->freq) {
1034 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1035
1036 if (ret) {
1037 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1038 clk_info->name, clk_info->freq,
1039 ret);
1040 break;
1041 }
1042 }
1043
1044 ret = clk_prepare_enable(clk_info->handle);
1045 if (ret) {
1046 icnss_pr_err("Clock %s, can't enable: %d\n",
1047 clk_info->name, ret);
1048 break;
1049 }
1050 }
1051
1052 if (ret == 0)
1053 return 0;
1054
1055 for (; i >= 0; i--) {
1056 clk_info = &priv->clk_info[i];
1057
1058 if (!clk_info->handle)
1059 continue;
1060
1061 clk_disable_unprepare(clk_info->handle);
1062 }
1063
1064 return ret;
1065}
1066
1067static int icnss_clk_deinit(struct icnss_priv *priv)
1068{
1069 struct icnss_clk_info *clk_info;
1070 int i;
1071
1072 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1073 clk_info = &priv->clk_info[i];
1074
1075 if (!clk_info->handle)
1076 continue;
1077
1078 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1079
1080 clk_disable_unprepare(clk_info->handle);
1081 }
1082
1083 return 0;
1084}
1085
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001086static int icnss_hw_power_on(struct icnss_priv *priv)
1087{
1088 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001089
1090 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1091
Yuanyuan Liu68939762017-04-04 16:43:03 -07001092 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001093 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001094 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001095 return ret;
1096 }
1097 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001098 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001099
Yuanyuan Liu68939762017-04-04 16:43:03 -07001100 ret = icnss_vreg_on(priv);
1101 if (ret)
1102 goto out;
1103
1104 ret = icnss_clk_init(priv);
1105 if (ret)
1106 goto vreg_off;
1107
1108 return ret;
1109
1110vreg_off:
1111 icnss_vreg_off(priv);
1112out:
1113 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001114 return ret;
1115}
1116
1117static int icnss_hw_power_off(struct icnss_priv *priv)
1118{
1119 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001120
1121 if (test_bit(HW_ALWAYS_ON, &quirks))
1122 return 0;
1123
1124 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1125
Yuanyuan Liu68939762017-04-04 16:43:03 -07001126 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001127 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001128 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001129 return ret;
1130 }
1131 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001132 spin_unlock(&priv->on_off_lock);
1133
1134 icnss_clk_deinit(priv);
1135
1136 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001137
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001138 return ret;
1139}
1140
1141int icnss_power_on(struct device *dev)
1142{
1143 struct icnss_priv *priv = dev_get_drvdata(dev);
1144
1145 if (!priv) {
1146 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1147 dev, priv);
1148 return -EINVAL;
1149 }
1150
1151 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1152
1153 return icnss_hw_power_on(priv);
1154}
1155EXPORT_SYMBOL(icnss_power_on);
1156
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001157bool icnss_is_fw_ready(void)
1158{
1159 if (!penv)
1160 return false;
1161 else
1162 return test_bit(ICNSS_FW_READY, &penv->state);
1163}
1164EXPORT_SYMBOL(icnss_is_fw_ready);
1165
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001166int icnss_power_off(struct device *dev)
1167{
1168 struct icnss_priv *priv = dev_get_drvdata(dev);
1169
1170 if (!priv) {
1171 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1172 dev, priv);
1173 return -EINVAL;
1174 }
1175
1176 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1177
1178 return icnss_hw_power_off(priv);
1179}
1180EXPORT_SYMBOL(icnss_power_off);
1181
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001182static int wlfw_msa_mem_info_send_sync_msg(void)
1183{
1184 int ret;
1185 int i;
1186 struct wlfw_msa_info_req_msg_v01 req;
1187 struct wlfw_msa_info_resp_msg_v01 resp;
1188 struct msg_desc req_desc, resp_desc;
1189
1190 if (!penv || !penv->wlfw_clnt)
1191 return -ENODEV;
1192
1193 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1194
1195 memset(&req, 0, sizeof(req));
1196 memset(&resp, 0, sizeof(resp));
1197
1198 req.msa_addr = penv->msa_pa;
1199 req.size = penv->msa_mem_size;
1200
1201 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1202 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1203 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1204
1205 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1206 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1207 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1208
1209 penv->stats.msa_info_req++;
1210
1211 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1212 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1213 if (ret < 0) {
1214 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1215 goto out;
1216 }
1217
1218 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1219 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1220 resp.resp.result, resp.resp.error);
1221 ret = resp.resp.result;
1222 goto out;
1223 }
1224
1225 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1226 resp.mem_region_info_len);
1227
Yuanyuan Liu68939762017-04-04 16:43:03 -07001228 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001229 icnss_pr_err("Invalid memory region length received: %d\n",
1230 resp.mem_region_info_len);
1231 ret = -EINVAL;
1232 goto out;
1233 }
1234
1235 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001236 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001237 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001238 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001239 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001240 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001241 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001242 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001243 resp.mem_region_info[i].secure_flag;
1244 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001245 i, penv->mem_region[i].reg_addr,
1246 penv->mem_region[i].size,
1247 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001248 }
1249
1250 return 0;
1251
1252out:
1253 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001254 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001255 return ret;
1256}
1257
1258static int wlfw_msa_ready_send_sync_msg(void)
1259{
1260 int ret;
1261 struct wlfw_msa_ready_req_msg_v01 req;
1262 struct wlfw_msa_ready_resp_msg_v01 resp;
1263 struct msg_desc req_desc, resp_desc;
1264
1265 if (!penv || !penv->wlfw_clnt)
1266 return -ENODEV;
1267
1268 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1269 penv->state);
1270
1271 memset(&req, 0, sizeof(req));
1272 memset(&resp, 0, sizeof(resp));
1273
1274 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1275 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1276 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1277
1278 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1279 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1280 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1281
1282 penv->stats.msa_ready_req++;
1283 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1284 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1285 if (ret < 0) {
1286 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1287 goto out;
1288 }
1289
1290 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1291 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1292 resp.resp.result, resp.resp.error);
1293 ret = resp.resp.result;
1294 goto out;
1295 }
1296 penv->stats.msa_ready_resp++;
1297
1298 return 0;
1299
1300out:
1301 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001302 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001303 return ret;
1304}
1305
1306static int wlfw_ind_register_send_sync_msg(void)
1307{
1308 int ret;
1309 struct wlfw_ind_register_req_msg_v01 req;
1310 struct wlfw_ind_register_resp_msg_v01 resp;
1311 struct msg_desc req_desc, resp_desc;
1312
1313 if (!penv || !penv->wlfw_clnt)
1314 return -ENODEV;
1315
1316 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1317 penv->state);
1318
1319 memset(&req, 0, sizeof(req));
1320 memset(&resp, 0, sizeof(resp));
1321
1322 req.client_id_valid = 1;
1323 req.client_id = WLFW_CLIENT_ID;
1324 req.fw_ready_enable_valid = 1;
1325 req.fw_ready_enable = 1;
1326 req.msa_ready_enable_valid = 1;
1327 req.msa_ready_enable = 1;
1328 req.pin_connect_result_enable_valid = 1;
1329 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001330 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1331 req.rejuvenate_enable_valid = 1;
1332 req.rejuvenate_enable = 1;
1333 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001334
1335 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1336 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1337 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1338
1339 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1340 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1341 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1342
1343 penv->stats.ind_register_req++;
1344
1345 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1346 &resp_desc, &resp, sizeof(resp),
1347 WLFW_TIMEOUT_MS);
1348 if (ret < 0) {
1349 icnss_pr_err("Send indication register req failed %d\n", ret);
1350 goto out;
1351 }
1352
1353 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1354 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1355 resp.resp.result, resp.resp.error);
1356 ret = resp.resp.result;
1357 goto out;
1358 }
1359 penv->stats.ind_register_resp++;
1360
1361 return 0;
1362
1363out:
1364 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001365 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001366 return ret;
1367}
1368
1369static int wlfw_cap_send_sync_msg(void)
1370{
1371 int ret;
1372 struct wlfw_cap_req_msg_v01 req;
1373 struct wlfw_cap_resp_msg_v01 resp;
1374 struct msg_desc req_desc, resp_desc;
1375
1376 if (!penv || !penv->wlfw_clnt)
1377 return -ENODEV;
1378
1379 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1380
1381 memset(&resp, 0, sizeof(resp));
1382
1383 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1384 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1385 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1386
1387 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1388 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1389 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1390
1391 penv->stats.cap_req++;
1392 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1393 &resp_desc, &resp, sizeof(resp),
1394 WLFW_TIMEOUT_MS);
1395 if (ret < 0) {
1396 icnss_pr_err("Send capability req failed %d\n", ret);
1397 goto out;
1398 }
1399
1400 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1401 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1402 resp.resp.result, resp.resp.error);
1403 ret = resp.resp.result;
1404 goto out;
1405 }
1406
1407 penv->stats.cap_resp++;
1408 /* store cap locally */
1409 if (resp.chip_info_valid)
1410 penv->chip_info = resp.chip_info;
1411 if (resp.board_info_valid)
1412 penv->board_info = resp.board_info;
1413 else
1414 penv->board_info.board_id = 0xFF;
1415 if (resp.soc_info_valid)
1416 penv->soc_info = resp.soc_info;
1417 if (resp.fw_version_info_valid)
1418 penv->fw_version_info = resp.fw_version_info;
1419 if (resp.fw_build_id_valid)
1420 strlcpy(penv->fw_build_id, resp.fw_build_id,
1421 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1422
1423 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",
1424 penv->chip_info.chip_id, penv->chip_info.chip_family,
1425 penv->board_info.board_id, penv->soc_info.soc_id,
1426 penv->fw_version_info.fw_version,
1427 penv->fw_version_info.fw_build_timestamp,
1428 penv->fw_build_id);
1429
1430 return 0;
1431
1432out:
1433 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001434 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001435 return ret;
1436}
1437
1438static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1439{
1440 int ret;
1441 struct wlfw_wlan_mode_req_msg_v01 req;
1442 struct wlfw_wlan_mode_resp_msg_v01 resp;
1443 struct msg_desc req_desc, resp_desc;
1444
1445 if (!penv || !penv->wlfw_clnt)
1446 return -ENODEV;
1447
1448 /* During recovery do not send mode request for WLAN OFF as
1449 * FW not able to process it.
1450 */
1451 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1452 mode == QMI_WLFW_OFF_V01)
1453 return 0;
1454
1455 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1456 penv->state, mode);
1457
1458 memset(&req, 0, sizeof(req));
1459 memset(&resp, 0, sizeof(resp));
1460
1461 req.mode = mode;
1462 req.hw_debug_valid = 1;
1463 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1464
1465 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1466 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1467 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1468
1469 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1470 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1471 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1472
1473 penv->stats.mode_req++;
1474 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1475 &resp_desc, &resp, sizeof(resp),
1476 WLFW_TIMEOUT_MS);
1477 if (ret < 0) {
1478 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1479 mode, ret);
1480 goto out;
1481 }
1482
1483 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1484 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1485 mode, resp.resp.result, resp.resp.error);
1486 ret = resp.resp.result;
1487 goto out;
1488 }
1489 penv->stats.mode_resp++;
1490
1491 return 0;
1492
1493out:
1494 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001495 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001496 return ret;
1497}
1498
1499static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1500{
1501 int ret;
1502 struct wlfw_wlan_cfg_req_msg_v01 req;
1503 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1504 struct msg_desc req_desc, resp_desc;
1505
1506 if (!penv || !penv->wlfw_clnt)
1507 return -ENODEV;
1508
1509 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1510
1511 memset(&req, 0, sizeof(req));
1512 memset(&resp, 0, sizeof(resp));
1513
1514 memcpy(&req, data, sizeof(req));
1515
1516 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1517 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1518 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1519
1520 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1521 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1522 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1523
1524 penv->stats.cfg_req++;
1525 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1526 &resp_desc, &resp, sizeof(resp),
1527 WLFW_TIMEOUT_MS);
1528 if (ret < 0) {
1529 icnss_pr_err("Send config req failed %d\n", ret);
1530 goto out;
1531 }
1532
1533 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1534 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1535 resp.resp.result, resp.resp.error);
1536 ret = resp.resp.result;
1537 goto out;
1538 }
1539 penv->stats.cfg_resp++;
1540
1541 return 0;
1542
1543out:
1544 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001545 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001546 return ret;
1547}
1548
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001549static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001550{
1551 int ret;
1552 struct wlfw_ini_req_msg_v01 req;
1553 struct wlfw_ini_resp_msg_v01 resp;
1554 struct msg_desc req_desc, resp_desc;
1555
1556 if (!penv || !penv->wlfw_clnt)
1557 return -ENODEV;
1558
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001559 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1560 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001561
1562 memset(&req, 0, sizeof(req));
1563 memset(&resp, 0, sizeof(resp));
1564
1565 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001566 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001567
1568 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1569 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1570 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1571
1572 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1573 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1574 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1575
1576 penv->stats.ini_req++;
1577
1578 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1579 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1580 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001581 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1582 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001583 goto out;
1584 }
1585
1586 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001587 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1588 fw_log_mode, resp.resp.result, resp.resp.error);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001589 ret = resp.resp.result;
1590 goto out;
1591 }
1592 penv->stats.ini_resp++;
1593
1594 return 0;
1595
1596out:
1597 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001598 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001599 return ret;
1600}
1601
1602static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1603 uint32_t offset, uint32_t mem_type,
1604 uint32_t data_len, uint8_t *data)
1605{
1606 int ret;
1607 struct wlfw_athdiag_read_req_msg_v01 req;
1608 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1609 struct msg_desc req_desc, resp_desc;
1610
1611 if (!priv->wlfw_clnt) {
1612 ret = -ENODEV;
1613 goto out;
1614 }
1615
1616 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1617 priv->state, offset, mem_type, data_len);
1618
1619 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1620 if (!resp) {
1621 ret = -ENOMEM;
1622 goto out;
1623 }
1624 memset(&req, 0, sizeof(req));
1625
1626 req.offset = offset;
1627 req.mem_type = mem_type;
1628 req.data_len = data_len;
1629
1630 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1631 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1632 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1633
1634 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1635 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1636 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1637
1638 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1639 &resp_desc, resp, sizeof(*resp),
1640 WLFW_TIMEOUT_MS);
1641 if (ret < 0) {
1642 icnss_pr_err("send athdiag read req failed %d\n", ret);
1643 goto out;
1644 }
1645
1646 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1647 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1648 resp->resp.result, resp->resp.error);
1649 ret = resp->resp.result;
1650 goto out;
1651 }
1652
Yuanyuan Liu68939762017-04-04 16:43:03 -07001653 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001654 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1655 resp->data_valid, resp->data_len);
1656 ret = -EINVAL;
1657 goto out;
1658 }
1659
1660 memcpy(data, resp->data, resp->data_len);
1661
1662out:
1663 kfree(resp);
1664 return ret;
1665}
1666
1667static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1668 uint32_t offset, uint32_t mem_type,
1669 uint32_t data_len, uint8_t *data)
1670{
1671 int ret;
1672 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1673 struct wlfw_athdiag_write_resp_msg_v01 resp;
1674 struct msg_desc req_desc, resp_desc;
1675
1676 if (!priv->wlfw_clnt) {
1677 ret = -ENODEV;
1678 goto out;
1679 }
1680
1681 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1682 priv->state, offset, mem_type, data_len, data);
1683
1684 req = kzalloc(sizeof(*req), GFP_KERNEL);
1685 if (!req) {
1686 ret = -ENOMEM;
1687 goto out;
1688 }
1689 memset(&resp, 0, sizeof(resp));
1690
1691 req->offset = offset;
1692 req->mem_type = mem_type;
1693 req->data_len = data_len;
1694 memcpy(req->data, data, data_len);
1695
1696 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1697 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1698 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1699
1700 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1701 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1702 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1703
1704 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1705 &resp_desc, &resp, sizeof(resp),
1706 WLFW_TIMEOUT_MS);
1707 if (ret < 0) {
1708 icnss_pr_err("send athdiag write req failed %d\n", ret);
1709 goto out;
1710 }
1711
1712 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1713 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1714 resp.resp.result, resp.resp.error);
1715 ret = resp.resp.result;
1716 goto out;
1717 }
1718out:
1719 kfree(req);
1720 return ret;
1721}
1722
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001723static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1724{
1725 struct msg_desc ind_desc;
1726 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1727 int ret = 0;
1728
1729 if (!penv || !penv->wlfw_clnt) {
1730 ret = -ENODEV;
1731 goto out;
1732 }
1733
1734 memset(&ind_msg, 0, sizeof(ind_msg));
1735
1736 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1737 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1738 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1739
1740 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1741 if (ret < 0) {
1742 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1743 ret, msg_len);
1744 goto out;
1745 }
1746
1747 if (ind_msg.cause_for_rejuvenation_valid)
1748 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1749 else
1750 penv->cause_for_rejuvenation = 0;
1751 if (ind_msg.requesting_sub_system_valid)
1752 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1753 else
1754 penv->requesting_sub_system = 0;
1755 if (ind_msg.line_number_valid)
1756 penv->line_number = ind_msg.line_number;
1757 else
1758 penv->line_number = 0;
1759 if (ind_msg.function_name_valid)
1760 memcpy(penv->function_name, ind_msg.function_name,
1761 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1762 else
1763 memset(penv->function_name, 0,
1764 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1765
1766 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1767 penv->cause_for_rejuvenation,
1768 penv->requesting_sub_system,
1769 penv->line_number,
1770 penv->function_name);
1771
1772 penv->stats.rejuvenate_ind++;
1773out:
1774 return ret;
1775}
1776
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001777static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1778{
1779 int ret;
1780 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1781 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1782 struct msg_desc req_desc, resp_desc;
1783
1784 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1785 priv->state);
1786
1787 memset(&req, 0, sizeof(req));
1788 memset(&resp, 0, sizeof(resp));
1789
1790 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1791 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1792 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1793
1794 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1795 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1796 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1797
1798 priv->stats.rejuvenate_ack_req++;
1799 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1800 &resp_desc, &resp, sizeof(resp),
1801 WLFW_TIMEOUT_MS);
1802 if (ret < 0) {
1803 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1804 goto out;
1805 }
1806
1807 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1808 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1809 resp.resp.result, resp.resp.error);
1810 ret = resp.resp.result;
1811 goto out;
1812 }
1813 priv->stats.rejuvenate_ack_resp++;
1814 return 0;
1815
1816out:
1817 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001818 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001819 return ret;
1820}
1821
1822static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1823 uint64_t dynamic_feature_mask)
1824{
1825 int ret;
1826 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1827 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1828 struct msg_desc req_desc, resp_desc;
1829
1830 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1831 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1832 priv->state);
1833 return -EINVAL;
1834 }
1835
1836 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1837 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1838 return 0;
1839 }
1840
1841 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1842 dynamic_feature_mask, priv->state);
1843
1844 memset(&req, 0, sizeof(req));
1845 memset(&resp, 0, sizeof(resp));
1846
1847 req.mask_valid = 1;
1848 req.mask = dynamic_feature_mask;
1849
1850 req_desc.max_msg_len =
1851 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1852 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1853 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1854
1855 resp_desc.max_msg_len =
1856 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1857 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1858 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1859
1860 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1861 &resp_desc, &resp, sizeof(resp),
1862 WLFW_TIMEOUT_MS);
1863 if (ret < 0) {
1864 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1865 goto out;
1866 }
1867
1868 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1869 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1870 resp.resp.result, resp.resp.error);
1871 ret = resp.resp.result;
1872 goto out;
1873 }
1874
1875 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1876 resp.prev_mask_valid, resp.prev_mask,
1877 resp.curr_mask_valid, resp.curr_mask);
1878
1879 return 0;
1880
1881out:
1882 return ret;
1883}
1884
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001885static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1886{
1887 int ret;
1888
1889 if (!penv || !penv->wlfw_clnt)
1890 return;
1891
Yuanyuan Liu68939762017-04-04 16:43:03 -07001892 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001893
1894 do {
1895 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1896
1897 if (ret != -ENOMSG)
1898 icnss_pr_err("Error receiving message: %d\n", ret);
1899
Yuanyuan Liu68939762017-04-04 16:43:03 -07001900 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001901}
1902
1903static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1904 enum qmi_event_type event, void *notify_priv)
1905{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001906 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001907
1908 if (!penv || !penv->wlfw_clnt)
1909 return;
1910
1911 switch (event) {
1912 case QMI_RECV_MSG:
1913 schedule_work(&penv->qmi_recv_msg_work);
1914 break;
1915 default:
1916 icnss_pr_dbg("Unknown Event: %d\n", event);
1917 break;
1918 }
1919}
1920
Yuanyuan Liu68939762017-04-04 16:43:03 -07001921static int icnss_call_driver_uevent(struct icnss_priv *priv,
1922 enum icnss_uevent uevent, void *data)
1923{
1924 struct icnss_uevent_data uevent_data;
1925
1926 if (!priv->ops || !priv->ops->uevent)
1927 return 0;
1928
1929 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1930 priv->state, uevent);
1931
1932 uevent_data.uevent = uevent;
1933 uevent_data.data = data;
1934
1935 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1936}
1937
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001938static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1939 unsigned int msg_id, void *msg,
1940 unsigned int msg_len, void *ind_cb_priv)
1941{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001942 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001943 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001944
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001945 if (!penv)
1946 return;
1947
1948 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1949
Sameer Thalappil93c00732017-08-18 13:02:32 -07001950 if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
1951 icnss_pr_dbg("FW down, ignoring 0x%x, state: 0x%lx\n",
1952 msg_id, penv->state);
1953 return;
1954 }
1955
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001956 switch (msg_id) {
1957 case QMI_WLFW_FW_READY_IND_V01:
1958 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
1959 0, NULL);
1960 break;
1961 case QMI_WLFW_MSA_READY_IND_V01:
1962 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
1963 msg_id);
1964 penv->stats.msa_ready_ind++;
1965 break;
1966 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
1967 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
1968 msg_id);
1969 icnss_qmi_pin_connect_result_ind(msg, msg_len);
1970 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001971 case QMI_WLFW_REJUVENATE_IND_V01:
1972 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
1973 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001974
1975 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001976 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001977 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
1978 if (event_data == NULL)
1979 return;
1980 event_data->crashed = true;
1981 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001982 fw_down_data.crashed = true;
1983 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
1984 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001985 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
1986 0, event_data);
1987 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001988 default:
1989 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
1990 break;
1991 }
1992}
1993
1994static int icnss_driver_event_server_arrive(void *data)
1995{
1996 int ret = 0;
1997
1998 if (!penv)
1999 return -ENODEV;
2000
2001 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
Sameer Thalappil93c00732017-08-18 13:02:32 -07002002 clear_bit(ICNSS_FW_DOWN, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002003
2004 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
2005 if (!penv->wlfw_clnt) {
2006 icnss_pr_err("QMI client handle create failed\n");
2007 ret = -ENOMEM;
2008 goto out;
2009 }
2010
2011 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2012 WLFW_SERVICE_VERS_V01,
2013 WLFW_SERVICE_INS_ID_V01);
2014 if (ret < 0) {
2015 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2016 goto fail;
2017 }
2018
2019 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2020 icnss_qmi_wlfw_clnt_ind, penv);
2021 if (ret < 0) {
2022 icnss_pr_err("Failed to register indication callback: %d\n",
2023 ret);
2024 goto fail;
2025 }
2026
2027 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2028
2029 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2030
2031 ret = icnss_hw_power_on(penv);
2032 if (ret)
2033 goto fail;
2034
2035 ret = wlfw_ind_register_send_sync_msg();
2036 if (ret < 0)
2037 goto err_power_on;
2038
2039 if (!penv->msa_va) {
2040 icnss_pr_err("Invalid MSA address\n");
2041 ret = -EINVAL;
2042 goto err_power_on;
2043 }
2044
2045 ret = wlfw_msa_mem_info_send_sync_msg();
2046 if (ret < 0)
2047 goto err_power_on;
2048
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302049 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2050 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2051 if (ret < 0)
2052 goto err_power_on;
2053 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2054 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002055
2056 ret = wlfw_msa_ready_send_sync_msg();
2057 if (ret < 0)
2058 goto err_setup_msa;
2059
2060 ret = wlfw_cap_send_sync_msg();
2061 if (ret < 0)
2062 goto err_setup_msa;
2063
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002064 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2065 dynamic_feature_mask);
2066
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002067 icnss_init_vph_monitor(penv);
2068
2069 return ret;
2070
2071err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302072 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002073err_power_on:
2074 icnss_hw_power_off(penv);
2075fail:
2076 qmi_handle_destroy(penv->wlfw_clnt);
2077 penv->wlfw_clnt = NULL;
2078out:
2079 ICNSS_ASSERT(0);
2080 return ret;
2081}
2082
2083static int icnss_driver_event_server_exit(void *data)
2084{
2085 if (!penv || !penv->wlfw_clnt)
2086 return -ENODEV;
2087
2088 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2089
2090 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2091 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2092 &penv->vph_monitor_params);
2093
2094 qmi_handle_destroy(penv->wlfw_clnt);
2095
2096 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2097 penv->wlfw_clnt = NULL;
2098
2099 return 0;
2100}
2101
2102static int icnss_call_driver_probe(struct icnss_priv *priv)
2103{
2104 int ret;
2105
2106 if (!priv->ops || !priv->ops->probe)
2107 return 0;
2108
Yuanyuan Liu68939762017-04-04 16:43:03 -07002109 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2110 return -EINVAL;
2111
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002112 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2113
2114 icnss_hw_power_on(priv);
2115
2116 ret = priv->ops->probe(&priv->pdev->dev);
2117 if (ret < 0) {
2118 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2119 ret, priv->state);
2120 goto out;
2121 }
2122
2123 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2124
2125 return 0;
2126
2127out:
2128 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002129 return ret;
2130}
2131
Yuanyuan Liu68939762017-04-04 16:43:03 -07002132static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2133{
2134 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2135 goto out;
2136
2137 if (!priv->ops || !priv->ops->shutdown)
2138 goto out;
2139
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302140 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2141 goto out;
2142
Yuanyuan Liu68939762017-04-04 16:43:03 -07002143 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2144
2145 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302146 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002147
2148out:
2149 return 0;
2150}
2151
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002152static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002153{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002154 int ret;
2155
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002156 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002157
Anurag Chouhan9de21192017-08-08 15:30:02 +05302158 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002159
2160 clear_bit(ICNSS_PD_RESTART, &priv->state);
2161
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002162 if (!priv->ops || !priv->ops->reinit)
2163 goto out;
2164
Yuanyuan Liu68939762017-04-04 16:43:03 -07002165 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002166 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002167
2168 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2169
2170 icnss_hw_power_on(priv);
2171
2172 ret = priv->ops->reinit(&priv->pdev->dev);
2173 if (ret < 0) {
2174 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2175 ret, priv->state);
2176 ICNSS_ASSERT(false);
2177 goto out_power_off;
2178 }
2179
2180out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302181 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002182 return 0;
2183
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002184call_probe:
2185 return icnss_call_driver_probe(priv);
2186
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002187out_power_off:
2188 icnss_hw_power_off(priv);
2189
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002190 return ret;
2191}
2192
2193
2194static int icnss_driver_event_fw_ready_ind(void *data)
2195{
2196 int ret = 0;
2197
2198 if (!penv)
2199 return -ENODEV;
2200
2201 set_bit(ICNSS_FW_READY, &penv->state);
2202
Yuanyuan Liu68939762017-04-04 16:43:03 -07002203 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2204
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002205 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2206
2207 icnss_hw_power_off(penv);
2208
2209 if (!penv->pdev) {
2210 icnss_pr_err("Device is not ready\n");
2211 ret = -ENODEV;
2212 goto out;
2213 }
2214
2215 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002216 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002217 else
2218 ret = icnss_call_driver_probe(penv);
2219
2220out:
2221 return ret;
2222}
2223
2224static int icnss_driver_event_register_driver(void *data)
2225{
2226 int ret = 0;
2227
2228 if (penv->ops)
2229 return -EEXIST;
2230
2231 penv->ops = data;
2232
2233 if (test_bit(SKIP_QMI, &quirks))
2234 set_bit(ICNSS_FW_READY, &penv->state);
2235
2236 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2237 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2238 penv->state);
2239 goto out;
2240 }
2241
2242 ret = icnss_hw_power_on(penv);
2243 if (ret)
2244 goto out;
2245
2246 ret = penv->ops->probe(&penv->pdev->dev);
2247
2248 if (ret) {
2249 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2250 ret, penv->state);
2251 goto power_off;
2252 }
2253
2254 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2255
2256 return 0;
2257
2258power_off:
2259 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002260out:
2261 return ret;
2262}
2263
2264static int icnss_driver_event_unregister_driver(void *data)
2265{
2266 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2267 penv->ops = NULL;
2268 goto out;
2269 }
2270
2271 if (penv->ops)
2272 penv->ops->remove(&penv->pdev->dev);
2273
2274 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2275
2276 penv->ops = NULL;
2277
2278 icnss_hw_power_off(penv);
2279
2280out:
2281 return 0;
2282}
2283
2284static int icnss_call_driver_remove(struct icnss_priv *priv)
2285{
2286 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2287
2288 clear_bit(ICNSS_FW_READY, &priv->state);
2289
2290 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2291 return 0;
2292
2293 if (!priv->ops || !priv->ops->remove)
2294 return 0;
2295
2296 penv->ops->remove(&priv->pdev->dev);
2297
2298 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002299
2300 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002301
2302 return 0;
2303}
2304
Yuanyuan Liu68939762017-04-04 16:43:03 -07002305static int icnss_fw_crashed(struct icnss_priv *priv,
2306 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002307{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302308 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002309
2310 set_bit(ICNSS_PD_RESTART, &priv->state);
2311 clear_bit(ICNSS_FW_READY, &priv->state);
2312
2313 icnss_pm_stay_awake(priv);
2314
Yuanyuan Liu68939762017-04-04 16:43:03 -07002315 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2316 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002317
Yuanyuan Liu68939762017-04-04 16:43:03 -07002318 if (event_data->fw_rejuvenate)
2319 wlfw_rejuvenate_ack_send_sync_msg(priv);
2320
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002321 return 0;
2322}
2323
2324static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2325 void *data)
2326{
2327 int ret = 0;
2328 struct icnss_event_pd_service_down_data *event_data = data;
2329
2330 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002331 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002332
2333 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
2334 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2335 event_data->crashed, priv->state);
2336 ICNSS_ASSERT(0);
2337 goto out;
2338 }
2339
2340 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002341 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002342 else
2343 icnss_call_driver_remove(priv);
2344
2345out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002346 kfree(data);
2347
Yuanyuan Liu68939762017-04-04 16:43:03 -07002348 icnss_ignore_qmi_timeout(false);
2349
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002350 return ret;
2351}
2352
2353static void icnss_driver_event_work(struct work_struct *work)
2354{
2355 struct icnss_driver_event *event;
2356 unsigned long flags;
2357 int ret;
2358
2359 icnss_pm_stay_awake(penv);
2360
2361 spin_lock_irqsave(&penv->event_lock, flags);
2362
2363 while (!list_empty(&penv->event_list)) {
2364 event = list_first_entry(&penv->event_list,
2365 struct icnss_driver_event, list);
2366 list_del(&event->list);
2367 spin_unlock_irqrestore(&penv->event_lock, flags);
2368
2369 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2370 icnss_driver_event_to_str(event->type),
2371 event->sync ? "-sync" : "", event->type,
2372 penv->state);
2373
2374 switch (event->type) {
2375 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2376 ret = icnss_driver_event_server_arrive(event->data);
2377 break;
2378 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2379 ret = icnss_driver_event_server_exit(event->data);
2380 break;
2381 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2382 ret = icnss_driver_event_fw_ready_ind(event->data);
2383 break;
2384 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2385 ret = icnss_driver_event_register_driver(event->data);
2386 break;
2387 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2388 ret = icnss_driver_event_unregister_driver(event->data);
2389 break;
2390 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2391 ret = icnss_driver_event_pd_service_down(penv,
2392 event->data);
2393 break;
2394 default:
2395 icnss_pr_err("Invalid Event type: %d", event->type);
2396 kfree(event);
2397 continue;
2398 }
2399
2400 penv->stats.events[event->type].processed++;
2401
2402 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2403 icnss_driver_event_to_str(event->type),
2404 event->sync ? "-sync" : "", event->type, ret,
2405 penv->state);
2406
2407 spin_lock_irqsave(&penv->event_lock, flags);
2408 if (event->sync) {
2409 event->ret = ret;
2410 complete(&event->complete);
2411 continue;
2412 }
2413 spin_unlock_irqrestore(&penv->event_lock, flags);
2414
2415 kfree(event);
2416
2417 spin_lock_irqsave(&penv->event_lock, flags);
2418 }
2419 spin_unlock_irqrestore(&penv->event_lock, flags);
2420
2421 icnss_pm_relax(penv);
2422}
2423
2424static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2425 unsigned long code,
2426 void *_cmd)
2427{
2428 int ret = 0;
2429
2430 if (!penv)
2431 return -ENODEV;
2432
2433 icnss_pr_dbg("Event Notify: code: %ld", code);
2434
2435 switch (code) {
2436 case QMI_SERVER_ARRIVE:
2437 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2438 0, NULL);
2439 break;
2440
2441 case QMI_SERVER_EXIT:
2442 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2443 0, NULL);
2444 break;
2445 default:
2446 icnss_pr_dbg("Invalid code: %ld", code);
2447 break;
2448 }
2449 return ret;
2450}
2451
2452static int icnss_msa0_ramdump(struct icnss_priv *priv)
2453{
2454 struct ramdump_segment segment;
2455
2456 memset(&segment, 0, sizeof(segment));
2457 segment.v_address = priv->msa_va;
2458 segment.size = priv->msa_mem_size;
2459 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2460}
2461
2462static struct notifier_block wlfw_clnt_nb = {
2463 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2464};
2465
2466static int icnss_modem_notifier_nb(struct notifier_block *nb,
2467 unsigned long code,
2468 void *data)
2469{
2470 struct icnss_event_pd_service_down_data *event_data;
2471 struct notif_data *notif = data;
2472 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2473 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002474 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302475 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002476
Yuanyuan Liu68939762017-04-04 16:43:03 -07002477 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002478
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302479 if (code == SUBSYS_AFTER_SHUTDOWN) {
2480 ret = icnss_assign_msa_perm_all(priv,
2481 ICNSS_MSA_PERM_DUMP_COLLECT);
2482 if (!ret) {
2483 icnss_pr_info("Collecting msa0 segment dump\n");
2484 icnss_msa0_ramdump(priv);
2485 icnss_assign_msa_perm_all(priv,
2486 ICNSS_MSA_PERM_WLAN_HW_RW);
2487 } else {
2488 icnss_pr_err("Not able to Collect msa0 segment dump"
2489 "Apps permissions not assigned %d\n", ret);
2490 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002491 return NOTIFY_OK;
2492 }
2493
2494 if (code != SUBSYS_BEFORE_SHUTDOWN)
2495 return NOTIFY_OK;
2496
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002497 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002498 return NOTIFY_OK;
2499
Yuanyuan Liu68939762017-04-04 16:43:03 -07002500 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2501 priv->state, notif->crashed);
2502
Sameer Thalappil93c00732017-08-18 13:02:32 -07002503 set_bit(ICNSS_FW_DOWN, &priv->state);
2504
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002505 if (notif->crashed)
2506 priv->stats.recovery.root_pd_crash++;
2507 else
2508 priv->stats.recovery.root_pd_shutdown++;
2509
Yuanyuan Liu68939762017-04-04 16:43:03 -07002510 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002511
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002512 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002513
2514 if (event_data == NULL)
2515 return notifier_from_errno(-ENOMEM);
2516
2517 event_data->crashed = notif->crashed;
2518
Yuanyuan Liu68939762017-04-04 16:43:03 -07002519 fw_down_data.crashed = !!notif->crashed;
2520 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2521
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002522 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2523 ICNSS_EVENT_SYNC, event_data);
2524
2525 return NOTIFY_OK;
2526}
2527
2528static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2529{
2530 int ret = 0;
2531
2532 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2533
2534 priv->modem_notify_handler =
2535 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2536
2537 if (IS_ERR(priv->modem_notify_handler)) {
2538 ret = PTR_ERR(priv->modem_notify_handler);
2539 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2540 }
2541
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002542 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002543
2544 return ret;
2545}
2546
2547static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2548{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002549 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002550 return 0;
2551
2552 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2553 &priv->modem_ssr_nb);
2554 priv->modem_notify_handler = NULL;
2555
2556 return 0;
2557}
2558
2559static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2560{
2561 int i;
2562
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002563 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002564 return 0;
2565
2566 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002567 service_notif_unregister_notifier(
2568 priv->service_notifier[i].handle,
2569 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002570
2571 kfree(priv->service_notifier);
2572
2573 priv->service_notifier = NULL;
2574
2575 return 0;
2576}
2577
2578static int icnss_service_notifier_notify(struct notifier_block *nb,
2579 unsigned long notification, void *data)
2580{
2581 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2582 service_notifier_nb);
2583 enum pd_subsys_state *state = data;
2584 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002585 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002586 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002587
Yuanyuan Liu68939762017-04-04 16:43:03 -07002588 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2589 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002590
Yuanyuan Liu68939762017-04-04 16:43:03 -07002591 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2592 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002593
Yuanyuan Liu68939762017-04-04 16:43:03 -07002594 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002595
Yuanyuan Liu68939762017-04-04 16:43:03 -07002596 if (event_data == NULL)
2597 return notifier_from_errno(-ENOMEM);
2598
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002599 event_data->crashed = true;
2600
Yuanyuan Liu68939762017-04-04 16:43:03 -07002601 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002602 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002603 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002604 }
2605
Yuanyuan Liu68939762017-04-04 16:43:03 -07002606 switch (*state) {
2607 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002608 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002609 break;
2610 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002611 cause = ICNSS_ROOT_PD_SHUTDOWN;
2612 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002613 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002614 break;
2615 case USER_PD_STATE_CHANGE:
2616 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2617 cause = ICNSS_HOST_ERROR;
2618 priv->stats.recovery.pdr_host_error++;
2619 } else {
2620 cause = ICNSS_FW_CRASH;
2621 priv->stats.recovery.pdr_fw_crash++;
2622 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002623 break;
2624 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002625 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002626 break;
2627 }
2628
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002629 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2630 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002631event_post:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002632 set_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002633 icnss_ignore_qmi_timeout(true);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002634 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002635
2636 fw_down_data.crashed = event_data->crashed;
2637 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2638 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2639 ICNSS_EVENT_SYNC, event_data);
2640done:
Sameer Thalappil93c00732017-08-18 13:02:32 -07002641 if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
2642 clear_bit(ICNSS_FW_DOWN, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002643 return NOTIFY_OK;
2644}
2645
2646static int icnss_get_service_location_notify(struct notifier_block *nb,
2647 unsigned long opcode, void *data)
2648{
2649 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2650 get_service_nb);
2651 struct pd_qmi_client_data *pd = data;
2652 int curr_state;
2653 int ret;
2654 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002655 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002656
2657 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2658 priv->state);
2659
2660 if (opcode != LOCATOR_UP)
2661 return NOTIFY_DONE;
2662
2663 if (pd->total_domains == 0) {
2664 icnss_pr_err("Did not find any domains\n");
2665 ret = -ENOENT;
2666 goto out;
2667 }
2668
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002669 notifier = kcalloc(pd->total_domains,
2670 sizeof(struct service_notifier_context),
2671 GFP_KERNEL);
2672 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002673 ret = -ENOMEM;
2674 goto out;
2675 }
2676
2677 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2678
2679 for (i = 0; i < pd->total_domains; i++) {
2680 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2681 pd->domain_list[i].name,
2682 pd->domain_list[i].instance_id);
2683
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002684 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002685 service_notif_register_notifier(pd->domain_list[i].name,
2686 pd->domain_list[i].instance_id,
2687 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002688 notifier[i].instance_id = pd->domain_list[i].instance_id;
2689 strlcpy(notifier[i].name, pd->domain_list[i].name,
2690 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002691
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002692 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002693 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2694 i, pd->domain_list->name,
2695 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002696 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002697 goto free_handle;
2698 }
2699 }
2700
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002701 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002702 priv->total_domains = pd->total_domains;
2703
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002704 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002705
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002706 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2707 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002708
2709 return NOTIFY_OK;
2710
2711free_handle:
2712 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002713 if (notifier[i].handle)
2714 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002715 &priv->service_notifier_nb);
2716 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002717 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002718
2719out:
2720 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2721 priv->state);
2722
2723 return NOTIFY_OK;
2724}
2725
2726
2727static int icnss_pd_restart_enable(struct icnss_priv *priv)
2728{
2729 int ret;
2730
2731 if (test_bit(SSR_ONLY, &quirks)) {
2732 icnss_pr_dbg("PDR disabled through module parameter\n");
2733 return 0;
2734 }
2735
2736 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2737
2738 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2739 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2740 ICNSS_WLAN_SERVICE_NAME,
2741 &priv->get_service_nb);
2742 if (ret) {
2743 icnss_pr_err("Get service location failed: %d\n", ret);
2744 goto out;
2745 }
2746
2747 return 0;
2748out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002749 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002750 return ret;
2751
2752}
2753
2754
2755static int icnss_enable_recovery(struct icnss_priv *priv)
2756{
2757 int ret;
2758
2759 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2760 icnss_pr_dbg("Recovery disabled through module parameter\n");
2761 return 0;
2762 }
2763
2764 if (test_bit(PDR_ONLY, &quirks)) {
2765 icnss_pr_dbg("SSR disabled through module parameter\n");
2766 goto enable_pdr;
2767 }
2768
2769 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2770 &priv->pdev->dev);
2771 if (!priv->msa0_dump_dev)
2772 return -ENOMEM;
2773
2774 icnss_modem_ssr_register_notifier(priv);
2775 if (test_bit(SSR_ONLY, &quirks)) {
2776 icnss_pr_dbg("PDR disabled through module parameter\n");
2777 return 0;
2778 }
2779
2780enable_pdr:
2781 ret = icnss_pd_restart_enable(priv);
2782
2783 if (ret)
2784 return ret;
2785
2786 return 0;
2787}
2788
2789int icnss_register_driver(struct icnss_driver_ops *ops)
2790{
2791 int ret = 0;
2792
2793 if (!penv || !penv->pdev) {
2794 ret = -ENODEV;
2795 goto out;
2796 }
2797
2798 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2799
2800 if (penv->ops) {
2801 icnss_pr_err("Driver already registered\n");
2802 ret = -EEXIST;
2803 goto out;
2804 }
2805
2806 if (!ops->probe || !ops->remove) {
2807 ret = -EINVAL;
2808 goto out;
2809 }
2810
2811 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302812 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002813
2814 if (ret == -EINTR)
2815 ret = 0;
2816
2817out:
2818 return ret;
2819}
2820EXPORT_SYMBOL(icnss_register_driver);
2821
2822int icnss_unregister_driver(struct icnss_driver_ops *ops)
2823{
2824 int ret;
2825
2826 if (!penv || !penv->pdev) {
2827 ret = -ENODEV;
2828 goto out;
2829 }
2830
2831 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2832
2833 if (!penv->ops) {
2834 icnss_pr_err("Driver not registered\n");
2835 ret = -ENOENT;
2836 goto out;
2837 }
2838
2839 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2840 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2841out:
2842 return ret;
2843}
2844EXPORT_SYMBOL(icnss_unregister_driver);
2845
2846int icnss_ce_request_irq(unsigned int ce_id,
2847 irqreturn_t (*handler)(int, void *),
2848 unsigned long flags, const char *name, void *ctx)
2849{
2850 int ret = 0;
2851 unsigned int irq;
2852 struct ce_irq_list *irq_entry;
2853
2854 if (!penv || !penv->pdev) {
2855 ret = -ENODEV;
2856 goto out;
2857 }
2858
Yuanyuan Liu68939762017-04-04 16:43:03 -07002859 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002860
2861 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2862 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2863 ret = -EINVAL;
2864 goto out;
2865 }
2866 irq = penv->ce_irqs[ce_id];
2867 irq_entry = &penv->ce_irq_list[ce_id];
2868
2869 if (irq_entry->handler || irq_entry->irq) {
2870 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2871 irq, ce_id);
2872 ret = -EEXIST;
2873 goto out;
2874 }
2875
2876 ret = request_irq(irq, handler, flags, name, ctx);
2877 if (ret) {
2878 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2879 irq, ce_id, ret);
2880 goto out;
2881 }
2882 irq_entry->irq = irq;
2883 irq_entry->handler = handler;
2884
Yuanyuan Liu68939762017-04-04 16:43:03 -07002885 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002886
2887 penv->stats.ce_irqs[ce_id].request++;
2888out:
2889 return ret;
2890}
2891EXPORT_SYMBOL(icnss_ce_request_irq);
2892
2893int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
2894{
2895 int ret = 0;
2896 unsigned int irq;
2897 struct ce_irq_list *irq_entry;
2898
2899 if (!penv || !penv->pdev) {
2900 ret = -ENODEV;
2901 goto out;
2902 }
2903
Yuanyuan Liu68939762017-04-04 16:43:03 -07002904 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002905
2906 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2907 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2908 ret = -EINVAL;
2909 goto out;
2910 }
2911
2912 irq = penv->ce_irqs[ce_id];
2913 irq_entry = &penv->ce_irq_list[ce_id];
2914 if (!irq_entry->handler || !irq_entry->irq) {
2915 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2916 ret = -EEXIST;
2917 goto out;
2918 }
2919 free_irq(irq, ctx);
2920 irq_entry->irq = 0;
2921 irq_entry->handler = NULL;
2922
2923 penv->stats.ce_irqs[ce_id].free++;
2924out:
2925 return ret;
2926}
2927EXPORT_SYMBOL(icnss_ce_free_irq);
2928
2929void icnss_enable_irq(unsigned int ce_id)
2930{
2931 unsigned int irq;
2932
2933 if (!penv || !penv->pdev) {
2934 icnss_pr_err("Platform driver not initialized\n");
2935 return;
2936 }
2937
Yuanyuan Liu68939762017-04-04 16:43:03 -07002938 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002939 penv->state);
2940
2941 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2942 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
2943 return;
2944 }
2945
2946 penv->stats.ce_irqs[ce_id].enable++;
2947
2948 irq = penv->ce_irqs[ce_id];
2949 enable_irq(irq);
2950}
2951EXPORT_SYMBOL(icnss_enable_irq);
2952
2953void icnss_disable_irq(unsigned int ce_id)
2954{
2955 unsigned int irq;
2956
2957 if (!penv || !penv->pdev) {
2958 icnss_pr_err("Platform driver not initialized\n");
2959 return;
2960 }
2961
Yuanyuan Liu68939762017-04-04 16:43:03 -07002962 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002963 penv->state);
2964
2965 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2966 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
2967 ce_id);
2968 return;
2969 }
2970
2971 irq = penv->ce_irqs[ce_id];
2972 disable_irq(irq);
2973
2974 penv->stats.ce_irqs[ce_id].disable++;
2975}
2976EXPORT_SYMBOL(icnss_disable_irq);
2977
2978int icnss_get_soc_info(struct icnss_soc_info *info)
2979{
2980 if (!penv) {
2981 icnss_pr_err("Platform driver not initialized\n");
2982 return -EINVAL;
2983 }
2984
2985 info->v_addr = penv->mem_base_va;
2986 info->p_addr = penv->mem_base_pa;
2987 info->chip_id = penv->chip_info.chip_id;
2988 info->chip_family = penv->chip_info.chip_family;
2989 info->board_id = penv->board_info.board_id;
2990 info->soc_id = penv->soc_info.soc_id;
2991 info->fw_version = penv->fw_version_info.fw_version;
2992 strlcpy(info->fw_build_timestamp,
2993 penv->fw_version_info.fw_build_timestamp,
2994 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
2995
2996 return 0;
2997}
2998EXPORT_SYMBOL(icnss_get_soc_info);
2999
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003000int icnss_set_fw_log_mode(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003001{
3002 int ret;
3003
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003004 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003005
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003006 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003007 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003008 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
3009 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003010 return ret;
3011}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003012EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003013
3014int icnss_athdiag_read(struct device *dev, uint32_t offset,
3015 uint32_t mem_type, uint32_t data_len,
3016 uint8_t *output)
3017{
3018 int ret = 0;
3019 struct icnss_priv *priv = dev_get_drvdata(dev);
3020
3021 if (priv->magic != ICNSS_MAGIC) {
3022 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3023 dev, priv, priv->magic);
3024 return -EINVAL;
3025 }
3026
3027 if (!output || data_len == 0
3028 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3029 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3030 output, data_len);
3031 ret = -EINVAL;
3032 goto out;
3033 }
3034
3035 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3036 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3037 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3038 priv->state);
3039 ret = -EINVAL;
3040 goto out;
3041 }
3042
3043 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3044 data_len, output);
3045out:
3046 return ret;
3047}
3048EXPORT_SYMBOL(icnss_athdiag_read);
3049
3050int icnss_athdiag_write(struct device *dev, uint32_t offset,
3051 uint32_t mem_type, uint32_t data_len,
3052 uint8_t *input)
3053{
3054 int ret = 0;
3055 struct icnss_priv *priv = dev_get_drvdata(dev);
3056
3057 if (priv->magic != ICNSS_MAGIC) {
3058 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3059 dev, priv, priv->magic);
3060 return -EINVAL;
3061 }
3062
3063 if (!input || data_len == 0
3064 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3065 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3066 input, data_len);
3067 ret = -EINVAL;
3068 goto out;
3069 }
3070
3071 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3072 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3073 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3074 priv->state);
3075 ret = -EINVAL;
3076 goto out;
3077 }
3078
3079 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3080 data_len, input);
3081out:
3082 return ret;
3083}
3084EXPORT_SYMBOL(icnss_athdiag_write);
3085
3086int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
3087 enum icnss_driver_mode mode,
3088 const char *host_version)
3089{
3090 struct wlfw_wlan_cfg_req_msg_v01 req;
3091 u32 i;
3092 int ret;
3093
3094 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3095 mode, config, host_version);
3096
3097 memset(&req, 0, sizeof(req));
3098
3099 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3100 goto skip;
3101
3102 if (!config || !host_version) {
3103 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3104 config, host_version);
3105 ret = -EINVAL;
3106 goto out;
3107 }
3108
3109 req.host_version_valid = 1;
3110 strlcpy(req.host_version, host_version,
3111 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3112
3113 req.tgt_cfg_valid = 1;
3114 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3115 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3116 else
3117 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3118 for (i = 0; i < req.tgt_cfg_len; i++) {
3119 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3120 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3121 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3122 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3123 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3124 }
3125
3126 req.svc_cfg_valid = 1;
3127 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3128 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3129 else
3130 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3131 for (i = 0; i < req.svc_cfg_len; i++) {
3132 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3133 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3134 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3135 }
3136
3137 req.shadow_reg_valid = 1;
3138 if (config->num_shadow_reg_cfg >
3139 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3140 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3141 else
3142 req.shadow_reg_len = config->num_shadow_reg_cfg;
3143
3144 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3145 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3146
3147 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3148 if (ret)
3149 goto out;
3150skip:
3151 ret = wlfw_wlan_mode_send_sync_msg(mode);
3152out:
3153 if (test_bit(SKIP_QMI, &quirks))
3154 ret = 0;
3155
3156 return ret;
3157}
3158EXPORT_SYMBOL(icnss_wlan_enable);
3159
3160int icnss_wlan_disable(enum icnss_driver_mode mode)
3161{
3162 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3163}
3164EXPORT_SYMBOL(icnss_wlan_disable);
3165
3166bool icnss_is_qmi_disable(void)
3167{
3168 return test_bit(SKIP_QMI, &quirks) ? true : false;
3169}
3170EXPORT_SYMBOL(icnss_is_qmi_disable);
3171
3172int icnss_get_ce_id(int irq)
3173{
3174 int i;
3175
3176 if (!penv || !penv->pdev)
3177 return -ENODEV;
3178
3179 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3180 if (penv->ce_irqs[i] == irq)
3181 return i;
3182 }
3183
3184 icnss_pr_err("No matching CE id for irq %d\n", irq);
3185
3186 return -EINVAL;
3187}
3188EXPORT_SYMBOL(icnss_get_ce_id);
3189
3190int icnss_get_irq(int ce_id)
3191{
3192 int irq;
3193
3194 if (!penv || !penv->pdev)
3195 return -ENODEV;
3196
3197 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3198 return -EINVAL;
3199
3200 irq = penv->ce_irqs[ce_id];
3201
3202 return irq;
3203}
3204EXPORT_SYMBOL(icnss_get_irq);
3205
3206struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3207{
3208 struct icnss_priv *priv = dev_get_drvdata(dev);
3209
3210 if (!priv) {
3211 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3212 dev, priv);
3213 return NULL;
3214 }
3215
3216 return priv->smmu_mapping;
3217}
3218EXPORT_SYMBOL(icnss_smmu_get_mapping);
3219
3220int icnss_smmu_map(struct device *dev,
3221 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3222{
3223 struct icnss_priv *priv = dev_get_drvdata(dev);
3224 unsigned long iova;
3225 size_t len;
3226 int ret = 0;
3227
3228 if (!priv) {
3229 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3230 dev, priv);
3231 return -EINVAL;
3232 }
3233
3234 if (!iova_addr) {
3235 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3236 &paddr, size);
3237 return -EINVAL;
3238 }
3239
3240 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3241 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3242
3243 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3244 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3245 iova,
3246 &priv->smmu_iova_ipa_start,
3247 priv->smmu_iova_ipa_len);
3248 return -ENOMEM;
3249 }
3250
3251 ret = iommu_map(priv->smmu_mapping->domain, iova,
3252 rounddown(paddr, PAGE_SIZE), len,
3253 IOMMU_READ | IOMMU_WRITE);
3254 if (ret) {
3255 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3256 return ret;
3257 }
3258
3259 priv->smmu_iova_ipa_start = iova + len;
3260 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3261
3262 return 0;
3263}
3264EXPORT_SYMBOL(icnss_smmu_map);
3265
3266unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3267{
3268 return socinfo_get_serial_number();
3269}
3270EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3271
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003272int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
3273{
3274 struct icnss_priv *priv = penv;
3275 uint32_t no_of_mac_addr;
3276 struct icnss_wlan_mac_addr *addr = NULL;
3277 int iter;
3278 u8 *temp = NULL;
3279
3280 if (!priv) {
3281 icnss_pr_err("Priv data is NULL\n");
3282 return -EINVAL;
3283 }
3284
3285 if (priv->is_wlan_mac_set) {
3286 icnss_pr_dbg("WLAN MAC address is already set\n");
3287 return 0;
3288 }
3289
3290 if (len == 0 || (len % ETH_ALEN) != 0) {
3291 icnss_pr_err("Invalid length %d\n", len);
3292 return -EINVAL;
3293 }
3294
3295 no_of_mac_addr = len / ETH_ALEN;
3296 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
3297 icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
3298 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
3299 return -EINVAL;
3300 }
3301
3302 priv->is_wlan_mac_set = true;
3303 addr = &priv->wlan_mac_addr;
3304 addr->no_of_mac_addr_set = no_of_mac_addr;
3305 temp = &addr->mac_addr[0][0];
3306
3307 for (iter = 0; iter < no_of_mac_addr;
3308 ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
3309 ether_addr_copy(temp, in);
3310 icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
3311 temp[0], temp[1], temp[2],
3312 temp[3], temp[4], temp[5]);
3313 }
3314
3315 return 0;
3316}
3317EXPORT_SYMBOL(icnss_set_wlan_mac_address);
3318
3319u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
3320{
3321 struct icnss_priv *priv = dev_get_drvdata(dev);
3322 struct icnss_wlan_mac_addr *addr = NULL;
3323
3324 if (priv->magic != ICNSS_MAGIC) {
3325 icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
3326 dev, priv, priv->magic);
3327 goto out;
3328 }
3329
3330 if (!priv->is_wlan_mac_set) {
3331 icnss_pr_dbg("WLAN MAC address is not set\n");
3332 goto out;
3333 }
3334
3335 addr = &priv->wlan_mac_addr;
3336 *num = addr->no_of_mac_addr_set;
3337 return &addr->mac_addr[0][0];
3338out:
3339 *num = 0;
3340 return NULL;
3341}
3342EXPORT_SYMBOL(icnss_get_wlan_mac_address);
3343
3344int icnss_trigger_recovery(struct device *dev)
3345{
3346 int ret = 0;
3347 struct icnss_priv *priv = dev_get_drvdata(dev);
3348
3349 if (priv->magic != ICNSS_MAGIC) {
3350 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3351 ret = -EINVAL;
3352 goto out;
3353 }
3354
3355 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3356 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3357 priv->state);
3358 ret = -EPERM;
3359 goto out;
3360 }
3361
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003362 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003363 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3364 priv->state);
3365 ret = -EOPNOTSUPP;
3366 goto out;
3367 }
3368
3369 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3370 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3371 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003372 ret = -EINVAL;
3373 goto out;
3374 }
3375
Yuanyuan Liu68939762017-04-04 16:43:03 -07003376 WARN_ON(1);
3377 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3378 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003379
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003380 /*
3381 * Initiate PDR, required only for the first instance
3382 */
3383 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3384 priv->service_notifier[0].instance_id);
3385
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003386 if (!ret)
3387 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3388
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003389out:
3390 return ret;
3391}
3392EXPORT_SYMBOL(icnss_trigger_recovery);
3393
3394
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003395static int icnss_smmu_init(struct icnss_priv *priv)
3396{
3397 struct dma_iommu_mapping *mapping;
3398 int atomic_ctx = 1;
3399 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303400 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003401 int ret = 0;
3402
3403 icnss_pr_dbg("Initializing SMMU\n");
3404
3405 mapping = arm_iommu_create_mapping(&platform_bus_type,
3406 priv->smmu_iova_start,
3407 priv->smmu_iova_len);
3408 if (IS_ERR(mapping)) {
3409 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3410 ret = PTR_ERR(mapping);
3411 goto map_fail;
3412 }
3413
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303414 if (priv->bypass_s1_smmu) {
3415 ret = iommu_domain_set_attr(mapping->domain,
3416 DOMAIN_ATTR_S1_BYPASS,
3417 &s1_bypass);
3418 if (ret < 0) {
3419 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3420 ret);
3421 goto set_attr_fail;
3422 }
3423 icnss_pr_dbg("SMMU S1 BYPASS\n");
3424 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003425 ret = iommu_domain_set_attr(mapping->domain,
3426 DOMAIN_ATTR_ATOMIC,
3427 &atomic_ctx);
3428 if (ret < 0) {
3429 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3430 ret);
3431 goto set_attr_fail;
3432 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303433 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003434
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303435 ret = iommu_domain_set_attr(mapping->domain,
3436 DOMAIN_ATTR_FAST,
3437 &fast);
3438 if (ret < 0) {
3439 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3440 ret);
3441 goto set_attr_fail;
3442 }
3443 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003444 }
3445
3446 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3447 if (ret < 0) {
3448 icnss_pr_err("Attach device failed, err = %d\n", ret);
3449 goto attach_fail;
3450 }
3451
3452 priv->smmu_mapping = mapping;
3453
3454 return ret;
3455
3456attach_fail:
3457set_attr_fail:
3458 arm_iommu_release_mapping(mapping);
3459map_fail:
3460 return ret;
3461}
3462
3463static void icnss_smmu_deinit(struct icnss_priv *priv)
3464{
3465 if (!priv->smmu_mapping)
3466 return;
3467
3468 arm_iommu_detach_device(&priv->pdev->dev);
3469 arm_iommu_release_mapping(priv->smmu_mapping);
3470
3471 priv->smmu_mapping = NULL;
3472}
3473
Yuanyuan Liu68939762017-04-04 16:43:03 -07003474static int icnss_get_vreg_info(struct device *dev,
3475 struct icnss_vreg_info *vreg_info)
3476{
3477 int ret = 0;
3478 char prop_name[MAX_PROP_SIZE];
3479 struct regulator *reg;
3480 const __be32 *prop;
3481 int len = 0;
3482 int i;
3483
3484 reg = devm_regulator_get_optional(dev, vreg_info->name);
3485 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3486 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3487 vreg_info->name);
3488 ret = PTR_ERR(reg);
3489 goto out;
3490 }
3491
3492 if (IS_ERR(reg)) {
3493 ret = PTR_ERR(reg);
3494
3495 if (vreg_info->required) {
3496 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3497 vreg_info->name, ret);
3498 goto out;
3499 } else {
3500 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3501 vreg_info->name, ret);
3502 goto done;
3503 }
3504 }
3505
3506 vreg_info->reg = reg;
3507
3508 snprintf(prop_name, MAX_PROP_SIZE,
3509 "qcom,%s-config", vreg_info->name);
3510
3511 prop = of_get_property(dev->of_node, prop_name, &len);
3512
3513 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3514 prop_name, len);
3515
3516 if (!prop || len < (2 * sizeof(__be32))) {
3517 icnss_pr_dbg("Property %s %s\n", prop_name,
3518 prop ? "invalid format" : "doesn't exist");
3519 goto done;
3520 }
3521
3522 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3523 switch (i) {
3524 case 0:
3525 vreg_info->min_v = be32_to_cpup(&prop[0]);
3526 break;
3527 case 1:
3528 vreg_info->max_v = be32_to_cpup(&prop[1]);
3529 break;
3530 case 2:
3531 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3532 break;
3533 case 3:
3534 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3535 break;
3536 default:
3537 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3538 prop_name, i);
3539 break;
3540 }
3541 }
3542
3543done:
3544 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3545 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3546 vreg_info->load_ua, vreg_info->settle_delay);
3547
3548 return 0;
3549
3550out:
3551 return ret;
3552}
3553
3554static int icnss_get_clk_info(struct device *dev,
3555 struct icnss_clk_info *clk_info)
3556{
3557 struct clk *handle;
3558 int ret = 0;
3559
3560 handle = devm_clk_get(dev, clk_info->name);
3561 if (IS_ERR(handle)) {
3562 ret = PTR_ERR(handle);
3563 if (clk_info->required) {
3564 icnss_pr_err("Clock %s isn't available: %d\n",
3565 clk_info->name, ret);
3566 goto out;
3567 } else {
3568 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3569 ret);
3570 ret = 0;
3571 goto out;
3572 }
3573 }
3574
3575 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3576
3577 clk_info->handle = handle;
3578out:
3579 return ret;
3580}
3581
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003582static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003583{
3584 struct icnss_priv *priv = s->private;
3585
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003586 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003587
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003588 seq_puts(s, "\nCMD: test_mode\n");
3589 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3590 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3591 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003592 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003593
3594 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3595 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003596
3597 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003598 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003599 goto out;
3600 }
3601
3602 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003603 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003604 goto out;
3605 }
3606
3607 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003608 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003609 goto out;
3610 }
3611
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003612out:
3613 seq_puts(s, "\n");
3614 return 0;
3615}
3616
3617static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3618{
3619 int ret;
3620
3621 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3622 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3623 priv->state);
3624 ret = -ENODEV;
3625 goto out;
3626 }
3627
3628 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3629 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3630 priv->state);
3631 ret = -EINVAL;
3632 goto out;
3633 }
3634
3635 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3636 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3637 priv->state);
3638 ret = -EINVAL;
3639 goto out;
3640 }
3641
3642 icnss_wlan_disable(ICNSS_OFF);
3643
3644 ret = icnss_hw_power_off(priv);
3645
3646 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3647
3648out:
3649 return ret;
3650}
3651static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3652 enum icnss_driver_mode mode)
3653{
3654 int ret;
3655
3656 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3657 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3658 priv->state);
3659 ret = -ENODEV;
3660 goto out;
3661 }
3662
3663 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3664 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3665 priv->state);
3666 ret = -EINVAL;
3667 goto out;
3668 }
3669
3670 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3671 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3672 priv->state);
3673 ret = -EBUSY;
3674 goto out;
3675 }
3676
3677 ret = icnss_hw_power_on(priv);
3678 if (ret)
3679 goto out;
3680
3681 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3682
3683 ret = icnss_wlan_enable(NULL, mode, NULL);
3684 if (ret)
3685 goto power_off;
3686
3687 return 0;
3688
3689power_off:
3690 icnss_hw_power_off(priv);
3691 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3692
3693out:
3694 return ret;
3695}
3696
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003697static ssize_t icnss_fw_debug_write(struct file *fp,
3698 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003699 size_t count, loff_t *off)
3700{
3701 struct icnss_priv *priv =
3702 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003703 char buf[64];
3704 char *sptr, *token;
3705 unsigned int len = 0;
3706 char *cmd;
3707 uint64_t val;
3708 const char *delim = " ";
3709 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003710
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003711 len = min(count, sizeof(buf) - 1);
3712 if (copy_from_user(buf, user_buf, len))
3713 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003714
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003715 buf[len] = '\0';
3716 sptr = buf;
3717
3718 token = strsep(&sptr, delim);
3719 if (!token)
3720 return -EINVAL;
3721 if (!sptr)
3722 return -EINVAL;
3723 cmd = token;
3724
3725 token = strsep(&sptr, delim);
3726 if (!token)
3727 return -EINVAL;
3728 if (kstrtou64(token, 0, &val))
3729 return -EINVAL;
3730
3731 if (strcmp(cmd, "test_mode") == 0) {
3732 switch (val) {
3733 case 0:
3734 ret = icnss_test_mode_fw_test_off(priv);
3735 break;
3736 case 1:
3737 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3738 break;
3739 case 2:
3740 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3741 break;
3742 case 3:
3743 ret = icnss_trigger_recovery(&priv->pdev->dev);
3744 break;
3745 default:
3746 return -EINVAL;
3747 }
3748 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3749 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3750 } else {
3751 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003752 }
3753
3754 if (ret)
3755 return ret;
3756
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003757 return count;
3758}
3759
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003760static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003761{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003762 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003763}
3764
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003765static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003766 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003767 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003768 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003769 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003770 .owner = THIS_MODULE,
3771 .llseek = seq_lseek,
3772};
3773
3774static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3775 size_t count, loff_t *off)
3776{
3777 struct icnss_priv *priv =
3778 ((struct seq_file *)fp->private_data)->private;
3779 int ret;
3780 u32 val;
3781
3782 ret = kstrtou32_from_user(buf, count, 0, &val);
3783 if (ret)
3784 return ret;
3785
3786 if (ret == 0)
3787 memset(&priv->stats, 0, sizeof(priv->stats));
3788
3789 return count;
3790}
3791
3792static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3793{
3794 enum icnss_driver_state i;
3795 int skip = 0;
3796 unsigned long state;
3797
3798 seq_printf(s, "\nState: 0x%lx(", priv->state);
3799 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3800
3801 if (!(state & 0x1))
3802 continue;
3803
3804 if (skip++)
3805 seq_puts(s, " | ");
3806
3807 switch (i) {
3808 case ICNSS_WLFW_QMI_CONNECTED:
3809 seq_puts(s, "QMI CONN");
3810 continue;
3811 case ICNSS_POWER_ON:
3812 seq_puts(s, "POWER ON");
3813 continue;
3814 case ICNSS_FW_READY:
3815 seq_puts(s, "FW READY");
3816 continue;
3817 case ICNSS_DRIVER_PROBED:
3818 seq_puts(s, "DRIVER PROBED");
3819 continue;
3820 case ICNSS_FW_TEST_MODE:
3821 seq_puts(s, "FW TEST MODE");
3822 continue;
3823 case ICNSS_PM_SUSPEND:
3824 seq_puts(s, "PM SUSPEND");
3825 continue;
3826 case ICNSS_PM_SUSPEND_NOIRQ:
3827 seq_puts(s, "PM SUSPEND NOIRQ");
3828 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003829 case ICNSS_SSR_REGISTERED:
3830 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003831 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003832 case ICNSS_PDR_REGISTERED:
3833 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003834 continue;
3835 case ICNSS_PD_RESTART:
3836 seq_puts(s, "PD RESTART");
3837 continue;
3838 case ICNSS_MSA0_ASSIGNED:
3839 seq_puts(s, "MSA0 ASSIGNED");
3840 continue;
3841 case ICNSS_WLFW_EXISTS:
3842 seq_puts(s, "WLAN FW EXISTS");
3843 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303844 case ICNSS_SHUTDOWN_DONE:
3845 seq_puts(s, "SHUTDOWN DONE");
3846 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003847 case ICNSS_HOST_TRIGGERED_PDR:
3848 seq_puts(s, "HOST TRIGGERED PDR");
3849 continue;
Sameer Thalappil93c00732017-08-18 13:02:32 -07003850 case ICNSS_FW_DOWN:
3851 seq_puts(s, "FW DOWN");
3852 continue;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003853 }
3854
3855 seq_printf(s, "UNKNOWN-%d", i);
3856 }
3857 seq_puts(s, ")\n");
3858
3859 return 0;
3860}
3861
3862static int icnss_stats_show_capability(struct seq_file *s,
3863 struct icnss_priv *priv)
3864{
3865 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3866 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3867 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3868 seq_printf(s, "Chip family: 0x%x\n",
3869 priv->chip_info.chip_family);
3870 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3871 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3872 seq_printf(s, "Firmware Version: 0x%x\n",
3873 priv->fw_version_info.fw_version);
3874 seq_printf(s, "Firmware Build Timestamp: %s\n",
3875 priv->fw_version_info.fw_build_timestamp);
3876 seq_printf(s, "Firmware Build ID: %s\n",
3877 priv->fw_build_id);
3878 }
3879
3880 return 0;
3881}
3882
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003883static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3884 struct icnss_priv *priv)
3885{
3886 if (priv->stats.rejuvenate_ind) {
3887 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3888 seq_printf(s, "Number of Rejuvenations: %u\n",
3889 priv->stats.rejuvenate_ind);
3890 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3891 priv->cause_for_rejuvenation);
3892 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3893 priv->requesting_sub_system);
3894 seq_printf(s, "Line Number: %u\n",
3895 priv->line_number);
3896 seq_printf(s, "Function Name: %s\n",
3897 priv->function_name);
3898 }
3899
3900 return 0;
3901}
3902
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003903static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3904{
3905 int i;
3906
3907 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3908 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3909 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3910 seq_printf(s, "%24s %16u %16u\n",
3911 icnss_driver_event_to_str(i),
3912 priv->stats.events[i].posted,
3913 priv->stats.events[i].processed);
3914
3915 return 0;
3916}
3917
3918static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3919{
3920 int i;
3921
3922 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3923 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3924 "Free", "Enable", "Disable");
3925 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3926 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3927 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3928 priv->stats.ce_irqs[i].free,
3929 priv->stats.ce_irqs[i].enable,
3930 priv->stats.ce_irqs[i].disable);
3931
3932 return 0;
3933}
3934
3935static int icnss_stats_show(struct seq_file *s, void *data)
3936{
3937#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3938 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3939
3940 struct icnss_priv *priv = s->private;
3941
3942 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3943 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3944 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3945 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3946 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3947 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3948 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3949 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3950 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3951 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3952 ICNSS_STATS_DUMP(s, priv, cap_req);
3953 ICNSS_STATS_DUMP(s, priv, cap_resp);
3954 ICNSS_STATS_DUMP(s, priv, cap_err);
3955 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3956 ICNSS_STATS_DUMP(s, priv, cfg_req);
3957 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3958 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3959 ICNSS_STATS_DUMP(s, priv, mode_req);
3960 ICNSS_STATS_DUMP(s, priv, mode_resp);
3961 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3962 ICNSS_STATS_DUMP(s, priv, ini_req);
3963 ICNSS_STATS_DUMP(s, priv, ini_resp);
3964 ICNSS_STATS_DUMP(s, priv, ini_req_err);
3965 ICNSS_STATS_DUMP(s, priv, vbatt_req);
3966 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
3967 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003968 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003969 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
3970 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
3971 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003972 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
3973 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
3974 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
3975 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003976
3977 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
3978 ICNSS_STATS_DUMP(s, priv, pm_suspend);
3979 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
3980 ICNSS_STATS_DUMP(s, priv, pm_resume);
3981 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
3982 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
3983 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
3984 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
3985 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
3986 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
3987 ICNSS_STATS_DUMP(s, priv, pm_relax);
3988
3989 icnss_stats_show_irqs(s, priv);
3990
3991 icnss_stats_show_capability(s, priv);
3992
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003993 icnss_stats_show_rejuvenate_info(s, priv);
3994
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003995 icnss_stats_show_events(s, priv);
3996
3997 icnss_stats_show_state(s, priv);
3998
3999 return 0;
4000#undef ICNSS_STATS_DUMP
4001}
4002
4003static int icnss_stats_open(struct inode *inode, struct file *file)
4004{
4005 return single_open(file, icnss_stats_show, inode->i_private);
4006}
4007
4008static const struct file_operations icnss_stats_fops = {
4009 .read = seq_read,
4010 .write = icnss_stats_write,
4011 .release = single_release,
4012 .open = icnss_stats_open,
4013 .owner = THIS_MODULE,
4014 .llseek = seq_lseek,
4015};
4016
4017static int icnss_regwrite_show(struct seq_file *s, void *data)
4018{
4019 struct icnss_priv *priv = s->private;
4020
4021 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4022
4023 if (!test_bit(ICNSS_FW_READY, &priv->state))
4024 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4025
4026 return 0;
4027}
4028
4029static ssize_t icnss_regwrite_write(struct file *fp,
4030 const char __user *user_buf,
4031 size_t count, loff_t *off)
4032{
4033 struct icnss_priv *priv =
4034 ((struct seq_file *)fp->private_data)->private;
4035 char buf[64];
4036 char *sptr, *token;
4037 unsigned int len = 0;
4038 uint32_t reg_offset, mem_type, reg_val;
4039 const char *delim = " ";
4040 int ret = 0;
4041
4042 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4043 !test_bit(ICNSS_POWER_ON, &priv->state))
4044 return -EINVAL;
4045
4046 len = min(count, sizeof(buf) - 1);
4047 if (copy_from_user(buf, user_buf, len))
4048 return -EFAULT;
4049
4050 buf[len] = '\0';
4051 sptr = buf;
4052
4053 token = strsep(&sptr, delim);
4054 if (!token)
4055 return -EINVAL;
4056
4057 if (!sptr)
4058 return -EINVAL;
4059
4060 if (kstrtou32(token, 0, &mem_type))
4061 return -EINVAL;
4062
4063 token = strsep(&sptr, delim);
4064 if (!token)
4065 return -EINVAL;
4066
4067 if (!sptr)
4068 return -EINVAL;
4069
4070 if (kstrtou32(token, 0, &reg_offset))
4071 return -EINVAL;
4072
4073 token = strsep(&sptr, delim);
4074 if (!token)
4075 return -EINVAL;
4076
4077 if (kstrtou32(token, 0, &reg_val))
4078 return -EINVAL;
4079
4080 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4081 sizeof(uint32_t),
4082 (uint8_t *)&reg_val);
4083 if (ret)
4084 return ret;
4085
4086 return count;
4087}
4088
4089static int icnss_regwrite_open(struct inode *inode, struct file *file)
4090{
4091 return single_open(file, icnss_regwrite_show, inode->i_private);
4092}
4093
4094static const struct file_operations icnss_regwrite_fops = {
4095 .read = seq_read,
4096 .write = icnss_regwrite_write,
4097 .open = icnss_regwrite_open,
4098 .owner = THIS_MODULE,
4099 .llseek = seq_lseek,
4100};
4101
4102static int icnss_regread_show(struct seq_file *s, void *data)
4103{
4104 struct icnss_priv *priv = s->private;
4105
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304106 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004107 if (!priv->diag_reg_read_buf) {
4108 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4109
4110 if (!test_bit(ICNSS_FW_READY, &priv->state))
4111 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4112
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304113 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004114 return 0;
4115 }
4116
4117 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4118 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4119 priv->diag_reg_read_len);
4120
4121 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4122 priv->diag_reg_read_len, false);
4123
4124 priv->diag_reg_read_len = 0;
4125 kfree(priv->diag_reg_read_buf);
4126 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304127 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004128
4129 return 0;
4130}
4131
4132static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4133 size_t count, loff_t *off)
4134{
4135 struct icnss_priv *priv =
4136 ((struct seq_file *)fp->private_data)->private;
4137 char buf[64];
4138 char *sptr, *token;
4139 unsigned int len = 0;
4140 uint32_t reg_offset, mem_type;
4141 uint32_t data_len = 0;
4142 uint8_t *reg_buf = NULL;
4143 const char *delim = " ";
4144 int ret = 0;
4145
4146 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4147 !test_bit(ICNSS_POWER_ON, &priv->state))
4148 return -EINVAL;
4149
4150 len = min(count, sizeof(buf) - 1);
4151 if (copy_from_user(buf, user_buf, len))
4152 return -EFAULT;
4153
4154 buf[len] = '\0';
4155 sptr = buf;
4156
4157 token = strsep(&sptr, delim);
4158 if (!token)
4159 return -EINVAL;
4160
4161 if (!sptr)
4162 return -EINVAL;
4163
4164 if (kstrtou32(token, 0, &mem_type))
4165 return -EINVAL;
4166
4167 token = strsep(&sptr, delim);
4168 if (!token)
4169 return -EINVAL;
4170
4171 if (!sptr)
4172 return -EINVAL;
4173
4174 if (kstrtou32(token, 0, &reg_offset))
4175 return -EINVAL;
4176
4177 token = strsep(&sptr, delim);
4178 if (!token)
4179 return -EINVAL;
4180
4181 if (kstrtou32(token, 0, &data_len))
4182 return -EINVAL;
4183
4184 if (data_len == 0 ||
4185 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4186 return -EINVAL;
4187
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304188 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004189 kfree(priv->diag_reg_read_buf);
4190 priv->diag_reg_read_buf = NULL;
4191
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004192 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304193 if (!reg_buf) {
4194 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004195 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304196 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004197
4198 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4199 mem_type, data_len,
4200 reg_buf);
4201 if (ret) {
4202 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304203 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004204 return ret;
4205 }
4206
4207 priv->diag_reg_read_addr = reg_offset;
4208 priv->diag_reg_read_mem_type = mem_type;
4209 priv->diag_reg_read_len = data_len;
4210 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304211 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004212
4213 return count;
4214}
4215
4216static int icnss_regread_open(struct inode *inode, struct file *file)
4217{
4218 return single_open(file, icnss_regread_show, inode->i_private);
4219}
4220
4221static const struct file_operations icnss_regread_fops = {
4222 .read = seq_read,
4223 .write = icnss_regread_write,
4224 .open = icnss_regread_open,
4225 .owner = THIS_MODULE,
4226 .llseek = seq_lseek,
4227};
4228
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004229#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004230static int icnss_debugfs_create(struct icnss_priv *priv)
4231{
4232 int ret = 0;
4233 struct dentry *root_dentry;
4234
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004235 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004236
4237 if (IS_ERR(root_dentry)) {
4238 ret = PTR_ERR(root_dentry);
4239 icnss_pr_err("Unable to create debugfs %d\n", ret);
4240 goto out;
4241 }
4242
4243 priv->root_dentry = root_dentry;
4244
Yuanyuan Liu84132752017-05-24 12:07:12 -07004245 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004246 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004247
Yuanyuan Liu84132752017-05-24 12:07:12 -07004248 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004249 &icnss_stats_fops);
4250 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4251 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004252 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004253 &icnss_regwrite_fops);
4254
4255out:
4256 return ret;
4257}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004258#else
4259static int icnss_debugfs_create(struct icnss_priv *priv)
4260{
4261 int ret = 0;
4262 struct dentry *root_dentry;
4263
4264 root_dentry = debugfs_create_dir("icnss", NULL);
4265
4266 if (IS_ERR(root_dentry)) {
4267 ret = PTR_ERR(root_dentry);
4268 icnss_pr_err("Unable to create debugfs %d\n", ret);
4269 return ret;
4270 }
4271
4272 priv->root_dentry = root_dentry;
4273
4274 debugfs_create_file("stats", 0600, root_dentry, priv,
4275 &icnss_stats_fops);
4276 return 0;
4277}
4278#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004279
4280static void icnss_debugfs_destroy(struct icnss_priv *priv)
4281{
4282 debugfs_remove_recursive(priv->root_dentry);
4283}
4284
4285static int icnss_get_vbatt_info(struct icnss_priv *priv)
4286{
4287 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4288 struct qpnp_vadc_chip *vadc_dev = NULL;
4289 int ret = 0;
4290
4291 if (test_bit(VBATT_DISABLE, &quirks)) {
4292 icnss_pr_dbg("VBATT feature is disabled\n");
4293 return ret;
4294 }
4295
4296 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4297 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4298 icnss_pr_err("adc_tm_dev probe defer\n");
4299 return -EPROBE_DEFER;
4300 }
4301
4302 if (IS_ERR(adc_tm_dev)) {
4303 ret = PTR_ERR(adc_tm_dev);
4304 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4305 ret);
4306 return ret;
4307 }
4308
4309 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4310 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4311 icnss_pr_err("vadc_dev probe defer\n");
4312 return -EPROBE_DEFER;
4313 }
4314
4315 if (IS_ERR(vadc_dev)) {
4316 ret = PTR_ERR(vadc_dev);
4317 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4318 ret);
4319 return ret;
4320 }
4321
4322 priv->adc_tm_dev = adc_tm_dev;
4323 priv->vadc_dev = vadc_dev;
4324
4325 return 0;
4326}
4327
4328static int icnss_probe(struct platform_device *pdev)
4329{
4330 int ret = 0;
4331 struct resource *res;
4332 int i;
4333 struct device *dev = &pdev->dev;
4334 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304335 const __be32 *addrp;
4336 u64 prop_size = 0;
4337 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004338
4339 if (penv) {
4340 icnss_pr_err("Driver is already initialized\n");
4341 return -EEXIST;
4342 }
4343
4344 icnss_pr_dbg("Platform driver probe\n");
4345
4346 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4347 if (!priv)
4348 return -ENOMEM;
4349
4350 priv->magic = ICNSS_MAGIC;
4351 dev_set_drvdata(dev, priv);
4352
4353 priv->pdev = pdev;
4354
4355 ret = icnss_get_vbatt_info(priv);
4356 if (ret == -EPROBE_DEFER)
4357 goto out;
4358
Yuanyuan Liu68939762017-04-04 16:43:03 -07004359 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4360 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4361 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4362
4363 if (ret)
4364 goto out;
4365 }
4366
4367 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4368 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4369 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4370 if (ret)
4371 goto out;
4372 }
4373
4374 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4375 priv->bypass_s1_smmu = true;
4376
4377 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4378
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004379 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4380 if (!res) {
4381 icnss_pr_err("Memory base not found in DT\n");
4382 ret = -EINVAL;
4383 goto out;
4384 }
4385
4386 priv->mem_base_pa = res->start;
4387 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4388 resource_size(res));
4389 if (!priv->mem_base_va) {
4390 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4391 &priv->mem_base_pa);
4392 ret = -EINVAL;
4393 goto out;
4394 }
4395 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4396 priv->mem_base_va);
4397
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004398 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4399 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4400 if (!res) {
4401 icnss_pr_err("Fail to get IRQ-%d\n", i);
4402 ret = -ENODEV;
4403 goto out;
4404 } else {
4405 priv->ce_irqs[i] = res->start;
4406 }
4407 }
4408
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304409 np = of_parse_phandle(dev->of_node,
4410 "qcom,wlan-msa-fixed-region", 0);
4411 if (np) {
4412 addrp = of_get_address(np, 0, &prop_size, NULL);
4413 if (!addrp) {
4414 icnss_pr_err("Failed to get assigned-addresses or property\n");
4415 ret = -EINVAL;
4416 goto out;
4417 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004418
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304419 priv->msa_pa = of_translate_address(np, addrp);
4420 if (priv->msa_pa == OF_BAD_ADDR) {
4421 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4422 ret = -EINVAL;
4423 goto out;
4424 }
4425
4426 priv->msa_va = memremap(priv->msa_pa,
4427 (unsigned long)prop_size, MEMREMAP_WT);
4428 if (!priv->msa_va) {
4429 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4430 &priv->msa_pa);
4431 ret = -EINVAL;
4432 goto out;
4433 }
4434 priv->msa_mem_size = prop_size;
4435 } else {
4436 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4437 &priv->msa_mem_size);
4438 if (ret || priv->msa_mem_size == 0) {
4439 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4440 priv->msa_mem_size, ret);
4441 goto out;
4442 }
4443
4444 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4445 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4446
4447 if (!priv->msa_va) {
4448 icnss_pr_err("DMA alloc failed for MSA\n");
4449 ret = -ENOMEM;
4450 goto out;
4451 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004452 }
4453
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304454 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4455 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004456
4457 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4458 "smmu_iova_base");
4459 if (!res) {
4460 icnss_pr_err("SMMU IOVA base not found\n");
4461 } else {
4462 priv->smmu_iova_start = res->start;
4463 priv->smmu_iova_len = resource_size(res);
4464 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4465 &priv->smmu_iova_start, priv->smmu_iova_len);
4466
4467 res = platform_get_resource_byname(pdev,
4468 IORESOURCE_MEM,
4469 "smmu_iova_ipa");
4470 if (!res) {
4471 icnss_pr_err("SMMU IOVA IPA not found\n");
4472 } else {
4473 priv->smmu_iova_ipa_start = res->start;
4474 priv->smmu_iova_ipa_len = resource_size(res);
4475 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4476 &priv->smmu_iova_ipa_start,
4477 priv->smmu_iova_ipa_len);
4478 }
4479
4480 ret = icnss_smmu_init(priv);
4481 if (ret < 0) {
4482 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4483 ret, &priv->smmu_iova_start,
4484 priv->smmu_iova_len);
4485 goto out;
4486 }
4487 }
4488
4489 spin_lock_init(&priv->event_lock);
4490 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304491 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004492
4493 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4494 if (!priv->event_wq) {
4495 icnss_pr_err("Workqueue creation failed\n");
4496 ret = -EFAULT;
4497 goto out_smmu_deinit;
4498 }
4499
4500 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4501 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4502 INIT_LIST_HEAD(&priv->event_list);
4503
4504 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4505 WLFW_SERVICE_VERS_V01,
4506 WLFW_SERVICE_INS_ID_V01,
4507 &wlfw_clnt_nb);
4508 if (ret < 0) {
4509 icnss_pr_err("Notifier register failed: %d\n", ret);
4510 goto out_destroy_wq;
4511 }
4512
4513 icnss_enable_recovery(priv);
4514
4515 icnss_debugfs_create(priv);
4516
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004517 ret = device_init_wakeup(&priv->pdev->dev, true);
4518 if (ret)
4519 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4520 ret);
4521
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004522 penv = priv;
4523
4524 icnss_pr_info("Platform driver probed successfully\n");
4525
4526 return 0;
4527
4528out_destroy_wq:
4529 destroy_workqueue(priv->event_wq);
4530out_smmu_deinit:
4531 icnss_smmu_deinit(priv);
4532out:
4533 dev_set_drvdata(dev, NULL);
4534
4535 return ret;
4536}
4537
4538static int icnss_remove(struct platform_device *pdev)
4539{
4540 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4541
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004542 device_init_wakeup(&penv->pdev->dev, false);
4543
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004544 icnss_debugfs_destroy(penv);
4545
4546 icnss_modem_ssr_unregister_notifier(penv);
4547
4548 destroy_ramdump_device(penv->msa0_dump_dev);
4549
4550 icnss_pdr_unregister_notifier(penv);
4551
4552 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4553 WLFW_SERVICE_VERS_V01,
4554 WLFW_SERVICE_INS_ID_V01,
4555 &wlfw_clnt_nb);
4556 if (penv->event_wq)
4557 destroy_workqueue(penv->event_wq);
4558
4559 icnss_hw_power_off(penv);
4560
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304561 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4562 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004563
4564 dev_set_drvdata(&pdev->dev, NULL);
4565
4566 return 0;
4567}
4568
4569#ifdef CONFIG_PM_SLEEP
4570static int icnss_pm_suspend(struct device *dev)
4571{
4572 struct icnss_priv *priv = dev_get_drvdata(dev);
4573 int ret = 0;
4574
4575 if (priv->magic != ICNSS_MAGIC) {
4576 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4577 dev, priv, priv->magic);
4578 return -EINVAL;
4579 }
4580
Yuanyuan Liu68939762017-04-04 16:43:03 -07004581 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004582
4583 if (!priv->ops || !priv->ops->pm_suspend ||
4584 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4585 goto out;
4586
4587 ret = priv->ops->pm_suspend(dev);
4588
4589out:
4590 if (ret == 0) {
4591 priv->stats.pm_suspend++;
4592 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4593 } else {
4594 priv->stats.pm_suspend_err++;
4595 }
4596 return ret;
4597}
4598
4599static int icnss_pm_resume(struct device *dev)
4600{
4601 struct icnss_priv *priv = dev_get_drvdata(dev);
4602 int ret = 0;
4603
4604 if (priv->magic != ICNSS_MAGIC) {
4605 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4606 dev, priv, priv->magic);
4607 return -EINVAL;
4608 }
4609
Yuanyuan Liu68939762017-04-04 16:43:03 -07004610 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004611
4612 if (!priv->ops || !priv->ops->pm_resume ||
4613 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4614 goto out;
4615
4616 ret = priv->ops->pm_resume(dev);
4617
4618out:
4619 if (ret == 0) {
4620 priv->stats.pm_resume++;
4621 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4622 } else {
4623 priv->stats.pm_resume_err++;
4624 }
4625 return ret;
4626}
4627
4628static int icnss_pm_suspend_noirq(struct device *dev)
4629{
4630 struct icnss_priv *priv = dev_get_drvdata(dev);
4631 int ret = 0;
4632
4633 if (priv->magic != ICNSS_MAGIC) {
4634 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4635 dev, priv, priv->magic);
4636 return -EINVAL;
4637 }
4638
Yuanyuan Liu68939762017-04-04 16:43:03 -07004639 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004640
4641 if (!priv->ops || !priv->ops->suspend_noirq ||
4642 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4643 goto out;
4644
4645 ret = priv->ops->suspend_noirq(dev);
4646
4647out:
4648 if (ret == 0) {
4649 priv->stats.pm_suspend_noirq++;
4650 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4651 } else {
4652 priv->stats.pm_suspend_noirq_err++;
4653 }
4654 return ret;
4655}
4656
4657static int icnss_pm_resume_noirq(struct device *dev)
4658{
4659 struct icnss_priv *priv = dev_get_drvdata(dev);
4660 int ret = 0;
4661
4662 if (priv->magic != ICNSS_MAGIC) {
4663 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4664 dev, priv, priv->magic);
4665 return -EINVAL;
4666 }
4667
Yuanyuan Liu68939762017-04-04 16:43:03 -07004668 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004669
4670 if (!priv->ops || !priv->ops->resume_noirq ||
4671 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4672 goto out;
4673
4674 ret = priv->ops->resume_noirq(dev);
4675
4676out:
4677 if (ret == 0) {
4678 priv->stats.pm_resume_noirq++;
4679 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4680 } else {
4681 priv->stats.pm_resume_noirq_err++;
4682 }
4683 return ret;
4684}
4685#endif
4686
4687static const struct dev_pm_ops icnss_pm_ops = {
4688 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4689 icnss_pm_resume)
4690 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4691 icnss_pm_resume_noirq)
4692};
4693
4694static const struct of_device_id icnss_dt_match[] = {
4695 {.compatible = "qcom,icnss"},
4696 {}
4697};
4698
4699MODULE_DEVICE_TABLE(of, icnss_dt_match);
4700
4701static struct platform_driver icnss_driver = {
4702 .probe = icnss_probe,
4703 .remove = icnss_remove,
4704 .driver = {
4705 .name = "icnss",
4706 .pm = &icnss_pm_ops,
4707 .owner = THIS_MODULE,
4708 .of_match_table = icnss_dt_match,
4709 },
4710};
4711
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004712static int __init icnss_initialize(void)
4713{
4714 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4715 "icnss", 0);
4716 if (!icnss_ipc_log_context)
4717 icnss_pr_err("Unable to create log context\n");
4718
Yuanyuan Liu68939762017-04-04 16:43:03 -07004719 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4720 "icnss_long", 0);
4721 if (!icnss_ipc_log_long_context)
4722 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004723
4724 return platform_driver_register(&icnss_driver);
4725}
4726
4727static void __exit icnss_exit(void)
4728{
4729 platform_driver_unregister(&icnss_driver);
4730 ipc_log_context_destroy(icnss_ipc_log_context);
4731 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004732 ipc_log_context_destroy(icnss_ipc_log_long_context);
4733 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004734}
4735
4736
4737module_init(icnss_initialize);
4738module_exit(icnss_exit);
4739
4740MODULE_LICENSE("GPL v2");
4741MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");