blob: a2ab2669b812460aebba9075d7dc335e47d1e71e [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,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800295};
296
297struct ce_irq_list {
298 int irq;
299 irqreturn_t (*handler)(int, void *);
300};
301
Yuanyuan Liu68939762017-04-04 16:43:03 -0700302struct icnss_vreg_info {
303 struct regulator *reg;
304 const char *name;
305 u32 min_v;
306 u32 max_v;
307 u32 load_ua;
308 unsigned long settle_delay;
309 bool required;
310};
311
312struct icnss_clk_info {
313 struct clk *handle;
314 const char *name;
315 u32 freq;
316 bool required;
317};
318
319static struct icnss_vreg_info icnss_vreg_info[] = {
320 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
321 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
322 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
323 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
324};
325
326#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
327
328static struct icnss_clk_info icnss_clk_info[] = {
329 {NULL, "cxo_ref_clk_pin", 0, false},
330};
331
332#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
333
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800334struct icnss_stats {
335 struct {
336 uint32_t posted;
337 uint32_t processed;
338 } events[ICNSS_DRIVER_EVENT_MAX];
339
340 struct {
341 uint32_t request;
342 uint32_t free;
343 uint32_t enable;
344 uint32_t disable;
345 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
346
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700347 struct {
348 uint32_t pdr_fw_crash;
349 uint32_t pdr_host_error;
350 uint32_t root_pd_crash;
351 uint32_t root_pd_shutdown;
352 } recovery;
353
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800354 uint32_t pm_suspend;
355 uint32_t pm_suspend_err;
356 uint32_t pm_resume;
357 uint32_t pm_resume_err;
358 uint32_t pm_suspend_noirq;
359 uint32_t pm_suspend_noirq_err;
360 uint32_t pm_resume_noirq;
361 uint32_t pm_resume_noirq_err;
362 uint32_t pm_stay_awake;
363 uint32_t pm_relax;
364
365 uint32_t ind_register_req;
366 uint32_t ind_register_resp;
367 uint32_t ind_register_err;
368 uint32_t msa_info_req;
369 uint32_t msa_info_resp;
370 uint32_t msa_info_err;
371 uint32_t msa_ready_req;
372 uint32_t msa_ready_resp;
373 uint32_t msa_ready_err;
374 uint32_t msa_ready_ind;
375 uint32_t cap_req;
376 uint32_t cap_resp;
377 uint32_t cap_err;
378 uint32_t pin_connect_result;
379 uint32_t cfg_req;
380 uint32_t cfg_resp;
381 uint32_t cfg_req_err;
382 uint32_t mode_req;
383 uint32_t mode_resp;
384 uint32_t mode_req_err;
385 uint32_t ini_req;
386 uint32_t ini_resp;
387 uint32_t ini_req_err;
388 uint32_t vbatt_req;
389 uint32_t vbatt_resp;
390 uint32_t vbatt_req_err;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800391 u32 rejuvenate_ind;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800392 uint32_t rejuvenate_ack_req;
393 uint32_t rejuvenate_ack_resp;
394 uint32_t rejuvenate_ack_err;
395};
396
397#define MAX_NO_OF_MAC_ADDR 4
398struct icnss_wlan_mac_addr {
399 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
400 uint32_t no_of_mac_addr_set;
401};
402
Sameer Thalappil0aaa7582017-06-23 15:43:43 -0700403enum icnss_pdr_cause_index {
404 ICNSS_FW_CRASH,
405 ICNSS_ROOT_PD_CRASH,
406 ICNSS_ROOT_PD_SHUTDOWN,
407 ICNSS_HOST_ERROR,
408};
409
410static const char * const icnss_pdr_cause[] = {
411 [ICNSS_FW_CRASH] = "FW crash",
412 [ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
413 [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
414 [ICNSS_HOST_ERROR] = "Host error",
415};
416
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800417struct service_notifier_context {
418 void *handle;
419 uint32_t instance_id;
420 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800421};
422
423static struct icnss_priv {
424 uint32_t magic;
425 struct platform_device *pdev;
426 struct icnss_driver_ops *ops;
427 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700428 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
429 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800430 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
431 phys_addr_t mem_base_pa;
432 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800433 struct dma_iommu_mapping *smmu_mapping;
434 dma_addr_t smmu_iova_start;
435 size_t smmu_iova_len;
436 dma_addr_t smmu_iova_ipa_start;
437 size_t smmu_iova_ipa_len;
438 struct qmi_handle *wlfw_clnt;
439 struct list_head event_list;
440 spinlock_t event_lock;
441 struct work_struct event_work;
442 struct work_struct qmi_recv_msg_work;
443 struct workqueue_struct *event_wq;
444 phys_addr_t msa_pa;
445 uint32_t msa_mem_size;
446 void *msa_va;
447 unsigned long state;
448 struct wlfw_rf_chip_info_s_v01 chip_info;
449 struct wlfw_rf_board_info_s_v01 board_info;
450 struct wlfw_soc_info_s_v01 soc_info;
451 struct wlfw_fw_version_info_s_v01 fw_version_info;
452 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
453 u32 pwr_pin_result;
454 u32 phy_io_pin_result;
455 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700456 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800457 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700458 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800459 struct dentry *root_dentry;
460 spinlock_t on_off_lock;
461 struct icnss_stats stats;
462 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800463 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800464 struct notifier_block service_notifier_nb;
465 int total_domains;
466 struct notifier_block get_service_nb;
467 void *modem_notify_handler;
468 struct notifier_block modem_ssr_nb;
469 uint32_t diag_reg_read_addr;
470 uint32_t diag_reg_read_mem_type;
471 uint32_t diag_reg_read_len;
472 uint8_t *diag_reg_read_buf;
473 struct qpnp_adc_tm_btm_param vph_monitor_params;
474 struct qpnp_adc_tm_chip *adc_tm_dev;
475 struct qpnp_vadc_chip *vadc_dev;
476 uint64_t vph_pwr;
477 atomic_t pm_count;
478 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800479 bool is_wlan_mac_set;
480 struct icnss_wlan_mac_addr wlan_mac_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700481 bool bypass_s1_smmu;
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -0800482 u8 cause_for_rejuvenation;
483 u8 requesting_sub_system;
484 u16 line_number;
485 char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +0530486 struct mutex dev_lock;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800487} *penv;
488
Yuanyuan Liu68939762017-04-04 16:43:03 -0700489#ifdef CONFIG_ICNSS_DEBUG
490static void icnss_ignore_qmi_timeout(bool ignore)
491{
492 ignore_qmi_timeout = ignore;
493}
494#else
495static void icnss_ignore_qmi_timeout(bool ignore) { }
496#endif
497
Anurag Chouhanfacf3922017-06-08 18:26:56 +0530498static int icnss_assign_msa_perm(struct icnss_mem_region_info
499 *mem_region, enum icnss_msa_perm new_perm)
500{
501 int ret = 0;
502 phys_addr_t addr;
503 u32 size;
504 u32 i = 0;
505 u32 source_vmids[ICNSS_MAX_VMIDS];
506 u32 source_nelems;
507 u32 dest_vmids[ICNSS_MAX_VMIDS];
508 u32 dest_perms[ICNSS_MAX_VMIDS];
509 u32 dest_nelems;
510 enum icnss_msa_perm cur_perm = mem_region->perm;
511 struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
512
513 addr = mem_region->reg_addr;
514 size = mem_region->size;
515
516 if (mem_region->secure_flag) {
517 new_perm_list = &msa_perm_secure_list[new_perm];
518 old_perm_list = &msa_perm_secure_list[cur_perm];
519 } else {
520 new_perm_list = &msa_perm_list[new_perm];
521 old_perm_list = &msa_perm_list[cur_perm];
522 }
523
524 source_nelems = old_perm_list->nelems;
525 dest_nelems = new_perm_list->nelems;
526
527 for (i = 0; i < source_nelems; ++i)
528 source_vmids[i] = old_perm_list->vmids[i];
529
530 for (i = 0; i < dest_nelems; ++i) {
531 dest_vmids[i] = new_perm_list->vmids[i];
532 dest_perms[i] = new_perm_list->perms[i];
533 }
534
535 ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
536 dest_vmids, dest_perms, dest_nelems);
537 if (ret) {
538 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
539 &addr, size, ret);
540 goto out;
541 }
542
543 icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
544 "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
545 source_nelems, source_vmids[0], source_vmids[1],
546 source_vmids[2], source_vmids[3], dest_nelems,
547 dest_vmids[0], dest_vmids[1], dest_vmids[2],
548 dest_vmids[3]);
549out:
550 return ret;
551}
552
553static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
554 enum icnss_msa_perm new_perm)
555{
556 int ret;
557 int i;
558 enum icnss_msa_perm old_perm;
559
560 for (i = 0; i < priv->nr_mem_region; i++) {
561 old_perm = priv->mem_region[i].perm;
562 ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
563 if (ret)
564 goto err_unmap;
565 priv->mem_region[i].perm = new_perm;
566 }
567 return 0;
568
569err_unmap:
570 for (i--; i >= 0; i--) {
571 icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
572 }
573 return ret;
574}
575
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800576static void icnss_pm_stay_awake(struct icnss_priv *priv)
577{
578 if (atomic_inc_return(&priv->pm_count) != 1)
579 return;
580
Yuanyuan Liu68939762017-04-04 16:43:03 -0700581 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800582 atomic_read(&priv->pm_count));
583
584 pm_stay_awake(&priv->pdev->dev);
585
586 priv->stats.pm_stay_awake++;
587}
588
589static void icnss_pm_relax(struct icnss_priv *priv)
590{
591 int r = atomic_dec_return(&priv->pm_count);
592
593 WARN_ON(r < 0);
594
595 if (r != 0)
596 return;
597
Yuanyuan Liu68939762017-04-04 16:43:03 -0700598 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800599 atomic_read(&priv->pm_count));
600
601 pm_relax(&priv->pdev->dev);
602 priv->stats.pm_relax++;
603}
604
605static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
606{
607 switch (type) {
608 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
609 return "SERVER_ARRIVE";
610 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
611 return "SERVER_EXIT";
612 case ICNSS_DRIVER_EVENT_FW_READY_IND:
613 return "FW_READY";
614 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
615 return "REGISTER_DRIVER";
616 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
617 return "UNREGISTER_DRIVER";
618 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
619 return "PD_SERVICE_DOWN";
620 case ICNSS_DRIVER_EVENT_MAX:
621 return "EVENT_MAX";
622 }
623
624 return "UNKNOWN";
625};
626
627static int icnss_driver_event_post(enum icnss_driver_event_type type,
628 u32 flags, void *data)
629{
630 struct icnss_driver_event *event;
631 unsigned long irq_flags;
632 int gfp = GFP_KERNEL;
633 int ret = 0;
634
635 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
636 icnss_driver_event_to_str(type), type, current->comm,
637 flags, penv->state);
638
639 if (type >= ICNSS_DRIVER_EVENT_MAX) {
640 icnss_pr_err("Invalid Event type: %d, can't post", type);
641 return -EINVAL;
642 }
643
644 if (in_interrupt() || irqs_disabled())
645 gfp = GFP_ATOMIC;
646
647 event = kzalloc(sizeof(*event), gfp);
648 if (event == NULL)
649 return -ENOMEM;
650
651 icnss_pm_stay_awake(penv);
652
653 event->type = type;
654 event->data = data;
655 init_completion(&event->complete);
656 event->ret = ICNSS_EVENT_PENDING;
657 event->sync = !!(flags & ICNSS_EVENT_SYNC);
658
659 spin_lock_irqsave(&penv->event_lock, irq_flags);
660 list_add_tail(&event->list, &penv->event_list);
661 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
662
663 penv->stats.events[type].posted++;
664 queue_work(penv->event_wq, &penv->event_work);
665
666 if (!(flags & ICNSS_EVENT_SYNC))
667 goto out;
668
669 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
670 wait_for_completion(&event->complete);
671 else
672 ret = wait_for_completion_interruptible(&event->complete);
673
674 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
675 icnss_driver_event_to_str(type), type, penv->state, ret,
676 event->ret);
677
678 spin_lock_irqsave(&penv->event_lock, irq_flags);
679 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
680 event->sync = false;
681 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
682 ret = -EINTR;
683 goto out;
684 }
685 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
686
687 ret = event->ret;
688 kfree(event);
689
690out:
691 icnss_pm_relax(penv);
692 return ret;
693}
694
695static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
696 uint64_t voltage_uv)
697{
698 int ret;
699 struct wlfw_vbatt_req_msg_v01 req;
700 struct wlfw_vbatt_resp_msg_v01 resp;
701 struct msg_desc req_desc, resp_desc;
702
703 if (!priv->wlfw_clnt) {
704 ret = -ENODEV;
705 goto out;
706 }
707
708 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
709 penv->state);
710
711 memset(&req, 0, sizeof(req));
712 memset(&resp, 0, sizeof(resp));
713
714 req.voltage_uv = voltage_uv;
715
716 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
717 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
718 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
719
720 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
721 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
722 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
723
724 priv->stats.vbatt_req++;
725
726 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
727 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
728 if (ret < 0) {
729 icnss_pr_err("Send vbatt req failed %d\n", ret);
730 goto out;
731 }
732
733 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
734 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
735 resp.resp.result, resp.resp.error);
736 ret = resp.resp.result;
737 goto out;
738 }
739 priv->stats.vbatt_resp++;
740
741out:
742 priv->stats.vbatt_req_err++;
743 return ret;
744}
745
746static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
747{
748 int ret = 0;
749 struct qpnp_vadc_result adc_result;
750
751 if (!priv->vadc_dev) {
752 icnss_pr_err("VADC dev doesn't exists\n");
753 ret = -EINVAL;
754 goto out;
755 }
756
757 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
758 if (ret) {
759 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
760 VADC_VPH_PWR, ret);
761 goto out;
762 }
763
764 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
765 adc_result.physical, adc_result.measurement);
766
767 *result_uv = adc_result.physical;
768out:
769 return ret;
770}
771
772static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
773{
774 struct icnss_priv *priv = ctx;
775 uint64_t vph_pwr = 0;
776 uint64_t vph_pwr_prev;
777 int ret = 0;
778 bool update = true;
779
780 if (!priv) {
781 icnss_pr_err("Priv pointer is NULL\n");
782 return;
783 }
784
785 vph_pwr_prev = priv->vph_pwr;
786
787 ret = icnss_get_phone_power(priv, &vph_pwr);
788 if (ret)
789 return;
790
791 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
792 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
793 update = false;
794 priv->vph_monitor_params.state_request =
795 ADC_TM_HIGH_THR_ENABLE;
796 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
797 ICNSS_THRESHOLD_GUARD;
798 priv->vph_monitor_params.low_thr = 0;
799 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
800 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
801 update = false;
802 priv->vph_monitor_params.state_request =
803 ADC_TM_LOW_THR_ENABLE;
804 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
805 ICNSS_THRESHOLD_GUARD;
806 priv->vph_monitor_params.high_thr = 0;
807 } else {
808 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
809 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
810 update = false;
811 priv->vph_monitor_params.state_request =
812 ADC_TM_HIGH_LOW_THR_ENABLE;
813 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
814 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
815 }
816
817 priv->vph_pwr = vph_pwr;
818
819 if (update)
820 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
821
822 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
823 priv->vph_monitor_params.low_thr,
824 priv->vph_monitor_params.high_thr);
825 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
826 &priv->vph_monitor_params);
827 if (ret)
828 icnss_pr_err("TM channel setup failed %d\n", ret);
829}
830
831static int icnss_setup_vph_monitor(struct icnss_priv *priv)
832{
833 int ret = 0;
834
835 if (!priv->adc_tm_dev) {
836 icnss_pr_err("ADC TM handler is NULL\n");
837 ret = -EINVAL;
838 goto out;
839 }
840
841 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
842 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
843 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
844 priv->vph_monitor_params.channel = VADC_VPH_PWR;
845 priv->vph_monitor_params.btm_ctx = priv;
846 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
847 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
848 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
849 priv->vph_monitor_params.low_thr,
850 priv->vph_monitor_params.high_thr);
851
852 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
853 &priv->vph_monitor_params);
854 if (ret)
855 icnss_pr_err("TM channel setup failed %d\n", ret);
856out:
857 return ret;
858}
859
860static int icnss_init_vph_monitor(struct icnss_priv *priv)
861{
862 int ret = 0;
863
864 if (test_bit(VBATT_DISABLE, &quirks))
865 goto out;
866
867 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
868 if (ret)
869 goto out;
870
871 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
872
873 ret = icnss_setup_vph_monitor(priv);
874 if (ret)
875 goto out;
876out:
877 return ret;
878}
879
880
881static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
882{
883 struct msg_desc ind_desc;
884 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
885 int ret = 0;
886
887 if (!penv || !penv->wlfw_clnt) {
888 ret = -ENODEV;
889 goto out;
890 }
891
Hardik Kantilal Patel27501b02017-05-03 14:01:16 +0530892 memset(&ind_msg, 0, sizeof(ind_msg));
893
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800894 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
895 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
896 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
897
898 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
899 if (ret < 0) {
900 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
901 ret, msg_len);
902 goto out;
903 }
904
905 /* store pin result locally */
906 if (ind_msg.pwr_pin_result_valid)
907 penv->pwr_pin_result = ind_msg.pwr_pin_result;
908 if (ind_msg.phy_io_pin_result_valid)
909 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
910 if (ind_msg.rf_pin_result_valid)
911 penv->rf_pin_result = ind_msg.rf_pin_result;
912
913 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
914 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
915 ind_msg.rf_pin_result);
916
917 penv->stats.pin_connect_result++;
918out:
919 return ret;
920}
921
Yuanyuan Liu68939762017-04-04 16:43:03 -0700922static int icnss_vreg_on(struct icnss_priv *priv)
923{
924 int ret = 0;
925 struct icnss_vreg_info *vreg_info;
926 int i;
927
928 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
929 vreg_info = &priv->vreg_info[i];
930
931 if (!vreg_info->reg)
932 continue;
933
934 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
935
936 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
937 vreg_info->max_v);
938 if (ret) {
939 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
940 vreg_info->name, vreg_info->min_v,
941 vreg_info->max_v, ret);
942 break;
943 }
944
945 if (vreg_info->load_ua) {
946 ret = regulator_set_load(vreg_info->reg,
947 vreg_info->load_ua);
948 if (ret < 0) {
949 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
950 vreg_info->name,
951 vreg_info->load_ua, ret);
952 break;
953 }
954 }
955
956 ret = regulator_enable(vreg_info->reg);
957 if (ret) {
958 icnss_pr_err("Regulator %s, can't enable: %d\n",
959 vreg_info->name, ret);
960 break;
961 }
962
963 if (vreg_info->settle_delay)
964 udelay(vreg_info->settle_delay);
965 }
966
967 if (!ret)
968 return 0;
969
970 for (; i >= 0; i--) {
971 vreg_info = &priv->vreg_info[i];
972
973 if (!vreg_info->reg)
974 continue;
975
976 regulator_disable(vreg_info->reg);
977 regulator_set_load(vreg_info->reg, 0);
978 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
979 }
980
981 return ret;
982}
983
984static int icnss_vreg_off(struct icnss_priv *priv)
985{
986 int ret = 0;
987 struct icnss_vreg_info *vreg_info;
988 int i;
989
990 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
991 vreg_info = &priv->vreg_info[i];
992
993 if (!vreg_info->reg)
994 continue;
995
996 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
997
998 ret = regulator_disable(vreg_info->reg);
999 if (ret)
1000 icnss_pr_err("Regulator %s, can't disable: %d\n",
1001 vreg_info->name, ret);
1002
1003 ret = regulator_set_load(vreg_info->reg, 0);
1004 if (ret < 0)
1005 icnss_pr_err("Regulator %s, can't set load: %d\n",
1006 vreg_info->name, ret);
1007
1008 ret = regulator_set_voltage(vreg_info->reg, 0,
1009 vreg_info->max_v);
1010 if (ret)
1011 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
1012 vreg_info->name, ret);
1013 }
1014
1015 return ret;
1016}
1017
1018static int icnss_clk_init(struct icnss_priv *priv)
1019{
1020 struct icnss_clk_info *clk_info;
1021 int i;
1022 int ret = 0;
1023
1024 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1025 clk_info = &priv->clk_info[i];
1026
1027 if (!clk_info->handle)
1028 continue;
1029
1030 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
1031
1032 if (clk_info->freq) {
1033 ret = clk_set_rate(clk_info->handle, clk_info->freq);
1034
1035 if (ret) {
1036 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
1037 clk_info->name, clk_info->freq,
1038 ret);
1039 break;
1040 }
1041 }
1042
1043 ret = clk_prepare_enable(clk_info->handle);
1044 if (ret) {
1045 icnss_pr_err("Clock %s, can't enable: %d\n",
1046 clk_info->name, ret);
1047 break;
1048 }
1049 }
1050
1051 if (ret == 0)
1052 return 0;
1053
1054 for (; i >= 0; i--) {
1055 clk_info = &priv->clk_info[i];
1056
1057 if (!clk_info->handle)
1058 continue;
1059
1060 clk_disable_unprepare(clk_info->handle);
1061 }
1062
1063 return ret;
1064}
1065
1066static int icnss_clk_deinit(struct icnss_priv *priv)
1067{
1068 struct icnss_clk_info *clk_info;
1069 int i;
1070
1071 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
1072 clk_info = &priv->clk_info[i];
1073
1074 if (!clk_info->handle)
1075 continue;
1076
1077 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
1078
1079 clk_disable_unprepare(clk_info->handle);
1080 }
1081
1082 return 0;
1083}
1084
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001085static int icnss_hw_power_on(struct icnss_priv *priv)
1086{
1087 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001088
1089 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
1090
Yuanyuan Liu68939762017-04-04 16:43:03 -07001091 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001092 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001093 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001094 return ret;
1095 }
1096 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001097 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001098
Yuanyuan Liu68939762017-04-04 16:43:03 -07001099 ret = icnss_vreg_on(priv);
1100 if (ret)
1101 goto out;
1102
1103 ret = icnss_clk_init(priv);
1104 if (ret)
1105 goto vreg_off;
1106
1107 return ret;
1108
1109vreg_off:
1110 icnss_vreg_off(priv);
1111out:
1112 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001113 return ret;
1114}
1115
1116static int icnss_hw_power_off(struct icnss_priv *priv)
1117{
1118 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001119
1120 if (test_bit(HW_ALWAYS_ON, &quirks))
1121 return 0;
1122
1123 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
1124
Yuanyuan Liu68939762017-04-04 16:43:03 -07001125 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001126 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001127 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001128 return ret;
1129 }
1130 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001131 spin_unlock(&priv->on_off_lock);
1132
1133 icnss_clk_deinit(priv);
1134
1135 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001136
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001137 return ret;
1138}
1139
1140int icnss_power_on(struct device *dev)
1141{
1142 struct icnss_priv *priv = dev_get_drvdata(dev);
1143
1144 if (!priv) {
1145 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1146 dev, priv);
1147 return -EINVAL;
1148 }
1149
1150 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
1151
1152 return icnss_hw_power_on(priv);
1153}
1154EXPORT_SYMBOL(icnss_power_on);
1155
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001156bool icnss_is_fw_ready(void)
1157{
1158 if (!penv)
1159 return false;
1160 else
1161 return test_bit(ICNSS_FW_READY, &penv->state);
1162}
1163EXPORT_SYMBOL(icnss_is_fw_ready);
1164
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001165int icnss_power_off(struct device *dev)
1166{
1167 struct icnss_priv *priv = dev_get_drvdata(dev);
1168
1169 if (!priv) {
1170 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
1171 dev, priv);
1172 return -EINVAL;
1173 }
1174
1175 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
1176
1177 return icnss_hw_power_off(priv);
1178}
1179EXPORT_SYMBOL(icnss_power_off);
1180
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001181static int wlfw_msa_mem_info_send_sync_msg(void)
1182{
1183 int ret;
1184 int i;
1185 struct wlfw_msa_info_req_msg_v01 req;
1186 struct wlfw_msa_info_resp_msg_v01 resp;
1187 struct msg_desc req_desc, resp_desc;
1188
1189 if (!penv || !penv->wlfw_clnt)
1190 return -ENODEV;
1191
1192 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1193
1194 memset(&req, 0, sizeof(req));
1195 memset(&resp, 0, sizeof(resp));
1196
1197 req.msa_addr = penv->msa_pa;
1198 req.size = penv->msa_mem_size;
1199
1200 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1201 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1202 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1203
1204 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1205 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1206 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1207
1208 penv->stats.msa_info_req++;
1209
1210 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1211 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1212 if (ret < 0) {
1213 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1214 goto out;
1215 }
1216
1217 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1218 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1219 resp.resp.result, resp.resp.error);
1220 ret = resp.resp.result;
1221 goto out;
1222 }
1223
1224 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1225 resp.mem_region_info_len);
1226
Yuanyuan Liu68939762017-04-04 16:43:03 -07001227 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001228 icnss_pr_err("Invalid memory region length received: %d\n",
1229 resp.mem_region_info_len);
1230 ret = -EINVAL;
1231 goto out;
1232 }
1233
1234 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001235 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001236 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001237 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001238 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001239 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001240 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001241 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001242 resp.mem_region_info[i].secure_flag;
1243 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001244 i, penv->mem_region[i].reg_addr,
1245 penv->mem_region[i].size,
1246 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001247 }
1248
1249 return 0;
1250
1251out:
1252 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001253 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001254 return ret;
1255}
1256
1257static int wlfw_msa_ready_send_sync_msg(void)
1258{
1259 int ret;
1260 struct wlfw_msa_ready_req_msg_v01 req;
1261 struct wlfw_msa_ready_resp_msg_v01 resp;
1262 struct msg_desc req_desc, resp_desc;
1263
1264 if (!penv || !penv->wlfw_clnt)
1265 return -ENODEV;
1266
1267 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1268 penv->state);
1269
1270 memset(&req, 0, sizeof(req));
1271 memset(&resp, 0, sizeof(resp));
1272
1273 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1274 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1275 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1276
1277 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1278 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1279 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1280
1281 penv->stats.msa_ready_req++;
1282 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1283 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1284 if (ret < 0) {
1285 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1286 goto out;
1287 }
1288
1289 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1290 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1291 resp.resp.result, resp.resp.error);
1292 ret = resp.resp.result;
1293 goto out;
1294 }
1295 penv->stats.msa_ready_resp++;
1296
1297 return 0;
1298
1299out:
1300 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001301 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001302 return ret;
1303}
1304
1305static int wlfw_ind_register_send_sync_msg(void)
1306{
1307 int ret;
1308 struct wlfw_ind_register_req_msg_v01 req;
1309 struct wlfw_ind_register_resp_msg_v01 resp;
1310 struct msg_desc req_desc, resp_desc;
1311
1312 if (!penv || !penv->wlfw_clnt)
1313 return -ENODEV;
1314
1315 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1316 penv->state);
1317
1318 memset(&req, 0, sizeof(req));
1319 memset(&resp, 0, sizeof(resp));
1320
1321 req.client_id_valid = 1;
1322 req.client_id = WLFW_CLIENT_ID;
1323 req.fw_ready_enable_valid = 1;
1324 req.fw_ready_enable = 1;
1325 req.msa_ready_enable_valid = 1;
1326 req.msa_ready_enable = 1;
1327 req.pin_connect_result_enable_valid = 1;
1328 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001329 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1330 req.rejuvenate_enable_valid = 1;
1331 req.rejuvenate_enable = 1;
1332 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001333
1334 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1335 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1336 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1337
1338 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1339 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1340 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1341
1342 penv->stats.ind_register_req++;
1343
1344 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1345 &resp_desc, &resp, sizeof(resp),
1346 WLFW_TIMEOUT_MS);
1347 if (ret < 0) {
1348 icnss_pr_err("Send indication register req failed %d\n", ret);
1349 goto out;
1350 }
1351
1352 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1353 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1354 resp.resp.result, resp.resp.error);
1355 ret = resp.resp.result;
1356 goto out;
1357 }
1358 penv->stats.ind_register_resp++;
1359
1360 return 0;
1361
1362out:
1363 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001364 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001365 return ret;
1366}
1367
1368static int wlfw_cap_send_sync_msg(void)
1369{
1370 int ret;
1371 struct wlfw_cap_req_msg_v01 req;
1372 struct wlfw_cap_resp_msg_v01 resp;
1373 struct msg_desc req_desc, resp_desc;
1374
1375 if (!penv || !penv->wlfw_clnt)
1376 return -ENODEV;
1377
1378 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1379
1380 memset(&resp, 0, sizeof(resp));
1381
1382 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1383 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1384 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1385
1386 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1387 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1388 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1389
1390 penv->stats.cap_req++;
1391 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1392 &resp_desc, &resp, sizeof(resp),
1393 WLFW_TIMEOUT_MS);
1394 if (ret < 0) {
1395 icnss_pr_err("Send capability req failed %d\n", ret);
1396 goto out;
1397 }
1398
1399 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1400 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1401 resp.resp.result, resp.resp.error);
1402 ret = resp.resp.result;
1403 goto out;
1404 }
1405
1406 penv->stats.cap_resp++;
1407 /* store cap locally */
1408 if (resp.chip_info_valid)
1409 penv->chip_info = resp.chip_info;
1410 if (resp.board_info_valid)
1411 penv->board_info = resp.board_info;
1412 else
1413 penv->board_info.board_id = 0xFF;
1414 if (resp.soc_info_valid)
1415 penv->soc_info = resp.soc_info;
1416 if (resp.fw_version_info_valid)
1417 penv->fw_version_info = resp.fw_version_info;
1418 if (resp.fw_build_id_valid)
1419 strlcpy(penv->fw_build_id, resp.fw_build_id,
1420 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1421
1422 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",
1423 penv->chip_info.chip_id, penv->chip_info.chip_family,
1424 penv->board_info.board_id, penv->soc_info.soc_id,
1425 penv->fw_version_info.fw_version,
1426 penv->fw_version_info.fw_build_timestamp,
1427 penv->fw_build_id);
1428
1429 return 0;
1430
1431out:
1432 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001433 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001434 return ret;
1435}
1436
1437static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1438{
1439 int ret;
1440 struct wlfw_wlan_mode_req_msg_v01 req;
1441 struct wlfw_wlan_mode_resp_msg_v01 resp;
1442 struct msg_desc req_desc, resp_desc;
1443
1444 if (!penv || !penv->wlfw_clnt)
1445 return -ENODEV;
1446
1447 /* During recovery do not send mode request for WLAN OFF as
1448 * FW not able to process it.
1449 */
1450 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1451 mode == QMI_WLFW_OFF_V01)
1452 return 0;
1453
1454 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1455 penv->state, mode);
1456
1457 memset(&req, 0, sizeof(req));
1458 memset(&resp, 0, sizeof(resp));
1459
1460 req.mode = mode;
1461 req.hw_debug_valid = 1;
1462 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1463
1464 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1465 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1466 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1467
1468 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1469 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1470 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1471
1472 penv->stats.mode_req++;
1473 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1474 &resp_desc, &resp, sizeof(resp),
1475 WLFW_TIMEOUT_MS);
1476 if (ret < 0) {
1477 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1478 mode, ret);
1479 goto out;
1480 }
1481
1482 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1483 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1484 mode, resp.resp.result, resp.resp.error);
1485 ret = resp.resp.result;
1486 goto out;
1487 }
1488 penv->stats.mode_resp++;
1489
1490 return 0;
1491
1492out:
1493 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001494 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001495 return ret;
1496}
1497
1498static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1499{
1500 int ret;
1501 struct wlfw_wlan_cfg_req_msg_v01 req;
1502 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1503 struct msg_desc req_desc, resp_desc;
1504
1505 if (!penv || !penv->wlfw_clnt)
1506 return -ENODEV;
1507
1508 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1509
1510 memset(&req, 0, sizeof(req));
1511 memset(&resp, 0, sizeof(resp));
1512
1513 memcpy(&req, data, sizeof(req));
1514
1515 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1516 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1517 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1518
1519 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1520 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1521 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1522
1523 penv->stats.cfg_req++;
1524 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1525 &resp_desc, &resp, sizeof(resp),
1526 WLFW_TIMEOUT_MS);
1527 if (ret < 0) {
1528 icnss_pr_err("Send config req failed %d\n", ret);
1529 goto out;
1530 }
1531
1532 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1533 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1534 resp.resp.result, resp.resp.error);
1535 ret = resp.resp.result;
1536 goto out;
1537 }
1538 penv->stats.cfg_resp++;
1539
1540 return 0;
1541
1542out:
1543 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001544 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001545 return ret;
1546}
1547
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001548static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001549{
1550 int ret;
1551 struct wlfw_ini_req_msg_v01 req;
1552 struct wlfw_ini_resp_msg_v01 resp;
1553 struct msg_desc req_desc, resp_desc;
1554
1555 if (!penv || !penv->wlfw_clnt)
1556 return -ENODEV;
1557
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001558 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1559 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001560
1561 memset(&req, 0, sizeof(req));
1562 memset(&resp, 0, sizeof(resp));
1563
1564 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001565 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001566
1567 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1568 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1569 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1570
1571 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1572 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1573 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1574
1575 penv->stats.ini_req++;
1576
1577 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1578 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1579 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001580 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1581 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001582 goto out;
1583 }
1584
1585 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001586 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1587 fw_log_mode, resp.resp.result, resp.resp.error);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001588 ret = resp.resp.result;
1589 goto out;
1590 }
1591 penv->stats.ini_resp++;
1592
1593 return 0;
1594
1595out:
1596 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001597 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001598 return ret;
1599}
1600
1601static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1602 uint32_t offset, uint32_t mem_type,
1603 uint32_t data_len, uint8_t *data)
1604{
1605 int ret;
1606 struct wlfw_athdiag_read_req_msg_v01 req;
1607 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1608 struct msg_desc req_desc, resp_desc;
1609
1610 if (!priv->wlfw_clnt) {
1611 ret = -ENODEV;
1612 goto out;
1613 }
1614
1615 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1616 priv->state, offset, mem_type, data_len);
1617
1618 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1619 if (!resp) {
1620 ret = -ENOMEM;
1621 goto out;
1622 }
1623 memset(&req, 0, sizeof(req));
1624
1625 req.offset = offset;
1626 req.mem_type = mem_type;
1627 req.data_len = data_len;
1628
1629 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1630 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1631 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1632
1633 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1634 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1635 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1636
1637 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1638 &resp_desc, resp, sizeof(*resp),
1639 WLFW_TIMEOUT_MS);
1640 if (ret < 0) {
1641 icnss_pr_err("send athdiag read req failed %d\n", ret);
1642 goto out;
1643 }
1644
1645 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1646 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1647 resp->resp.result, resp->resp.error);
1648 ret = resp->resp.result;
1649 goto out;
1650 }
1651
Yuanyuan Liu68939762017-04-04 16:43:03 -07001652 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001653 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1654 resp->data_valid, resp->data_len);
1655 ret = -EINVAL;
1656 goto out;
1657 }
1658
1659 memcpy(data, resp->data, resp->data_len);
1660
1661out:
1662 kfree(resp);
1663 return ret;
1664}
1665
1666static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1667 uint32_t offset, uint32_t mem_type,
1668 uint32_t data_len, uint8_t *data)
1669{
1670 int ret;
1671 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1672 struct wlfw_athdiag_write_resp_msg_v01 resp;
1673 struct msg_desc req_desc, resp_desc;
1674
1675 if (!priv->wlfw_clnt) {
1676 ret = -ENODEV;
1677 goto out;
1678 }
1679
1680 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1681 priv->state, offset, mem_type, data_len, data);
1682
1683 req = kzalloc(sizeof(*req), GFP_KERNEL);
1684 if (!req) {
1685 ret = -ENOMEM;
1686 goto out;
1687 }
1688 memset(&resp, 0, sizeof(resp));
1689
1690 req->offset = offset;
1691 req->mem_type = mem_type;
1692 req->data_len = data_len;
1693 memcpy(req->data, data, data_len);
1694
1695 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1696 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1697 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1698
1699 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1700 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1701 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1702
1703 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1704 &resp_desc, &resp, sizeof(resp),
1705 WLFW_TIMEOUT_MS);
1706 if (ret < 0) {
1707 icnss_pr_err("send athdiag write req failed %d\n", ret);
1708 goto out;
1709 }
1710
1711 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1712 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1713 resp.resp.result, resp.resp.error);
1714 ret = resp.resp.result;
1715 goto out;
1716 }
1717out:
1718 kfree(req);
1719 return ret;
1720}
1721
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001722static int icnss_decode_rejuvenate_ind(void *msg, unsigned int msg_len)
1723{
1724 struct msg_desc ind_desc;
1725 struct wlfw_rejuvenate_ind_msg_v01 ind_msg;
1726 int ret = 0;
1727
1728 if (!penv || !penv->wlfw_clnt) {
1729 ret = -ENODEV;
1730 goto out;
1731 }
1732
1733 memset(&ind_msg, 0, sizeof(ind_msg));
1734
1735 ind_desc.msg_id = QMI_WLFW_REJUVENATE_IND_V01;
1736 ind_desc.max_msg_len = WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN;
1737 ind_desc.ei_array = wlfw_rejuvenate_ind_msg_v01_ei;
1738
1739 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
1740 if (ret < 0) {
1741 icnss_pr_err("Failed to decode rejuvenate ind message: ret %d, msg_len %u\n",
1742 ret, msg_len);
1743 goto out;
1744 }
1745
1746 if (ind_msg.cause_for_rejuvenation_valid)
1747 penv->cause_for_rejuvenation = ind_msg.cause_for_rejuvenation;
1748 else
1749 penv->cause_for_rejuvenation = 0;
1750 if (ind_msg.requesting_sub_system_valid)
1751 penv->requesting_sub_system = ind_msg.requesting_sub_system;
1752 else
1753 penv->requesting_sub_system = 0;
1754 if (ind_msg.line_number_valid)
1755 penv->line_number = ind_msg.line_number;
1756 else
1757 penv->line_number = 0;
1758 if (ind_msg.function_name_valid)
1759 memcpy(penv->function_name, ind_msg.function_name,
1760 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1761 else
1762 memset(penv->function_name, 0,
1763 QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
1764
1765 icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
1766 penv->cause_for_rejuvenation,
1767 penv->requesting_sub_system,
1768 penv->line_number,
1769 penv->function_name);
1770
1771 penv->stats.rejuvenate_ind++;
1772out:
1773 return ret;
1774}
1775
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001776static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1777{
1778 int ret;
1779 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1780 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1781 struct msg_desc req_desc, resp_desc;
1782
1783 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1784 priv->state);
1785
1786 memset(&req, 0, sizeof(req));
1787 memset(&resp, 0, sizeof(resp));
1788
1789 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1790 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1791 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1792
1793 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1794 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1795 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1796
1797 priv->stats.rejuvenate_ack_req++;
1798 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1799 &resp_desc, &resp, sizeof(resp),
1800 WLFW_TIMEOUT_MS);
1801 if (ret < 0) {
1802 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1803 goto out;
1804 }
1805
1806 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1807 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1808 resp.resp.result, resp.resp.error);
1809 ret = resp.resp.result;
1810 goto out;
1811 }
1812 priv->stats.rejuvenate_ack_resp++;
1813 return 0;
1814
1815out:
1816 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001817 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001818 return ret;
1819}
1820
1821static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1822 uint64_t dynamic_feature_mask)
1823{
1824 int ret;
1825 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1826 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1827 struct msg_desc req_desc, resp_desc;
1828
1829 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1830 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1831 priv->state);
1832 return -EINVAL;
1833 }
1834
1835 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1836 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1837 return 0;
1838 }
1839
1840 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1841 dynamic_feature_mask, priv->state);
1842
1843 memset(&req, 0, sizeof(req));
1844 memset(&resp, 0, sizeof(resp));
1845
1846 req.mask_valid = 1;
1847 req.mask = dynamic_feature_mask;
1848
1849 req_desc.max_msg_len =
1850 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1851 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1852 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1853
1854 resp_desc.max_msg_len =
1855 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1856 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1857 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1858
1859 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1860 &resp_desc, &resp, sizeof(resp),
1861 WLFW_TIMEOUT_MS);
1862 if (ret < 0) {
1863 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1864 goto out;
1865 }
1866
1867 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1868 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1869 resp.resp.result, resp.resp.error);
1870 ret = resp.resp.result;
1871 goto out;
1872 }
1873
1874 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1875 resp.prev_mask_valid, resp.prev_mask,
1876 resp.curr_mask_valid, resp.curr_mask);
1877
1878 return 0;
1879
1880out:
1881 return ret;
1882}
1883
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001884static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1885{
1886 int ret;
1887
1888 if (!penv || !penv->wlfw_clnt)
1889 return;
1890
Yuanyuan Liu68939762017-04-04 16:43:03 -07001891 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001892
1893 do {
1894 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1895
1896 if (ret != -ENOMSG)
1897 icnss_pr_err("Error receiving message: %d\n", ret);
1898
Yuanyuan Liu68939762017-04-04 16:43:03 -07001899 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001900}
1901
1902static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1903 enum qmi_event_type event, void *notify_priv)
1904{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001905 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001906
1907 if (!penv || !penv->wlfw_clnt)
1908 return;
1909
1910 switch (event) {
1911 case QMI_RECV_MSG:
1912 schedule_work(&penv->qmi_recv_msg_work);
1913 break;
1914 default:
1915 icnss_pr_dbg("Unknown Event: %d\n", event);
1916 break;
1917 }
1918}
1919
Yuanyuan Liu68939762017-04-04 16:43:03 -07001920static int icnss_call_driver_uevent(struct icnss_priv *priv,
1921 enum icnss_uevent uevent, void *data)
1922{
1923 struct icnss_uevent_data uevent_data;
1924
1925 if (!priv->ops || !priv->ops->uevent)
1926 return 0;
1927
1928 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1929 priv->state, uevent);
1930
1931 uevent_data.uevent = uevent;
1932 uevent_data.data = data;
1933
1934 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1935}
1936
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001937static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1938 unsigned int msg_id, void *msg,
1939 unsigned int msg_len, void *ind_cb_priv)
1940{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001941 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001942 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001943
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001944 if (!penv)
1945 return;
1946
1947 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1948
1949 switch (msg_id) {
1950 case QMI_WLFW_FW_READY_IND_V01:
1951 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
1952 0, NULL);
1953 break;
1954 case QMI_WLFW_MSA_READY_IND_V01:
1955 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
1956 msg_id);
1957 penv->stats.msa_ready_ind++;
1958 break;
1959 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
1960 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
1961 msg_id);
1962 icnss_qmi_pin_connect_result_ind(msg, msg_len);
1963 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001964 case QMI_WLFW_REJUVENATE_IND_V01:
1965 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
1966 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001967
1968 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08001969 icnss_decode_rejuvenate_ind(msg, msg_len);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001970 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
1971 if (event_data == NULL)
1972 return;
1973 event_data->crashed = true;
1974 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001975 fw_down_data.crashed = true;
1976 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
1977 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001978 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
1979 0, event_data);
1980 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001981 default:
1982 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
1983 break;
1984 }
1985}
1986
1987static int icnss_driver_event_server_arrive(void *data)
1988{
1989 int ret = 0;
1990
1991 if (!penv)
1992 return -ENODEV;
1993
1994 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
1995
1996 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
1997 if (!penv->wlfw_clnt) {
1998 icnss_pr_err("QMI client handle create failed\n");
1999 ret = -ENOMEM;
2000 goto out;
2001 }
2002
2003 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
2004 WLFW_SERVICE_VERS_V01,
2005 WLFW_SERVICE_INS_ID_V01);
2006 if (ret < 0) {
2007 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
2008 goto fail;
2009 }
2010
2011 ret = qmi_register_ind_cb(penv->wlfw_clnt,
2012 icnss_qmi_wlfw_clnt_ind, penv);
2013 if (ret < 0) {
2014 icnss_pr_err("Failed to register indication callback: %d\n",
2015 ret);
2016 goto fail;
2017 }
2018
2019 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2020
2021 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
2022
2023 ret = icnss_hw_power_on(penv);
2024 if (ret)
2025 goto fail;
2026
2027 ret = wlfw_ind_register_send_sync_msg();
2028 if (ret < 0)
2029 goto err_power_on;
2030
2031 if (!penv->msa_va) {
2032 icnss_pr_err("Invalid MSA address\n");
2033 ret = -EINVAL;
2034 goto err_power_on;
2035 }
2036
2037 ret = wlfw_msa_mem_info_send_sync_msg();
2038 if (ret < 0)
2039 goto err_power_on;
2040
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302041 if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
2042 ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
2043 if (ret < 0)
2044 goto err_power_on;
2045 set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
2046 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002047
2048 ret = wlfw_msa_ready_send_sync_msg();
2049 if (ret < 0)
2050 goto err_setup_msa;
2051
2052 ret = wlfw_cap_send_sync_msg();
2053 if (ret < 0)
2054 goto err_setup_msa;
2055
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002056 wlfw_dynamic_feature_mask_send_sync_msg(penv,
2057 dynamic_feature_mask);
2058
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002059 icnss_init_vph_monitor(penv);
2060
2061 return ret;
2062
2063err_setup_msa:
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302064 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002065err_power_on:
2066 icnss_hw_power_off(penv);
2067fail:
2068 qmi_handle_destroy(penv->wlfw_clnt);
2069 penv->wlfw_clnt = NULL;
2070out:
2071 ICNSS_ASSERT(0);
2072 return ret;
2073}
2074
2075static int icnss_driver_event_server_exit(void *data)
2076{
2077 if (!penv || !penv->wlfw_clnt)
2078 return -ENODEV;
2079
2080 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
2081
2082 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
2083 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
2084 &penv->vph_monitor_params);
2085
2086 qmi_handle_destroy(penv->wlfw_clnt);
2087
2088 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
2089 penv->wlfw_clnt = NULL;
2090
2091 return 0;
2092}
2093
2094static int icnss_call_driver_probe(struct icnss_priv *priv)
2095{
2096 int ret;
2097
2098 if (!priv->ops || !priv->ops->probe)
2099 return 0;
2100
Yuanyuan Liu68939762017-04-04 16:43:03 -07002101 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2102 return -EINVAL;
2103
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002104 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
2105
2106 icnss_hw_power_on(priv);
2107
2108 ret = priv->ops->probe(&priv->pdev->dev);
2109 if (ret < 0) {
2110 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2111 ret, priv->state);
2112 goto out;
2113 }
2114
2115 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
2116
2117 return 0;
2118
2119out:
2120 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002121 return ret;
2122}
2123
Yuanyuan Liu68939762017-04-04 16:43:03 -07002124static int icnss_call_driver_shutdown(struct icnss_priv *priv)
2125{
2126 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2127 goto out;
2128
2129 if (!priv->ops || !priv->ops->shutdown)
2130 goto out;
2131
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302132 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
2133 goto out;
2134
Yuanyuan Liu68939762017-04-04 16:43:03 -07002135 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
2136
2137 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302138 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002139
2140out:
2141 return 0;
2142}
2143
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002144static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002145{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002146 int ret;
2147
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002148 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002149
Anurag Chouhan9de21192017-08-08 15:30:02 +05302150 icnss_call_driver_shutdown(priv);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002151
2152 clear_bit(ICNSS_PD_RESTART, &priv->state);
2153
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002154 if (!priv->ops || !priv->ops->reinit)
2155 goto out;
2156
Yuanyuan Liu68939762017-04-04 16:43:03 -07002157 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002158 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002159
2160 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2161
2162 icnss_hw_power_on(priv);
2163
2164 ret = priv->ops->reinit(&priv->pdev->dev);
2165 if (ret < 0) {
2166 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2167 ret, priv->state);
2168 ICNSS_ASSERT(false);
2169 goto out_power_off;
2170 }
2171
2172out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302173 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002174 return 0;
2175
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002176call_probe:
2177 return icnss_call_driver_probe(priv);
2178
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002179out_power_off:
2180 icnss_hw_power_off(priv);
2181
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002182 return ret;
2183}
2184
2185
2186static int icnss_driver_event_fw_ready_ind(void *data)
2187{
2188 int ret = 0;
2189
2190 if (!penv)
2191 return -ENODEV;
2192
2193 set_bit(ICNSS_FW_READY, &penv->state);
2194
Yuanyuan Liu68939762017-04-04 16:43:03 -07002195 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2196
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002197 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2198
2199 icnss_hw_power_off(penv);
2200
2201 if (!penv->pdev) {
2202 icnss_pr_err("Device is not ready\n");
2203 ret = -ENODEV;
2204 goto out;
2205 }
2206
2207 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002208 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002209 else
2210 ret = icnss_call_driver_probe(penv);
2211
2212out:
2213 return ret;
2214}
2215
2216static int icnss_driver_event_register_driver(void *data)
2217{
2218 int ret = 0;
2219
2220 if (penv->ops)
2221 return -EEXIST;
2222
2223 penv->ops = data;
2224
2225 if (test_bit(SKIP_QMI, &quirks))
2226 set_bit(ICNSS_FW_READY, &penv->state);
2227
2228 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2229 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2230 penv->state);
2231 goto out;
2232 }
2233
2234 ret = icnss_hw_power_on(penv);
2235 if (ret)
2236 goto out;
2237
2238 ret = penv->ops->probe(&penv->pdev->dev);
2239
2240 if (ret) {
2241 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2242 ret, penv->state);
2243 goto power_off;
2244 }
2245
2246 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2247
2248 return 0;
2249
2250power_off:
2251 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002252out:
2253 return ret;
2254}
2255
2256static int icnss_driver_event_unregister_driver(void *data)
2257{
2258 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2259 penv->ops = NULL;
2260 goto out;
2261 }
2262
2263 if (penv->ops)
2264 penv->ops->remove(&penv->pdev->dev);
2265
2266 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2267
2268 penv->ops = NULL;
2269
2270 icnss_hw_power_off(penv);
2271
2272out:
2273 return 0;
2274}
2275
2276static int icnss_call_driver_remove(struct icnss_priv *priv)
2277{
2278 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2279
2280 clear_bit(ICNSS_FW_READY, &priv->state);
2281
2282 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2283 return 0;
2284
2285 if (!priv->ops || !priv->ops->remove)
2286 return 0;
2287
2288 penv->ops->remove(&priv->pdev->dev);
2289
2290 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002291
2292 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002293
2294 return 0;
2295}
2296
Yuanyuan Liu68939762017-04-04 16:43:03 -07002297static int icnss_fw_crashed(struct icnss_priv *priv,
2298 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002299{
Anurag Chouhan9de21192017-08-08 15:30:02 +05302300 icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002301
2302 set_bit(ICNSS_PD_RESTART, &priv->state);
2303 clear_bit(ICNSS_FW_READY, &priv->state);
2304
2305 icnss_pm_stay_awake(priv);
2306
Yuanyuan Liu68939762017-04-04 16:43:03 -07002307 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2308 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002309
Yuanyuan Liu68939762017-04-04 16:43:03 -07002310 if (event_data->fw_rejuvenate)
2311 wlfw_rejuvenate_ack_send_sync_msg(priv);
2312
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002313 return 0;
2314}
2315
2316static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2317 void *data)
2318{
2319 int ret = 0;
2320 struct icnss_event_pd_service_down_data *event_data = data;
2321
2322 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002323 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002324
2325 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
2326 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2327 event_data->crashed, priv->state);
2328 ICNSS_ASSERT(0);
2329 goto out;
2330 }
2331
2332 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002333 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002334 else
2335 icnss_call_driver_remove(priv);
2336
2337out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002338 kfree(data);
2339
Yuanyuan Liu68939762017-04-04 16:43:03 -07002340 icnss_ignore_qmi_timeout(false);
2341
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002342 return ret;
2343}
2344
2345static void icnss_driver_event_work(struct work_struct *work)
2346{
2347 struct icnss_driver_event *event;
2348 unsigned long flags;
2349 int ret;
2350
2351 icnss_pm_stay_awake(penv);
2352
2353 spin_lock_irqsave(&penv->event_lock, flags);
2354
2355 while (!list_empty(&penv->event_list)) {
2356 event = list_first_entry(&penv->event_list,
2357 struct icnss_driver_event, list);
2358 list_del(&event->list);
2359 spin_unlock_irqrestore(&penv->event_lock, flags);
2360
2361 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2362 icnss_driver_event_to_str(event->type),
2363 event->sync ? "-sync" : "", event->type,
2364 penv->state);
2365
2366 switch (event->type) {
2367 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2368 ret = icnss_driver_event_server_arrive(event->data);
2369 break;
2370 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2371 ret = icnss_driver_event_server_exit(event->data);
2372 break;
2373 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2374 ret = icnss_driver_event_fw_ready_ind(event->data);
2375 break;
2376 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2377 ret = icnss_driver_event_register_driver(event->data);
2378 break;
2379 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2380 ret = icnss_driver_event_unregister_driver(event->data);
2381 break;
2382 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2383 ret = icnss_driver_event_pd_service_down(penv,
2384 event->data);
2385 break;
2386 default:
2387 icnss_pr_err("Invalid Event type: %d", event->type);
2388 kfree(event);
2389 continue;
2390 }
2391
2392 penv->stats.events[event->type].processed++;
2393
2394 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2395 icnss_driver_event_to_str(event->type),
2396 event->sync ? "-sync" : "", event->type, ret,
2397 penv->state);
2398
2399 spin_lock_irqsave(&penv->event_lock, flags);
2400 if (event->sync) {
2401 event->ret = ret;
2402 complete(&event->complete);
2403 continue;
2404 }
2405 spin_unlock_irqrestore(&penv->event_lock, flags);
2406
2407 kfree(event);
2408
2409 spin_lock_irqsave(&penv->event_lock, flags);
2410 }
2411 spin_unlock_irqrestore(&penv->event_lock, flags);
2412
2413 icnss_pm_relax(penv);
2414}
2415
2416static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2417 unsigned long code,
2418 void *_cmd)
2419{
2420 int ret = 0;
2421
2422 if (!penv)
2423 return -ENODEV;
2424
2425 icnss_pr_dbg("Event Notify: code: %ld", code);
2426
2427 switch (code) {
2428 case QMI_SERVER_ARRIVE:
2429 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2430 0, NULL);
2431 break;
2432
2433 case QMI_SERVER_EXIT:
2434 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2435 0, NULL);
2436 break;
2437 default:
2438 icnss_pr_dbg("Invalid code: %ld", code);
2439 break;
2440 }
2441 return ret;
2442}
2443
2444static int icnss_msa0_ramdump(struct icnss_priv *priv)
2445{
2446 struct ramdump_segment segment;
2447
2448 memset(&segment, 0, sizeof(segment));
2449 segment.v_address = priv->msa_va;
2450 segment.size = priv->msa_mem_size;
2451 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2452}
2453
2454static struct notifier_block wlfw_clnt_nb = {
2455 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2456};
2457
2458static int icnss_modem_notifier_nb(struct notifier_block *nb,
2459 unsigned long code,
2460 void *data)
2461{
2462 struct icnss_event_pd_service_down_data *event_data;
2463 struct notif_data *notif = data;
2464 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2465 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002466 struct icnss_uevent_fw_down_data fw_down_data;
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302467 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002468
Yuanyuan Liu68939762017-04-04 16:43:03 -07002469 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002470
Anurag Chouhanfacf3922017-06-08 18:26:56 +05302471 if (code == SUBSYS_AFTER_SHUTDOWN) {
2472 ret = icnss_assign_msa_perm_all(priv,
2473 ICNSS_MSA_PERM_DUMP_COLLECT);
2474 if (!ret) {
2475 icnss_pr_info("Collecting msa0 segment dump\n");
2476 icnss_msa0_ramdump(priv);
2477 icnss_assign_msa_perm_all(priv,
2478 ICNSS_MSA_PERM_WLAN_HW_RW);
2479 } else {
2480 icnss_pr_err("Not able to Collect msa0 segment dump"
2481 "Apps permissions not assigned %d\n", ret);
2482 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002483 return NOTIFY_OK;
2484 }
2485
2486 if (code != SUBSYS_BEFORE_SHUTDOWN)
2487 return NOTIFY_OK;
2488
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002489 if (test_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002490 return NOTIFY_OK;
2491
Yuanyuan Liu68939762017-04-04 16:43:03 -07002492 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2493 priv->state, notif->crashed);
2494
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002495 if (notif->crashed)
2496 priv->stats.recovery.root_pd_crash++;
2497 else
2498 priv->stats.recovery.root_pd_shutdown++;
2499
Yuanyuan Liu68939762017-04-04 16:43:03 -07002500 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002501
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002502 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002503
2504 if (event_data == NULL)
2505 return notifier_from_errno(-ENOMEM);
2506
2507 event_data->crashed = notif->crashed;
2508
Yuanyuan Liu68939762017-04-04 16:43:03 -07002509 fw_down_data.crashed = !!notif->crashed;
2510 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2511
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002512 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2513 ICNSS_EVENT_SYNC, event_data);
2514
2515 return NOTIFY_OK;
2516}
2517
2518static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2519{
2520 int ret = 0;
2521
2522 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2523
2524 priv->modem_notify_handler =
2525 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2526
2527 if (IS_ERR(priv->modem_notify_handler)) {
2528 ret = PTR_ERR(priv->modem_notify_handler);
2529 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2530 }
2531
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002532 set_bit(ICNSS_SSR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002533
2534 return ret;
2535}
2536
2537static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2538{
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002539 if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002540 return 0;
2541
2542 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2543 &priv->modem_ssr_nb);
2544 priv->modem_notify_handler = NULL;
2545
2546 return 0;
2547}
2548
2549static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2550{
2551 int i;
2552
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002553 if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002554 return 0;
2555
2556 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002557 service_notif_unregister_notifier(
2558 priv->service_notifier[i].handle,
2559 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002560
2561 kfree(priv->service_notifier);
2562
2563 priv->service_notifier = NULL;
2564
2565 return 0;
2566}
2567
2568static int icnss_service_notifier_notify(struct notifier_block *nb,
2569 unsigned long notification, void *data)
2570{
2571 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2572 service_notifier_nb);
2573 enum pd_subsys_state *state = data;
2574 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002575 struct icnss_uevent_fw_down_data fw_down_data;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002576 enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002577
Yuanyuan Liu68939762017-04-04 16:43:03 -07002578 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2579 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002580
Yuanyuan Liu68939762017-04-04 16:43:03 -07002581 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2582 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002583
Yuanyuan Liu68939762017-04-04 16:43:03 -07002584 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002585
Yuanyuan Liu68939762017-04-04 16:43:03 -07002586 if (event_data == NULL)
2587 return notifier_from_errno(-ENOMEM);
2588
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002589 event_data->crashed = true;
2590
Yuanyuan Liu68939762017-04-04 16:43:03 -07002591 if (state == NULL) {
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002592 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002593 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002594 }
2595
Yuanyuan Liu68939762017-04-04 16:43:03 -07002596 switch (*state) {
2597 case ROOT_PD_WDOG_BITE:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002598 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002599 break;
2600 case ROOT_PD_SHUTDOWN:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002601 cause = ICNSS_ROOT_PD_SHUTDOWN;
2602 priv->stats.recovery.root_pd_shutdown++;
Sameer Thalappil3bbb4312017-07-25 13:24:48 -07002603 event_data->crashed = false;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002604 break;
2605 case USER_PD_STATE_CHANGE:
2606 if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
2607 cause = ICNSS_HOST_ERROR;
2608 priv->stats.recovery.pdr_host_error++;
2609 } else {
2610 cause = ICNSS_FW_CRASH;
2611 priv->stats.recovery.pdr_fw_crash++;
2612 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07002613 break;
2614 default:
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002615 priv->stats.recovery.root_pd_crash++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002616 break;
2617 }
2618
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002619 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
2620 *state, priv->state, icnss_pdr_cause[cause]);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002621event_post:
2622 icnss_ignore_qmi_timeout(true);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07002623 clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002624
2625 fw_down_data.crashed = event_data->crashed;
2626 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2627 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2628 ICNSS_EVENT_SYNC, event_data);
2629done:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002630 return NOTIFY_OK;
2631}
2632
2633static int icnss_get_service_location_notify(struct notifier_block *nb,
2634 unsigned long opcode, void *data)
2635{
2636 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2637 get_service_nb);
2638 struct pd_qmi_client_data *pd = data;
2639 int curr_state;
2640 int ret;
2641 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002642 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002643
2644 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2645 priv->state);
2646
2647 if (opcode != LOCATOR_UP)
2648 return NOTIFY_DONE;
2649
2650 if (pd->total_domains == 0) {
2651 icnss_pr_err("Did not find any domains\n");
2652 ret = -ENOENT;
2653 goto out;
2654 }
2655
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002656 notifier = kcalloc(pd->total_domains,
2657 sizeof(struct service_notifier_context),
2658 GFP_KERNEL);
2659 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002660 ret = -ENOMEM;
2661 goto out;
2662 }
2663
2664 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2665
2666 for (i = 0; i < pd->total_domains; i++) {
2667 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2668 pd->domain_list[i].name,
2669 pd->domain_list[i].instance_id);
2670
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002671 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002672 service_notif_register_notifier(pd->domain_list[i].name,
2673 pd->domain_list[i].instance_id,
2674 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002675 notifier[i].instance_id = pd->domain_list[i].instance_id;
2676 strlcpy(notifier[i].name, pd->domain_list[i].name,
2677 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002678
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002679 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002680 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2681 i, pd->domain_list->name,
2682 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002683 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002684 goto free_handle;
2685 }
2686 }
2687
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002688 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002689 priv->total_domains = pd->total_domains;
2690
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002691 set_bit(ICNSS_PDR_REGISTERED, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002692
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07002693 icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n",
2694 priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002695
2696 return NOTIFY_OK;
2697
2698free_handle:
2699 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002700 if (notifier[i].handle)
2701 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002702 &priv->service_notifier_nb);
2703 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002704 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002705
2706out:
2707 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2708 priv->state);
2709
2710 return NOTIFY_OK;
2711}
2712
2713
2714static int icnss_pd_restart_enable(struct icnss_priv *priv)
2715{
2716 int ret;
2717
2718 if (test_bit(SSR_ONLY, &quirks)) {
2719 icnss_pr_dbg("PDR disabled through module parameter\n");
2720 return 0;
2721 }
2722
2723 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2724
2725 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2726 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2727 ICNSS_WLAN_SERVICE_NAME,
2728 &priv->get_service_nb);
2729 if (ret) {
2730 icnss_pr_err("Get service location failed: %d\n", ret);
2731 goto out;
2732 }
2733
2734 return 0;
2735out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002736 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002737 return ret;
2738
2739}
2740
2741
2742static int icnss_enable_recovery(struct icnss_priv *priv)
2743{
2744 int ret;
2745
2746 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2747 icnss_pr_dbg("Recovery disabled through module parameter\n");
2748 return 0;
2749 }
2750
2751 if (test_bit(PDR_ONLY, &quirks)) {
2752 icnss_pr_dbg("SSR disabled through module parameter\n");
2753 goto enable_pdr;
2754 }
2755
2756 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2757 &priv->pdev->dev);
2758 if (!priv->msa0_dump_dev)
2759 return -ENOMEM;
2760
2761 icnss_modem_ssr_register_notifier(priv);
2762 if (test_bit(SSR_ONLY, &quirks)) {
2763 icnss_pr_dbg("PDR disabled through module parameter\n");
2764 return 0;
2765 }
2766
2767enable_pdr:
2768 ret = icnss_pd_restart_enable(priv);
2769
2770 if (ret)
2771 return ret;
2772
2773 return 0;
2774}
2775
2776int icnss_register_driver(struct icnss_driver_ops *ops)
2777{
2778 int ret = 0;
2779
2780 if (!penv || !penv->pdev) {
2781 ret = -ENODEV;
2782 goto out;
2783 }
2784
2785 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2786
2787 if (penv->ops) {
2788 icnss_pr_err("Driver already registered\n");
2789 ret = -EEXIST;
2790 goto out;
2791 }
2792
2793 if (!ops->probe || !ops->remove) {
2794 ret = -EINVAL;
2795 goto out;
2796 }
2797
2798 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
Anurag Chouhan3dd77c12017-03-24 16:07:45 +05302799 0, ops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002800
2801 if (ret == -EINTR)
2802 ret = 0;
2803
2804out:
2805 return ret;
2806}
2807EXPORT_SYMBOL(icnss_register_driver);
2808
2809int icnss_unregister_driver(struct icnss_driver_ops *ops)
2810{
2811 int ret;
2812
2813 if (!penv || !penv->pdev) {
2814 ret = -ENODEV;
2815 goto out;
2816 }
2817
2818 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2819
2820 if (!penv->ops) {
2821 icnss_pr_err("Driver not registered\n");
2822 ret = -ENOENT;
2823 goto out;
2824 }
2825
2826 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2827 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2828out:
2829 return ret;
2830}
2831EXPORT_SYMBOL(icnss_unregister_driver);
2832
2833int icnss_ce_request_irq(unsigned int ce_id,
2834 irqreturn_t (*handler)(int, void *),
2835 unsigned long flags, const char *name, void *ctx)
2836{
2837 int ret = 0;
2838 unsigned int irq;
2839 struct ce_irq_list *irq_entry;
2840
2841 if (!penv || !penv->pdev) {
2842 ret = -ENODEV;
2843 goto out;
2844 }
2845
Yuanyuan Liu68939762017-04-04 16:43:03 -07002846 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002847
2848 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2849 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2850 ret = -EINVAL;
2851 goto out;
2852 }
2853 irq = penv->ce_irqs[ce_id];
2854 irq_entry = &penv->ce_irq_list[ce_id];
2855
2856 if (irq_entry->handler || irq_entry->irq) {
2857 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2858 irq, ce_id);
2859 ret = -EEXIST;
2860 goto out;
2861 }
2862
2863 ret = request_irq(irq, handler, flags, name, ctx);
2864 if (ret) {
2865 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2866 irq, ce_id, ret);
2867 goto out;
2868 }
2869 irq_entry->irq = irq;
2870 irq_entry->handler = handler;
2871
Yuanyuan Liu68939762017-04-04 16:43:03 -07002872 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002873
2874 penv->stats.ce_irqs[ce_id].request++;
2875out:
2876 return ret;
2877}
2878EXPORT_SYMBOL(icnss_ce_request_irq);
2879
2880int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
2881{
2882 int ret = 0;
2883 unsigned int irq;
2884 struct ce_irq_list *irq_entry;
2885
2886 if (!penv || !penv->pdev) {
2887 ret = -ENODEV;
2888 goto out;
2889 }
2890
Yuanyuan Liu68939762017-04-04 16:43:03 -07002891 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002892
2893 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2894 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2895 ret = -EINVAL;
2896 goto out;
2897 }
2898
2899 irq = penv->ce_irqs[ce_id];
2900 irq_entry = &penv->ce_irq_list[ce_id];
2901 if (!irq_entry->handler || !irq_entry->irq) {
2902 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2903 ret = -EEXIST;
2904 goto out;
2905 }
2906 free_irq(irq, ctx);
2907 irq_entry->irq = 0;
2908 irq_entry->handler = NULL;
2909
2910 penv->stats.ce_irqs[ce_id].free++;
2911out:
2912 return ret;
2913}
2914EXPORT_SYMBOL(icnss_ce_free_irq);
2915
2916void icnss_enable_irq(unsigned int ce_id)
2917{
2918 unsigned int irq;
2919
2920 if (!penv || !penv->pdev) {
2921 icnss_pr_err("Platform driver not initialized\n");
2922 return;
2923 }
2924
Yuanyuan Liu68939762017-04-04 16:43:03 -07002925 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002926 penv->state);
2927
2928 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2929 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
2930 return;
2931 }
2932
2933 penv->stats.ce_irqs[ce_id].enable++;
2934
2935 irq = penv->ce_irqs[ce_id];
2936 enable_irq(irq);
2937}
2938EXPORT_SYMBOL(icnss_enable_irq);
2939
2940void icnss_disable_irq(unsigned int ce_id)
2941{
2942 unsigned int irq;
2943
2944 if (!penv || !penv->pdev) {
2945 icnss_pr_err("Platform driver not initialized\n");
2946 return;
2947 }
2948
Yuanyuan Liu68939762017-04-04 16:43:03 -07002949 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002950 penv->state);
2951
2952 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2953 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
2954 ce_id);
2955 return;
2956 }
2957
2958 irq = penv->ce_irqs[ce_id];
2959 disable_irq(irq);
2960
2961 penv->stats.ce_irqs[ce_id].disable++;
2962}
2963EXPORT_SYMBOL(icnss_disable_irq);
2964
2965int icnss_get_soc_info(struct icnss_soc_info *info)
2966{
2967 if (!penv) {
2968 icnss_pr_err("Platform driver not initialized\n");
2969 return -EINVAL;
2970 }
2971
2972 info->v_addr = penv->mem_base_va;
2973 info->p_addr = penv->mem_base_pa;
2974 info->chip_id = penv->chip_info.chip_id;
2975 info->chip_family = penv->chip_info.chip_family;
2976 info->board_id = penv->board_info.board_id;
2977 info->soc_id = penv->soc_info.soc_id;
2978 info->fw_version = penv->fw_version_info.fw_version;
2979 strlcpy(info->fw_build_timestamp,
2980 penv->fw_version_info.fw_build_timestamp,
2981 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
2982
2983 return 0;
2984}
2985EXPORT_SYMBOL(icnss_get_soc_info);
2986
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002987int icnss_set_fw_log_mode(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002988{
2989 int ret;
2990
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002991 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002992
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002993 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002994 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002995 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
2996 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002997 return ret;
2998}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002999EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003000
3001int icnss_athdiag_read(struct device *dev, uint32_t offset,
3002 uint32_t mem_type, uint32_t data_len,
3003 uint8_t *output)
3004{
3005 int ret = 0;
3006 struct icnss_priv *priv = dev_get_drvdata(dev);
3007
3008 if (priv->magic != ICNSS_MAGIC) {
3009 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
3010 dev, priv, priv->magic);
3011 return -EINVAL;
3012 }
3013
3014 if (!output || data_len == 0
3015 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3016 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
3017 output, data_len);
3018 ret = -EINVAL;
3019 goto out;
3020 }
3021
3022 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3023 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3024 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
3025 priv->state);
3026 ret = -EINVAL;
3027 goto out;
3028 }
3029
3030 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
3031 data_len, output);
3032out:
3033 return ret;
3034}
3035EXPORT_SYMBOL(icnss_athdiag_read);
3036
3037int icnss_athdiag_write(struct device *dev, uint32_t offset,
3038 uint32_t mem_type, uint32_t data_len,
3039 uint8_t *input)
3040{
3041 int ret = 0;
3042 struct icnss_priv *priv = dev_get_drvdata(dev);
3043
3044 if (priv->magic != ICNSS_MAGIC) {
3045 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
3046 dev, priv, priv->magic);
3047 return -EINVAL;
3048 }
3049
3050 if (!input || data_len == 0
3051 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
3052 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
3053 input, data_len);
3054 ret = -EINVAL;
3055 goto out;
3056 }
3057
3058 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3059 !test_bit(ICNSS_POWER_ON, &priv->state)) {
3060 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
3061 priv->state);
3062 ret = -EINVAL;
3063 goto out;
3064 }
3065
3066 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
3067 data_len, input);
3068out:
3069 return ret;
3070}
3071EXPORT_SYMBOL(icnss_athdiag_write);
3072
3073int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
3074 enum icnss_driver_mode mode,
3075 const char *host_version)
3076{
3077 struct wlfw_wlan_cfg_req_msg_v01 req;
3078 u32 i;
3079 int ret;
3080
3081 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
3082 mode, config, host_version);
3083
3084 memset(&req, 0, sizeof(req));
3085
3086 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
3087 goto skip;
3088
3089 if (!config || !host_version) {
3090 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
3091 config, host_version);
3092 ret = -EINVAL;
3093 goto out;
3094 }
3095
3096 req.host_version_valid = 1;
3097 strlcpy(req.host_version, host_version,
3098 QMI_WLFW_MAX_STR_LEN_V01 + 1);
3099
3100 req.tgt_cfg_valid = 1;
3101 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
3102 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
3103 else
3104 req.tgt_cfg_len = config->num_ce_tgt_cfg;
3105 for (i = 0; i < req.tgt_cfg_len; i++) {
3106 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
3107 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
3108 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
3109 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
3110 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
3111 }
3112
3113 req.svc_cfg_valid = 1;
3114 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
3115 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
3116 else
3117 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
3118 for (i = 0; i < req.svc_cfg_len; i++) {
3119 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
3120 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
3121 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
3122 }
3123
3124 req.shadow_reg_valid = 1;
3125 if (config->num_shadow_reg_cfg >
3126 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
3127 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
3128 else
3129 req.shadow_reg_len = config->num_shadow_reg_cfg;
3130
3131 memcpy(req.shadow_reg, config->shadow_reg_cfg,
3132 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
3133
3134 ret = wlfw_wlan_cfg_send_sync_msg(&req);
3135 if (ret)
3136 goto out;
3137skip:
3138 ret = wlfw_wlan_mode_send_sync_msg(mode);
3139out:
3140 if (test_bit(SKIP_QMI, &quirks))
3141 ret = 0;
3142
3143 return ret;
3144}
3145EXPORT_SYMBOL(icnss_wlan_enable);
3146
3147int icnss_wlan_disable(enum icnss_driver_mode mode)
3148{
3149 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
3150}
3151EXPORT_SYMBOL(icnss_wlan_disable);
3152
3153bool icnss_is_qmi_disable(void)
3154{
3155 return test_bit(SKIP_QMI, &quirks) ? true : false;
3156}
3157EXPORT_SYMBOL(icnss_is_qmi_disable);
3158
3159int icnss_get_ce_id(int irq)
3160{
3161 int i;
3162
3163 if (!penv || !penv->pdev)
3164 return -ENODEV;
3165
3166 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3167 if (penv->ce_irqs[i] == irq)
3168 return i;
3169 }
3170
3171 icnss_pr_err("No matching CE id for irq %d\n", irq);
3172
3173 return -EINVAL;
3174}
3175EXPORT_SYMBOL(icnss_get_ce_id);
3176
3177int icnss_get_irq(int ce_id)
3178{
3179 int irq;
3180
3181 if (!penv || !penv->pdev)
3182 return -ENODEV;
3183
3184 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3185 return -EINVAL;
3186
3187 irq = penv->ce_irqs[ce_id];
3188
3189 return irq;
3190}
3191EXPORT_SYMBOL(icnss_get_irq);
3192
3193struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3194{
3195 struct icnss_priv *priv = dev_get_drvdata(dev);
3196
3197 if (!priv) {
3198 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3199 dev, priv);
3200 return NULL;
3201 }
3202
3203 return priv->smmu_mapping;
3204}
3205EXPORT_SYMBOL(icnss_smmu_get_mapping);
3206
3207int icnss_smmu_map(struct device *dev,
3208 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3209{
3210 struct icnss_priv *priv = dev_get_drvdata(dev);
3211 unsigned long iova;
3212 size_t len;
3213 int ret = 0;
3214
3215 if (!priv) {
3216 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3217 dev, priv);
3218 return -EINVAL;
3219 }
3220
3221 if (!iova_addr) {
3222 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3223 &paddr, size);
3224 return -EINVAL;
3225 }
3226
3227 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3228 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3229
3230 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3231 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3232 iova,
3233 &priv->smmu_iova_ipa_start,
3234 priv->smmu_iova_ipa_len);
3235 return -ENOMEM;
3236 }
3237
3238 ret = iommu_map(priv->smmu_mapping->domain, iova,
3239 rounddown(paddr, PAGE_SIZE), len,
3240 IOMMU_READ | IOMMU_WRITE);
3241 if (ret) {
3242 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3243 return ret;
3244 }
3245
3246 priv->smmu_iova_ipa_start = iova + len;
3247 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3248
3249 return 0;
3250}
3251EXPORT_SYMBOL(icnss_smmu_map);
3252
3253unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3254{
3255 return socinfo_get_serial_number();
3256}
3257EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3258
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003259int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
3260{
3261 struct icnss_priv *priv = penv;
3262 uint32_t no_of_mac_addr;
3263 struct icnss_wlan_mac_addr *addr = NULL;
3264 int iter;
3265 u8 *temp = NULL;
3266
3267 if (!priv) {
3268 icnss_pr_err("Priv data is NULL\n");
3269 return -EINVAL;
3270 }
3271
3272 if (priv->is_wlan_mac_set) {
3273 icnss_pr_dbg("WLAN MAC address is already set\n");
3274 return 0;
3275 }
3276
3277 if (len == 0 || (len % ETH_ALEN) != 0) {
3278 icnss_pr_err("Invalid length %d\n", len);
3279 return -EINVAL;
3280 }
3281
3282 no_of_mac_addr = len / ETH_ALEN;
3283 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
3284 icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
3285 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
3286 return -EINVAL;
3287 }
3288
3289 priv->is_wlan_mac_set = true;
3290 addr = &priv->wlan_mac_addr;
3291 addr->no_of_mac_addr_set = no_of_mac_addr;
3292 temp = &addr->mac_addr[0][0];
3293
3294 for (iter = 0; iter < no_of_mac_addr;
3295 ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
3296 ether_addr_copy(temp, in);
3297 icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
3298 temp[0], temp[1], temp[2],
3299 temp[3], temp[4], temp[5]);
3300 }
3301
3302 return 0;
3303}
3304EXPORT_SYMBOL(icnss_set_wlan_mac_address);
3305
3306u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
3307{
3308 struct icnss_priv *priv = dev_get_drvdata(dev);
3309 struct icnss_wlan_mac_addr *addr = NULL;
3310
3311 if (priv->magic != ICNSS_MAGIC) {
3312 icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
3313 dev, priv, priv->magic);
3314 goto out;
3315 }
3316
3317 if (!priv->is_wlan_mac_set) {
3318 icnss_pr_dbg("WLAN MAC address is not set\n");
3319 goto out;
3320 }
3321
3322 addr = &priv->wlan_mac_addr;
3323 *num = addr->no_of_mac_addr_set;
3324 return &addr->mac_addr[0][0];
3325out:
3326 *num = 0;
3327 return NULL;
3328}
3329EXPORT_SYMBOL(icnss_get_wlan_mac_address);
3330
3331int icnss_trigger_recovery(struct device *dev)
3332{
3333 int ret = 0;
3334 struct icnss_priv *priv = dev_get_drvdata(dev);
3335
3336 if (priv->magic != ICNSS_MAGIC) {
3337 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3338 ret = -EINVAL;
3339 goto out;
3340 }
3341
3342 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3343 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3344 priv->state);
3345 ret = -EPERM;
3346 goto out;
3347 }
3348
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003349 if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003350 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3351 priv->state);
3352 ret = -EOPNOTSUPP;
3353 goto out;
3354 }
3355
3356 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3357 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3358 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003359 ret = -EINVAL;
3360 goto out;
3361 }
3362
Yuanyuan Liu68939762017-04-04 16:43:03 -07003363 WARN_ON(1);
3364 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3365 priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003366
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003367 /*
3368 * Initiate PDR, required only for the first instance
3369 */
3370 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3371 priv->service_notifier[0].instance_id);
3372
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003373 if (!ret)
3374 set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
3375
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003376out:
3377 return ret;
3378}
3379EXPORT_SYMBOL(icnss_trigger_recovery);
3380
3381
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003382static int icnss_smmu_init(struct icnss_priv *priv)
3383{
3384 struct dma_iommu_mapping *mapping;
3385 int atomic_ctx = 1;
3386 int s1_bypass = 1;
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303387 int fast = 1;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003388 int ret = 0;
3389
3390 icnss_pr_dbg("Initializing SMMU\n");
3391
3392 mapping = arm_iommu_create_mapping(&platform_bus_type,
3393 priv->smmu_iova_start,
3394 priv->smmu_iova_len);
3395 if (IS_ERR(mapping)) {
3396 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3397 ret = PTR_ERR(mapping);
3398 goto map_fail;
3399 }
3400
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303401 if (priv->bypass_s1_smmu) {
3402 ret = iommu_domain_set_attr(mapping->domain,
3403 DOMAIN_ATTR_S1_BYPASS,
3404 &s1_bypass);
3405 if (ret < 0) {
3406 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n",
3407 ret);
3408 goto set_attr_fail;
3409 }
3410 icnss_pr_dbg("SMMU S1 BYPASS\n");
3411 } else {
Yuanyuan Liu68939762017-04-04 16:43:03 -07003412 ret = iommu_domain_set_attr(mapping->domain,
3413 DOMAIN_ATTR_ATOMIC,
3414 &atomic_ctx);
3415 if (ret < 0) {
3416 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3417 ret);
3418 goto set_attr_fail;
3419 }
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303420 icnss_pr_dbg("SMMU ATTR ATOMIC\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003421
Hardik Kantilal Patele92672b2017-07-11 15:47:21 +05303422 ret = iommu_domain_set_attr(mapping->domain,
3423 DOMAIN_ATTR_FAST,
3424 &fast);
3425 if (ret < 0) {
3426 icnss_pr_err("Set fast map attribute failed, err = %d\n",
3427 ret);
3428 goto set_attr_fail;
3429 }
3430 icnss_pr_dbg("SMMU FAST map set\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003431 }
3432
3433 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3434 if (ret < 0) {
3435 icnss_pr_err("Attach device failed, err = %d\n", ret);
3436 goto attach_fail;
3437 }
3438
3439 priv->smmu_mapping = mapping;
3440
3441 return ret;
3442
3443attach_fail:
3444set_attr_fail:
3445 arm_iommu_release_mapping(mapping);
3446map_fail:
3447 return ret;
3448}
3449
3450static void icnss_smmu_deinit(struct icnss_priv *priv)
3451{
3452 if (!priv->smmu_mapping)
3453 return;
3454
3455 arm_iommu_detach_device(&priv->pdev->dev);
3456 arm_iommu_release_mapping(priv->smmu_mapping);
3457
3458 priv->smmu_mapping = NULL;
3459}
3460
Yuanyuan Liu68939762017-04-04 16:43:03 -07003461static int icnss_get_vreg_info(struct device *dev,
3462 struct icnss_vreg_info *vreg_info)
3463{
3464 int ret = 0;
3465 char prop_name[MAX_PROP_SIZE];
3466 struct regulator *reg;
3467 const __be32 *prop;
3468 int len = 0;
3469 int i;
3470
3471 reg = devm_regulator_get_optional(dev, vreg_info->name);
3472 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3473 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3474 vreg_info->name);
3475 ret = PTR_ERR(reg);
3476 goto out;
3477 }
3478
3479 if (IS_ERR(reg)) {
3480 ret = PTR_ERR(reg);
3481
3482 if (vreg_info->required) {
3483 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3484 vreg_info->name, ret);
3485 goto out;
3486 } else {
3487 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3488 vreg_info->name, ret);
3489 goto done;
3490 }
3491 }
3492
3493 vreg_info->reg = reg;
3494
3495 snprintf(prop_name, MAX_PROP_SIZE,
3496 "qcom,%s-config", vreg_info->name);
3497
3498 prop = of_get_property(dev->of_node, prop_name, &len);
3499
3500 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3501 prop_name, len);
3502
3503 if (!prop || len < (2 * sizeof(__be32))) {
3504 icnss_pr_dbg("Property %s %s\n", prop_name,
3505 prop ? "invalid format" : "doesn't exist");
3506 goto done;
3507 }
3508
3509 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3510 switch (i) {
3511 case 0:
3512 vreg_info->min_v = be32_to_cpup(&prop[0]);
3513 break;
3514 case 1:
3515 vreg_info->max_v = be32_to_cpup(&prop[1]);
3516 break;
3517 case 2:
3518 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3519 break;
3520 case 3:
3521 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3522 break;
3523 default:
3524 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3525 prop_name, i);
3526 break;
3527 }
3528 }
3529
3530done:
3531 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3532 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3533 vreg_info->load_ua, vreg_info->settle_delay);
3534
3535 return 0;
3536
3537out:
3538 return ret;
3539}
3540
3541static int icnss_get_clk_info(struct device *dev,
3542 struct icnss_clk_info *clk_info)
3543{
3544 struct clk *handle;
3545 int ret = 0;
3546
3547 handle = devm_clk_get(dev, clk_info->name);
3548 if (IS_ERR(handle)) {
3549 ret = PTR_ERR(handle);
3550 if (clk_info->required) {
3551 icnss_pr_err("Clock %s isn't available: %d\n",
3552 clk_info->name, ret);
3553 goto out;
3554 } else {
3555 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3556 ret);
3557 ret = 0;
3558 goto out;
3559 }
3560 }
3561
3562 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3563
3564 clk_info->handle = handle;
3565out:
3566 return ret;
3567}
3568
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003569static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003570{
3571 struct icnss_priv *priv = s->private;
3572
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003573 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003574
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003575 seq_puts(s, "\nCMD: test_mode\n");
3576 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3577 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3578 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003579 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003580
3581 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3582 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003583
3584 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003585 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003586 goto out;
3587 }
3588
3589 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003590 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003591 goto out;
3592 }
3593
3594 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003595 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003596 goto out;
3597 }
3598
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003599out:
3600 seq_puts(s, "\n");
3601 return 0;
3602}
3603
3604static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3605{
3606 int ret;
3607
3608 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3609 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3610 priv->state);
3611 ret = -ENODEV;
3612 goto out;
3613 }
3614
3615 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3616 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3617 priv->state);
3618 ret = -EINVAL;
3619 goto out;
3620 }
3621
3622 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3623 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3624 priv->state);
3625 ret = -EINVAL;
3626 goto out;
3627 }
3628
3629 icnss_wlan_disable(ICNSS_OFF);
3630
3631 ret = icnss_hw_power_off(priv);
3632
3633 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3634
3635out:
3636 return ret;
3637}
3638static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3639 enum icnss_driver_mode mode)
3640{
3641 int ret;
3642
3643 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3644 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3645 priv->state);
3646 ret = -ENODEV;
3647 goto out;
3648 }
3649
3650 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3651 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3652 priv->state);
3653 ret = -EINVAL;
3654 goto out;
3655 }
3656
3657 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3658 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3659 priv->state);
3660 ret = -EBUSY;
3661 goto out;
3662 }
3663
3664 ret = icnss_hw_power_on(priv);
3665 if (ret)
3666 goto out;
3667
3668 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3669
3670 ret = icnss_wlan_enable(NULL, mode, NULL);
3671 if (ret)
3672 goto power_off;
3673
3674 return 0;
3675
3676power_off:
3677 icnss_hw_power_off(priv);
3678 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3679
3680out:
3681 return ret;
3682}
3683
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003684static ssize_t icnss_fw_debug_write(struct file *fp,
3685 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003686 size_t count, loff_t *off)
3687{
3688 struct icnss_priv *priv =
3689 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003690 char buf[64];
3691 char *sptr, *token;
3692 unsigned int len = 0;
3693 char *cmd;
3694 uint64_t val;
3695 const char *delim = " ";
3696 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003697
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003698 len = min(count, sizeof(buf) - 1);
3699 if (copy_from_user(buf, user_buf, len))
3700 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003701
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003702 buf[len] = '\0';
3703 sptr = buf;
3704
3705 token = strsep(&sptr, delim);
3706 if (!token)
3707 return -EINVAL;
3708 if (!sptr)
3709 return -EINVAL;
3710 cmd = token;
3711
3712 token = strsep(&sptr, delim);
3713 if (!token)
3714 return -EINVAL;
3715 if (kstrtou64(token, 0, &val))
3716 return -EINVAL;
3717
3718 if (strcmp(cmd, "test_mode") == 0) {
3719 switch (val) {
3720 case 0:
3721 ret = icnss_test_mode_fw_test_off(priv);
3722 break;
3723 case 1:
3724 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3725 break;
3726 case 2:
3727 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3728 break;
3729 case 3:
3730 ret = icnss_trigger_recovery(&priv->pdev->dev);
3731 break;
3732 default:
3733 return -EINVAL;
3734 }
3735 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3736 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3737 } else {
3738 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003739 }
3740
3741 if (ret)
3742 return ret;
3743
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003744 return count;
3745}
3746
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003747static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003748{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003749 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003750}
3751
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003752static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003753 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003754 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003755 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003756 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003757 .owner = THIS_MODULE,
3758 .llseek = seq_lseek,
3759};
3760
3761static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3762 size_t count, loff_t *off)
3763{
3764 struct icnss_priv *priv =
3765 ((struct seq_file *)fp->private_data)->private;
3766 int ret;
3767 u32 val;
3768
3769 ret = kstrtou32_from_user(buf, count, 0, &val);
3770 if (ret)
3771 return ret;
3772
3773 if (ret == 0)
3774 memset(&priv->stats, 0, sizeof(priv->stats));
3775
3776 return count;
3777}
3778
3779static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3780{
3781 enum icnss_driver_state i;
3782 int skip = 0;
3783 unsigned long state;
3784
3785 seq_printf(s, "\nState: 0x%lx(", priv->state);
3786 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3787
3788 if (!(state & 0x1))
3789 continue;
3790
3791 if (skip++)
3792 seq_puts(s, " | ");
3793
3794 switch (i) {
3795 case ICNSS_WLFW_QMI_CONNECTED:
3796 seq_puts(s, "QMI CONN");
3797 continue;
3798 case ICNSS_POWER_ON:
3799 seq_puts(s, "POWER ON");
3800 continue;
3801 case ICNSS_FW_READY:
3802 seq_puts(s, "FW READY");
3803 continue;
3804 case ICNSS_DRIVER_PROBED:
3805 seq_puts(s, "DRIVER PROBED");
3806 continue;
3807 case ICNSS_FW_TEST_MODE:
3808 seq_puts(s, "FW TEST MODE");
3809 continue;
3810 case ICNSS_PM_SUSPEND:
3811 seq_puts(s, "PM SUSPEND");
3812 continue;
3813 case ICNSS_PM_SUSPEND_NOIRQ:
3814 seq_puts(s, "PM SUSPEND NOIRQ");
3815 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003816 case ICNSS_SSR_REGISTERED:
3817 seq_puts(s, "SSR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003818 continue;
Yuanyuan Liua0fd3f52017-06-07 11:19:22 -07003819 case ICNSS_PDR_REGISTERED:
3820 seq_puts(s, "PDR REGISTERED");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003821 continue;
3822 case ICNSS_PD_RESTART:
3823 seq_puts(s, "PD RESTART");
3824 continue;
3825 case ICNSS_MSA0_ASSIGNED:
3826 seq_puts(s, "MSA0 ASSIGNED");
3827 continue;
3828 case ICNSS_WLFW_EXISTS:
3829 seq_puts(s, "WLAN FW EXISTS");
3830 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303831 case ICNSS_SHUTDOWN_DONE:
3832 seq_puts(s, "SHUTDOWN DONE");
3833 continue;
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003834 case ICNSS_HOST_TRIGGERED_PDR:
3835 seq_puts(s, "HOST TRIGGERED PDR");
3836 continue;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003837 }
3838
3839 seq_printf(s, "UNKNOWN-%d", i);
3840 }
3841 seq_puts(s, ")\n");
3842
3843 return 0;
3844}
3845
3846static int icnss_stats_show_capability(struct seq_file *s,
3847 struct icnss_priv *priv)
3848{
3849 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3850 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3851 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3852 seq_printf(s, "Chip family: 0x%x\n",
3853 priv->chip_info.chip_family);
3854 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3855 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3856 seq_printf(s, "Firmware Version: 0x%x\n",
3857 priv->fw_version_info.fw_version);
3858 seq_printf(s, "Firmware Build Timestamp: %s\n",
3859 priv->fw_version_info.fw_build_timestamp);
3860 seq_printf(s, "Firmware Build ID: %s\n",
3861 priv->fw_build_id);
3862 }
3863
3864 return 0;
3865}
3866
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003867static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
3868 struct icnss_priv *priv)
3869{
3870 if (priv->stats.rejuvenate_ind) {
3871 seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
3872 seq_printf(s, "Number of Rejuvenations: %u\n",
3873 priv->stats.rejuvenate_ind);
3874 seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
3875 priv->cause_for_rejuvenation);
3876 seq_printf(s, "Requesting Sub-System: 0x%x\n",
3877 priv->requesting_sub_system);
3878 seq_printf(s, "Line Number: %u\n",
3879 priv->line_number);
3880 seq_printf(s, "Function Name: %s\n",
3881 priv->function_name);
3882 }
3883
3884 return 0;
3885}
3886
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003887static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3888{
3889 int i;
3890
3891 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3892 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3893 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3894 seq_printf(s, "%24s %16u %16u\n",
3895 icnss_driver_event_to_str(i),
3896 priv->stats.events[i].posted,
3897 priv->stats.events[i].processed);
3898
3899 return 0;
3900}
3901
3902static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3903{
3904 int i;
3905
3906 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3907 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3908 "Free", "Enable", "Disable");
3909 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3910 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3911 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3912 priv->stats.ce_irqs[i].free,
3913 priv->stats.ce_irqs[i].enable,
3914 priv->stats.ce_irqs[i].disable);
3915
3916 return 0;
3917}
3918
3919static int icnss_stats_show(struct seq_file *s, void *data)
3920{
3921#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3922 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3923
3924 struct icnss_priv *priv = s->private;
3925
3926 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3927 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3928 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3929 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3930 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3931 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3932 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3933 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3934 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3935 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3936 ICNSS_STATS_DUMP(s, priv, cap_req);
3937 ICNSS_STATS_DUMP(s, priv, cap_resp);
3938 ICNSS_STATS_DUMP(s, priv, cap_err);
3939 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3940 ICNSS_STATS_DUMP(s, priv, cfg_req);
3941 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3942 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3943 ICNSS_STATS_DUMP(s, priv, mode_req);
3944 ICNSS_STATS_DUMP(s, priv, mode_resp);
3945 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3946 ICNSS_STATS_DUMP(s, priv, ini_req);
3947 ICNSS_STATS_DUMP(s, priv, ini_resp);
3948 ICNSS_STATS_DUMP(s, priv, ini_req_err);
3949 ICNSS_STATS_DUMP(s, priv, vbatt_req);
3950 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
3951 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003952 ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003953 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
3954 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
3955 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Sameer Thalappil0aaa7582017-06-23 15:43:43 -07003956 ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
3957 ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
3958 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
3959 ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003960
3961 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
3962 ICNSS_STATS_DUMP(s, priv, pm_suspend);
3963 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
3964 ICNSS_STATS_DUMP(s, priv, pm_resume);
3965 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
3966 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
3967 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
3968 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
3969 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
3970 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
3971 ICNSS_STATS_DUMP(s, priv, pm_relax);
3972
3973 icnss_stats_show_irqs(s, priv);
3974
3975 icnss_stats_show_capability(s, priv);
3976
Yuanyuan Liu4c497ff2016-12-16 16:36:08 -08003977 icnss_stats_show_rejuvenate_info(s, priv);
3978
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003979 icnss_stats_show_events(s, priv);
3980
3981 icnss_stats_show_state(s, priv);
3982
3983 return 0;
3984#undef ICNSS_STATS_DUMP
3985}
3986
3987static int icnss_stats_open(struct inode *inode, struct file *file)
3988{
3989 return single_open(file, icnss_stats_show, inode->i_private);
3990}
3991
3992static const struct file_operations icnss_stats_fops = {
3993 .read = seq_read,
3994 .write = icnss_stats_write,
3995 .release = single_release,
3996 .open = icnss_stats_open,
3997 .owner = THIS_MODULE,
3998 .llseek = seq_lseek,
3999};
4000
4001static int icnss_regwrite_show(struct seq_file *s, void *data)
4002{
4003 struct icnss_priv *priv = s->private;
4004
4005 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
4006
4007 if (!test_bit(ICNSS_FW_READY, &priv->state))
4008 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4009
4010 return 0;
4011}
4012
4013static ssize_t icnss_regwrite_write(struct file *fp,
4014 const char __user *user_buf,
4015 size_t count, loff_t *off)
4016{
4017 struct icnss_priv *priv =
4018 ((struct seq_file *)fp->private_data)->private;
4019 char buf[64];
4020 char *sptr, *token;
4021 unsigned int len = 0;
4022 uint32_t reg_offset, mem_type, reg_val;
4023 const char *delim = " ";
4024 int ret = 0;
4025
4026 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4027 !test_bit(ICNSS_POWER_ON, &priv->state))
4028 return -EINVAL;
4029
4030 len = min(count, sizeof(buf) - 1);
4031 if (copy_from_user(buf, user_buf, len))
4032 return -EFAULT;
4033
4034 buf[len] = '\0';
4035 sptr = buf;
4036
4037 token = strsep(&sptr, delim);
4038 if (!token)
4039 return -EINVAL;
4040
4041 if (!sptr)
4042 return -EINVAL;
4043
4044 if (kstrtou32(token, 0, &mem_type))
4045 return -EINVAL;
4046
4047 token = strsep(&sptr, delim);
4048 if (!token)
4049 return -EINVAL;
4050
4051 if (!sptr)
4052 return -EINVAL;
4053
4054 if (kstrtou32(token, 0, &reg_offset))
4055 return -EINVAL;
4056
4057 token = strsep(&sptr, delim);
4058 if (!token)
4059 return -EINVAL;
4060
4061 if (kstrtou32(token, 0, &reg_val))
4062 return -EINVAL;
4063
4064 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
4065 sizeof(uint32_t),
4066 (uint8_t *)&reg_val);
4067 if (ret)
4068 return ret;
4069
4070 return count;
4071}
4072
4073static int icnss_regwrite_open(struct inode *inode, struct file *file)
4074{
4075 return single_open(file, icnss_regwrite_show, inode->i_private);
4076}
4077
4078static const struct file_operations icnss_regwrite_fops = {
4079 .read = seq_read,
4080 .write = icnss_regwrite_write,
4081 .open = icnss_regwrite_open,
4082 .owner = THIS_MODULE,
4083 .llseek = seq_lseek,
4084};
4085
4086static int icnss_regread_show(struct seq_file *s, void *data)
4087{
4088 struct icnss_priv *priv = s->private;
4089
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304090 mutex_lock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004091 if (!priv->diag_reg_read_buf) {
4092 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
4093
4094 if (!test_bit(ICNSS_FW_READY, &priv->state))
4095 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
4096
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304097 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004098 return 0;
4099 }
4100
4101 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
4102 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
4103 priv->diag_reg_read_len);
4104
4105 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
4106 priv->diag_reg_read_len, false);
4107
4108 priv->diag_reg_read_len = 0;
4109 kfree(priv->diag_reg_read_buf);
4110 priv->diag_reg_read_buf = NULL;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304111 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004112
4113 return 0;
4114}
4115
4116static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
4117 size_t count, loff_t *off)
4118{
4119 struct icnss_priv *priv =
4120 ((struct seq_file *)fp->private_data)->private;
4121 char buf[64];
4122 char *sptr, *token;
4123 unsigned int len = 0;
4124 uint32_t reg_offset, mem_type;
4125 uint32_t data_len = 0;
4126 uint8_t *reg_buf = NULL;
4127 const char *delim = " ";
4128 int ret = 0;
4129
4130 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
4131 !test_bit(ICNSS_POWER_ON, &priv->state))
4132 return -EINVAL;
4133
4134 len = min(count, sizeof(buf) - 1);
4135 if (copy_from_user(buf, user_buf, len))
4136 return -EFAULT;
4137
4138 buf[len] = '\0';
4139 sptr = buf;
4140
4141 token = strsep(&sptr, delim);
4142 if (!token)
4143 return -EINVAL;
4144
4145 if (!sptr)
4146 return -EINVAL;
4147
4148 if (kstrtou32(token, 0, &mem_type))
4149 return -EINVAL;
4150
4151 token = strsep(&sptr, delim);
4152 if (!token)
4153 return -EINVAL;
4154
4155 if (!sptr)
4156 return -EINVAL;
4157
4158 if (kstrtou32(token, 0, &reg_offset))
4159 return -EINVAL;
4160
4161 token = strsep(&sptr, delim);
4162 if (!token)
4163 return -EINVAL;
4164
4165 if (kstrtou32(token, 0, &data_len))
4166 return -EINVAL;
4167
4168 if (data_len == 0 ||
4169 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
4170 return -EINVAL;
4171
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304172 mutex_lock(&priv->dev_lock);
Yuanyuan Liu5347b0c2017-05-24 11:57:37 -07004173 kfree(priv->diag_reg_read_buf);
4174 priv->diag_reg_read_buf = NULL;
4175
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004176 reg_buf = kzalloc(data_len, GFP_KERNEL);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304177 if (!reg_buf) {
4178 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004179 return -ENOMEM;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304180 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004181
4182 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
4183 mem_type, data_len,
4184 reg_buf);
4185 if (ret) {
4186 kfree(reg_buf);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304187 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004188 return ret;
4189 }
4190
4191 priv->diag_reg_read_addr = reg_offset;
4192 priv->diag_reg_read_mem_type = mem_type;
4193 priv->diag_reg_read_len = data_len;
4194 priv->diag_reg_read_buf = reg_buf;
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304195 mutex_unlock(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004196
4197 return count;
4198}
4199
4200static int icnss_regread_open(struct inode *inode, struct file *file)
4201{
4202 return single_open(file, icnss_regread_show, inode->i_private);
4203}
4204
4205static const struct file_operations icnss_regread_fops = {
4206 .read = seq_read,
4207 .write = icnss_regread_write,
4208 .open = icnss_regread_open,
4209 .owner = THIS_MODULE,
4210 .llseek = seq_lseek,
4211};
4212
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004213#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004214static int icnss_debugfs_create(struct icnss_priv *priv)
4215{
4216 int ret = 0;
4217 struct dentry *root_dentry;
4218
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004219 root_dentry = debugfs_create_dir("icnss", NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004220
4221 if (IS_ERR(root_dentry)) {
4222 ret = PTR_ERR(root_dentry);
4223 icnss_pr_err("Unable to create debugfs %d\n", ret);
4224 goto out;
4225 }
4226
4227 priv->root_dentry = root_dentry;
4228
Yuanyuan Liu84132752017-05-24 12:07:12 -07004229 debugfs_create_file("fw_debug", 0600, root_dentry, priv,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004230 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004231
Yuanyuan Liu84132752017-05-24 12:07:12 -07004232 debugfs_create_file("stats", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004233 &icnss_stats_fops);
4234 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4235 &icnss_regread_fops);
Yuanyuan Liu84132752017-05-24 12:07:12 -07004236 debugfs_create_file("reg_write", 0600, root_dentry, priv,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004237 &icnss_regwrite_fops);
4238
4239out:
4240 return ret;
4241}
Yuanyuan Liuc74489f2017-05-24 12:13:49 -07004242#else
4243static int icnss_debugfs_create(struct icnss_priv *priv)
4244{
4245 int ret = 0;
4246 struct dentry *root_dentry;
4247
4248 root_dentry = debugfs_create_dir("icnss", NULL);
4249
4250 if (IS_ERR(root_dentry)) {
4251 ret = PTR_ERR(root_dentry);
4252 icnss_pr_err("Unable to create debugfs %d\n", ret);
4253 return ret;
4254 }
4255
4256 priv->root_dentry = root_dentry;
4257
4258 debugfs_create_file("stats", 0600, root_dentry, priv,
4259 &icnss_stats_fops);
4260 return 0;
4261}
4262#endif
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004263
4264static void icnss_debugfs_destroy(struct icnss_priv *priv)
4265{
4266 debugfs_remove_recursive(priv->root_dentry);
4267}
4268
4269static int icnss_get_vbatt_info(struct icnss_priv *priv)
4270{
4271 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4272 struct qpnp_vadc_chip *vadc_dev = NULL;
4273 int ret = 0;
4274
4275 if (test_bit(VBATT_DISABLE, &quirks)) {
4276 icnss_pr_dbg("VBATT feature is disabled\n");
4277 return ret;
4278 }
4279
4280 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4281 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4282 icnss_pr_err("adc_tm_dev probe defer\n");
4283 return -EPROBE_DEFER;
4284 }
4285
4286 if (IS_ERR(adc_tm_dev)) {
4287 ret = PTR_ERR(adc_tm_dev);
4288 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4289 ret);
4290 return ret;
4291 }
4292
4293 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4294 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4295 icnss_pr_err("vadc_dev probe defer\n");
4296 return -EPROBE_DEFER;
4297 }
4298
4299 if (IS_ERR(vadc_dev)) {
4300 ret = PTR_ERR(vadc_dev);
4301 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4302 ret);
4303 return ret;
4304 }
4305
4306 priv->adc_tm_dev = adc_tm_dev;
4307 priv->vadc_dev = vadc_dev;
4308
4309 return 0;
4310}
4311
4312static int icnss_probe(struct platform_device *pdev)
4313{
4314 int ret = 0;
4315 struct resource *res;
4316 int i;
4317 struct device *dev = &pdev->dev;
4318 struct icnss_priv *priv;
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304319 const __be32 *addrp;
4320 u64 prop_size = 0;
4321 struct device_node *np;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004322
4323 if (penv) {
4324 icnss_pr_err("Driver is already initialized\n");
4325 return -EEXIST;
4326 }
4327
4328 icnss_pr_dbg("Platform driver probe\n");
4329
4330 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4331 if (!priv)
4332 return -ENOMEM;
4333
4334 priv->magic = ICNSS_MAGIC;
4335 dev_set_drvdata(dev, priv);
4336
4337 priv->pdev = pdev;
4338
4339 ret = icnss_get_vbatt_info(priv);
4340 if (ret == -EPROBE_DEFER)
4341 goto out;
4342
Yuanyuan Liu68939762017-04-04 16:43:03 -07004343 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4344 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4345 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4346
4347 if (ret)
4348 goto out;
4349 }
4350
4351 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4352 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4353 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4354 if (ret)
4355 goto out;
4356 }
4357
4358 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4359 priv->bypass_s1_smmu = true;
4360
4361 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4362
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004363 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4364 if (!res) {
4365 icnss_pr_err("Memory base not found in DT\n");
4366 ret = -EINVAL;
4367 goto out;
4368 }
4369
4370 priv->mem_base_pa = res->start;
4371 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4372 resource_size(res));
4373 if (!priv->mem_base_va) {
4374 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4375 &priv->mem_base_pa);
4376 ret = -EINVAL;
4377 goto out;
4378 }
4379 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4380 priv->mem_base_va);
4381
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004382 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4383 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4384 if (!res) {
4385 icnss_pr_err("Fail to get IRQ-%d\n", i);
4386 ret = -ENODEV;
4387 goto out;
4388 } else {
4389 priv->ce_irqs[i] = res->start;
4390 }
4391 }
4392
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304393 np = of_parse_phandle(dev->of_node,
4394 "qcom,wlan-msa-fixed-region", 0);
4395 if (np) {
4396 addrp = of_get_address(np, 0, &prop_size, NULL);
4397 if (!addrp) {
4398 icnss_pr_err("Failed to get assigned-addresses or property\n");
4399 ret = -EINVAL;
4400 goto out;
4401 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004402
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304403 priv->msa_pa = of_translate_address(np, addrp);
4404 if (priv->msa_pa == OF_BAD_ADDR) {
4405 icnss_pr_err("Failed to translate MSA PA from device-tree\n");
4406 ret = -EINVAL;
4407 goto out;
4408 }
4409
4410 priv->msa_va = memremap(priv->msa_pa,
4411 (unsigned long)prop_size, MEMREMAP_WT);
4412 if (!priv->msa_va) {
4413 icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
4414 &priv->msa_pa);
4415 ret = -EINVAL;
4416 goto out;
4417 }
4418 priv->msa_mem_size = prop_size;
4419 } else {
4420 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4421 &priv->msa_mem_size);
4422 if (ret || priv->msa_mem_size == 0) {
4423 icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
4424 priv->msa_mem_size, ret);
4425 goto out;
4426 }
4427
4428 priv->msa_va = dmam_alloc_coherent(&pdev->dev,
4429 priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
4430
4431 if (!priv->msa_va) {
4432 icnss_pr_err("DMA alloc failed for MSA\n");
4433 ret = -ENOMEM;
4434 goto out;
4435 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004436 }
4437
Hardik Kantilal Patel420482e2017-05-05 16:57:48 +05304438 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p MSA Memory Size: 0x%x\n",
4439 &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004440
4441 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4442 "smmu_iova_base");
4443 if (!res) {
4444 icnss_pr_err("SMMU IOVA base not found\n");
4445 } else {
4446 priv->smmu_iova_start = res->start;
4447 priv->smmu_iova_len = resource_size(res);
4448 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4449 &priv->smmu_iova_start, priv->smmu_iova_len);
4450
4451 res = platform_get_resource_byname(pdev,
4452 IORESOURCE_MEM,
4453 "smmu_iova_ipa");
4454 if (!res) {
4455 icnss_pr_err("SMMU IOVA IPA not found\n");
4456 } else {
4457 priv->smmu_iova_ipa_start = res->start;
4458 priv->smmu_iova_ipa_len = resource_size(res);
4459 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4460 &priv->smmu_iova_ipa_start,
4461 priv->smmu_iova_ipa_len);
4462 }
4463
4464 ret = icnss_smmu_init(priv);
4465 if (ret < 0) {
4466 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4467 ret, &priv->smmu_iova_start,
4468 priv->smmu_iova_len);
4469 goto out;
4470 }
4471 }
4472
4473 spin_lock_init(&priv->event_lock);
4474 spin_lock_init(&priv->on_off_lock);
Sarada Prasanna Garnayakb728ff02017-06-08 15:34:13 +05304475 mutex_init(&priv->dev_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004476
4477 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4478 if (!priv->event_wq) {
4479 icnss_pr_err("Workqueue creation failed\n");
4480 ret = -EFAULT;
4481 goto out_smmu_deinit;
4482 }
4483
4484 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4485 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4486 INIT_LIST_HEAD(&priv->event_list);
4487
4488 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4489 WLFW_SERVICE_VERS_V01,
4490 WLFW_SERVICE_INS_ID_V01,
4491 &wlfw_clnt_nb);
4492 if (ret < 0) {
4493 icnss_pr_err("Notifier register failed: %d\n", ret);
4494 goto out_destroy_wq;
4495 }
4496
4497 icnss_enable_recovery(priv);
4498
4499 icnss_debugfs_create(priv);
4500
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004501 ret = device_init_wakeup(&priv->pdev->dev, true);
4502 if (ret)
4503 icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
4504 ret);
4505
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004506 penv = priv;
4507
4508 icnss_pr_info("Platform driver probed successfully\n");
4509
4510 return 0;
4511
4512out_destroy_wq:
4513 destroy_workqueue(priv->event_wq);
4514out_smmu_deinit:
4515 icnss_smmu_deinit(priv);
4516out:
4517 dev_set_drvdata(dev, NULL);
4518
4519 return ret;
4520}
4521
4522static int icnss_remove(struct platform_device *pdev)
4523{
4524 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4525
Yuanyuan Liuc1ad9282017-06-07 17:39:54 -07004526 device_init_wakeup(&penv->pdev->dev, false);
4527
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004528 icnss_debugfs_destroy(penv);
4529
4530 icnss_modem_ssr_unregister_notifier(penv);
4531
4532 destroy_ramdump_device(penv->msa0_dump_dev);
4533
4534 icnss_pdr_unregister_notifier(penv);
4535
4536 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4537 WLFW_SERVICE_VERS_V01,
4538 WLFW_SERVICE_INS_ID_V01,
4539 &wlfw_clnt_nb);
4540 if (penv->event_wq)
4541 destroy_workqueue(penv->event_wq);
4542
4543 icnss_hw_power_off(penv);
4544
Anurag Chouhanfacf3922017-06-08 18:26:56 +05304545 icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
4546 clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004547
4548 dev_set_drvdata(&pdev->dev, NULL);
4549
4550 return 0;
4551}
4552
4553#ifdef CONFIG_PM_SLEEP
4554static int icnss_pm_suspend(struct device *dev)
4555{
4556 struct icnss_priv *priv = dev_get_drvdata(dev);
4557 int ret = 0;
4558
4559 if (priv->magic != ICNSS_MAGIC) {
4560 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4561 dev, priv, priv->magic);
4562 return -EINVAL;
4563 }
4564
Yuanyuan Liu68939762017-04-04 16:43:03 -07004565 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004566
4567 if (!priv->ops || !priv->ops->pm_suspend ||
4568 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4569 goto out;
4570
4571 ret = priv->ops->pm_suspend(dev);
4572
4573out:
4574 if (ret == 0) {
4575 priv->stats.pm_suspend++;
4576 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4577 } else {
4578 priv->stats.pm_suspend_err++;
4579 }
4580 return ret;
4581}
4582
4583static int icnss_pm_resume(struct device *dev)
4584{
4585 struct icnss_priv *priv = dev_get_drvdata(dev);
4586 int ret = 0;
4587
4588 if (priv->magic != ICNSS_MAGIC) {
4589 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4590 dev, priv, priv->magic);
4591 return -EINVAL;
4592 }
4593
Yuanyuan Liu68939762017-04-04 16:43:03 -07004594 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004595
4596 if (!priv->ops || !priv->ops->pm_resume ||
4597 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4598 goto out;
4599
4600 ret = priv->ops->pm_resume(dev);
4601
4602out:
4603 if (ret == 0) {
4604 priv->stats.pm_resume++;
4605 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4606 } else {
4607 priv->stats.pm_resume_err++;
4608 }
4609 return ret;
4610}
4611
4612static int icnss_pm_suspend_noirq(struct device *dev)
4613{
4614 struct icnss_priv *priv = dev_get_drvdata(dev);
4615 int ret = 0;
4616
4617 if (priv->magic != ICNSS_MAGIC) {
4618 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4619 dev, priv, priv->magic);
4620 return -EINVAL;
4621 }
4622
Yuanyuan Liu68939762017-04-04 16:43:03 -07004623 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004624
4625 if (!priv->ops || !priv->ops->suspend_noirq ||
4626 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4627 goto out;
4628
4629 ret = priv->ops->suspend_noirq(dev);
4630
4631out:
4632 if (ret == 0) {
4633 priv->stats.pm_suspend_noirq++;
4634 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4635 } else {
4636 priv->stats.pm_suspend_noirq_err++;
4637 }
4638 return ret;
4639}
4640
4641static int icnss_pm_resume_noirq(struct device *dev)
4642{
4643 struct icnss_priv *priv = dev_get_drvdata(dev);
4644 int ret = 0;
4645
4646 if (priv->magic != ICNSS_MAGIC) {
4647 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4648 dev, priv, priv->magic);
4649 return -EINVAL;
4650 }
4651
Yuanyuan Liu68939762017-04-04 16:43:03 -07004652 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004653
4654 if (!priv->ops || !priv->ops->resume_noirq ||
4655 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4656 goto out;
4657
4658 ret = priv->ops->resume_noirq(dev);
4659
4660out:
4661 if (ret == 0) {
4662 priv->stats.pm_resume_noirq++;
4663 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4664 } else {
4665 priv->stats.pm_resume_noirq_err++;
4666 }
4667 return ret;
4668}
4669#endif
4670
4671static const struct dev_pm_ops icnss_pm_ops = {
4672 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4673 icnss_pm_resume)
4674 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4675 icnss_pm_resume_noirq)
4676};
4677
4678static const struct of_device_id icnss_dt_match[] = {
4679 {.compatible = "qcom,icnss"},
4680 {}
4681};
4682
4683MODULE_DEVICE_TABLE(of, icnss_dt_match);
4684
4685static struct platform_driver icnss_driver = {
4686 .probe = icnss_probe,
4687 .remove = icnss_remove,
4688 .driver = {
4689 .name = "icnss",
4690 .pm = &icnss_pm_ops,
4691 .owner = THIS_MODULE,
4692 .of_match_table = icnss_dt_match,
4693 },
4694};
4695
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004696static int __init icnss_initialize(void)
4697{
4698 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4699 "icnss", 0);
4700 if (!icnss_ipc_log_context)
4701 icnss_pr_err("Unable to create log context\n");
4702
Yuanyuan Liu68939762017-04-04 16:43:03 -07004703 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4704 "icnss_long", 0);
4705 if (!icnss_ipc_log_long_context)
4706 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004707
4708 return platform_driver_register(&icnss_driver);
4709}
4710
4711static void __exit icnss_exit(void)
4712{
4713 platform_driver_unregister(&icnss_driver);
4714 ipc_log_context_destroy(icnss_ipc_log_context);
4715 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004716 ipc_log_context_destroy(icnss_ipc_log_long_context);
4717 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004718}
4719
4720
4721module_init(icnss_initialize);
4722module_exit(icnss_exit);
4723
4724MODULE_LICENSE("GPL v2");
4725MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");