1 /*
2  * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <linux/platform_device.h>
21 #include <linux/err.h>
22 #include <linux/list.h>
23 #include <linux/slab.h>
24 
25 #ifdef CONFIG_PLD_IPCI_ICNSS
26 #ifdef CONFIG_CNSS_OUT_OF_TREE
27 #include "icnss2.h"
28 #else
29 #include <soc/qcom/icnss2.h>
30 #endif
31 #endif
32 
33 #include "pld_internal.h"
34 #include "pld_ipci.h"
35 #include "osif_psoc_sync.h"
36 
37 #ifdef CONFIG_PLD_IPCI_ICNSS
38 
39 #define WCN6750_DEVICE_ID 0x6750
40 #define WCN6450_DEVICE_ID 0x6450
41 /**
42  * pld_ipci_probe() - Probe function for platform driver
43  * @dev: device
44  *
45  * The probe function will be called when platform device
46  * is detected.
47  *
48  * Return: int
49  */
pld_ipci_probe(struct device * dev)50 static int pld_ipci_probe(struct device *dev)
51 {
52 	struct pld_context *pld_context;
53 	int ret = 0;
54 
55 	pld_context = pld_get_global_context();
56 	if (!pld_context) {
57 		ret = -ENODEV;
58 		goto out;
59 	}
60 
61 	ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_IPCI);
62 	if (ret)
63 		goto out;
64 
65 	return pld_context->ops->probe(dev, PLD_BUS_TYPE_IPCI,
66 				       NULL, NULL);
67 
68 out:
69 	return ret;
70 }
71 
72 /**
73  * pld_ipci_remove() - Remove function for platform device
74  * @dev: device
75  *
76  * The remove function will be called when platform device
77  * is disconnected
78  *
79  * Return: void
80  */
pld_ipci_remove(struct device * dev)81 static void pld_ipci_remove(struct device *dev)
82 {
83 	struct pld_context *pld_context;
84 	int errno;
85 	struct osif_psoc_sync *psoc_sync;
86 
87 	errno = osif_psoc_sync_trans_start_wait(dev, &psoc_sync);
88 	if (errno)
89 		return;
90 
91 	osif_psoc_sync_unregister(dev);
92 	osif_psoc_sync_wait_for_ops(psoc_sync);
93 
94 	pld_context = pld_get_global_context();
95 
96 	if (!pld_context)
97 		goto out;
98 
99 	pld_context->ops->remove(dev, PLD_BUS_TYPE_SNOC);
100 
101 	pld_del_dev(pld_context, dev);
102 
103 out:
104 	osif_psoc_sync_trans_stop(psoc_sync);
105 	osif_psoc_sync_destroy(psoc_sync);
106 }
107 
108 /**
109  * pld_ipci_reinit() - SSR re-initialize function for platform device
110  * @dev: device
111  *
112  * During subsystem restart(SSR), this function will be called to
113  * re-initialize platform device.
114  *
115  * Return: int
116  */
pld_ipci_reinit(struct device * dev)117 static int pld_ipci_reinit(struct device *dev)
118 {
119 	struct pld_context *pld_context;
120 
121 	pld_context = pld_get_global_context();
122 	if (pld_context->ops->reinit)
123 		return pld_context->ops->reinit(dev, PLD_BUS_TYPE_IPCI,
124 						NULL, NULL);
125 
126 	return -ENODEV;
127 }
128 
129 /**
130  * pld_ipci_shutdown() - SSR shutdown function for platform device
131  * @dev: device
132  *
133  * During SSR, this function will be called to shutdown platform device.
134  *
135  * Return: void
136  */
pld_ipci_shutdown(struct device * dev)137 static void pld_ipci_shutdown(struct device *dev)
138 {
139 	struct pld_context *pld_context;
140 
141 	pld_context = pld_get_global_context();
142 	if (pld_context->ops->shutdown)
143 		pld_context->ops->shutdown(dev, PLD_BUS_TYPE_IPCI);
144 }
145 
146 /**
147  * pld_ipci_crash_shutdown() - Crash shutdown function for platform device
148  * @dev: device
149  *
150  * This function will be called when a crash is detected, it will shutdown
151  * platform device.
152  *
153  * Return: void
154  */
pld_ipci_crash_shutdown(void * dev)155 static void pld_ipci_crash_shutdown(void *dev)
156 {
157 	struct pld_context *pld_context;
158 
159 	pld_context = pld_get_global_context();
160 	if (pld_context->ops->crash_shutdown)
161 		pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_IPCI);
162 }
163 
164 /**
165  * pld_ipci_pm_suspend() - PM suspend callback function for power management
166  * @dev: device
167  *
168  * This function is to suspend the platform device when power management
169  * is enabled.
170  *
171  * Return: void
172  */
pld_ipci_pm_suspend(struct device * dev)173 static int pld_ipci_pm_suspend(struct device *dev)
174 {
175 	struct pld_context *pld_context;
176 	pm_message_t state;
177 
178 	state.event = PM_EVENT_SUSPEND;
179 	pld_context = pld_get_global_context();
180 	return pld_context->ops->suspend(dev, PLD_BUS_TYPE_IPCI, state);
181 }
182 
183 /**
184  * pld_ipci_pm_resume() - PM resume callback function for power management
185  * @dev: device
186  *
187  * This function is to resume the platform device when power management
188  * is enabled.
189  *
190  * Return: void
191  */
pld_ipci_pm_resume(struct device * dev)192 static int pld_ipci_pm_resume(struct device *dev)
193 {
194 	struct pld_context *pld_context;
195 
196 	pld_context = pld_get_global_context();
197 	return pld_context->ops->resume(dev, PLD_BUS_TYPE_IPCI);
198 }
199 
200 /**
201  * pld_ipci_suspend_noirq() - Complete the actions started by suspend()
202  * @dev: device
203  *
204  * Complete the actions started by suspend().  Carry out any
205  * additional operations required for suspending the device that might be
206  * racing with its driver's interrupt handler, which is guaranteed not to
207  * run while suspend_noirq() is being executed.
208  *
209  * Return: 0 for success
210  *         Non zero failure code for errors
211  */
pld_ipci_suspend_noirq(struct device * dev)212 static int pld_ipci_suspend_noirq(struct device *dev)
213 {
214 	struct pld_context *pld_context;
215 
216 	pld_context = pld_get_global_context();
217 	if (!pld_context)
218 		return -EINVAL;
219 
220 	if (pld_context->ops->suspend_noirq)
221 		return pld_context->ops->suspend_noirq(dev, PLD_BUS_TYPE_IPCI);
222 	return 0;
223 }
224 
225 /**
226  * pld_ipci_resume_noirq() - Prepare for the execution of resume()
227  * @dev: device
228  *
229  * Prepare for the execution of resume() by carrying out any
230  * operations required for resuming the device that might be racing with
231  * its driver's interrupt handler, which is guaranteed not to run while
232  * resume_noirq() is being executed.
233  *
234  * Return: 0 for success
235  *         Non zero failure code for errors
236  */
pld_ipci_resume_noirq(struct device * dev)237 static int pld_ipci_resume_noirq(struct device *dev)
238 {
239 	struct pld_context *pld_context;
240 
241 	pld_context = pld_get_global_context();
242 	if (!pld_context)
243 		return -EINVAL;
244 
245 	if (pld_context->ops->resume_noirq)
246 		return pld_context->ops->resume_noirq(dev, PLD_BUS_TYPE_IPCI);
247 
248 	return 0;
249 }
250 
251 /**
252  * pld_ipci_runtime_suspend() - Runtime suspend callback for power management
253  * @dev: device
254  *
255  * This function is to runtime suspend the platform device when power management
256  * is enabled.
257  *
258  * Return: status
259  */
pld_ipci_runtime_suspend(struct device * dev)260 static int pld_ipci_runtime_suspend(struct device *dev)
261 {
262 	struct pld_context *pld_context;
263 
264 	pld_context = pld_get_global_context();
265 	if (!pld_context)
266 		return -EINVAL;
267 
268 	if (pld_context->ops && pld_context->ops->runtime_suspend)
269 		return pld_context->ops->runtime_suspend(dev,
270 							 PLD_BUS_TYPE_IPCI);
271 
272 	return 0;
273 }
274 
275 /**
276  * pld_ipci_runtime_resume() - Runtime resume callback for power management
277  * @dev: device
278  *
279  * This function is to runtime resume the platform device when power management
280  * is enabled.
281  *
282  * Return: status
283  */
pld_ipci_runtime_resume(struct device * dev)284 static int pld_ipci_runtime_resume(struct device *dev)
285 {
286 	struct pld_context *pld_context;
287 
288 	pld_context = pld_get_global_context();
289 	if (!pld_context)
290 		return -EINVAL;
291 
292 	if (pld_context->ops && pld_context->ops->runtime_resume)
293 		return pld_context->ops->runtime_resume(dev, PLD_BUS_TYPE_IPCI);
294 
295 	return 0;
296 }
297 
298 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
pld_update_hang_evt_data(struct icnss_uevent_hang_data * evt_data,struct pld_uevent_data * data)299 static int pld_update_hang_evt_data(struct icnss_uevent_hang_data *evt_data,
300 				    struct pld_uevent_data *data)
301 {
302 	if (!evt_data || !data)
303 		return -EINVAL;
304 
305 	data->hang_data.hang_event_data = evt_data->hang_event_data;
306 	data->hang_data.hang_event_data_len = evt_data->hang_event_data_len;
307 	return 0;
308 }
309 
pld_ipci_uevent(struct device * dev,struct icnss_uevent_data * uevent)310 static int pld_ipci_uevent(struct device *dev,
311 			   struct icnss_uevent_data *uevent)
312 {
313 	struct pld_context *pld_context;
314 	struct icnss_uevent_fw_down_data *uevent_data = NULL;
315 	struct pld_uevent_data data = {0};
316 	struct icnss_uevent_hang_data *hang_data = NULL;
317 
318 	pld_context = pld_get_global_context();
319 	if (!pld_context)
320 		return -EINVAL;
321 
322 	if (!pld_context->ops->uevent)
323 		goto out;
324 
325 	if (!uevent)
326 		return -EINVAL;
327 
328 	switch (uevent->uevent) {
329 	case ICNSS_UEVENT_FW_CRASHED:
330 		data.uevent = PLD_FW_CRASHED;
331 		break;
332 	case ICNSS_UEVENT_FW_DOWN:
333 		if (!uevent->data)
334 			return -EINVAL;
335 		uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data;
336 		data.uevent = PLD_FW_DOWN;
337 		data.fw_down.crashed = uevent_data->crashed;
338 		break;
339 	case ICNSS_UEVENT_HANG_DATA:
340 		if (!uevent->data)
341 			return -EINVAL;
342 		hang_data = (struct icnss_uevent_hang_data *)uevent->data;
343 		data.uevent = PLD_FW_HANG_EVENT;
344 		pld_update_hang_evt_data(hang_data, &data);
345 		break;
346 	case ICNSS_UEVENT_SMMU_FAULT:
347 		if (!uevent->data)
348 			return -EINVAL;
349 		uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data;
350 		data.uevent = PLD_SMMU_FAULT;
351 		data.fw_down.crashed = uevent_data->crashed;
352 		break;
353 	default:
354 		goto out;
355 	}
356 
357 	pld_context->ops->uevent(dev, &data);
358 out:
359 	return 0;
360 }
361 #else
pld_ipci_uevent(struct device * dev,struct icnss_uevent_data * uevent)362 static int pld_ipci_uevent(struct device *dev,
363 			   struct icnss_uevent_data *uevent)
364 {
365 	struct pld_context *pld_context;
366 	struct icnss_uevent_fw_down_data *uevent_data = NULL;
367 	struct pld_uevent_data data = {0};
368 
369 	pld_context = pld_get_global_context();
370 	if (!pld_context)
371 		return -EINVAL;
372 
373 	if (!pld_context->ops->uevent)
374 		goto out;
375 
376 	if (!uevent)
377 		return -EINVAL;
378 
379 	switch (uevent->uevent) {
380 	case ICNSS_UEVENT_FW_CRASHED:
381 		data.uevent = PLD_FW_CRASHED;
382 		break;
383 	case ICNSS_UEVENT_FW_DOWN:
384 		if (!uevent->data)
385 			return -EINVAL;
386 		uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data;
387 		data.uevent = PLD_FW_DOWN;
388 		data.fw_down.crashed = uevent_data->crashed;
389 		break;
390 	default:
391 		goto out;
392 	}
393 
394 	pld_context->ops->uevent(dev, &data);
395 out:
396 	return 0;
397 }
398 #endif
399 
400 /**
401  * pld_ipci_idle_restart_cb() - Perform idle restart
402  * @dev: platform device
403  *
404  * This function will be called if there is an idle restart request
405  *
406  * Return: int
407  */
pld_ipci_idle_restart_cb(struct device * dev)408 static int pld_ipci_idle_restart_cb(struct device *dev)
409 {
410 	struct pld_context *pld_context;
411 
412 	pld_context = pld_get_global_context();
413 
414 	if (!pld_context)
415 		return -EINVAL;
416 
417 	if (pld_context->ops->idle_restart)
418 		return pld_context->ops->idle_restart(dev,
419 						      PLD_BUS_TYPE_IPCI);
420 
421 	return -ENODEV;
422 }
423 
424 /**
425  * pld_ipci_idle_shutdown_cb() - Perform idle shutdown
426  * @dev: PCIE device
427  *
428  * This function will be called if there is an idle shutdown request
429  *
430  * Return: int
431  */
pld_ipci_idle_shutdown_cb(struct device * dev)432 static int pld_ipci_idle_shutdown_cb(struct device *dev)
433 {
434 	struct pld_context *pld_context;
435 
436 	pld_context = pld_get_global_context();
437 
438 	if (!pld_context)
439 		return -EINVAL;
440 
441 	if (pld_context->ops->shutdown)
442 		return pld_context->ops->idle_shutdown(dev,
443 						       PLD_BUS_TYPE_IPCI);
444 
445 	return -ENODEV;
446 }
447 
448 /**
449  * pld_ipci_set_thermal_state() - Set thermal state for thermal mitigation
450  * @dev: device
451  * @thermal_state: Thermal state set by thermal subsystem
452  * @mon_id: Thermal cooling device ID
453  *
454  * This function will be called when thermal subsystem notifies platform
455  * driver about change in thermal state.
456  *
457  * Return: 0 for success
458  * Non zero failure code for errors
459  */
pld_ipci_set_thermal_state(struct device * dev,unsigned long thermal_state,int mon_id)460 static int pld_ipci_set_thermal_state(struct device *dev,
461 				      unsigned long thermal_state,
462 				      int mon_id)
463 {
464 	struct pld_context *pld_context;
465 
466 	pld_context = pld_get_global_context();
467 	if (!pld_context)
468 		return -EINVAL;
469 
470 	if (pld_context->ops->set_curr_therm_cdev_state)
471 		return pld_context->ops->set_curr_therm_cdev_state(dev,
472 							      thermal_state,
473 							      mon_id);
474 
475 	return -ENOTSUPP;
476 }
477 
478 #ifdef MULTI_IF_NAME
479 #define PLD_IPCI_OPS_NAME "pld_ipci_" MULTI_IF_NAME
480 #else
481 #define PLD_IPCI_OPS_NAME "pld_ipci"
482 #endif
483 
484 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
485 static struct device_info pld_ipci_dev_info[] = {
486 #ifdef QCA_WIFI_QCA6750
487 	{ "wcn6750", WCN6750_DEVICE_ID },
488 #elif defined(QCA_WIFI_WCN6450)
489 	{ "wcn6450", WCN6450_DEVICE_ID },
490 #endif
491 	{ { 0 } }
492 };
493 #endif
494 
495 struct icnss_driver_ops pld_ipci_ops = {
496 	.name       = PLD_IPCI_OPS_NAME,
497 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
498 	.dev_info   = pld_ipci_dev_info,
499 #endif
500 	.probe      = pld_ipci_probe,
501 	.remove     = pld_ipci_remove,
502 	.shutdown   = pld_ipci_shutdown,
503 	.reinit     = pld_ipci_reinit,
504 	.crash_shutdown = pld_ipci_crash_shutdown,
505 	.pm_suspend = pld_ipci_pm_suspend,
506 	.pm_resume  = pld_ipci_pm_resume,
507 	.suspend_noirq = pld_ipci_suspend_noirq,
508 	.resume_noirq = pld_ipci_resume_noirq,
509 	.runtime_suspend = pld_ipci_runtime_suspend,
510 	.runtime_resume  = pld_ipci_runtime_resume,
511 	.uevent = pld_ipci_uevent,
512 	.idle_restart = pld_ipci_idle_restart_cb,
513 	.idle_shutdown = pld_ipci_idle_shutdown_cb,
514 	.set_therm_cdev_state = pld_ipci_set_thermal_state,
515 };
516 
pld_ipci_register_driver(void)517 int pld_ipci_register_driver(void)
518 {
519 	return icnss_register_driver(&pld_ipci_ops);
520 }
521 
pld_ipci_unregister_driver(void)522 void pld_ipci_unregister_driver(void)
523 {
524 	icnss_unregister_driver(&pld_ipci_ops);
525 }
526 
527 #ifdef CONFIG_SHADOW_V3
528 static inline void
pld_ipci_populate_shadow_v3_cfg(struct icnss_wlan_enable_cfg * cfg,struct pld_wlan_enable_cfg * config)529 pld_ipci_populate_shadow_v3_cfg(struct icnss_wlan_enable_cfg *cfg,
530 				struct pld_wlan_enable_cfg *config)
531 {
532 	cfg->num_shadow_reg_v3_cfg = config->num_shadow_reg_v3_cfg;
533 	cfg->shadow_reg_v3_cfg = (struct icnss_shadow_reg_v3_cfg *)
534 				 config->shadow_reg_v3_cfg;
535 }
536 #else
537 static inline void
pld_ipci_populate_shadow_v3_cfg(struct icnss_wlan_enable_cfg * cfg,struct pld_wlan_enable_cfg * config)538 pld_ipci_populate_shadow_v3_cfg(struct icnss_wlan_enable_cfg *cfg,
539 				struct pld_wlan_enable_cfg *config)
540 {
541 }
542 #endif
543 
pld_ipci_wlan_enable(struct device * dev,struct pld_wlan_enable_cfg * config,enum pld_driver_mode mode,const char * host_version)544 int pld_ipci_wlan_enable(struct device *dev, struct pld_wlan_enable_cfg *config,
545 			 enum pld_driver_mode mode, const char *host_version)
546 {
547 	struct icnss_wlan_enable_cfg cfg;
548 	enum icnss_driver_mode icnss_mode;
549 
550 	if (!dev)
551 		return -ENODEV;
552 
553 	cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
554 	cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *)
555 		config->ce_tgt_cfg;
556 	cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
557 	cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *)
558 		config->ce_svc_cfg;
559 	cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
560 	cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *)
561 		config->shadow_reg_cfg;
562 	cfg.num_shadow_reg_v2_cfg = config->num_shadow_reg_v2_cfg;
563 	cfg.shadow_reg_v2_cfg = (struct icnss_shadow_reg_v2_cfg *)
564 		config->shadow_reg_v2_cfg;
565 	cfg.rri_over_ddr_cfg_valid = config->rri_over_ddr_cfg_valid;
566 	if (config->rri_over_ddr_cfg_valid) {
567 		cfg.rri_over_ddr_cfg.base_addr_low =
568 			 config->rri_over_ddr_cfg.base_addr_low;
569 		cfg.rri_over_ddr_cfg.base_addr_high =
570 			 config->rri_over_ddr_cfg.base_addr_high;
571 	}
572 
573 	pld_ipci_populate_shadow_v3_cfg(&cfg, config);
574 
575 	switch (mode) {
576 	case PLD_FTM:
577 		icnss_mode = ICNSS_FTM;
578 		break;
579 	case PLD_EPPING:
580 		icnss_mode = ICNSS_EPPING;
581 		break;
582 	default:
583 		icnss_mode = ICNSS_MISSION;
584 		break;
585 	}
586 
587 	return icnss_wlan_enable(dev, &cfg, icnss_mode, host_version);
588 }
589 
pld_ipci_wlan_disable(struct device * dev,enum pld_driver_mode mode)590 int pld_ipci_wlan_disable(struct device *dev, enum pld_driver_mode mode)
591 {
592 	if (!dev)
593 		return -ENODEV;
594 
595 	return icnss_wlan_disable(dev, ICNSS_OFF);
596 }
597 
598 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
pld_ipci_populate_hw_cap_info(struct icnss_soc_info * icnss_info,struct pld_soc_info * info)599 static void pld_ipci_populate_hw_cap_info(struct icnss_soc_info *icnss_info,
600 					  struct pld_soc_info *info)
601 {
602 	/*WLAN HW cap info*/
603 	info->hw_cap_info.nss =
604 		(enum pld_wlan_hw_nss_info)icnss_info->rd_card_chain_cap;
605 	info->hw_cap_info.bw =
606 	(enum pld_wlan_hw_channel_bw_info)icnss_info->phy_he_channel_width_cap;
607 	info->hw_cap_info.qam =
608 		(enum pld_wlan_hw_qam_info)icnss_info->phy_qam_cap;
609 }
610 #else
pld_ipci_populate_hw_cap_info(struct icnss_soc_info * icnss_info,struct pld_soc_info * info)611 static void pld_ipci_populate_hw_cap_info(struct icnss_soc_info *icnss_info,
612 					  struct pld_soc_info *info)
613 {
614 }
615 #endif
616 
pld_ipci_get_soc_info(struct device * dev,struct pld_soc_info * info)617 int pld_ipci_get_soc_info(struct device *dev, struct pld_soc_info *info)
618 {
619 	int errno;
620 	struct icnss_soc_info icnss_info = {0};
621 
622 	if (!info || !dev)
623 		return -ENODEV;
624 
625 	errno = icnss_get_soc_info(dev, &icnss_info);
626 	if (errno)
627 		return errno;
628 
629 	info->v_addr = icnss_info.v_addr;
630 	info->p_addr = icnss_info.p_addr;
631 	info->chip_id = icnss_info.chip_id;
632 	info->chip_family = icnss_info.chip_family;
633 	info->board_id = icnss_info.board_id;
634 	info->soc_id = icnss_info.soc_id;
635 	info->fw_version = icnss_info.fw_version;
636 	strlcpy(info->fw_build_timestamp, icnss_info.fw_build_timestamp,
637 		sizeof(info->fw_build_timestamp));
638 	strlcpy(info->fw_build_id, icnss_info.fw_build_id,
639 		sizeof(info->fw_build_id));
640 
641 	pld_ipci_populate_hw_cap_info(&icnss_info, info);
642 
643 	return 0;
644 }
645 
646 /*
647  * pld_ipci_get_irq() - Get irq by ce_id
648  * @dev: device
649  * @ce_id: CE id for which irq is requested
650  *
651  * Return irq number.
652  *
653  * Return: irq number for success
654  *		Non zero failure code for errors
655  */
pld_ipci_get_irq(struct device * dev,int ce_id)656 int pld_ipci_get_irq(struct device *dev, int ce_id)
657 {
658 	uint32_t msi_data_start;
659 	uint32_t msi_data_count;
660 	uint32_t msi_irq_start;
661 	uint32_t msi_data;
662 	int ret;
663 
664 	ret = icnss_get_user_msi_assignment(dev, "CE", &msi_data_count,
665 					    &msi_data_start, &msi_irq_start);
666 	if (ret)
667 		return ret;
668 
669 	msi_data = (ce_id % msi_data_count) + msi_irq_start;
670 	ret = icnss_get_msi_irq(dev, msi_data);
671 
672 	return ret;
673 }
674 #endif
675