blob: 25bb1999162ed0dbb6abd8883e6d5f9b7ce29ae2 [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>
16#include <linux/clk.h>
17#include <linux/iommu.h>
18#include <linux/export.h>
19#include <linux/err.h>
20#include <linux/of.h>
21#include <linux/init.h>
22#include <linux/io.h>
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/debugfs.h>
26#include <linux/seq_file.h>
27#include <linux/slab.h>
28#include <linux/platform_device.h>
29#include <linux/regulator/consumer.h>
30#include <linux/interrupt.h>
31#include <linux/sched.h>
32#include <linux/delay.h>
33#include <linux/dma-mapping.h>
34#include <linux/qmi_encdec.h>
35#include <linux/ipc_logging.h>
36#include <linux/thread_info.h>
37#include <linux/uaccess.h>
38#include <linux/qpnp/qpnp-adc.h>
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080039#include <linux/etherdevice.h>
Yuanyuan Liu607051c2016-11-28 17:04:13 -080040#include <soc/qcom/memory_dump.h>
41#include <soc/qcom/icnss.h>
42#include <soc/qcom/msm_qmi_interface.h>
43#include <soc/qcom/secure_buffer.h>
44#include <soc/qcom/subsystem_notif.h>
45#include <soc/qcom/subsystem_restart.h>
46#include <soc/qcom/service-locator.h>
47#include <soc/qcom/service-notifier.h>
48#include <soc/qcom/socinfo.h>
49#include <soc/qcom/ramdump.h>
50
51#include "wlan_firmware_service_v01.h"
52
53#ifdef CONFIG_ICNSS_DEBUG
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080054unsigned long qmi_timeout = 10000;
Yuanyuan Liu607051c2016-11-28 17:04:13 -080055module_param(qmi_timeout, ulong, 0600);
56
57#define WLFW_TIMEOUT_MS qmi_timeout
58#else
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -080059#define WLFW_TIMEOUT_MS 10000
Yuanyuan Liu607051c2016-11-28 17:04:13 -080060#endif
61#define WLFW_SERVICE_INS_ID_V01 0
62#define WLFW_CLIENT_ID 0x4b4e454c
63#define MAX_PROP_SIZE 32
64#define NUM_LOG_PAGES 10
Yuanyuan Liu68939762017-04-04 16:43:03 -070065#define NUM_LOG_LONG_PAGES 4
Yuanyuan Liu607051c2016-11-28 17:04:13 -080066#define ICNSS_MAGIC 0x5abc5abc
67
Yuanyuan Liu607051c2016-11-28 17:04:13 -080068#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
69#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
70
71#define ICNSS_THRESHOLD_HIGH 3600000
72#define ICNSS_THRESHOLD_LOW 3450000
73#define ICNSS_THRESHOLD_GUARD 20000
74
75#define icnss_ipc_log_string(_x...) do { \
76 if (icnss_ipc_log_context) \
77 ipc_log_string(icnss_ipc_log_context, _x); \
78 } while (0)
79
Yuanyuan Liu607051c2016-11-28 17:04:13 -080080#define icnss_ipc_log_long_string(_x...) do { \
81 if (icnss_ipc_log_long_context) \
82 ipc_log_string(icnss_ipc_log_long_context, _x); \
83 } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -080084
85#define icnss_pr_err(_fmt, ...) do { \
86 pr_err(_fmt, ##__VA_ARGS__); \
87 icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \
88 ##__VA_ARGS__); \
89 } while (0)
90
91#define icnss_pr_warn(_fmt, ...) do { \
92 pr_warn(_fmt, ##__VA_ARGS__); \
93 icnss_ipc_log_string("WRN: " pr_fmt(_fmt), \
94 ##__VA_ARGS__); \
95 } while (0)
96
97#define icnss_pr_info(_fmt, ...) do { \
98 pr_info(_fmt, ##__VA_ARGS__); \
99 icnss_ipc_log_string("INF: " pr_fmt(_fmt), \
100 ##__VA_ARGS__); \
101 } while (0)
102
103#define icnss_pr_dbg(_fmt, ...) do { \
104 pr_debug(_fmt, ##__VA_ARGS__); \
105 icnss_ipc_log_string("DBG: " pr_fmt(_fmt), \
106 ##__VA_ARGS__); \
107 } while (0)
108
Yuanyuan Liu68939762017-04-04 16:43:03 -0700109#define icnss_pr_vdbg(_fmt, ...) do { \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800110 pr_debug(_fmt, ##__VA_ARGS__); \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700111 icnss_ipc_log_long_string("DBG: " pr_fmt(_fmt), \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800112 ##__VA_ARGS__); \
113 } while (0)
114
115#ifdef CONFIG_ICNSS_DEBUG
116#define ICNSS_ASSERT(_condition) do { \
117 if (!(_condition)) { \
Yuanyuan Liu68939762017-04-04 16:43:03 -0700118 icnss_pr_err("ASSERT at line %d\n", __LINE__); \
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800119 BUG_ON(1); \
120 } \
121 } while (0)
Yuanyuan Liu68939762017-04-04 16:43:03 -0700122
123bool ignore_qmi_timeout;
124#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800125#else
Yuanyuan Liu68939762017-04-04 16:43:03 -0700126#define ICNSS_ASSERT(_condition) do { } while (0)
127#define ICNSS_QMI_ASSERT() do { } while (0)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800128#endif
129
130enum icnss_debug_quirks {
131 HW_ALWAYS_ON,
132 HW_DEBUG_ENABLE,
133 SKIP_QMI,
134 HW_ONLY_TOP_LEVEL_RESET,
135 RECOVERY_DISABLE,
136 SSR_ONLY,
137 PDR_ONLY,
138 VBATT_DISABLE,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800139 FW_REJUVENATE_ENABLE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800140};
141
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800142#define ICNSS_QUIRKS_DEFAULT (BIT(VBATT_DISABLE) | \
143 BIT(FW_REJUVENATE_ENABLE))
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800144
145unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
146module_param(quirks, ulong, 0600);
147
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800148uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
149module_param(dynamic_feature_mask, ullong, 0600);
150
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800151void *icnss_ipc_log_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800152void *icnss_ipc_log_long_context;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800153
154#define ICNSS_EVENT_PENDING 2989
155
156#define ICNSS_EVENT_SYNC BIT(0)
157#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
158#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
159 ICNSS_EVENT_SYNC)
160
161enum icnss_driver_event_type {
162 ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
163 ICNSS_DRIVER_EVENT_SERVER_EXIT,
164 ICNSS_DRIVER_EVENT_FW_READY_IND,
165 ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
166 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
167 ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
168 ICNSS_DRIVER_EVENT_MAX,
169};
170
171struct icnss_event_pd_service_down_data {
172 bool crashed;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800173 bool fw_rejuvenate;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700174 bool wdog_bite;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800175};
176
177struct icnss_driver_event {
178 struct list_head list;
179 enum icnss_driver_event_type type;
180 bool sync;
181 struct completion complete;
182 int ret;
183 void *data;
184};
185
186enum icnss_driver_state {
187 ICNSS_WLFW_QMI_CONNECTED,
188 ICNSS_POWER_ON,
189 ICNSS_FW_READY,
190 ICNSS_DRIVER_PROBED,
191 ICNSS_FW_TEST_MODE,
192 ICNSS_PM_SUSPEND,
193 ICNSS_PM_SUSPEND_NOIRQ,
194 ICNSS_SSR_ENABLED,
195 ICNSS_PDR_ENABLED,
196 ICNSS_PD_RESTART,
197 ICNSS_MSA0_ASSIGNED,
198 ICNSS_WLFW_EXISTS,
Yuanyuan Liu68939762017-04-04 16:43:03 -0700199 ICNSS_WDOG_BITE,
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +0530200 ICNSS_SHUTDOWN_DONE,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800201};
202
203struct ce_irq_list {
204 int irq;
205 irqreturn_t (*handler)(int, void *);
206};
207
Yuanyuan Liu68939762017-04-04 16:43:03 -0700208struct icnss_vreg_info {
209 struct regulator *reg;
210 const char *name;
211 u32 min_v;
212 u32 max_v;
213 u32 load_ua;
214 unsigned long settle_delay;
215 bool required;
216};
217
218struct icnss_clk_info {
219 struct clk *handle;
220 const char *name;
221 u32 freq;
222 bool required;
223};
224
225static struct icnss_vreg_info icnss_vreg_info[] = {
226 {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
227 {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
228 {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
229 {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
230};
231
232#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
233
234static struct icnss_clk_info icnss_clk_info[] = {
235 {NULL, "cxo_ref_clk_pin", 0, false},
236};
237
238#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
239
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800240struct icnss_stats {
241 struct {
242 uint32_t posted;
243 uint32_t processed;
244 } events[ICNSS_DRIVER_EVENT_MAX];
245
246 struct {
247 uint32_t request;
248 uint32_t free;
249 uint32_t enable;
250 uint32_t disable;
251 } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
252
253 uint32_t pm_suspend;
254 uint32_t pm_suspend_err;
255 uint32_t pm_resume;
256 uint32_t pm_resume_err;
257 uint32_t pm_suspend_noirq;
258 uint32_t pm_suspend_noirq_err;
259 uint32_t pm_resume_noirq;
260 uint32_t pm_resume_noirq_err;
261 uint32_t pm_stay_awake;
262 uint32_t pm_relax;
263
264 uint32_t ind_register_req;
265 uint32_t ind_register_resp;
266 uint32_t ind_register_err;
267 uint32_t msa_info_req;
268 uint32_t msa_info_resp;
269 uint32_t msa_info_err;
270 uint32_t msa_ready_req;
271 uint32_t msa_ready_resp;
272 uint32_t msa_ready_err;
273 uint32_t msa_ready_ind;
274 uint32_t cap_req;
275 uint32_t cap_resp;
276 uint32_t cap_err;
277 uint32_t pin_connect_result;
278 uint32_t cfg_req;
279 uint32_t cfg_resp;
280 uint32_t cfg_req_err;
281 uint32_t mode_req;
282 uint32_t mode_resp;
283 uint32_t mode_req_err;
284 uint32_t ini_req;
285 uint32_t ini_resp;
286 uint32_t ini_req_err;
287 uint32_t vbatt_req;
288 uint32_t vbatt_resp;
289 uint32_t vbatt_req_err;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800290 uint32_t rejuvenate_ack_req;
291 uint32_t rejuvenate_ack_resp;
292 uint32_t rejuvenate_ack_err;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700293 uint32_t trigger_recovery;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800294};
295
296#define MAX_NO_OF_MAC_ADDR 4
297struct icnss_wlan_mac_addr {
298 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
299 uint32_t no_of_mac_addr_set;
300};
301
302struct service_notifier_context {
303 void *handle;
304 uint32_t instance_id;
305 char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800306};
307
308static struct icnss_priv {
309 uint32_t magic;
310 struct platform_device *pdev;
311 struct icnss_driver_ops *ops;
312 struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
Yuanyuan Liu68939762017-04-04 16:43:03 -0700313 struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
314 struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800315 u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
316 phys_addr_t mem_base_pa;
317 void __iomem *mem_base_va;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800318 struct dma_iommu_mapping *smmu_mapping;
319 dma_addr_t smmu_iova_start;
320 size_t smmu_iova_len;
321 dma_addr_t smmu_iova_ipa_start;
322 size_t smmu_iova_ipa_len;
323 struct qmi_handle *wlfw_clnt;
324 struct list_head event_list;
325 spinlock_t event_lock;
326 struct work_struct event_work;
327 struct work_struct qmi_recv_msg_work;
328 struct workqueue_struct *event_wq;
329 phys_addr_t msa_pa;
330 uint32_t msa_mem_size;
331 void *msa_va;
332 unsigned long state;
333 struct wlfw_rf_chip_info_s_v01 chip_info;
334 struct wlfw_rf_board_info_s_v01 board_info;
335 struct wlfw_soc_info_s_v01 soc_info;
336 struct wlfw_fw_version_info_s_v01 fw_version_info;
337 char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
338 u32 pwr_pin_result;
339 u32 phy_io_pin_result;
340 u32 rf_pin_result;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700341 uint32_t nr_mem_region;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800342 struct icnss_mem_region_info
Yuanyuan Liu68939762017-04-04 16:43:03 -0700343 mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800344 struct dentry *root_dentry;
345 spinlock_t on_off_lock;
346 struct icnss_stats stats;
347 struct work_struct service_notifier_work;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800348 struct service_notifier_context *service_notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800349 struct notifier_block service_notifier_nb;
350 int total_domains;
351 struct notifier_block get_service_nb;
352 void *modem_notify_handler;
353 struct notifier_block modem_ssr_nb;
354 uint32_t diag_reg_read_addr;
355 uint32_t diag_reg_read_mem_type;
356 uint32_t diag_reg_read_len;
357 uint8_t *diag_reg_read_buf;
358 struct qpnp_adc_tm_btm_param vph_monitor_params;
359 struct qpnp_adc_tm_chip *adc_tm_dev;
360 struct qpnp_vadc_chip *vadc_dev;
361 uint64_t vph_pwr;
362 atomic_t pm_count;
363 struct ramdump_device *msa0_dump_dev;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800364 bool is_wlan_mac_set;
365 struct icnss_wlan_mac_addr wlan_mac_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -0700366 bool bypass_s1_smmu;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800367} *penv;
368
Yuanyuan Liu68939762017-04-04 16:43:03 -0700369#ifdef CONFIG_ICNSS_DEBUG
370static void icnss_ignore_qmi_timeout(bool ignore)
371{
372 ignore_qmi_timeout = ignore;
373}
374#else
375static void icnss_ignore_qmi_timeout(bool ignore) { }
376#endif
377
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800378static void icnss_pm_stay_awake(struct icnss_priv *priv)
379{
380 if (atomic_inc_return(&priv->pm_count) != 1)
381 return;
382
Yuanyuan Liu68939762017-04-04 16:43:03 -0700383 icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800384 atomic_read(&priv->pm_count));
385
386 pm_stay_awake(&priv->pdev->dev);
387
388 priv->stats.pm_stay_awake++;
389}
390
391static void icnss_pm_relax(struct icnss_priv *priv)
392{
393 int r = atomic_dec_return(&priv->pm_count);
394
395 WARN_ON(r < 0);
396
397 if (r != 0)
398 return;
399
Yuanyuan Liu68939762017-04-04 16:43:03 -0700400 icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800401 atomic_read(&priv->pm_count));
402
403 pm_relax(&priv->pdev->dev);
404 priv->stats.pm_relax++;
405}
406
407static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
408{
409 switch (type) {
410 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
411 return "SERVER_ARRIVE";
412 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
413 return "SERVER_EXIT";
414 case ICNSS_DRIVER_EVENT_FW_READY_IND:
415 return "FW_READY";
416 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
417 return "REGISTER_DRIVER";
418 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
419 return "UNREGISTER_DRIVER";
420 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
421 return "PD_SERVICE_DOWN";
422 case ICNSS_DRIVER_EVENT_MAX:
423 return "EVENT_MAX";
424 }
425
426 return "UNKNOWN";
427};
428
429static int icnss_driver_event_post(enum icnss_driver_event_type type,
430 u32 flags, void *data)
431{
432 struct icnss_driver_event *event;
433 unsigned long irq_flags;
434 int gfp = GFP_KERNEL;
435 int ret = 0;
436
437 icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
438 icnss_driver_event_to_str(type), type, current->comm,
439 flags, penv->state);
440
441 if (type >= ICNSS_DRIVER_EVENT_MAX) {
442 icnss_pr_err("Invalid Event type: %d, can't post", type);
443 return -EINVAL;
444 }
445
446 if (in_interrupt() || irqs_disabled())
447 gfp = GFP_ATOMIC;
448
449 event = kzalloc(sizeof(*event), gfp);
450 if (event == NULL)
451 return -ENOMEM;
452
453 icnss_pm_stay_awake(penv);
454
455 event->type = type;
456 event->data = data;
457 init_completion(&event->complete);
458 event->ret = ICNSS_EVENT_PENDING;
459 event->sync = !!(flags & ICNSS_EVENT_SYNC);
460
461 spin_lock_irqsave(&penv->event_lock, irq_flags);
462 list_add_tail(&event->list, &penv->event_list);
463 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
464
465 penv->stats.events[type].posted++;
466 queue_work(penv->event_wq, &penv->event_work);
467
468 if (!(flags & ICNSS_EVENT_SYNC))
469 goto out;
470
471 if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
472 wait_for_completion(&event->complete);
473 else
474 ret = wait_for_completion_interruptible(&event->complete);
475
476 icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
477 icnss_driver_event_to_str(type), type, penv->state, ret,
478 event->ret);
479
480 spin_lock_irqsave(&penv->event_lock, irq_flags);
481 if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
482 event->sync = false;
483 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
484 ret = -EINTR;
485 goto out;
486 }
487 spin_unlock_irqrestore(&penv->event_lock, irq_flags);
488
489 ret = event->ret;
490 kfree(event);
491
492out:
493 icnss_pm_relax(penv);
494 return ret;
495}
496
497static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
498 uint64_t voltage_uv)
499{
500 int ret;
501 struct wlfw_vbatt_req_msg_v01 req;
502 struct wlfw_vbatt_resp_msg_v01 resp;
503 struct msg_desc req_desc, resp_desc;
504
505 if (!priv->wlfw_clnt) {
506 ret = -ENODEV;
507 goto out;
508 }
509
510 icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
511 penv->state);
512
513 memset(&req, 0, sizeof(req));
514 memset(&resp, 0, sizeof(resp));
515
516 req.voltage_uv = voltage_uv;
517
518 req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
519 req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
520 req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
521
522 resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
523 resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
524 resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
525
526 priv->stats.vbatt_req++;
527
528 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
529 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
530 if (ret < 0) {
531 icnss_pr_err("Send vbatt req failed %d\n", ret);
532 goto out;
533 }
534
535 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
536 icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
537 resp.resp.result, resp.resp.error);
538 ret = resp.resp.result;
539 goto out;
540 }
541 priv->stats.vbatt_resp++;
542
543out:
544 priv->stats.vbatt_req_err++;
545 return ret;
546}
547
548static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
549{
550 int ret = 0;
551 struct qpnp_vadc_result adc_result;
552
553 if (!priv->vadc_dev) {
554 icnss_pr_err("VADC dev doesn't exists\n");
555 ret = -EINVAL;
556 goto out;
557 }
558
559 ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
560 if (ret) {
561 icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
562 VADC_VPH_PWR, ret);
563 goto out;
564 }
565
566 icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
567 adc_result.physical, adc_result.measurement);
568
569 *result_uv = adc_result.physical;
570out:
571 return ret;
572}
573
574static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
575{
576 struct icnss_priv *priv = ctx;
577 uint64_t vph_pwr = 0;
578 uint64_t vph_pwr_prev;
579 int ret = 0;
580 bool update = true;
581
582 if (!priv) {
583 icnss_pr_err("Priv pointer is NULL\n");
584 return;
585 }
586
587 vph_pwr_prev = priv->vph_pwr;
588
589 ret = icnss_get_phone_power(priv, &vph_pwr);
590 if (ret)
591 return;
592
593 if (vph_pwr < ICNSS_THRESHOLD_LOW) {
594 if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
595 update = false;
596 priv->vph_monitor_params.state_request =
597 ADC_TM_HIGH_THR_ENABLE;
598 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
599 ICNSS_THRESHOLD_GUARD;
600 priv->vph_monitor_params.low_thr = 0;
601 } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
602 if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
603 update = false;
604 priv->vph_monitor_params.state_request =
605 ADC_TM_LOW_THR_ENABLE;
606 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
607 ICNSS_THRESHOLD_GUARD;
608 priv->vph_monitor_params.high_thr = 0;
609 } else {
610 if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
611 vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
612 update = false;
613 priv->vph_monitor_params.state_request =
614 ADC_TM_HIGH_LOW_THR_ENABLE;
615 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
616 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
617 }
618
619 priv->vph_pwr = vph_pwr;
620
621 if (update)
622 wlfw_vbatt_send_sync_msg(priv, vph_pwr);
623
624 icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
625 priv->vph_monitor_params.low_thr,
626 priv->vph_monitor_params.high_thr);
627 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
628 &priv->vph_monitor_params);
629 if (ret)
630 icnss_pr_err("TM channel setup failed %d\n", ret);
631}
632
633static int icnss_setup_vph_monitor(struct icnss_priv *priv)
634{
635 int ret = 0;
636
637 if (!priv->adc_tm_dev) {
638 icnss_pr_err("ADC TM handler is NULL\n");
639 ret = -EINVAL;
640 goto out;
641 }
642
643 priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
644 priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
645 priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
646 priv->vph_monitor_params.channel = VADC_VPH_PWR;
647 priv->vph_monitor_params.btm_ctx = priv;
648 priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
649 priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
650 icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
651 priv->vph_monitor_params.low_thr,
652 priv->vph_monitor_params.high_thr);
653
654 ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
655 &priv->vph_monitor_params);
656 if (ret)
657 icnss_pr_err("TM channel setup failed %d\n", ret);
658out:
659 return ret;
660}
661
662static int icnss_init_vph_monitor(struct icnss_priv *priv)
663{
664 int ret = 0;
665
666 if (test_bit(VBATT_DISABLE, &quirks))
667 goto out;
668
669 ret = icnss_get_phone_power(priv, &priv->vph_pwr);
670 if (ret)
671 goto out;
672
673 wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
674
675 ret = icnss_setup_vph_monitor(priv);
676 if (ret)
677 goto out;
678out:
679 return ret;
680}
681
682
683static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
684{
685 struct msg_desc ind_desc;
686 struct wlfw_pin_connect_result_ind_msg_v01 ind_msg;
687 int ret = 0;
688
689 if (!penv || !penv->wlfw_clnt) {
690 ret = -ENODEV;
691 goto out;
692 }
693
694 ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
695 ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
696 ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
697
698 ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
699 if (ret < 0) {
700 icnss_pr_err("Failed to decode message: %d, msg_len: %u\n",
701 ret, msg_len);
702 goto out;
703 }
704
705 /* store pin result locally */
706 if (ind_msg.pwr_pin_result_valid)
707 penv->pwr_pin_result = ind_msg.pwr_pin_result;
708 if (ind_msg.phy_io_pin_result_valid)
709 penv->phy_io_pin_result = ind_msg.phy_io_pin_result;
710 if (ind_msg.rf_pin_result_valid)
711 penv->rf_pin_result = ind_msg.rf_pin_result;
712
713 icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
714 ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
715 ind_msg.rf_pin_result);
716
717 penv->stats.pin_connect_result++;
718out:
719 return ret;
720}
721
Yuanyuan Liu68939762017-04-04 16:43:03 -0700722static int icnss_vreg_on(struct icnss_priv *priv)
723{
724 int ret = 0;
725 struct icnss_vreg_info *vreg_info;
726 int i;
727
728 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
729 vreg_info = &priv->vreg_info[i];
730
731 if (!vreg_info->reg)
732 continue;
733
734 icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
735
736 ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
737 vreg_info->max_v);
738 if (ret) {
739 icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
740 vreg_info->name, vreg_info->min_v,
741 vreg_info->max_v, ret);
742 break;
743 }
744
745 if (vreg_info->load_ua) {
746 ret = regulator_set_load(vreg_info->reg,
747 vreg_info->load_ua);
748 if (ret < 0) {
749 icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
750 vreg_info->name,
751 vreg_info->load_ua, ret);
752 break;
753 }
754 }
755
756 ret = regulator_enable(vreg_info->reg);
757 if (ret) {
758 icnss_pr_err("Regulator %s, can't enable: %d\n",
759 vreg_info->name, ret);
760 break;
761 }
762
763 if (vreg_info->settle_delay)
764 udelay(vreg_info->settle_delay);
765 }
766
767 if (!ret)
768 return 0;
769
770 for (; i >= 0; i--) {
771 vreg_info = &priv->vreg_info[i];
772
773 if (!vreg_info->reg)
774 continue;
775
776 regulator_disable(vreg_info->reg);
777 regulator_set_load(vreg_info->reg, 0);
778 regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
779 }
780
781 return ret;
782}
783
784static int icnss_vreg_off(struct icnss_priv *priv)
785{
786 int ret = 0;
787 struct icnss_vreg_info *vreg_info;
788 int i;
789
790 for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
791 vreg_info = &priv->vreg_info[i];
792
793 if (!vreg_info->reg)
794 continue;
795
796 icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
797
798 ret = regulator_disable(vreg_info->reg);
799 if (ret)
800 icnss_pr_err("Regulator %s, can't disable: %d\n",
801 vreg_info->name, ret);
802
803 ret = regulator_set_load(vreg_info->reg, 0);
804 if (ret < 0)
805 icnss_pr_err("Regulator %s, can't set load: %d\n",
806 vreg_info->name, ret);
807
808 ret = regulator_set_voltage(vreg_info->reg, 0,
809 vreg_info->max_v);
810 if (ret)
811 icnss_pr_err("Regulator %s, can't set voltage: %d\n",
812 vreg_info->name, ret);
813 }
814
815 return ret;
816}
817
818static int icnss_clk_init(struct icnss_priv *priv)
819{
820 struct icnss_clk_info *clk_info;
821 int i;
822 int ret = 0;
823
824 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
825 clk_info = &priv->clk_info[i];
826
827 if (!clk_info->handle)
828 continue;
829
830 icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
831
832 if (clk_info->freq) {
833 ret = clk_set_rate(clk_info->handle, clk_info->freq);
834
835 if (ret) {
836 icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
837 clk_info->name, clk_info->freq,
838 ret);
839 break;
840 }
841 }
842
843 ret = clk_prepare_enable(clk_info->handle);
844 if (ret) {
845 icnss_pr_err("Clock %s, can't enable: %d\n",
846 clk_info->name, ret);
847 break;
848 }
849 }
850
851 if (ret == 0)
852 return 0;
853
854 for (; i >= 0; i--) {
855 clk_info = &priv->clk_info[i];
856
857 if (!clk_info->handle)
858 continue;
859
860 clk_disable_unprepare(clk_info->handle);
861 }
862
863 return ret;
864}
865
866static int icnss_clk_deinit(struct icnss_priv *priv)
867{
868 struct icnss_clk_info *clk_info;
869 int i;
870
871 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
872 clk_info = &priv->clk_info[i];
873
874 if (!clk_info->handle)
875 continue;
876
877 icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
878
879 clk_disable_unprepare(clk_info->handle);
880 }
881
882 return 0;
883}
884
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800885static int icnss_hw_power_on(struct icnss_priv *priv)
886{
887 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800888
889 icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
890
Yuanyuan Liu68939762017-04-04 16:43:03 -0700891 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800892 if (test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -0700893 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800894 return ret;
895 }
896 set_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -0700897 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800898
Yuanyuan Liu68939762017-04-04 16:43:03 -0700899 ret = icnss_vreg_on(priv);
900 if (ret)
901 goto out;
902
903 ret = icnss_clk_init(priv);
904 if (ret)
905 goto vreg_off;
906
907 return ret;
908
909vreg_off:
910 icnss_vreg_off(priv);
911out:
912 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800913 return ret;
914}
915
916static int icnss_hw_power_off(struct icnss_priv *priv)
917{
918 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800919
920 if (test_bit(HW_ALWAYS_ON, &quirks))
921 return 0;
922
923 icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
924
Yuanyuan Liu68939762017-04-04 16:43:03 -0700925 spin_lock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800926 if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
Yuanyuan Liu68939762017-04-04 16:43:03 -0700927 spin_unlock(&priv->on_off_lock);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800928 return ret;
929 }
930 clear_bit(ICNSS_POWER_ON, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -0700931 spin_unlock(&priv->on_off_lock);
932
933 icnss_clk_deinit(priv);
934
935 ret = icnss_vreg_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800936
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800937 return ret;
938}
939
940int icnss_power_on(struct device *dev)
941{
942 struct icnss_priv *priv = dev_get_drvdata(dev);
943
944 if (!priv) {
945 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
946 dev, priv);
947 return -EINVAL;
948 }
949
950 icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
951
952 return icnss_hw_power_on(priv);
953}
954EXPORT_SYMBOL(icnss_power_on);
955
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -0800956bool icnss_is_fw_ready(void)
957{
958 if (!penv)
959 return false;
960 else
961 return test_bit(ICNSS_FW_READY, &penv->state);
962}
963EXPORT_SYMBOL(icnss_is_fw_ready);
964
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800965int icnss_power_off(struct device *dev)
966{
967 struct icnss_priv *priv = dev_get_drvdata(dev);
968
969 if (!priv) {
970 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
971 dev, priv);
972 return -EINVAL;
973 }
974
975 icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
976
977 return icnss_hw_power_off(priv);
978}
979EXPORT_SYMBOL(icnss_power_off);
980
Yuanyuan Liu68939762017-04-04 16:43:03 -0700981static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800982{
983 int ret = 0;
984 phys_addr_t addr;
985 u32 size;
986 u32 source_vmlist[1] = {VMID_HLOS};
987 int dest_vmids[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
988 int dest_perms[3] = {PERM_READ|PERM_WRITE,
989 PERM_READ|PERM_WRITE,
990 PERM_READ|PERM_WRITE};
991 int source_nelems = sizeof(source_vmlist)/sizeof(u32);
992 int dest_nelems = 0;
993
Yuanyuan Liu68939762017-04-04 16:43:03 -0700994 addr = mem_region->reg_addr;
995 size = mem_region->size;
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800996
Yuanyuan Liu68939762017-04-04 16:43:03 -0700997 if (!mem_region->secure_flag) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -0800998 dest_vmids[2] = VMID_WLAN_CE;
999 dest_nelems = 3;
1000 } else {
1001 dest_vmids[2] = 0;
1002 dest_nelems = 2;
1003 }
1004 ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
1005 dest_vmids, dest_perms, dest_nelems);
1006 if (ret) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001007 icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
1008 &addr, size, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001009 goto out;
1010 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07001011
1012 icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
1013 source_vmlist[0], dest_nelems, dest_vmids[0],
1014 dest_vmids[1], dest_vmids[2]);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001015out:
1016 return ret;
1017
1018}
1019
Yuanyuan Liu68939762017-04-04 16:43:03 -07001020static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001021{
1022 int ret = 0;
1023 phys_addr_t addr;
1024 u32 size;
1025 u32 dest_vmids[1] = {VMID_HLOS};
1026 int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001027 int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001028 int source_nelems = 0;
1029 int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
1030
Yuanyuan Liu68939762017-04-04 16:43:03 -07001031 addr = mem_region->reg_addr;
1032 size = mem_region->size;
1033
1034 if (!mem_region->secure_flag) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001035 source_vmlist[2] = VMID_WLAN_CE;
1036 source_nelems = 3;
1037 } else {
1038 source_vmlist[2] = 0;
1039 source_nelems = 2;
1040 }
1041
1042 ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
1043 dest_vmids, dest_perms, dest_nelems);
1044 if (ret) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001045 icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
1046 &addr, size, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001047 goto out;
1048 }
Yuanyuan Liu68939762017-04-04 16:43:03 -07001049 icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
1050 source_nelems, source_vmlist[0], source_vmlist[1],
1051 source_vmlist[2], dest_vmids[0]);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001052out:
1053 return ret;
1054}
1055
1056static int icnss_setup_msa_permissions(struct icnss_priv *priv)
1057{
1058 int ret;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001059 int i;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001060
1061 if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
1062 return 0;
1063
Yuanyuan Liu68939762017-04-04 16:43:03 -07001064 for (i = 0; i < priv->nr_mem_region; i++) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001065
Yuanyuan Liu68939762017-04-04 16:43:03 -07001066 ret = icnss_map_msa_permissions(&priv->mem_region[i]);
1067 if (ret)
1068 goto err_unmap;
1069 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001070
1071 set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
1072
Yuanyuan Liu68939762017-04-04 16:43:03 -07001073 return 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001074
Yuanyuan Liu68939762017-04-04 16:43:03 -07001075err_unmap:
1076 for (i--; i >= 0; i--)
1077 icnss_unmap_msa_permissions(&priv->mem_region[i]);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001078 return ret;
1079}
1080
1081static void icnss_remove_msa_permissions(struct icnss_priv *priv)
1082{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001083 int i;
1084
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001085 if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
1086 return;
1087
Yuanyuan Liu68939762017-04-04 16:43:03 -07001088 for (i = 0; i < priv->nr_mem_region; i++)
1089 icnss_unmap_msa_permissions(&priv->mem_region[i]);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001090
1091 clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
1092}
1093
1094static int wlfw_msa_mem_info_send_sync_msg(void)
1095{
1096 int ret;
1097 int i;
1098 struct wlfw_msa_info_req_msg_v01 req;
1099 struct wlfw_msa_info_resp_msg_v01 resp;
1100 struct msg_desc req_desc, resp_desc;
1101
1102 if (!penv || !penv->wlfw_clnt)
1103 return -ENODEV;
1104
1105 icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
1106
1107 memset(&req, 0, sizeof(req));
1108 memset(&resp, 0, sizeof(resp));
1109
1110 req.msa_addr = penv->msa_pa;
1111 req.size = penv->msa_mem_size;
1112
1113 req_desc.max_msg_len = WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN;
1114 req_desc.msg_id = QMI_WLFW_MSA_INFO_REQ_V01;
1115 req_desc.ei_array = wlfw_msa_info_req_msg_v01_ei;
1116
1117 resp_desc.max_msg_len = WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN;
1118 resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
1119 resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
1120
1121 penv->stats.msa_info_req++;
1122
1123 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1124 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1125 if (ret < 0) {
1126 icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
1127 goto out;
1128 }
1129
1130 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1131 icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
1132 resp.resp.result, resp.resp.error);
1133 ret = resp.resp.result;
1134 goto out;
1135 }
1136
1137 icnss_pr_dbg("Receive mem_region_info_len: %d\n",
1138 resp.mem_region_info_len);
1139
Yuanyuan Liu68939762017-04-04 16:43:03 -07001140 if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001141 icnss_pr_err("Invalid memory region length received: %d\n",
1142 resp.mem_region_info_len);
1143 ret = -EINVAL;
1144 goto out;
1145 }
1146
1147 penv->stats.msa_info_resp++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001148 penv->nr_mem_region = resp.mem_region_info_len;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001149 for (i = 0; i < resp.mem_region_info_len; i++) {
Yuanyuan Liu68939762017-04-04 16:43:03 -07001150 penv->mem_region[i].reg_addr =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001151 resp.mem_region_info[i].region_addr;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001152 penv->mem_region[i].size =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001153 resp.mem_region_info[i].size;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001154 penv->mem_region[i].secure_flag =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001155 resp.mem_region_info[i].secure_flag;
1156 icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
Yuanyuan Liu68939762017-04-04 16:43:03 -07001157 i, penv->mem_region[i].reg_addr,
1158 penv->mem_region[i].size,
1159 penv->mem_region[i].secure_flag);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001160 }
1161
1162 return 0;
1163
1164out:
1165 penv->stats.msa_info_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001166 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001167 return ret;
1168}
1169
1170static int wlfw_msa_ready_send_sync_msg(void)
1171{
1172 int ret;
1173 struct wlfw_msa_ready_req_msg_v01 req;
1174 struct wlfw_msa_ready_resp_msg_v01 resp;
1175 struct msg_desc req_desc, resp_desc;
1176
1177 if (!penv || !penv->wlfw_clnt)
1178 return -ENODEV;
1179
1180 icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
1181 penv->state);
1182
1183 memset(&req, 0, sizeof(req));
1184 memset(&resp, 0, sizeof(resp));
1185
1186 req_desc.max_msg_len = WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN;
1187 req_desc.msg_id = QMI_WLFW_MSA_READY_REQ_V01;
1188 req_desc.ei_array = wlfw_msa_ready_req_msg_v01_ei;
1189
1190 resp_desc.max_msg_len = WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN;
1191 resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
1192 resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
1193
1194 penv->stats.msa_ready_req++;
1195 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1196 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1197 if (ret < 0) {
1198 icnss_pr_err("Send MSA ready req failed %d\n", ret);
1199 goto out;
1200 }
1201
1202 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1203 icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
1204 resp.resp.result, resp.resp.error);
1205 ret = resp.resp.result;
1206 goto out;
1207 }
1208 penv->stats.msa_ready_resp++;
1209
1210 return 0;
1211
1212out:
1213 penv->stats.msa_ready_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001214 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001215 return ret;
1216}
1217
1218static int wlfw_ind_register_send_sync_msg(void)
1219{
1220 int ret;
1221 struct wlfw_ind_register_req_msg_v01 req;
1222 struct wlfw_ind_register_resp_msg_v01 resp;
1223 struct msg_desc req_desc, resp_desc;
1224
1225 if (!penv || !penv->wlfw_clnt)
1226 return -ENODEV;
1227
1228 icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
1229 penv->state);
1230
1231 memset(&req, 0, sizeof(req));
1232 memset(&resp, 0, sizeof(resp));
1233
1234 req.client_id_valid = 1;
1235 req.client_id = WLFW_CLIENT_ID;
1236 req.fw_ready_enable_valid = 1;
1237 req.fw_ready_enable = 1;
1238 req.msa_ready_enable_valid = 1;
1239 req.msa_ready_enable = 1;
1240 req.pin_connect_result_enable_valid = 1;
1241 req.pin_connect_result_enable = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001242 if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1243 req.rejuvenate_enable_valid = 1;
1244 req.rejuvenate_enable = 1;
1245 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001246
1247 req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
1248 req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
1249 req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
1250
1251 resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
1252 resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
1253 resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
1254
1255 penv->stats.ind_register_req++;
1256
1257 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1258 &resp_desc, &resp, sizeof(resp),
1259 WLFW_TIMEOUT_MS);
1260 if (ret < 0) {
1261 icnss_pr_err("Send indication register req failed %d\n", ret);
1262 goto out;
1263 }
1264
1265 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1266 icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
1267 resp.resp.result, resp.resp.error);
1268 ret = resp.resp.result;
1269 goto out;
1270 }
1271 penv->stats.ind_register_resp++;
1272
1273 return 0;
1274
1275out:
1276 penv->stats.ind_register_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001277 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001278 return ret;
1279}
1280
1281static int wlfw_cap_send_sync_msg(void)
1282{
1283 int ret;
1284 struct wlfw_cap_req_msg_v01 req;
1285 struct wlfw_cap_resp_msg_v01 resp;
1286 struct msg_desc req_desc, resp_desc;
1287
1288 if (!penv || !penv->wlfw_clnt)
1289 return -ENODEV;
1290
1291 icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
1292
1293 memset(&resp, 0, sizeof(resp));
1294
1295 req_desc.max_msg_len = WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN;
1296 req_desc.msg_id = QMI_WLFW_CAP_REQ_V01;
1297 req_desc.ei_array = wlfw_cap_req_msg_v01_ei;
1298
1299 resp_desc.max_msg_len = WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN;
1300 resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
1301 resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
1302
1303 penv->stats.cap_req++;
1304 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1305 &resp_desc, &resp, sizeof(resp),
1306 WLFW_TIMEOUT_MS);
1307 if (ret < 0) {
1308 icnss_pr_err("Send capability req failed %d\n", ret);
1309 goto out;
1310 }
1311
1312 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1313 icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
1314 resp.resp.result, resp.resp.error);
1315 ret = resp.resp.result;
1316 goto out;
1317 }
1318
1319 penv->stats.cap_resp++;
1320 /* store cap locally */
1321 if (resp.chip_info_valid)
1322 penv->chip_info = resp.chip_info;
1323 if (resp.board_info_valid)
1324 penv->board_info = resp.board_info;
1325 else
1326 penv->board_info.board_id = 0xFF;
1327 if (resp.soc_info_valid)
1328 penv->soc_info = resp.soc_info;
1329 if (resp.fw_version_info_valid)
1330 penv->fw_version_info = resp.fw_version_info;
1331 if (resp.fw_build_id_valid)
1332 strlcpy(penv->fw_build_id, resp.fw_build_id,
1333 QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
1334
1335 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",
1336 penv->chip_info.chip_id, penv->chip_info.chip_family,
1337 penv->board_info.board_id, penv->soc_info.soc_id,
1338 penv->fw_version_info.fw_version,
1339 penv->fw_version_info.fw_build_timestamp,
1340 penv->fw_build_id);
1341
1342 return 0;
1343
1344out:
1345 penv->stats.cap_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001346 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001347 return ret;
1348}
1349
1350static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
1351{
1352 int ret;
1353 struct wlfw_wlan_mode_req_msg_v01 req;
1354 struct wlfw_wlan_mode_resp_msg_v01 resp;
1355 struct msg_desc req_desc, resp_desc;
1356
1357 if (!penv || !penv->wlfw_clnt)
1358 return -ENODEV;
1359
1360 /* During recovery do not send mode request for WLAN OFF as
1361 * FW not able to process it.
1362 */
1363 if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
1364 mode == QMI_WLFW_OFF_V01)
1365 return 0;
1366
1367 icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
1368 penv->state, mode);
1369
1370 memset(&req, 0, sizeof(req));
1371 memset(&resp, 0, sizeof(resp));
1372
1373 req.mode = mode;
1374 req.hw_debug_valid = 1;
1375 req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks);
1376
1377 req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
1378 req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
1379 req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
1380
1381 resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
1382 resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
1383 resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
1384
1385 penv->stats.mode_req++;
1386 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1387 &resp_desc, &resp, sizeof(resp),
1388 WLFW_TIMEOUT_MS);
1389 if (ret < 0) {
1390 icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
1391 mode, ret);
1392 goto out;
1393 }
1394
1395 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1396 icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
1397 mode, resp.resp.result, resp.resp.error);
1398 ret = resp.resp.result;
1399 goto out;
1400 }
1401 penv->stats.mode_resp++;
1402
1403 return 0;
1404
1405out:
1406 penv->stats.mode_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001407 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001408 return ret;
1409}
1410
1411static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
1412{
1413 int ret;
1414 struct wlfw_wlan_cfg_req_msg_v01 req;
1415 struct wlfw_wlan_cfg_resp_msg_v01 resp;
1416 struct msg_desc req_desc, resp_desc;
1417
1418 if (!penv || !penv->wlfw_clnt)
1419 return -ENODEV;
1420
1421 icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
1422
1423 memset(&req, 0, sizeof(req));
1424 memset(&resp, 0, sizeof(resp));
1425
1426 memcpy(&req, data, sizeof(req));
1427
1428 req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
1429 req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
1430 req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
1431
1432 resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
1433 resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
1434 resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
1435
1436 penv->stats.cfg_req++;
1437 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1438 &resp_desc, &resp, sizeof(resp),
1439 WLFW_TIMEOUT_MS);
1440 if (ret < 0) {
1441 icnss_pr_err("Send config req failed %d\n", ret);
1442 goto out;
1443 }
1444
1445 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1446 icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
1447 resp.resp.result, resp.resp.error);
1448 ret = resp.resp.result;
1449 goto out;
1450 }
1451 penv->stats.cfg_resp++;
1452
1453 return 0;
1454
1455out:
1456 penv->stats.cfg_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001457 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001458 return ret;
1459}
1460
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001461static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001462{
1463 int ret;
1464 struct wlfw_ini_req_msg_v01 req;
1465 struct wlfw_ini_resp_msg_v01 resp;
1466 struct msg_desc req_desc, resp_desc;
1467
1468 if (!penv || !penv->wlfw_clnt)
1469 return -ENODEV;
1470
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001471 icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
1472 penv->state, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001473
1474 memset(&req, 0, sizeof(req));
1475 memset(&resp, 0, sizeof(resp));
1476
1477 req.enablefwlog_valid = 1;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001478 req.enablefwlog = fw_log_mode;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001479
1480 req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
1481 req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
1482 req_desc.ei_array = wlfw_ini_req_msg_v01_ei;
1483
1484 resp_desc.max_msg_len = WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN;
1485 resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
1486 resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
1487
1488 penv->stats.ini_req++;
1489
1490 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1491 &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
1492 if (ret < 0) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001493 icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
1494 fw_log_mode, ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001495 goto out;
1496 }
1497
1498 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001499 icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
1500 fw_log_mode, resp.resp.result, resp.resp.error);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001501 ret = resp.resp.result;
1502 goto out;
1503 }
1504 penv->stats.ini_resp++;
1505
1506 return 0;
1507
1508out:
1509 penv->stats.ini_req_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001510 ICNSS_QMI_ASSERT();
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001511 return ret;
1512}
1513
1514static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
1515 uint32_t offset, uint32_t mem_type,
1516 uint32_t data_len, uint8_t *data)
1517{
1518 int ret;
1519 struct wlfw_athdiag_read_req_msg_v01 req;
1520 struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
1521 struct msg_desc req_desc, resp_desc;
1522
1523 if (!priv->wlfw_clnt) {
1524 ret = -ENODEV;
1525 goto out;
1526 }
1527
1528 icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
1529 priv->state, offset, mem_type, data_len);
1530
1531 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1532 if (!resp) {
1533 ret = -ENOMEM;
1534 goto out;
1535 }
1536 memset(&req, 0, sizeof(req));
1537
1538 req.offset = offset;
1539 req.mem_type = mem_type;
1540 req.data_len = data_len;
1541
1542 req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
1543 req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
1544 req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
1545
1546 resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
1547 resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
1548 resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
1549
1550 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
1551 &resp_desc, resp, sizeof(*resp),
1552 WLFW_TIMEOUT_MS);
1553 if (ret < 0) {
1554 icnss_pr_err("send athdiag read req failed %d\n", ret);
1555 goto out;
1556 }
1557
1558 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
1559 icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
1560 resp->resp.result, resp->resp.error);
1561 ret = resp->resp.result;
1562 goto out;
1563 }
1564
Yuanyuan Liu68939762017-04-04 16:43:03 -07001565 if (!resp->data_valid || resp->data_len < data_len) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001566 icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
1567 resp->data_valid, resp->data_len);
1568 ret = -EINVAL;
1569 goto out;
1570 }
1571
1572 memcpy(data, resp->data, resp->data_len);
1573
1574out:
1575 kfree(resp);
1576 return ret;
1577}
1578
1579static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
1580 uint32_t offset, uint32_t mem_type,
1581 uint32_t data_len, uint8_t *data)
1582{
1583 int ret;
1584 struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
1585 struct wlfw_athdiag_write_resp_msg_v01 resp;
1586 struct msg_desc req_desc, resp_desc;
1587
1588 if (!priv->wlfw_clnt) {
1589 ret = -ENODEV;
1590 goto out;
1591 }
1592
1593 icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
1594 priv->state, offset, mem_type, data_len, data);
1595
1596 req = kzalloc(sizeof(*req), GFP_KERNEL);
1597 if (!req) {
1598 ret = -ENOMEM;
1599 goto out;
1600 }
1601 memset(&resp, 0, sizeof(resp));
1602
1603 req->offset = offset;
1604 req->mem_type = mem_type;
1605 req->data_len = data_len;
1606 memcpy(req->data, data, data_len);
1607
1608 req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
1609 req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
1610 req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
1611
1612 resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
1613 resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
1614 resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
1615
1616 ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
1617 &resp_desc, &resp, sizeof(resp),
1618 WLFW_TIMEOUT_MS);
1619 if (ret < 0) {
1620 icnss_pr_err("send athdiag write req failed %d\n", ret);
1621 goto out;
1622 }
1623
1624 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1625 icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
1626 resp.resp.result, resp.resp.error);
1627 ret = resp.resp.result;
1628 goto out;
1629 }
1630out:
1631 kfree(req);
1632 return ret;
1633}
1634
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001635static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
1636{
1637 int ret;
1638 struct wlfw_rejuvenate_ack_req_msg_v01 req;
1639 struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
1640 struct msg_desc req_desc, resp_desc;
1641
1642 icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
1643 priv->state);
1644
1645 memset(&req, 0, sizeof(req));
1646 memset(&resp, 0, sizeof(resp));
1647
1648 req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
1649 req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
1650 req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
1651
1652 resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
1653 resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
1654 resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
1655
1656 priv->stats.rejuvenate_ack_req++;
1657 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1658 &resp_desc, &resp, sizeof(resp),
1659 WLFW_TIMEOUT_MS);
1660 if (ret < 0) {
1661 icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
1662 goto out;
1663 }
1664
1665 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1666 icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
1667 resp.resp.result, resp.resp.error);
1668 ret = resp.resp.result;
1669 goto out;
1670 }
1671 priv->stats.rejuvenate_ack_resp++;
1672 return 0;
1673
1674out:
1675 priv->stats.rejuvenate_ack_err++;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001676 ICNSS_QMI_ASSERT();
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001677 return ret;
1678}
1679
1680static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
1681 uint64_t dynamic_feature_mask)
1682{
1683 int ret;
1684 struct wlfw_dynamic_feature_mask_req_msg_v01 req;
1685 struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
1686 struct msg_desc req_desc, resp_desc;
1687
1688 if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
1689 icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
1690 priv->state);
1691 return -EINVAL;
1692 }
1693
1694 if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
1695 icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
1696 return 0;
1697 }
1698
1699 icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
1700 dynamic_feature_mask, priv->state);
1701
1702 memset(&req, 0, sizeof(req));
1703 memset(&resp, 0, sizeof(resp));
1704
1705 req.mask_valid = 1;
1706 req.mask = dynamic_feature_mask;
1707
1708 req_desc.max_msg_len =
1709 WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
1710 req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
1711 req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
1712
1713 resp_desc.max_msg_len =
1714 WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
1715 resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
1716 resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
1717
1718 ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
1719 &resp_desc, &resp, sizeof(resp),
1720 WLFW_TIMEOUT_MS);
1721 if (ret < 0) {
1722 icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
1723 goto out;
1724 }
1725
1726 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
1727 icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
1728 resp.resp.result, resp.resp.error);
1729 ret = resp.resp.result;
1730 goto out;
1731 }
1732
1733 icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
1734 resp.prev_mask_valid, resp.prev_mask,
1735 resp.curr_mask_valid, resp.curr_mask);
1736
1737 return 0;
1738
1739out:
1740 return ret;
1741}
1742
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001743static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
1744{
1745 int ret;
1746
1747 if (!penv || !penv->wlfw_clnt)
1748 return;
1749
Yuanyuan Liu68939762017-04-04 16:43:03 -07001750 icnss_pr_vdbg("Receiving Event in work queue context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001751
1752 do {
1753 } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
1754
1755 if (ret != -ENOMSG)
1756 icnss_pr_err("Error receiving message: %d\n", ret);
1757
Yuanyuan Liu68939762017-04-04 16:43:03 -07001758 icnss_pr_vdbg("Receiving Event completed\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001759}
1760
1761static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
1762 enum qmi_event_type event, void *notify_priv)
1763{
Yuanyuan Liu68939762017-04-04 16:43:03 -07001764 icnss_pr_vdbg("QMI client notify: %d\n", event);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001765
1766 if (!penv || !penv->wlfw_clnt)
1767 return;
1768
1769 switch (event) {
1770 case QMI_RECV_MSG:
1771 schedule_work(&penv->qmi_recv_msg_work);
1772 break;
1773 default:
1774 icnss_pr_dbg("Unknown Event: %d\n", event);
1775 break;
1776 }
1777}
1778
Yuanyuan Liu68939762017-04-04 16:43:03 -07001779static int icnss_call_driver_uevent(struct icnss_priv *priv,
1780 enum icnss_uevent uevent, void *data)
1781{
1782 struct icnss_uevent_data uevent_data;
1783
1784 if (!priv->ops || !priv->ops->uevent)
1785 return 0;
1786
1787 icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
1788 priv->state, uevent);
1789
1790 uevent_data.uevent = uevent;
1791 uevent_data.data = data;
1792
1793 return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
1794}
1795
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001796static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
1797 unsigned int msg_id, void *msg,
1798 unsigned int msg_len, void *ind_cb_priv)
1799{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001800 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001801 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001802
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001803 if (!penv)
1804 return;
1805
1806 icnss_pr_dbg("Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
1807
1808 switch (msg_id) {
1809 case QMI_WLFW_FW_READY_IND_V01:
1810 icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
1811 0, NULL);
1812 break;
1813 case QMI_WLFW_MSA_READY_IND_V01:
1814 icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
1815 msg_id);
1816 penv->stats.msa_ready_ind++;
1817 break;
1818 case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
1819 icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
1820 msg_id);
1821 icnss_qmi_pin_connect_result_ind(msg, msg_len);
1822 break;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001823 case QMI_WLFW_REJUVENATE_IND_V01:
1824 icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
1825 msg_id, penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001826
1827 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001828 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
1829 if (event_data == NULL)
1830 return;
1831 event_data->crashed = true;
1832 event_data->fw_rejuvenate = true;
Yuanyuan Liu68939762017-04-04 16:43:03 -07001833 fw_down_data.crashed = true;
1834 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
1835 &fw_down_data);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001836 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
1837 0, event_data);
1838 break;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001839 default:
1840 icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
1841 break;
1842 }
1843}
1844
1845static int icnss_driver_event_server_arrive(void *data)
1846{
1847 int ret = 0;
1848
1849 if (!penv)
1850 return -ENODEV;
1851
1852 set_bit(ICNSS_WLFW_EXISTS, &penv->state);
1853
1854 penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
1855 if (!penv->wlfw_clnt) {
1856 icnss_pr_err("QMI client handle create failed\n");
1857 ret = -ENOMEM;
1858 goto out;
1859 }
1860
1861 ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
1862 WLFW_SERVICE_VERS_V01,
1863 WLFW_SERVICE_INS_ID_V01);
1864 if (ret < 0) {
1865 icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
1866 goto fail;
1867 }
1868
1869 ret = qmi_register_ind_cb(penv->wlfw_clnt,
1870 icnss_qmi_wlfw_clnt_ind, penv);
1871 if (ret < 0) {
1872 icnss_pr_err("Failed to register indication callback: %d\n",
1873 ret);
1874 goto fail;
1875 }
1876
1877 set_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
1878
1879 icnss_pr_info("QMI Server Connected: state: 0x%lx\n", penv->state);
1880
1881 ret = icnss_hw_power_on(penv);
1882 if (ret)
1883 goto fail;
1884
1885 ret = wlfw_ind_register_send_sync_msg();
1886 if (ret < 0)
1887 goto err_power_on;
1888
1889 if (!penv->msa_va) {
1890 icnss_pr_err("Invalid MSA address\n");
1891 ret = -EINVAL;
1892 goto err_power_on;
1893 }
1894
1895 ret = wlfw_msa_mem_info_send_sync_msg();
1896 if (ret < 0)
1897 goto err_power_on;
1898
1899 ret = icnss_setup_msa_permissions(penv);
1900 if (ret < 0)
1901 goto err_power_on;
1902
1903 ret = wlfw_msa_ready_send_sync_msg();
1904 if (ret < 0)
1905 goto err_setup_msa;
1906
1907 ret = wlfw_cap_send_sync_msg();
1908 if (ret < 0)
1909 goto err_setup_msa;
1910
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001911 wlfw_dynamic_feature_mask_send_sync_msg(penv,
1912 dynamic_feature_mask);
1913
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001914 icnss_init_vph_monitor(penv);
1915
1916 return ret;
1917
1918err_setup_msa:
1919 icnss_remove_msa_permissions(penv);
1920err_power_on:
1921 icnss_hw_power_off(penv);
1922fail:
1923 qmi_handle_destroy(penv->wlfw_clnt);
1924 penv->wlfw_clnt = NULL;
1925out:
1926 ICNSS_ASSERT(0);
1927 return ret;
1928}
1929
1930static int icnss_driver_event_server_exit(void *data)
1931{
1932 if (!penv || !penv->wlfw_clnt)
1933 return -ENODEV;
1934
1935 icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
1936
1937 if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
1938 qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
1939 &penv->vph_monitor_params);
1940
1941 qmi_handle_destroy(penv->wlfw_clnt);
1942
1943 clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
1944 penv->wlfw_clnt = NULL;
1945
1946 return 0;
1947}
1948
1949static int icnss_call_driver_probe(struct icnss_priv *priv)
1950{
1951 int ret;
1952
1953 if (!priv->ops || !priv->ops->probe)
1954 return 0;
1955
Yuanyuan Liu68939762017-04-04 16:43:03 -07001956 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
1957 return -EINVAL;
1958
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001959 icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
1960
1961 icnss_hw_power_on(priv);
1962
1963 ret = priv->ops->probe(&priv->pdev->dev);
1964 if (ret < 0) {
1965 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
1966 ret, priv->state);
1967 goto out;
1968 }
1969
1970 set_bit(ICNSS_DRIVER_PROBED, &priv->state);
1971
1972 return 0;
1973
1974out:
1975 icnss_hw_power_off(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08001976 return ret;
1977}
1978
Yuanyuan Liu68939762017-04-04 16:43:03 -07001979static int icnss_call_driver_shutdown(struct icnss_priv *priv)
1980{
1981 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
1982 goto out;
1983
1984 if (!priv->ops || !priv->ops->shutdown)
1985 goto out;
1986
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05301987 if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
1988 goto out;
1989
Yuanyuan Liu68939762017-04-04 16:43:03 -07001990 icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
1991
1992 priv->ops->shutdown(&priv->pdev->dev);
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05301993 set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07001994
1995out:
1996 return 0;
1997}
1998
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08001999static int icnss_pd_restart_complete(struct icnss_priv *priv)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002000{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002001 int ret;
2002
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002003 icnss_pm_relax(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002004
Yuanyuan Liu68939762017-04-04 16:43:03 -07002005 if (test_bit(ICNSS_WDOG_BITE, &priv->state)) {
2006 icnss_call_driver_shutdown(priv);
2007 clear_bit(ICNSS_WDOG_BITE, &priv->state);
2008 }
2009
2010 clear_bit(ICNSS_PD_RESTART, &priv->state);
2011
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002012 if (!priv->ops || !priv->ops->reinit)
2013 goto out;
2014
Yuanyuan Liu68939762017-04-04 16:43:03 -07002015 if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002016 goto call_probe;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002017
2018 icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
2019
2020 icnss_hw_power_on(priv);
2021
2022 ret = priv->ops->reinit(&priv->pdev->dev);
2023 if (ret < 0) {
2024 icnss_pr_err("Driver reinit failed: %d, state: 0x%lx\n",
2025 ret, priv->state);
2026 ICNSS_ASSERT(false);
2027 goto out_power_off;
2028 }
2029
2030out:
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05302031 clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002032 return 0;
2033
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002034call_probe:
2035 return icnss_call_driver_probe(priv);
2036
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002037out_power_off:
2038 icnss_hw_power_off(priv);
2039
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002040 return ret;
2041}
2042
2043
2044static int icnss_driver_event_fw_ready_ind(void *data)
2045{
2046 int ret = 0;
2047
2048 if (!penv)
2049 return -ENODEV;
2050
2051 set_bit(ICNSS_FW_READY, &penv->state);
2052
Yuanyuan Liu68939762017-04-04 16:43:03 -07002053 icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
2054
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002055 icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
2056
2057 icnss_hw_power_off(penv);
2058
2059 if (!penv->pdev) {
2060 icnss_pr_err("Device is not ready\n");
2061 ret = -ENODEV;
2062 goto out;
2063 }
2064
2065 if (test_bit(ICNSS_PD_RESTART, &penv->state))
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002066 ret = icnss_pd_restart_complete(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002067 else
2068 ret = icnss_call_driver_probe(penv);
2069
2070out:
2071 return ret;
2072}
2073
2074static int icnss_driver_event_register_driver(void *data)
2075{
2076 int ret = 0;
2077
2078 if (penv->ops)
2079 return -EEXIST;
2080
2081 penv->ops = data;
2082
2083 if (test_bit(SKIP_QMI, &quirks))
2084 set_bit(ICNSS_FW_READY, &penv->state);
2085
2086 if (!test_bit(ICNSS_FW_READY, &penv->state)) {
2087 icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
2088 penv->state);
2089 goto out;
2090 }
2091
2092 ret = icnss_hw_power_on(penv);
2093 if (ret)
2094 goto out;
2095
2096 ret = penv->ops->probe(&penv->pdev->dev);
2097
2098 if (ret) {
2099 icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
2100 ret, penv->state);
2101 goto power_off;
2102 }
2103
2104 set_bit(ICNSS_DRIVER_PROBED, &penv->state);
2105
2106 return 0;
2107
2108power_off:
2109 icnss_hw_power_off(penv);
2110 penv->ops = NULL;
2111out:
2112 return ret;
2113}
2114
2115static int icnss_driver_event_unregister_driver(void *data)
2116{
2117 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
2118 penv->ops = NULL;
2119 goto out;
2120 }
2121
2122 if (penv->ops)
2123 penv->ops->remove(&penv->pdev->dev);
2124
2125 clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
2126
2127 penv->ops = NULL;
2128
2129 icnss_hw_power_off(penv);
2130
2131out:
2132 return 0;
2133}
2134
2135static int icnss_call_driver_remove(struct icnss_priv *priv)
2136{
2137 icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
2138
2139 clear_bit(ICNSS_FW_READY, &priv->state);
2140
2141 if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
2142 return 0;
2143
2144 if (!priv->ops || !priv->ops->remove)
2145 return 0;
2146
2147 penv->ops->remove(&priv->pdev->dev);
2148
2149 clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002150
2151 icnss_hw_power_off(penv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002152
2153 return 0;
2154}
2155
Yuanyuan Liu68939762017-04-04 16:43:03 -07002156static int icnss_fw_crashed(struct icnss_priv *priv,
2157 struct icnss_event_pd_service_down_data *event_data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002158{
Yuanyuan Liu68939762017-04-04 16:43:03 -07002159 icnss_pr_dbg("FW crashed, state: 0x%lx, wdog_bite: %d\n",
2160 priv->state, event_data->wdog_bite);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002161
2162 set_bit(ICNSS_PD_RESTART, &priv->state);
2163 clear_bit(ICNSS_FW_READY, &priv->state);
2164
2165 icnss_pm_stay_awake(priv);
2166
Yuanyuan Liu68939762017-04-04 16:43:03 -07002167 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
2168 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002169
Yuanyuan Liu68939762017-04-04 16:43:03 -07002170 if (event_data->wdog_bite) {
2171 set_bit(ICNSS_WDOG_BITE, &priv->state);
2172 goto out;
2173 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002174
Yuanyuan Liu68939762017-04-04 16:43:03 -07002175 icnss_call_driver_shutdown(priv);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002176
Yuanyuan Liu68939762017-04-04 16:43:03 -07002177 if (event_data->fw_rejuvenate)
2178 wlfw_rejuvenate_ack_send_sync_msg(priv);
2179
2180out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002181 return 0;
2182}
2183
2184static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
2185 void *data)
2186{
2187 int ret = 0;
2188 struct icnss_event_pd_service_down_data *event_data = data;
2189
2190 if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
Yuanyuan Liu68939762017-04-04 16:43:03 -07002191 goto out;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002192
2193 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
2194 icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
2195 event_data->crashed, priv->state);
2196 ICNSS_ASSERT(0);
2197 goto out;
2198 }
2199
2200 if (event_data->crashed)
Yuanyuan Liu68939762017-04-04 16:43:03 -07002201 icnss_fw_crashed(priv, event_data);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002202 else
2203 icnss_call_driver_remove(priv);
2204
2205out:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002206 kfree(data);
2207
Yuanyuan Liu68939762017-04-04 16:43:03 -07002208 icnss_ignore_qmi_timeout(false);
2209
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002210 return ret;
2211}
2212
2213static void icnss_driver_event_work(struct work_struct *work)
2214{
2215 struct icnss_driver_event *event;
2216 unsigned long flags;
2217 int ret;
2218
2219 icnss_pm_stay_awake(penv);
2220
2221 spin_lock_irqsave(&penv->event_lock, flags);
2222
2223 while (!list_empty(&penv->event_list)) {
2224 event = list_first_entry(&penv->event_list,
2225 struct icnss_driver_event, list);
2226 list_del(&event->list);
2227 spin_unlock_irqrestore(&penv->event_lock, flags);
2228
2229 icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
2230 icnss_driver_event_to_str(event->type),
2231 event->sync ? "-sync" : "", event->type,
2232 penv->state);
2233
2234 switch (event->type) {
2235 case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
2236 ret = icnss_driver_event_server_arrive(event->data);
2237 break;
2238 case ICNSS_DRIVER_EVENT_SERVER_EXIT:
2239 ret = icnss_driver_event_server_exit(event->data);
2240 break;
2241 case ICNSS_DRIVER_EVENT_FW_READY_IND:
2242 ret = icnss_driver_event_fw_ready_ind(event->data);
2243 break;
2244 case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
2245 ret = icnss_driver_event_register_driver(event->data);
2246 break;
2247 case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
2248 ret = icnss_driver_event_unregister_driver(event->data);
2249 break;
2250 case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
2251 ret = icnss_driver_event_pd_service_down(penv,
2252 event->data);
2253 break;
2254 default:
2255 icnss_pr_err("Invalid Event type: %d", event->type);
2256 kfree(event);
2257 continue;
2258 }
2259
2260 penv->stats.events[event->type].processed++;
2261
2262 icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
2263 icnss_driver_event_to_str(event->type),
2264 event->sync ? "-sync" : "", event->type, ret,
2265 penv->state);
2266
2267 spin_lock_irqsave(&penv->event_lock, flags);
2268 if (event->sync) {
2269 event->ret = ret;
2270 complete(&event->complete);
2271 continue;
2272 }
2273 spin_unlock_irqrestore(&penv->event_lock, flags);
2274
2275 kfree(event);
2276
2277 spin_lock_irqsave(&penv->event_lock, flags);
2278 }
2279 spin_unlock_irqrestore(&penv->event_lock, flags);
2280
2281 icnss_pm_relax(penv);
2282}
2283
2284static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
2285 unsigned long code,
2286 void *_cmd)
2287{
2288 int ret = 0;
2289
2290 if (!penv)
2291 return -ENODEV;
2292
2293 icnss_pr_dbg("Event Notify: code: %ld", code);
2294
2295 switch (code) {
2296 case QMI_SERVER_ARRIVE:
2297 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
2298 0, NULL);
2299 break;
2300
2301 case QMI_SERVER_EXIT:
2302 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
2303 0, NULL);
2304 break;
2305 default:
2306 icnss_pr_dbg("Invalid code: %ld", code);
2307 break;
2308 }
2309 return ret;
2310}
2311
2312static int icnss_msa0_ramdump(struct icnss_priv *priv)
2313{
2314 struct ramdump_segment segment;
2315
2316 memset(&segment, 0, sizeof(segment));
2317 segment.v_address = priv->msa_va;
2318 segment.size = priv->msa_mem_size;
2319 return do_ramdump(priv->msa0_dump_dev, &segment, 1);
2320}
2321
2322static struct notifier_block wlfw_clnt_nb = {
2323 .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
2324};
2325
2326static int icnss_modem_notifier_nb(struct notifier_block *nb,
2327 unsigned long code,
2328 void *data)
2329{
2330 struct icnss_event_pd_service_down_data *event_data;
2331 struct notif_data *notif = data;
2332 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2333 modem_ssr_nb);
Yuanyuan Liu68939762017-04-04 16:43:03 -07002334 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002335
Yuanyuan Liu68939762017-04-04 16:43:03 -07002336 icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002337
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002338 if (code == SUBSYS_AFTER_SHUTDOWN &&
2339 notif->crashed == CRASH_STATUS_ERR_FATAL) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002340 icnss_remove_msa_permissions(priv);
2341 icnss_pr_info("Collecting msa0 segment dump\n");
2342 icnss_msa0_ramdump(priv);
2343 return NOTIFY_OK;
2344 }
2345
2346 if (code != SUBSYS_BEFORE_SHUTDOWN)
2347 return NOTIFY_OK;
2348
2349 if (test_bit(ICNSS_PDR_ENABLED, &priv->state))
2350 return NOTIFY_OK;
2351
Yuanyuan Liu68939762017-04-04 16:43:03 -07002352 icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
2353 priv->state, notif->crashed);
2354
2355 icnss_ignore_qmi_timeout(true);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002356
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002357 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002358
2359 if (event_data == NULL)
2360 return notifier_from_errno(-ENOMEM);
2361
2362 event_data->crashed = notif->crashed;
2363
Yuanyuan Liu68939762017-04-04 16:43:03 -07002364 if (notif->crashed == CRASH_STATUS_WDOG_BITE)
2365 event_data->wdog_bite = true;
2366
2367 fw_down_data.crashed = !!notif->crashed;
2368 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2369
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002370 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2371 ICNSS_EVENT_SYNC, event_data);
2372
2373 return NOTIFY_OK;
2374}
2375
2376static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
2377{
2378 int ret = 0;
2379
2380 priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
2381
2382 priv->modem_notify_handler =
2383 subsys_notif_register_notifier("modem", &priv->modem_ssr_nb);
2384
2385 if (IS_ERR(priv->modem_notify_handler)) {
2386 ret = PTR_ERR(priv->modem_notify_handler);
2387 icnss_pr_err("Modem register notifier failed: %d\n", ret);
2388 }
2389
2390 set_bit(ICNSS_SSR_ENABLED, &priv->state);
2391
2392 return ret;
2393}
2394
2395static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
2396{
2397 if (!test_and_clear_bit(ICNSS_SSR_ENABLED, &priv->state))
2398 return 0;
2399
2400 subsys_notif_unregister_notifier(priv->modem_notify_handler,
2401 &priv->modem_ssr_nb);
2402 priv->modem_notify_handler = NULL;
2403
2404 return 0;
2405}
2406
2407static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
2408{
2409 int i;
2410
2411 if (!test_and_clear_bit(ICNSS_PDR_ENABLED, &priv->state))
2412 return 0;
2413
2414 for (i = 0; i < priv->total_domains; i++)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002415 service_notif_unregister_notifier(
2416 priv->service_notifier[i].handle,
2417 &priv->service_notifier_nb);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002418
2419 kfree(priv->service_notifier);
2420
2421 priv->service_notifier = NULL;
2422
2423 return 0;
2424}
2425
2426static int icnss_service_notifier_notify(struct notifier_block *nb,
2427 unsigned long notification, void *data)
2428{
2429 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2430 service_notifier_nb);
2431 enum pd_subsys_state *state = data;
2432 struct icnss_event_pd_service_down_data *event_data;
Yuanyuan Liu68939762017-04-04 16:43:03 -07002433 struct icnss_uevent_fw_down_data fw_down_data;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002434
Yuanyuan Liu68939762017-04-04 16:43:03 -07002435 icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
2436 notification, priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002437
Yuanyuan Liu68939762017-04-04 16:43:03 -07002438 if (notification != SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
2439 goto done;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002440
Yuanyuan Liu68939762017-04-04 16:43:03 -07002441 event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002442
Yuanyuan Liu68939762017-04-04 16:43:03 -07002443 if (event_data == NULL)
2444 return notifier_from_errno(-ENOMEM);
2445
2446 if (state == NULL) {
2447 event_data->crashed = true;
2448 goto event_post;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002449 }
2450
Yuanyuan Liu68939762017-04-04 16:43:03 -07002451 icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx\n",
2452 *state, priv->state);
2453
2454 switch (*state) {
2455 case ROOT_PD_WDOG_BITE:
2456 event_data->crashed = true;
2457 event_data->wdog_bite = true;
2458 break;
2459 case ROOT_PD_SHUTDOWN:
2460 break;
2461 default:
2462 event_data->crashed = true;
2463 break;
2464 }
2465
2466event_post:
2467 icnss_ignore_qmi_timeout(true);
2468
2469 fw_down_data.crashed = event_data->crashed;
2470 icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
2471 icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
2472 ICNSS_EVENT_SYNC, event_data);
2473done:
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002474 return NOTIFY_OK;
2475}
2476
2477static int icnss_get_service_location_notify(struct notifier_block *nb,
2478 unsigned long opcode, void *data)
2479{
2480 struct icnss_priv *priv = container_of(nb, struct icnss_priv,
2481 get_service_nb);
2482 struct pd_qmi_client_data *pd = data;
2483 int curr_state;
2484 int ret;
2485 int i;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002486 struct service_notifier_context *notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002487
2488 icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
2489 priv->state);
2490
2491 if (opcode != LOCATOR_UP)
2492 return NOTIFY_DONE;
2493
2494 if (pd->total_domains == 0) {
2495 icnss_pr_err("Did not find any domains\n");
2496 ret = -ENOENT;
2497 goto out;
2498 }
2499
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002500 notifier = kcalloc(pd->total_domains,
2501 sizeof(struct service_notifier_context),
2502 GFP_KERNEL);
2503 if (!notifier) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002504 ret = -ENOMEM;
2505 goto out;
2506 }
2507
2508 priv->service_notifier_nb.notifier_call = icnss_service_notifier_notify;
2509
2510 for (i = 0; i < pd->total_domains; i++) {
2511 icnss_pr_dbg("%d: domain_name: %s, instance_id: %d\n", i,
2512 pd->domain_list[i].name,
2513 pd->domain_list[i].instance_id);
2514
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002515 notifier[i].handle =
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002516 service_notif_register_notifier(pd->domain_list[i].name,
2517 pd->domain_list[i].instance_id,
2518 &priv->service_notifier_nb, &curr_state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002519 notifier[i].instance_id = pd->domain_list[i].instance_id;
2520 strlcpy(notifier[i].name, pd->domain_list[i].name,
2521 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002522
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002523 if (IS_ERR(notifier[i].handle)) {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002524 icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
2525 i, pd->domain_list->name,
2526 pd->domain_list->instance_id);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002527 ret = PTR_ERR(notifier[i].handle);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002528 goto free_handle;
2529 }
2530 }
2531
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002532 priv->service_notifier = notifier;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002533 priv->total_domains = pd->total_domains;
2534
2535 set_bit(ICNSS_PDR_ENABLED, &priv->state);
2536
2537 icnss_pr_dbg("PD restart enabled, state: 0x%lx\n", priv->state);
2538
2539 return NOTIFY_OK;
2540
2541free_handle:
2542 for (i = 0; i < pd->total_domains; i++) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002543 if (notifier[i].handle)
2544 service_notif_unregister_notifier(notifier[i].handle,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002545 &priv->service_notifier_nb);
2546 }
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002547 kfree(notifier);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002548
2549out:
2550 icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
2551 priv->state);
2552
2553 return NOTIFY_OK;
2554}
2555
2556
2557static int icnss_pd_restart_enable(struct icnss_priv *priv)
2558{
2559 int ret;
2560
2561 if (test_bit(SSR_ONLY, &quirks)) {
2562 icnss_pr_dbg("PDR disabled through module parameter\n");
2563 return 0;
2564 }
2565
2566 icnss_pr_dbg("Get service location, state: 0x%lx\n", priv->state);
2567
2568 priv->get_service_nb.notifier_call = icnss_get_service_location_notify;
2569 ret = get_service_location(ICNSS_SERVICE_LOCATION_CLIENT_NAME,
2570 ICNSS_WLAN_SERVICE_NAME,
2571 &priv->get_service_nb);
2572 if (ret) {
2573 icnss_pr_err("Get service location failed: %d\n", ret);
2574 goto out;
2575 }
2576
2577 return 0;
2578out:
Yuanyuan Liu68939762017-04-04 16:43:03 -07002579 icnss_pr_err("Failed to enable PD restart: %d\n", ret);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002580 return ret;
2581
2582}
2583
2584
2585static int icnss_enable_recovery(struct icnss_priv *priv)
2586{
2587 int ret;
2588
2589 if (test_bit(RECOVERY_DISABLE, &quirks)) {
2590 icnss_pr_dbg("Recovery disabled through module parameter\n");
2591 return 0;
2592 }
2593
2594 if (test_bit(PDR_ONLY, &quirks)) {
2595 icnss_pr_dbg("SSR disabled through module parameter\n");
2596 goto enable_pdr;
2597 }
2598
2599 priv->msa0_dump_dev = create_ramdump_device("wcss_msa0",
2600 &priv->pdev->dev);
2601 if (!priv->msa0_dump_dev)
2602 return -ENOMEM;
2603
2604 icnss_modem_ssr_register_notifier(priv);
2605 if (test_bit(SSR_ONLY, &quirks)) {
2606 icnss_pr_dbg("PDR disabled through module parameter\n");
2607 return 0;
2608 }
2609
2610enable_pdr:
2611 ret = icnss_pd_restart_enable(priv);
2612
2613 if (ret)
2614 return ret;
2615
2616 return 0;
2617}
2618
2619int icnss_register_driver(struct icnss_driver_ops *ops)
2620{
2621 int ret = 0;
2622
2623 if (!penv || !penv->pdev) {
2624 ret = -ENODEV;
2625 goto out;
2626 }
2627
2628 icnss_pr_dbg("Registering driver, state: 0x%lx\n", penv->state);
2629
2630 if (penv->ops) {
2631 icnss_pr_err("Driver already registered\n");
2632 ret = -EEXIST;
2633 goto out;
2634 }
2635
2636 if (!ops->probe || !ops->remove) {
2637 ret = -EINVAL;
2638 goto out;
2639 }
2640
2641 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
2642 ICNSS_EVENT_SYNC, ops);
2643
2644 if (ret == -EINTR)
2645 ret = 0;
2646
2647out:
2648 return ret;
2649}
2650EXPORT_SYMBOL(icnss_register_driver);
2651
2652int icnss_unregister_driver(struct icnss_driver_ops *ops)
2653{
2654 int ret;
2655
2656 if (!penv || !penv->pdev) {
2657 ret = -ENODEV;
2658 goto out;
2659 }
2660
2661 icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", penv->state);
2662
2663 if (!penv->ops) {
2664 icnss_pr_err("Driver not registered\n");
2665 ret = -ENOENT;
2666 goto out;
2667 }
2668
2669 ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
2670 ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
2671out:
2672 return ret;
2673}
2674EXPORT_SYMBOL(icnss_unregister_driver);
2675
2676int icnss_ce_request_irq(unsigned int ce_id,
2677 irqreturn_t (*handler)(int, void *),
2678 unsigned long flags, const char *name, void *ctx)
2679{
2680 int ret = 0;
2681 unsigned int irq;
2682 struct ce_irq_list *irq_entry;
2683
2684 if (!penv || !penv->pdev) {
2685 ret = -ENODEV;
2686 goto out;
2687 }
2688
Yuanyuan Liu68939762017-04-04 16:43:03 -07002689 icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002690
2691 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2692 icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
2693 ret = -EINVAL;
2694 goto out;
2695 }
2696 irq = penv->ce_irqs[ce_id];
2697 irq_entry = &penv->ce_irq_list[ce_id];
2698
2699 if (irq_entry->handler || irq_entry->irq) {
2700 icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
2701 irq, ce_id);
2702 ret = -EEXIST;
2703 goto out;
2704 }
2705
2706 ret = request_irq(irq, handler, flags, name, ctx);
2707 if (ret) {
2708 icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
2709 irq, ce_id, ret);
2710 goto out;
2711 }
2712 irq_entry->irq = irq;
2713 irq_entry->handler = handler;
2714
Yuanyuan Liu68939762017-04-04 16:43:03 -07002715 icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002716
2717 penv->stats.ce_irqs[ce_id].request++;
2718out:
2719 return ret;
2720}
2721EXPORT_SYMBOL(icnss_ce_request_irq);
2722
2723int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
2724{
2725 int ret = 0;
2726 unsigned int irq;
2727 struct ce_irq_list *irq_entry;
2728
2729 if (!penv || !penv->pdev) {
2730 ret = -ENODEV;
2731 goto out;
2732 }
2733
Yuanyuan Liu68939762017-04-04 16:43:03 -07002734 icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002735
2736 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2737 icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
2738 ret = -EINVAL;
2739 goto out;
2740 }
2741
2742 irq = penv->ce_irqs[ce_id];
2743 irq_entry = &penv->ce_irq_list[ce_id];
2744 if (!irq_entry->handler || !irq_entry->irq) {
2745 icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
2746 ret = -EEXIST;
2747 goto out;
2748 }
2749 free_irq(irq, ctx);
2750 irq_entry->irq = 0;
2751 irq_entry->handler = NULL;
2752
2753 penv->stats.ce_irqs[ce_id].free++;
2754out:
2755 return ret;
2756}
2757EXPORT_SYMBOL(icnss_ce_free_irq);
2758
2759void icnss_enable_irq(unsigned int ce_id)
2760{
2761 unsigned int irq;
2762
2763 if (!penv || !penv->pdev) {
2764 icnss_pr_err("Platform driver not initialized\n");
2765 return;
2766 }
2767
Yuanyuan Liu68939762017-04-04 16:43:03 -07002768 icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002769 penv->state);
2770
2771 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2772 icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
2773 return;
2774 }
2775
2776 penv->stats.ce_irqs[ce_id].enable++;
2777
2778 irq = penv->ce_irqs[ce_id];
2779 enable_irq(irq);
2780}
2781EXPORT_SYMBOL(icnss_enable_irq);
2782
2783void icnss_disable_irq(unsigned int ce_id)
2784{
2785 unsigned int irq;
2786
2787 if (!penv || !penv->pdev) {
2788 icnss_pr_err("Platform driver not initialized\n");
2789 return;
2790 }
2791
Yuanyuan Liu68939762017-04-04 16:43:03 -07002792 icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002793 penv->state);
2794
2795 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
2796 icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
2797 ce_id);
2798 return;
2799 }
2800
2801 irq = penv->ce_irqs[ce_id];
2802 disable_irq(irq);
2803
2804 penv->stats.ce_irqs[ce_id].disable++;
2805}
2806EXPORT_SYMBOL(icnss_disable_irq);
2807
2808int icnss_get_soc_info(struct icnss_soc_info *info)
2809{
2810 if (!penv) {
2811 icnss_pr_err("Platform driver not initialized\n");
2812 return -EINVAL;
2813 }
2814
2815 info->v_addr = penv->mem_base_va;
2816 info->p_addr = penv->mem_base_pa;
2817 info->chip_id = penv->chip_info.chip_id;
2818 info->chip_family = penv->chip_info.chip_family;
2819 info->board_id = penv->board_info.board_id;
2820 info->soc_id = penv->soc_info.soc_id;
2821 info->fw_version = penv->fw_version_info.fw_version;
2822 strlcpy(info->fw_build_timestamp,
2823 penv->fw_version_info.fw_build_timestamp,
2824 QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
2825
2826 return 0;
2827}
2828EXPORT_SYMBOL(icnss_get_soc_info);
2829
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002830int icnss_set_fw_log_mode(uint8_t fw_log_mode)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002831{
2832 int ret;
2833
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002834 icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002835
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002836 ret = wlfw_ini_send_sync_msg(fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002837 if (ret)
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002838 icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
2839 ret, fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002840 return ret;
2841}
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08002842EXPORT_SYMBOL(icnss_set_fw_log_mode);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08002843
2844int icnss_athdiag_read(struct device *dev, uint32_t offset,
2845 uint32_t mem_type, uint32_t data_len,
2846 uint8_t *output)
2847{
2848 int ret = 0;
2849 struct icnss_priv *priv = dev_get_drvdata(dev);
2850
2851 if (priv->magic != ICNSS_MAGIC) {
2852 icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
2853 dev, priv, priv->magic);
2854 return -EINVAL;
2855 }
2856
2857 if (!output || data_len == 0
2858 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
2859 icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
2860 output, data_len);
2861 ret = -EINVAL;
2862 goto out;
2863 }
2864
2865 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
2866 !test_bit(ICNSS_POWER_ON, &priv->state)) {
2867 icnss_pr_err("Invalid state for diag read: 0x%lx\n",
2868 priv->state);
2869 ret = -EINVAL;
2870 goto out;
2871 }
2872
2873 ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
2874 data_len, output);
2875out:
2876 return ret;
2877}
2878EXPORT_SYMBOL(icnss_athdiag_read);
2879
2880int icnss_athdiag_write(struct device *dev, uint32_t offset,
2881 uint32_t mem_type, uint32_t data_len,
2882 uint8_t *input)
2883{
2884 int ret = 0;
2885 struct icnss_priv *priv = dev_get_drvdata(dev);
2886
2887 if (priv->magic != ICNSS_MAGIC) {
2888 icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
2889 dev, priv, priv->magic);
2890 return -EINVAL;
2891 }
2892
2893 if (!input || data_len == 0
2894 || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
2895 icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
2896 input, data_len);
2897 ret = -EINVAL;
2898 goto out;
2899 }
2900
2901 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
2902 !test_bit(ICNSS_POWER_ON, &priv->state)) {
2903 icnss_pr_err("Invalid state for diag write: 0x%lx\n",
2904 priv->state);
2905 ret = -EINVAL;
2906 goto out;
2907 }
2908
2909 ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
2910 data_len, input);
2911out:
2912 return ret;
2913}
2914EXPORT_SYMBOL(icnss_athdiag_write);
2915
2916int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
2917 enum icnss_driver_mode mode,
2918 const char *host_version)
2919{
2920 struct wlfw_wlan_cfg_req_msg_v01 req;
2921 u32 i;
2922 int ret;
2923
2924 icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
2925 mode, config, host_version);
2926
2927 memset(&req, 0, sizeof(req));
2928
2929 if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
2930 goto skip;
2931
2932 if (!config || !host_version) {
2933 icnss_pr_err("Invalid cfg pointer, config: %p, host_version: %p\n",
2934 config, host_version);
2935 ret = -EINVAL;
2936 goto out;
2937 }
2938
2939 req.host_version_valid = 1;
2940 strlcpy(req.host_version, host_version,
2941 QMI_WLFW_MAX_STR_LEN_V01 + 1);
2942
2943 req.tgt_cfg_valid = 1;
2944 if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
2945 req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
2946 else
2947 req.tgt_cfg_len = config->num_ce_tgt_cfg;
2948 for (i = 0; i < req.tgt_cfg_len; i++) {
2949 req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
2950 req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
2951 req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
2952 req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
2953 req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
2954 }
2955
2956 req.svc_cfg_valid = 1;
2957 if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
2958 req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
2959 else
2960 req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
2961 for (i = 0; i < req.svc_cfg_len; i++) {
2962 req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
2963 req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
2964 req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
2965 }
2966
2967 req.shadow_reg_valid = 1;
2968 if (config->num_shadow_reg_cfg >
2969 QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
2970 req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
2971 else
2972 req.shadow_reg_len = config->num_shadow_reg_cfg;
2973
2974 memcpy(req.shadow_reg, config->shadow_reg_cfg,
2975 sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
2976
2977 ret = wlfw_wlan_cfg_send_sync_msg(&req);
2978 if (ret)
2979 goto out;
2980skip:
2981 ret = wlfw_wlan_mode_send_sync_msg(mode);
2982out:
2983 if (test_bit(SKIP_QMI, &quirks))
2984 ret = 0;
2985
2986 return ret;
2987}
2988EXPORT_SYMBOL(icnss_wlan_enable);
2989
2990int icnss_wlan_disable(enum icnss_driver_mode mode)
2991{
2992 return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01);
2993}
2994EXPORT_SYMBOL(icnss_wlan_disable);
2995
2996bool icnss_is_qmi_disable(void)
2997{
2998 return test_bit(SKIP_QMI, &quirks) ? true : false;
2999}
3000EXPORT_SYMBOL(icnss_is_qmi_disable);
3001
3002int icnss_get_ce_id(int irq)
3003{
3004 int i;
3005
3006 if (!penv || !penv->pdev)
3007 return -ENODEV;
3008
3009 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
3010 if (penv->ce_irqs[i] == irq)
3011 return i;
3012 }
3013
3014 icnss_pr_err("No matching CE id for irq %d\n", irq);
3015
3016 return -EINVAL;
3017}
3018EXPORT_SYMBOL(icnss_get_ce_id);
3019
3020int icnss_get_irq(int ce_id)
3021{
3022 int irq;
3023
3024 if (!penv || !penv->pdev)
3025 return -ENODEV;
3026
3027 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
3028 return -EINVAL;
3029
3030 irq = penv->ce_irqs[ce_id];
3031
3032 return irq;
3033}
3034EXPORT_SYMBOL(icnss_get_irq);
3035
3036struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev)
3037{
3038 struct icnss_priv *priv = dev_get_drvdata(dev);
3039
3040 if (!priv) {
3041 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3042 dev, priv);
3043 return NULL;
3044 }
3045
3046 return priv->smmu_mapping;
3047}
3048EXPORT_SYMBOL(icnss_smmu_get_mapping);
3049
3050int icnss_smmu_map(struct device *dev,
3051 phys_addr_t paddr, uint32_t *iova_addr, size_t size)
3052{
3053 struct icnss_priv *priv = dev_get_drvdata(dev);
3054 unsigned long iova;
3055 size_t len;
3056 int ret = 0;
3057
3058 if (!priv) {
3059 icnss_pr_err("Invalid drvdata: dev %p, data %p\n",
3060 dev, priv);
3061 return -EINVAL;
3062 }
3063
3064 if (!iova_addr) {
3065 icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
3066 &paddr, size);
3067 return -EINVAL;
3068 }
3069
3070 len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
3071 iova = roundup(penv->smmu_iova_ipa_start, PAGE_SIZE);
3072
3073 if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
3074 icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
3075 iova,
3076 &priv->smmu_iova_ipa_start,
3077 priv->smmu_iova_ipa_len);
3078 return -ENOMEM;
3079 }
3080
3081 ret = iommu_map(priv->smmu_mapping->domain, iova,
3082 rounddown(paddr, PAGE_SIZE), len,
3083 IOMMU_READ | IOMMU_WRITE);
3084 if (ret) {
3085 icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
3086 return ret;
3087 }
3088
3089 priv->smmu_iova_ipa_start = iova + len;
3090 *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
3091
3092 return 0;
3093}
3094EXPORT_SYMBOL(icnss_smmu_map);
3095
3096unsigned int icnss_socinfo_get_serial_number(struct device *dev)
3097{
3098 return socinfo_get_serial_number();
3099}
3100EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
3101
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003102int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
3103{
3104 struct icnss_priv *priv = penv;
3105 uint32_t no_of_mac_addr;
3106 struct icnss_wlan_mac_addr *addr = NULL;
3107 int iter;
3108 u8 *temp = NULL;
3109
3110 if (!priv) {
3111 icnss_pr_err("Priv data is NULL\n");
3112 return -EINVAL;
3113 }
3114
3115 if (priv->is_wlan_mac_set) {
3116 icnss_pr_dbg("WLAN MAC address is already set\n");
3117 return 0;
3118 }
3119
3120 if (len == 0 || (len % ETH_ALEN) != 0) {
3121 icnss_pr_err("Invalid length %d\n", len);
3122 return -EINVAL;
3123 }
3124
3125 no_of_mac_addr = len / ETH_ALEN;
3126 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
3127 icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
3128 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
3129 return -EINVAL;
3130 }
3131
3132 priv->is_wlan_mac_set = true;
3133 addr = &priv->wlan_mac_addr;
3134 addr->no_of_mac_addr_set = no_of_mac_addr;
3135 temp = &addr->mac_addr[0][0];
3136
3137 for (iter = 0; iter < no_of_mac_addr;
3138 ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
3139 ether_addr_copy(temp, in);
3140 icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
3141 temp[0], temp[1], temp[2],
3142 temp[3], temp[4], temp[5]);
3143 }
3144
3145 return 0;
3146}
3147EXPORT_SYMBOL(icnss_set_wlan_mac_address);
3148
3149u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
3150{
3151 struct icnss_priv *priv = dev_get_drvdata(dev);
3152 struct icnss_wlan_mac_addr *addr = NULL;
3153
3154 if (priv->magic != ICNSS_MAGIC) {
3155 icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
3156 dev, priv, priv->magic);
3157 goto out;
3158 }
3159
3160 if (!priv->is_wlan_mac_set) {
3161 icnss_pr_dbg("WLAN MAC address is not set\n");
3162 goto out;
3163 }
3164
3165 addr = &priv->wlan_mac_addr;
3166 *num = addr->no_of_mac_addr_set;
3167 return &addr->mac_addr[0][0];
3168out:
3169 *num = 0;
3170 return NULL;
3171}
3172EXPORT_SYMBOL(icnss_get_wlan_mac_address);
3173
3174int icnss_trigger_recovery(struct device *dev)
3175{
3176 int ret = 0;
3177 struct icnss_priv *priv = dev_get_drvdata(dev);
3178
3179 if (priv->magic != ICNSS_MAGIC) {
3180 icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
3181 ret = -EINVAL;
3182 goto out;
3183 }
3184
3185 if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
3186 icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
3187 priv->state);
3188 ret = -EPERM;
3189 goto out;
3190 }
3191
Yuanyuan Liu68939762017-04-04 16:43:03 -07003192 if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) {
3193 icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
3194 priv->state);
3195 ret = -EOPNOTSUPP;
3196 goto out;
3197 }
3198
3199 if (!priv->service_notifier || !priv->service_notifier[0].handle) {
3200 icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
3201 priv->state);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003202 ret = -EINVAL;
3203 goto out;
3204 }
3205
Yuanyuan Liu68939762017-04-04 16:43:03 -07003206 WARN_ON(1);
3207 icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
3208 priv->state);
3209 priv->stats.trigger_recovery++;
3210
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003211 /*
3212 * Initiate PDR, required only for the first instance
3213 */
3214 ret = service_notif_pd_restart(priv->service_notifier[0].name,
3215 priv->service_notifier[0].instance_id);
3216
3217out:
3218 return ret;
3219}
3220EXPORT_SYMBOL(icnss_trigger_recovery);
3221
3222
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003223static int icnss_smmu_init(struct icnss_priv *priv)
3224{
3225 struct dma_iommu_mapping *mapping;
3226 int atomic_ctx = 1;
3227 int s1_bypass = 1;
3228 int ret = 0;
3229
3230 icnss_pr_dbg("Initializing SMMU\n");
3231
3232 mapping = arm_iommu_create_mapping(&platform_bus_type,
3233 priv->smmu_iova_start,
3234 priv->smmu_iova_len);
3235 if (IS_ERR(mapping)) {
3236 icnss_pr_err("Create mapping failed, err = %d\n", ret);
3237 ret = PTR_ERR(mapping);
3238 goto map_fail;
3239 }
3240
Yuanyuan Liu68939762017-04-04 16:43:03 -07003241 if (!priv->bypass_s1_smmu) {
3242 ret = iommu_domain_set_attr(mapping->domain,
3243 DOMAIN_ATTR_ATOMIC,
3244 &atomic_ctx);
3245 if (ret < 0) {
3246 icnss_pr_err("Set atomic_ctx attribute failed, err = %d\n",
3247 ret);
3248 goto set_attr_fail;
3249 }
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003250 }
3251
3252 ret = iommu_domain_set_attr(mapping->domain,
3253 DOMAIN_ATTR_S1_BYPASS,
3254 &s1_bypass);
3255 if (ret < 0) {
3256 icnss_pr_err("Set s1_bypass attribute failed, err = %d\n", ret);
3257 goto set_attr_fail;
3258 }
3259
3260 ret = arm_iommu_attach_device(&priv->pdev->dev, mapping);
3261 if (ret < 0) {
3262 icnss_pr_err("Attach device failed, err = %d\n", ret);
3263 goto attach_fail;
3264 }
3265
3266 priv->smmu_mapping = mapping;
3267
3268 return ret;
3269
3270attach_fail:
3271set_attr_fail:
3272 arm_iommu_release_mapping(mapping);
3273map_fail:
3274 return ret;
3275}
3276
3277static void icnss_smmu_deinit(struct icnss_priv *priv)
3278{
3279 if (!priv->smmu_mapping)
3280 return;
3281
3282 arm_iommu_detach_device(&priv->pdev->dev);
3283 arm_iommu_release_mapping(priv->smmu_mapping);
3284
3285 priv->smmu_mapping = NULL;
3286}
3287
Yuanyuan Liu68939762017-04-04 16:43:03 -07003288static int icnss_get_vreg_info(struct device *dev,
3289 struct icnss_vreg_info *vreg_info)
3290{
3291 int ret = 0;
3292 char prop_name[MAX_PROP_SIZE];
3293 struct regulator *reg;
3294 const __be32 *prop;
3295 int len = 0;
3296 int i;
3297
3298 reg = devm_regulator_get_optional(dev, vreg_info->name);
3299 if (PTR_ERR(reg) == -EPROBE_DEFER) {
3300 icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
3301 vreg_info->name);
3302 ret = PTR_ERR(reg);
3303 goto out;
3304 }
3305
3306 if (IS_ERR(reg)) {
3307 ret = PTR_ERR(reg);
3308
3309 if (vreg_info->required) {
3310 icnss_pr_err("Regulator %s doesn't exist: %d\n",
3311 vreg_info->name, ret);
3312 goto out;
3313 } else {
3314 icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
3315 vreg_info->name, ret);
3316 goto done;
3317 }
3318 }
3319
3320 vreg_info->reg = reg;
3321
3322 snprintf(prop_name, MAX_PROP_SIZE,
3323 "qcom,%s-config", vreg_info->name);
3324
3325 prop = of_get_property(dev->of_node, prop_name, &len);
3326
3327 icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
3328 prop_name, len);
3329
3330 if (!prop || len < (2 * sizeof(__be32))) {
3331 icnss_pr_dbg("Property %s %s\n", prop_name,
3332 prop ? "invalid format" : "doesn't exist");
3333 goto done;
3334 }
3335
3336 for (i = 0; (i * sizeof(__be32)) < len; i++) {
3337 switch (i) {
3338 case 0:
3339 vreg_info->min_v = be32_to_cpup(&prop[0]);
3340 break;
3341 case 1:
3342 vreg_info->max_v = be32_to_cpup(&prop[1]);
3343 break;
3344 case 2:
3345 vreg_info->load_ua = be32_to_cpup(&prop[2]);
3346 break;
3347 case 3:
3348 vreg_info->settle_delay = be32_to_cpup(&prop[3]);
3349 break;
3350 default:
3351 icnss_pr_dbg("Property %s, ignoring value at %d\n",
3352 prop_name, i);
3353 break;
3354 }
3355 }
3356
3357done:
3358 icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
3359 vreg_info->name, vreg_info->min_v, vreg_info->max_v,
3360 vreg_info->load_ua, vreg_info->settle_delay);
3361
3362 return 0;
3363
3364out:
3365 return ret;
3366}
3367
3368static int icnss_get_clk_info(struct device *dev,
3369 struct icnss_clk_info *clk_info)
3370{
3371 struct clk *handle;
3372 int ret = 0;
3373
3374 handle = devm_clk_get(dev, clk_info->name);
3375 if (IS_ERR(handle)) {
3376 ret = PTR_ERR(handle);
3377 if (clk_info->required) {
3378 icnss_pr_err("Clock %s isn't available: %d\n",
3379 clk_info->name, ret);
3380 goto out;
3381 } else {
3382 icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
3383 ret);
3384 ret = 0;
3385 goto out;
3386 }
3387 }
3388
3389 icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
3390
3391 clk_info->handle = handle;
3392out:
3393 return ret;
3394}
3395
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003396static int icnss_fw_debug_show(struct seq_file *s, void *data)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003397{
3398 struct icnss_priv *priv = s->private;
3399
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003400 seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003401
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003402 seq_puts(s, "\nCMD: test_mode\n");
3403 seq_puts(s, " VAL: 0 (Test mode disable)\n");
3404 seq_puts(s, " VAL: 1 (WLAN FW test)\n");
3405 seq_puts(s, " VAL: 2 (CCPM test)\n");
Yuanyuan Liu68939762017-04-04 16:43:03 -07003406 seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003407
3408 seq_puts(s, "\nCMD: dynamic_feature_mask\n");
3409 seq_puts(s, " VAL: (64 bit feature mask)\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003410
3411 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003412 seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003413 goto out;
3414 }
3415
3416 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003417 seq_puts(s, "Machine mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003418 goto out;
3419 }
3420
3421 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003422 seq_puts(s, "test_mode is running, can't run test_mode!\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003423 goto out;
3424 }
3425
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003426out:
3427 seq_puts(s, "\n");
3428 return 0;
3429}
3430
3431static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
3432{
3433 int ret;
3434
3435 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3436 icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
3437 priv->state);
3438 ret = -ENODEV;
3439 goto out;
3440 }
3441
3442 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3443 icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
3444 priv->state);
3445 ret = -EINVAL;
3446 goto out;
3447 }
3448
3449 if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3450 icnss_pr_err("Test mode not started, state: 0x%lx\n",
3451 priv->state);
3452 ret = -EINVAL;
3453 goto out;
3454 }
3455
3456 icnss_wlan_disable(ICNSS_OFF);
3457
3458 ret = icnss_hw_power_off(priv);
3459
3460 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3461
3462out:
3463 return ret;
3464}
3465static int icnss_test_mode_fw_test(struct icnss_priv *priv,
3466 enum icnss_driver_mode mode)
3467{
3468 int ret;
3469
3470 if (!test_bit(ICNSS_FW_READY, &priv->state)) {
3471 icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
3472 priv->state);
3473 ret = -ENODEV;
3474 goto out;
3475 }
3476
3477 if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
3478 icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
3479 priv->state);
3480 ret = -EINVAL;
3481 goto out;
3482 }
3483
3484 if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
3485 icnss_pr_err("Test mode already started, state: 0x%lx\n",
3486 priv->state);
3487 ret = -EBUSY;
3488 goto out;
3489 }
3490
3491 ret = icnss_hw_power_on(priv);
3492 if (ret)
3493 goto out;
3494
3495 set_bit(ICNSS_FW_TEST_MODE, &priv->state);
3496
3497 ret = icnss_wlan_enable(NULL, mode, NULL);
3498 if (ret)
3499 goto power_off;
3500
3501 return 0;
3502
3503power_off:
3504 icnss_hw_power_off(priv);
3505 clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
3506
3507out:
3508 return ret;
3509}
3510
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003511static ssize_t icnss_fw_debug_write(struct file *fp,
3512 const char __user *user_buf,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003513 size_t count, loff_t *off)
3514{
3515 struct icnss_priv *priv =
3516 ((struct seq_file *)fp->private_data)->private;
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003517 char buf[64];
3518 char *sptr, *token;
3519 unsigned int len = 0;
3520 char *cmd;
3521 uint64_t val;
3522 const char *delim = " ";
3523 int ret = 0;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003524
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003525 len = min(count, sizeof(buf) - 1);
3526 if (copy_from_user(buf, user_buf, len))
3527 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003528
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003529 buf[len] = '\0';
3530 sptr = buf;
3531
3532 token = strsep(&sptr, delim);
3533 if (!token)
3534 return -EINVAL;
3535 if (!sptr)
3536 return -EINVAL;
3537 cmd = token;
3538
3539 token = strsep(&sptr, delim);
3540 if (!token)
3541 return -EINVAL;
3542 if (kstrtou64(token, 0, &val))
3543 return -EINVAL;
3544
3545 if (strcmp(cmd, "test_mode") == 0) {
3546 switch (val) {
3547 case 0:
3548 ret = icnss_test_mode_fw_test_off(priv);
3549 break;
3550 case 1:
3551 ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
3552 break;
3553 case 2:
3554 ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
3555 break;
3556 case 3:
3557 ret = icnss_trigger_recovery(&priv->pdev->dev);
3558 break;
3559 default:
3560 return -EINVAL;
3561 }
3562 } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
3563 ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
3564 } else {
3565 return -EINVAL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003566 }
3567
3568 if (ret)
3569 return ret;
3570
3571 if (ret == 0)
3572 memset(&priv->stats, 0, sizeof(priv->stats));
3573
3574 return count;
3575}
3576
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003577static int icnss_fw_debug_open(struct inode *inode, struct file *file)
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003578{
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003579 return single_open(file, icnss_fw_debug_show, inode->i_private);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003580}
3581
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003582static const struct file_operations icnss_fw_debug_fops = {
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003583 .read = seq_read,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003584 .write = icnss_fw_debug_write,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003585 .release = single_release,
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003586 .open = icnss_fw_debug_open,
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003587 .owner = THIS_MODULE,
3588 .llseek = seq_lseek,
3589};
3590
3591static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
3592 size_t count, loff_t *off)
3593{
3594 struct icnss_priv *priv =
3595 ((struct seq_file *)fp->private_data)->private;
3596 int ret;
3597 u32 val;
3598
3599 ret = kstrtou32_from_user(buf, count, 0, &val);
3600 if (ret)
3601 return ret;
3602
3603 if (ret == 0)
3604 memset(&priv->stats, 0, sizeof(priv->stats));
3605
3606 return count;
3607}
3608
3609static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
3610{
3611 enum icnss_driver_state i;
3612 int skip = 0;
3613 unsigned long state;
3614
3615 seq_printf(s, "\nState: 0x%lx(", priv->state);
3616 for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
3617
3618 if (!(state & 0x1))
3619 continue;
3620
3621 if (skip++)
3622 seq_puts(s, " | ");
3623
3624 switch (i) {
3625 case ICNSS_WLFW_QMI_CONNECTED:
3626 seq_puts(s, "QMI CONN");
3627 continue;
3628 case ICNSS_POWER_ON:
3629 seq_puts(s, "POWER ON");
3630 continue;
3631 case ICNSS_FW_READY:
3632 seq_puts(s, "FW READY");
3633 continue;
3634 case ICNSS_DRIVER_PROBED:
3635 seq_puts(s, "DRIVER PROBED");
3636 continue;
3637 case ICNSS_FW_TEST_MODE:
3638 seq_puts(s, "FW TEST MODE");
3639 continue;
3640 case ICNSS_PM_SUSPEND:
3641 seq_puts(s, "PM SUSPEND");
3642 continue;
3643 case ICNSS_PM_SUSPEND_NOIRQ:
3644 seq_puts(s, "PM SUSPEND NOIRQ");
3645 continue;
3646 case ICNSS_SSR_ENABLED:
3647 seq_puts(s, "SSR ENABLED");
3648 continue;
3649 case ICNSS_PDR_ENABLED:
3650 seq_puts(s, "PDR ENABLED");
3651 continue;
3652 case ICNSS_PD_RESTART:
3653 seq_puts(s, "PD RESTART");
3654 continue;
3655 case ICNSS_MSA0_ASSIGNED:
3656 seq_puts(s, "MSA0 ASSIGNED");
3657 continue;
3658 case ICNSS_WLFW_EXISTS:
3659 seq_puts(s, "WLAN FW EXISTS");
3660 continue;
Yuanyuan Liu68939762017-04-04 16:43:03 -07003661 case ICNSS_WDOG_BITE:
3662 seq_puts(s, "MODEM WDOG BITE");
3663 continue;
Anurag Chouhanfa0c70d2017-04-10 17:26:47 +05303664 case ICNSS_SHUTDOWN_DONE:
3665 seq_puts(s, "SHUTDOWN DONE");
3666 continue;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003667 }
3668
3669 seq_printf(s, "UNKNOWN-%d", i);
3670 }
3671 seq_puts(s, ")\n");
3672
3673 return 0;
3674}
3675
3676static int icnss_stats_show_capability(struct seq_file *s,
3677 struct icnss_priv *priv)
3678{
3679 if (test_bit(ICNSS_FW_READY, &priv->state)) {
3680 seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
3681 seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
3682 seq_printf(s, "Chip family: 0x%x\n",
3683 priv->chip_info.chip_family);
3684 seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
3685 seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
3686 seq_printf(s, "Firmware Version: 0x%x\n",
3687 priv->fw_version_info.fw_version);
3688 seq_printf(s, "Firmware Build Timestamp: %s\n",
3689 priv->fw_version_info.fw_build_timestamp);
3690 seq_printf(s, "Firmware Build ID: %s\n",
3691 priv->fw_build_id);
3692 }
3693
3694 return 0;
3695}
3696
3697static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
3698{
3699 int i;
3700
3701 seq_puts(s, "\n<----------------- Events stats ------------------->\n");
3702 seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
3703 for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
3704 seq_printf(s, "%24s %16u %16u\n",
3705 icnss_driver_event_to_str(i),
3706 priv->stats.events[i].posted,
3707 priv->stats.events[i].processed);
3708
3709 return 0;
3710}
3711
3712static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
3713{
3714 int i;
3715
3716 seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
3717 seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
3718 "Free", "Enable", "Disable");
3719 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
3720 seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
3721 priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
3722 priv->stats.ce_irqs[i].free,
3723 priv->stats.ce_irqs[i].enable,
3724 priv->stats.ce_irqs[i].disable);
3725
3726 return 0;
3727}
3728
3729static int icnss_stats_show(struct seq_file *s, void *data)
3730{
3731#define ICNSS_STATS_DUMP(_s, _priv, _x) \
3732 seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
3733
3734 struct icnss_priv *priv = s->private;
3735
3736 ICNSS_STATS_DUMP(s, priv, ind_register_req);
3737 ICNSS_STATS_DUMP(s, priv, ind_register_resp);
3738 ICNSS_STATS_DUMP(s, priv, ind_register_err);
3739 ICNSS_STATS_DUMP(s, priv, msa_info_req);
3740 ICNSS_STATS_DUMP(s, priv, msa_info_resp);
3741 ICNSS_STATS_DUMP(s, priv, msa_info_err);
3742 ICNSS_STATS_DUMP(s, priv, msa_ready_req);
3743 ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
3744 ICNSS_STATS_DUMP(s, priv, msa_ready_err);
3745 ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
3746 ICNSS_STATS_DUMP(s, priv, cap_req);
3747 ICNSS_STATS_DUMP(s, priv, cap_resp);
3748 ICNSS_STATS_DUMP(s, priv, cap_err);
3749 ICNSS_STATS_DUMP(s, priv, pin_connect_result);
3750 ICNSS_STATS_DUMP(s, priv, cfg_req);
3751 ICNSS_STATS_DUMP(s, priv, cfg_resp);
3752 ICNSS_STATS_DUMP(s, priv, cfg_req_err);
3753 ICNSS_STATS_DUMP(s, priv, mode_req);
3754 ICNSS_STATS_DUMP(s, priv, mode_resp);
3755 ICNSS_STATS_DUMP(s, priv, mode_req_err);
3756 ICNSS_STATS_DUMP(s, priv, ini_req);
3757 ICNSS_STATS_DUMP(s, priv, ini_resp);
3758 ICNSS_STATS_DUMP(s, priv, ini_req_err);
3759 ICNSS_STATS_DUMP(s, priv, vbatt_req);
3760 ICNSS_STATS_DUMP(s, priv, vbatt_resp);
3761 ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08003762 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
3763 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
3764 ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
Yuanyuan Liu68939762017-04-04 16:43:03 -07003765 ICNSS_STATS_DUMP(s, priv, trigger_recovery);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08003766
3767 seq_puts(s, "\n<------------------ PM stats ------------------->\n");
3768 ICNSS_STATS_DUMP(s, priv, pm_suspend);
3769 ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
3770 ICNSS_STATS_DUMP(s, priv, pm_resume);
3771 ICNSS_STATS_DUMP(s, priv, pm_resume_err);
3772 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
3773 ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
3774 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
3775 ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
3776 ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
3777 ICNSS_STATS_DUMP(s, priv, pm_relax);
3778
3779 icnss_stats_show_irqs(s, priv);
3780
3781 icnss_stats_show_capability(s, priv);
3782
3783 icnss_stats_show_events(s, priv);
3784
3785 icnss_stats_show_state(s, priv);
3786
3787 return 0;
3788#undef ICNSS_STATS_DUMP
3789}
3790
3791static int icnss_stats_open(struct inode *inode, struct file *file)
3792{
3793 return single_open(file, icnss_stats_show, inode->i_private);
3794}
3795
3796static const struct file_operations icnss_stats_fops = {
3797 .read = seq_read,
3798 .write = icnss_stats_write,
3799 .release = single_release,
3800 .open = icnss_stats_open,
3801 .owner = THIS_MODULE,
3802 .llseek = seq_lseek,
3803};
3804
3805static int icnss_regwrite_show(struct seq_file *s, void *data)
3806{
3807 struct icnss_priv *priv = s->private;
3808
3809 seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
3810
3811 if (!test_bit(ICNSS_FW_READY, &priv->state))
3812 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
3813
3814 return 0;
3815}
3816
3817static ssize_t icnss_regwrite_write(struct file *fp,
3818 const char __user *user_buf,
3819 size_t count, loff_t *off)
3820{
3821 struct icnss_priv *priv =
3822 ((struct seq_file *)fp->private_data)->private;
3823 char buf[64];
3824 char *sptr, *token;
3825 unsigned int len = 0;
3826 uint32_t reg_offset, mem_type, reg_val;
3827 const char *delim = " ";
3828 int ret = 0;
3829
3830 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3831 !test_bit(ICNSS_POWER_ON, &priv->state))
3832 return -EINVAL;
3833
3834 len = min(count, sizeof(buf) - 1);
3835 if (copy_from_user(buf, user_buf, len))
3836 return -EFAULT;
3837
3838 buf[len] = '\0';
3839 sptr = buf;
3840
3841 token = strsep(&sptr, delim);
3842 if (!token)
3843 return -EINVAL;
3844
3845 if (!sptr)
3846 return -EINVAL;
3847
3848 if (kstrtou32(token, 0, &mem_type))
3849 return -EINVAL;
3850
3851 token = strsep(&sptr, delim);
3852 if (!token)
3853 return -EINVAL;
3854
3855 if (!sptr)
3856 return -EINVAL;
3857
3858 if (kstrtou32(token, 0, &reg_offset))
3859 return -EINVAL;
3860
3861 token = strsep(&sptr, delim);
3862 if (!token)
3863 return -EINVAL;
3864
3865 if (kstrtou32(token, 0, &reg_val))
3866 return -EINVAL;
3867
3868 ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
3869 sizeof(uint32_t),
3870 (uint8_t *)&reg_val);
3871 if (ret)
3872 return ret;
3873
3874 return count;
3875}
3876
3877static int icnss_regwrite_open(struct inode *inode, struct file *file)
3878{
3879 return single_open(file, icnss_regwrite_show, inode->i_private);
3880}
3881
3882static const struct file_operations icnss_regwrite_fops = {
3883 .read = seq_read,
3884 .write = icnss_regwrite_write,
3885 .open = icnss_regwrite_open,
3886 .owner = THIS_MODULE,
3887 .llseek = seq_lseek,
3888};
3889
3890static int icnss_regread_show(struct seq_file *s, void *data)
3891{
3892 struct icnss_priv *priv = s->private;
3893
3894 if (!priv->diag_reg_read_buf) {
3895 seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
3896
3897 if (!test_bit(ICNSS_FW_READY, &priv->state))
3898 seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
3899
3900 return 0;
3901 }
3902
3903 seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
3904 priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
3905 priv->diag_reg_read_len);
3906
3907 seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
3908 priv->diag_reg_read_len, false);
3909
3910 priv->diag_reg_read_len = 0;
3911 kfree(priv->diag_reg_read_buf);
3912 priv->diag_reg_read_buf = NULL;
3913
3914 return 0;
3915}
3916
3917static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
3918 size_t count, loff_t *off)
3919{
3920 struct icnss_priv *priv =
3921 ((struct seq_file *)fp->private_data)->private;
3922 char buf[64];
3923 char *sptr, *token;
3924 unsigned int len = 0;
3925 uint32_t reg_offset, mem_type;
3926 uint32_t data_len = 0;
3927 uint8_t *reg_buf = NULL;
3928 const char *delim = " ";
3929 int ret = 0;
3930
3931 if (!test_bit(ICNSS_FW_READY, &priv->state) ||
3932 !test_bit(ICNSS_POWER_ON, &priv->state))
3933 return -EINVAL;
3934
3935 len = min(count, sizeof(buf) - 1);
3936 if (copy_from_user(buf, user_buf, len))
3937 return -EFAULT;
3938
3939 buf[len] = '\0';
3940 sptr = buf;
3941
3942 token = strsep(&sptr, delim);
3943 if (!token)
3944 return -EINVAL;
3945
3946 if (!sptr)
3947 return -EINVAL;
3948
3949 if (kstrtou32(token, 0, &mem_type))
3950 return -EINVAL;
3951
3952 token = strsep(&sptr, delim);
3953 if (!token)
3954 return -EINVAL;
3955
3956 if (!sptr)
3957 return -EINVAL;
3958
3959 if (kstrtou32(token, 0, &reg_offset))
3960 return -EINVAL;
3961
3962 token = strsep(&sptr, delim);
3963 if (!token)
3964 return -EINVAL;
3965
3966 if (kstrtou32(token, 0, &data_len))
3967 return -EINVAL;
3968
3969 if (data_len == 0 ||
3970 data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
3971 return -EINVAL;
3972
3973 reg_buf = kzalloc(data_len, GFP_KERNEL);
3974 if (!reg_buf)
3975 return -ENOMEM;
3976
3977 ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
3978 mem_type, data_len,
3979 reg_buf);
3980 if (ret) {
3981 kfree(reg_buf);
3982 return ret;
3983 }
3984
3985 priv->diag_reg_read_addr = reg_offset;
3986 priv->diag_reg_read_mem_type = mem_type;
3987 priv->diag_reg_read_len = data_len;
3988 priv->diag_reg_read_buf = reg_buf;
3989
3990 return count;
3991}
3992
3993static int icnss_regread_open(struct inode *inode, struct file *file)
3994{
3995 return single_open(file, icnss_regread_show, inode->i_private);
3996}
3997
3998static const struct file_operations icnss_regread_fops = {
3999 .read = seq_read,
4000 .write = icnss_regread_write,
4001 .open = icnss_regread_open,
4002 .owner = THIS_MODULE,
4003 .llseek = seq_lseek,
4004};
4005
4006static int icnss_debugfs_create(struct icnss_priv *priv)
4007{
4008 int ret = 0;
4009 struct dentry *root_dentry;
4010
4011 root_dentry = debugfs_create_dir("icnss", 0);
4012
4013 if (IS_ERR(root_dentry)) {
4014 ret = PTR_ERR(root_dentry);
4015 icnss_pr_err("Unable to create debugfs %d\n", ret);
4016 goto out;
4017 }
4018
4019 priv->root_dentry = root_dentry;
4020
Yuanyuan Liu5cd732a2017-02-09 10:42:01 -08004021 debugfs_create_file("fw_debug", 0644, root_dentry, priv,
4022 &icnss_fw_debug_fops);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004023
4024 debugfs_create_file("stats", 0644, root_dentry, priv,
4025 &icnss_stats_fops);
4026 debugfs_create_file("reg_read", 0600, root_dentry, priv,
4027 &icnss_regread_fops);
4028 debugfs_create_file("reg_write", 0644, root_dentry, priv,
4029 &icnss_regwrite_fops);
4030
4031out:
4032 return ret;
4033}
4034
4035static void icnss_debugfs_destroy(struct icnss_priv *priv)
4036{
4037 debugfs_remove_recursive(priv->root_dentry);
4038}
4039
4040static int icnss_get_vbatt_info(struct icnss_priv *priv)
4041{
4042 struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
4043 struct qpnp_vadc_chip *vadc_dev = NULL;
4044 int ret = 0;
4045
4046 if (test_bit(VBATT_DISABLE, &quirks)) {
4047 icnss_pr_dbg("VBATT feature is disabled\n");
4048 return ret;
4049 }
4050
4051 adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
4052 if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
4053 icnss_pr_err("adc_tm_dev probe defer\n");
4054 return -EPROBE_DEFER;
4055 }
4056
4057 if (IS_ERR(adc_tm_dev)) {
4058 ret = PTR_ERR(adc_tm_dev);
4059 icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
4060 ret);
4061 return ret;
4062 }
4063
4064 vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
4065 if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
4066 icnss_pr_err("vadc_dev probe defer\n");
4067 return -EPROBE_DEFER;
4068 }
4069
4070 if (IS_ERR(vadc_dev)) {
4071 ret = PTR_ERR(vadc_dev);
4072 icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
4073 ret);
4074 return ret;
4075 }
4076
4077 priv->adc_tm_dev = adc_tm_dev;
4078 priv->vadc_dev = vadc_dev;
4079
4080 return 0;
4081}
4082
4083static int icnss_probe(struct platform_device *pdev)
4084{
4085 int ret = 0;
4086 struct resource *res;
4087 int i;
4088 struct device *dev = &pdev->dev;
4089 struct icnss_priv *priv;
4090
4091 if (penv) {
4092 icnss_pr_err("Driver is already initialized\n");
4093 return -EEXIST;
4094 }
4095
4096 icnss_pr_dbg("Platform driver probe\n");
4097
4098 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
4099 if (!priv)
4100 return -ENOMEM;
4101
4102 priv->magic = ICNSS_MAGIC;
4103 dev_set_drvdata(dev, priv);
4104
4105 priv->pdev = pdev;
4106
4107 ret = icnss_get_vbatt_info(priv);
4108 if (ret == -EPROBE_DEFER)
4109 goto out;
4110
Yuanyuan Liu68939762017-04-04 16:43:03 -07004111 memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
4112 for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
4113 ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
4114
4115 if (ret)
4116 goto out;
4117 }
4118
4119 memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
4120 for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
4121 ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
4122 if (ret)
4123 goto out;
4124 }
4125
4126 if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
4127 priv->bypass_s1_smmu = true;
4128
4129 icnss_pr_dbg("SMMU S1 BYPASS = %d\n", priv->bypass_s1_smmu);
4130
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004131 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
4132 if (!res) {
4133 icnss_pr_err("Memory base not found in DT\n");
4134 ret = -EINVAL;
4135 goto out;
4136 }
4137
4138 priv->mem_base_pa = res->start;
4139 priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
4140 resource_size(res));
4141 if (!priv->mem_base_va) {
4142 icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
4143 &priv->mem_base_pa);
4144 ret = -EINVAL;
4145 goto out;
4146 }
4147 icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
4148 priv->mem_base_va);
4149
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004150 for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
4151 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
4152 if (!res) {
4153 icnss_pr_err("Fail to get IRQ-%d\n", i);
4154 ret = -ENODEV;
4155 goto out;
4156 } else {
4157 priv->ce_irqs[i] = res->start;
4158 }
4159 }
4160
4161 ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
4162 &priv->msa_mem_size);
4163
4164 if (ret || priv->msa_mem_size == 0) {
4165 icnss_pr_err("Fail to get MSA Memory Size: %u, ret: %d\n",
4166 priv->msa_mem_size, ret);
4167 goto out;
4168 }
4169
4170 priv->msa_va = dmam_alloc_coherent(&pdev->dev, priv->msa_mem_size,
4171 &priv->msa_pa, GFP_KERNEL);
4172 if (!priv->msa_va) {
4173 icnss_pr_err("DMA alloc failed for MSA\n");
4174 ret = -ENOMEM;
4175 goto out;
4176 }
4177 icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%p\n", &priv->msa_pa,
4178 priv->msa_va);
4179
4180 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
4181 "smmu_iova_base");
4182 if (!res) {
4183 icnss_pr_err("SMMU IOVA base not found\n");
4184 } else {
4185 priv->smmu_iova_start = res->start;
4186 priv->smmu_iova_len = resource_size(res);
4187 icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
4188 &priv->smmu_iova_start, priv->smmu_iova_len);
4189
4190 res = platform_get_resource_byname(pdev,
4191 IORESOURCE_MEM,
4192 "smmu_iova_ipa");
4193 if (!res) {
4194 icnss_pr_err("SMMU IOVA IPA not found\n");
4195 } else {
4196 priv->smmu_iova_ipa_start = res->start;
4197 priv->smmu_iova_ipa_len = resource_size(res);
4198 icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
4199 &priv->smmu_iova_ipa_start,
4200 priv->smmu_iova_ipa_len);
4201 }
4202
4203 ret = icnss_smmu_init(priv);
4204 if (ret < 0) {
4205 icnss_pr_err("SMMU init failed, err = %d, start: %pad, len: %zx\n",
4206 ret, &priv->smmu_iova_start,
4207 priv->smmu_iova_len);
4208 goto out;
4209 }
4210 }
4211
4212 spin_lock_init(&priv->event_lock);
4213 spin_lock_init(&priv->on_off_lock);
4214
4215 priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
4216 if (!priv->event_wq) {
4217 icnss_pr_err("Workqueue creation failed\n");
4218 ret = -EFAULT;
4219 goto out_smmu_deinit;
4220 }
4221
4222 INIT_WORK(&priv->event_work, icnss_driver_event_work);
4223 INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work);
4224 INIT_LIST_HEAD(&priv->event_list);
4225
4226 ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
4227 WLFW_SERVICE_VERS_V01,
4228 WLFW_SERVICE_INS_ID_V01,
4229 &wlfw_clnt_nb);
4230 if (ret < 0) {
4231 icnss_pr_err("Notifier register failed: %d\n", ret);
4232 goto out_destroy_wq;
4233 }
4234
4235 icnss_enable_recovery(priv);
4236
4237 icnss_debugfs_create(priv);
4238
4239 penv = priv;
4240
4241 icnss_pr_info("Platform driver probed successfully\n");
4242
4243 return 0;
4244
4245out_destroy_wq:
4246 destroy_workqueue(priv->event_wq);
4247out_smmu_deinit:
4248 icnss_smmu_deinit(priv);
4249out:
4250 dev_set_drvdata(dev, NULL);
4251
4252 return ret;
4253}
4254
4255static int icnss_remove(struct platform_device *pdev)
4256{
4257 icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state);
4258
4259 icnss_debugfs_destroy(penv);
4260
4261 icnss_modem_ssr_unregister_notifier(penv);
4262
4263 destroy_ramdump_device(penv->msa0_dump_dev);
4264
4265 icnss_pdr_unregister_notifier(penv);
4266
4267 qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
4268 WLFW_SERVICE_VERS_V01,
4269 WLFW_SERVICE_INS_ID_V01,
4270 &wlfw_clnt_nb);
4271 if (penv->event_wq)
4272 destroy_workqueue(penv->event_wq);
4273
4274 icnss_hw_power_off(penv);
4275
4276 icnss_remove_msa_permissions(penv);
4277
4278 dev_set_drvdata(&pdev->dev, NULL);
4279
4280 return 0;
4281}
4282
4283#ifdef CONFIG_PM_SLEEP
4284static int icnss_pm_suspend(struct device *dev)
4285{
4286 struct icnss_priv *priv = dev_get_drvdata(dev);
4287 int ret = 0;
4288
4289 if (priv->magic != ICNSS_MAGIC) {
4290 icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
4291 dev, priv, priv->magic);
4292 return -EINVAL;
4293 }
4294
Yuanyuan Liu68939762017-04-04 16:43:03 -07004295 icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004296
4297 if (!priv->ops || !priv->ops->pm_suspend ||
4298 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4299 goto out;
4300
4301 ret = priv->ops->pm_suspend(dev);
4302
4303out:
4304 if (ret == 0) {
4305 priv->stats.pm_suspend++;
4306 set_bit(ICNSS_PM_SUSPEND, &priv->state);
4307 } else {
4308 priv->stats.pm_suspend_err++;
4309 }
4310 return ret;
4311}
4312
4313static int icnss_pm_resume(struct device *dev)
4314{
4315 struct icnss_priv *priv = dev_get_drvdata(dev);
4316 int ret = 0;
4317
4318 if (priv->magic != ICNSS_MAGIC) {
4319 icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
4320 dev, priv, priv->magic);
4321 return -EINVAL;
4322 }
4323
Yuanyuan Liu68939762017-04-04 16:43:03 -07004324 icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004325
4326 if (!priv->ops || !priv->ops->pm_resume ||
4327 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4328 goto out;
4329
4330 ret = priv->ops->pm_resume(dev);
4331
4332out:
4333 if (ret == 0) {
4334 priv->stats.pm_resume++;
4335 clear_bit(ICNSS_PM_SUSPEND, &priv->state);
4336 } else {
4337 priv->stats.pm_resume_err++;
4338 }
4339 return ret;
4340}
4341
4342static int icnss_pm_suspend_noirq(struct device *dev)
4343{
4344 struct icnss_priv *priv = dev_get_drvdata(dev);
4345 int ret = 0;
4346
4347 if (priv->magic != ICNSS_MAGIC) {
4348 icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
4349 dev, priv, priv->magic);
4350 return -EINVAL;
4351 }
4352
Yuanyuan Liu68939762017-04-04 16:43:03 -07004353 icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004354
4355 if (!priv->ops || !priv->ops->suspend_noirq ||
4356 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4357 goto out;
4358
4359 ret = priv->ops->suspend_noirq(dev);
4360
4361out:
4362 if (ret == 0) {
4363 priv->stats.pm_suspend_noirq++;
4364 set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4365 } else {
4366 priv->stats.pm_suspend_noirq_err++;
4367 }
4368 return ret;
4369}
4370
4371static int icnss_pm_resume_noirq(struct device *dev)
4372{
4373 struct icnss_priv *priv = dev_get_drvdata(dev);
4374 int ret = 0;
4375
4376 if (priv->magic != ICNSS_MAGIC) {
4377 icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
4378 dev, priv, priv->magic);
4379 return -EINVAL;
4380 }
4381
Yuanyuan Liu68939762017-04-04 16:43:03 -07004382 icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004383
4384 if (!priv->ops || !priv->ops->resume_noirq ||
4385 !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
4386 goto out;
4387
4388 ret = priv->ops->resume_noirq(dev);
4389
4390out:
4391 if (ret == 0) {
4392 priv->stats.pm_resume_noirq++;
4393 clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
4394 } else {
4395 priv->stats.pm_resume_noirq_err++;
4396 }
4397 return ret;
4398}
4399#endif
4400
4401static const struct dev_pm_ops icnss_pm_ops = {
4402 SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
4403 icnss_pm_resume)
4404 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
4405 icnss_pm_resume_noirq)
4406};
4407
4408static const struct of_device_id icnss_dt_match[] = {
4409 {.compatible = "qcom,icnss"},
4410 {}
4411};
4412
4413MODULE_DEVICE_TABLE(of, icnss_dt_match);
4414
4415static struct platform_driver icnss_driver = {
4416 .probe = icnss_probe,
4417 .remove = icnss_remove,
4418 .driver = {
4419 .name = "icnss",
4420 .pm = &icnss_pm_ops,
4421 .owner = THIS_MODULE,
4422 .of_match_table = icnss_dt_match,
4423 },
4424};
4425
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004426static int __init icnss_initialize(void)
4427{
4428 icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
4429 "icnss", 0);
4430 if (!icnss_ipc_log_context)
4431 icnss_pr_err("Unable to create log context\n");
4432
Yuanyuan Liu68939762017-04-04 16:43:03 -07004433 icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
4434 "icnss_long", 0);
4435 if (!icnss_ipc_log_long_context)
4436 icnss_pr_err("Unable to create log long context\n");
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004437
4438 return platform_driver_register(&icnss_driver);
4439}
4440
4441static void __exit icnss_exit(void)
4442{
4443 platform_driver_unregister(&icnss_driver);
4444 ipc_log_context_destroy(icnss_ipc_log_context);
4445 icnss_ipc_log_context = NULL;
Yuanyuan Liu68939762017-04-04 16:43:03 -07004446 ipc_log_context_destroy(icnss_ipc_log_long_context);
4447 icnss_ipc_log_long_context = NULL;
Yuanyuan Liu607051c2016-11-28 17:04:13 -08004448}
4449
4450
4451module_init(icnss_initialize);
4452module_exit(icnss_exit);
4453
4454MODULE_LICENSE("GPL v2");
4455MODULE_DESCRIPTION(DEVICE "iCNSS CORE platform driver");