blob: e5585e6a8728aef94b390a880ba287950c04cc4f [file] [log] [blame]
Yuanyuan Liud9f7a362016-01-22 14:27:12 -08001/*
2 * Copyright (c) 2016 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28#include <linux/platform_device.h>
29#include <linux/err.h>
30#include <linux/pci.h>
31#include <linux/list.h>
32#include <linux/slab.h>
33
Yuanyuan Liu11f526a2016-05-18 10:22:07 -070034#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080035#include <net/cnss.h>
36#endif
37
38#include "pld_common.h"
39#include "pld_internal.h"
40
41#ifdef CONFIG_PCI
42
43#ifdef QCA_WIFI_3_0_ADRASTEA
44#define CE_COUNT_MAX 12
45#else
46#define CE_COUNT_MAX 8
47#endif
48
49/**
50 * pld_pcie_probe() - Probe function for PCIE platform driver
51 * @pdev: PCIE device
52 * @id: PCIE device ID table
53 *
54 * The probe function will be called when PCIE device provided
55 * in the ID table is detected.
56 *
57 * Return: int
58 */
59static int pld_pcie_probe(struct pci_dev *pdev,
60 const struct pci_device_id *id)
61{
62 struct pld_context *pld_context;
63 unsigned long flags;
64 struct dev_node *dev_node;
65 int ret = 0;
66
67 pld_context = pld_get_global_context();
68 if (!pld_context) {
69 ret = -ENODEV;
70 goto out;
71 }
72
73 dev_node = kzalloc(sizeof(*dev_node), GFP_KERNEL);
74 if (dev_node == NULL) {
75 ret = -ENOMEM;
76 goto out;
77 }
78 dev_node->dev = &pdev->dev;
79 dev_node->bus_type = PLD_BUS_TYPE_PCIE;
80
81 spin_lock_irqsave(&pld_context->pld_lock, flags);
82 list_add_tail(&dev_node->list, &pld_context->dev_list);
83 spin_unlock_irqrestore(&pld_context->pld_lock, flags);
84
85 return pld_context->ops->probe(&pdev->dev,
86 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
87
88out:
89 return ret;
90}
91
92
93/**
94 * pld_pcie_remove() - Remove function for PCIE device
95 * @pdev: PCIE device
96 *
97 * The remove function will be called when PCIE device is disconnected
98 *
99 * Return: void
100 */
101static void pld_pcie_remove(struct pci_dev *pdev)
102{
103 struct pld_context *pld_context;
104 unsigned long flags;
105 struct dev_node *dev_node, *tmp;
106
107 pld_context = pld_get_global_context();
108
109 if (!pld_context)
110 return;
111
Yuanyuan Liu960fa212016-05-24 17:15:43 -0700112 pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_PCIE);
113
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800114 spin_lock_irqsave(&pld_context->pld_lock, flags);
115 list_for_each_entry_safe(dev_node, tmp, &pld_context->dev_list, list) {
116 if (dev_node->dev == &pdev->dev) {
117 list_del(&dev_node->list);
118 kfree(dev_node);
119 }
120 }
121 spin_unlock_irqrestore(&pld_context->pld_lock, flags);
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800122}
123
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700124#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800125/**
126 * pld_pcie_reinit() - SSR re-initialize function for PCIE device
127 * @pdev: PCIE device
128 * @id: PCIE device ID
129 *
130 * During subsystem restart(SSR), this function will be called to
131 * re-initialize PCIE device.
132 *
133 * Return: int
134 */
135static int pld_pcie_reinit(struct pci_dev *pdev,
136 const struct pci_device_id *id)
137{
138 struct pld_context *pld_context;
139
140 pld_context = pld_get_global_context();
141 if (pld_context->ops->reinit)
142 return pld_context->ops->reinit(&pdev->dev,
143 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
144
145 return -ENODEV;
146}
147
148/**
149 * pld_pcie_shutdown() - SSR shutdown function for PCIE device
150 * @pdev: PCIE device
151 *
152 * During SSR, this function will be called to shutdown PCIE device.
153 *
154 * Return: void
155 */
156static void pld_pcie_shutdown(struct pci_dev *pdev)
157{
158 struct pld_context *pld_context;
159
160 pld_context = pld_get_global_context();
161 if (pld_context->ops->shutdown)
162 pld_context->ops->shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE);
163}
164
165/**
166 * pld_pcie_crash_shutdown() - Crash shutdown function for PCIE device
167 * @pdev: PCIE device
168 *
169 * This function will be called when a crash is detected, it will shutdown
170 * the PCIE device.
171 *
172 * Return: void
173 */
174static void pld_pcie_crash_shutdown(struct pci_dev *pdev)
175{
176 struct pld_context *pld_context;
177
178 pld_context = pld_get_global_context();
179 if (pld_context->ops->crash_shutdown)
180 pld_context->ops->crash_shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE);
181}
182
183/**
184 * pld_pcie_notify_handler() - Modem state notification callback function
185 * @pdev: PCIE device
186 * @state: modem power state
187 *
188 * This function will be called when there's a modem power state change.
189 *
190 * Return: void
191 */
192static void pld_pcie_notify_handler(struct pci_dev *pdev, int state)
193{
194 struct pld_context *pld_context;
195
196 pld_context = pld_get_global_context();
197 if (pld_context->ops->modem_status)
198 pld_context->ops->modem_status(&pdev->dev,
199 PLD_BUS_TYPE_PCIE, state);
200}
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700201
202#ifdef FEATURE_RUNTIME_PM
203/**
204 * pld_pcie_runtime_suspend() - PM runtime suspend
205 * @pdev: PCIE device
206 *
207 * PM runtime suspend callback function.
208 *
209 * Return: int
210 */
211static int pld_pcie_runtime_suspend(struct pci_dev *pdev)
212{
213 struct pld_context *pld_context;
214
215 pld_context = pld_get_global_context();
216 if (pld_context->ops->runtime_suspend)
217 return pld_context->ops->runtime_suspend(&pdev->dev,
218 PLD_BUS_TYPE_PCIE);
219
220 return -ENODEV;
221}
222
223/**
224 * pld_pcie_runtime_resume() - PM runtime resume
225 * @pdev: PCIE device
226 *
227 * PM runtime resume callback function.
228 *
229 * Return: int
230 */
231static int pld_pcie_runtime_resume(struct pci_dev *pdev)
232{
233 struct pld_context *pld_context;
234
235 pld_context = pld_get_global_context();
236 if (pld_context->ops->runtime_resume)
237 return pld_context->ops->runtime_resume(&pdev->dev,
238 PLD_BUS_TYPE_PCIE);
239
240 return -ENODEV;
241}
242#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800243#endif
244
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700245#ifdef CONFIG_PM
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800246/**
247 * pld_pcie_suspend() - Suspend callback function for power management
248 * @pdev: PCIE device
249 * @state: power state
250 *
251 * This function is to suspend the PCIE device when power management is
252 * enabled.
253 *
254 * Return: void
255 */
256static int pld_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
257{
258 struct pld_context *pld_context;
259
260 pld_context = pld_get_global_context();
261 return pld_context->ops->suspend(&pdev->dev,
262 PLD_BUS_TYPE_PCIE, state);
263}
264
265/**
266 * pld_pcie_resume() - Resume callback function for power management
267 * @pdev: PCIE device
268 *
269 * This function is to resume the PCIE device when power management is
270 * enabled.
271 *
272 * Return: void
273 */
274static int pld_pcie_resume(struct pci_dev *pdev)
275{
276 struct pld_context *pld_context;
277
278 pld_context = pld_get_global_context();
279 return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_PCIE);
280}
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700281#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800282
283static struct pci_device_id pld_pcie_id_table[] = {
284 { 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID },
285 { 0x168c, 0x003e, PCI_ANY_ID, PCI_ANY_ID },
286 { 0x168c, 0x0041, PCI_ANY_ID, PCI_ANY_ID },
287 { 0x168c, 0xabcd, PCI_ANY_ID, PCI_ANY_ID },
288 { 0x168c, 0x7021, PCI_ANY_ID, PCI_ANY_ID },
289 { 0 }
290};
291
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700292#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700293#ifdef FEATURE_RUNTIME_PM
294struct cnss_wlan_runtime_ops runtime_pm_ops = {
295 .runtime_suspend = pld_pcie_runtime_suspend,
296 .runtime_resume = pld_pcie_runtime_resume,
297};
298#endif
299
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800300struct cnss_wlan_driver pld_pcie_ops = {
301 .name = "pld_pcie",
302 .id_table = pld_pcie_id_table,
303 .probe = pld_pcie_probe,
304 .remove = pld_pcie_remove,
305 .reinit = pld_pcie_reinit,
306 .shutdown = pld_pcie_shutdown,
307 .crash_shutdown = pld_pcie_crash_shutdown,
308 .modem_status = pld_pcie_notify_handler,
309#ifdef CONFIG_PM
310 .suspend = pld_pcie_suspend,
311 .resume = pld_pcie_resume,
312#endif
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700313#ifdef FEATURE_RUNTIME_PM
314 .runtime_ops = &runtime_pm_ops,
315#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800316};
317
318/**
319 * pld_pcie_register_driver() - Register PCIE device callback functions
320 *
321 * Return: int
322 */
323int pld_pcie_register_driver(void)
324{
325 return cnss_wlan_register_driver(&pld_pcie_ops);
326}
327
328/**
329 * pld_pcie_unregister_driver() - Unregister PCIE device callback functions
330 *
331 * Return: void
332 */
333void pld_pcie_unregister_driver(void)
334{
335 cnss_wlan_unregister_driver(&pld_pcie_ops);
336}
337#else
338struct pci_driver pld_pcie_ops = {
339 .name = "pld_pcie",
340 .id_table = pld_pcie_id_table,
341 .probe = pld_pcie_probe,
342 .remove = pld_pcie_remove,
343#ifdef CONFIG_PM
344 .suspend = pld_pcie_suspend,
345 .resume = pld_pcie_resume,
346#endif
347};
348
349int pld_pcie_register_driver(void)
350{
351 return pci_register_driver(&pld_pcie_ops);
352}
353
354void pld_pcie_unregister_driver(void)
355{
356 pci_unregister_driver(&pld_pcie_ops);
357}
358#endif
359
360/**
361 * pld_pcie_get_ce_id() - Get CE number for the provided IRQ
362 * @irq: IRQ number
363 *
364 * Return: CE number
365 */
366int pld_pcie_get_ce_id(int irq)
367{
368 int ce_id = irq - 100;
369 if (ce_id < CE_COUNT_MAX && ce_id >= 0)
370 return ce_id;
371
372 return -EINVAL;
373}
374
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700375#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800376#ifdef QCA_WIFI_3_0_ADRASTEA
377/**
378 * pld_pcie_wlan_enable() - Enable WLAN
379 * @config: WLAN configuration data
380 * @mode: WLAN mode
381 * @host_version: host software version
382 *
383 * This function enables WLAN FW. It passed WLAN configuration data,
384 * WLAN mode and host software version to FW.
385 *
386 * Return: 0 for success
387 * Non zero failure code for errors
388 */
389int pld_pcie_wlan_enable(struct pld_wlan_enable_cfg *config,
390 enum pld_driver_mode mode, const char *host_version)
391{
392 struct cnss_wlan_enable_cfg cfg;
393 enum cnss_driver_mode cnss_mode;
394
395 cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
396 cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *)
397 config->ce_tgt_cfg;
398 cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
399 cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *)
400 config->ce_svc_cfg;
401 cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
402 cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *)
403 config->shadow_reg_cfg;
404
405 switch (mode) {
406 case PLD_FTM:
407 cnss_mode = CNSS_FTM;
408 break;
409 case PLD_EPPING:
410 cnss_mode = CNSS_EPPING;
411 break;
412 default:
413 cnss_mode = CNSS_MISSION;
414 break;
415 }
416 return cnss_wlan_enable(&cfg, cnss_mode, host_version);
417}
418
419/**
420 * pld_pcie_wlan_disable() - Disable WLAN
421 * @mode: WLAN mode
422 *
423 * This function disables WLAN FW. It passes WLAN mode to FW.
424 *
425 * Return: 0 for success
426 * Non zero failure code for errors
427 */
428int pld_pcie_wlan_disable(enum pld_driver_mode mode)
429{
430 return cnss_wlan_disable(CNSS_OFF);
431}
432
433/**
434 * pld_pcie_set_fw_debug_mode() - Set FW debug mode
435 * @mode: 0 for QXDM, 1 for WMI
436 *
437 * Switch Fw debug mode between DIAG logging and WMI logging.
438 *
439 * Return: 0 for success
440 * Non zero failure code for errors
441 */
442int pld_pcie_set_fw_debug_mode(bool mode)
443{
444 return cnss_set_fw_debug_mode(mode);
445}
446#endif
447
448/**
449 * pld_pcie_get_fw_files_for_target() - Get FW file names
450 * @pfw_files: buffer for FW file names
451 * @target_type: target type
452 * @target_version: target version
453 *
454 * Return target specific FW file names to the buffer.
455 *
456 * Return: 0 for success
457 * Non zero failure code for errors
458 */
459int pld_pcie_get_fw_files_for_target(struct pld_fw_files *pfw_files,
460 u32 target_type, u32 target_version)
461{
462 int ret = 0;
463 struct cnss_fw_files cnss_fw_files;
464
465 if (pfw_files == NULL)
466 return -ENODEV;
467
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700468 memset(pfw_files, 0, sizeof(*pfw_files));
469
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800470 ret = cnss_get_fw_files_for_target(&cnss_fw_files,
471 target_type, target_version);
472 if (0 != ret)
473 return ret;
474
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700475 strlcpy(pfw_files->image_file, cnss_fw_files.image_file,
476 PLD_MAX_FILE_NAME);
477 strlcpy(pfw_files->board_data, cnss_fw_files.board_data,
478 PLD_MAX_FILE_NAME);
479 strlcpy(pfw_files->otp_data, cnss_fw_files.otp_data,
480 PLD_MAX_FILE_NAME);
481 strlcpy(pfw_files->utf_file, cnss_fw_files.utf_file,
482 PLD_MAX_FILE_NAME);
483 strlcpy(pfw_files->utf_board_data, cnss_fw_files.utf_board_data,
484 PLD_MAX_FILE_NAME);
485 strlcpy(pfw_files->epping_file, cnss_fw_files.epping_file,
486 PLD_MAX_FILE_NAME);
487 strlcpy(pfw_files->evicted_data, cnss_fw_files.evicted_data,
488 PLD_MAX_FILE_NAME);
489
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800490 return 0;
491}
492
493/**
494 * pld_pcie_get_fw_image() - Get FW image descriptor
495 * @image_desc_info: buffer for image descriptor
496 *
497 * Return FW image descriptor to the buffer.
498 *
499 * Return: 0 for success
500 * Non zero failure code for errors
501 */
502int pld_pcie_get_fw_image(struct pld_image_desc_info *image_desc_info)
503{
504 int ret = 0;
505 struct image_desc_info cnss_image_desc_info;
506
507 if (image_desc_info == NULL)
508 return -ENODEV;
509
510 ret = cnss_get_fw_image(&cnss_image_desc_info);
511 if (0 != ret)
512 return ret;
513
514 memcpy(image_desc_info, &cnss_image_desc_info,
515 sizeof(*image_desc_info));
516 return 0;
517}
518
519/**
520 * pld_pcie_get_codeswap_struct() - Get codeswap structure
521 * @swap_seg: buffer to codeswap information
522 *
523 * Return codeswap structure information to the buffer.
524 *
525 * Return: 0 for success
526 * Non zero failure code for errors
527 */
528int pld_pcie_get_codeswap_struct(struct pld_codeswap_codeseg_info *swap_seg)
529{
530 int ret = 0;
531 struct codeswap_codeseg_info cnss_swap_seg;
532
533 if (swap_seg == NULL)
534 return -ENODEV;
535
536 ret = cnss_get_codeswap_struct(&cnss_swap_seg);
537 if (0 != ret)
538 return ret;
539
540 memcpy(swap_seg, &cnss_swap_seg, sizeof(*swap_seg));
541 return 0;
542}
543
544/**
545 * pld_pcie_get_platform_cap() - Get platform capabilities
546 * @cap: buffer to the capabilities
547 *
548 * Return capabilities to the buffer.
549 *
550 * Return: 0 for success
551 * Non zero failure code for errors
552 */
553int pld_pcie_get_platform_cap(struct pld_platform_cap *cap)
554{
555 int ret = 0;
556 struct cnss_platform_cap cnss_cap;
557
558 if (cap == NULL)
559 return -ENODEV;
560
561 ret = cnss_get_platform_cap(&cnss_cap);
562 if (0 != ret)
563 return ret;
564
565 memcpy(cap, &cnss_cap, sizeof(*cap));
566 return 0;
567}
568
569/**
570 * pld_pcie_set_driver_status() - Set driver status
571 * @status: driver status
572 *
573 * Return: void
574 */
575void pld_pcie_set_driver_status(enum pld_driver_status status)
576{
577 enum cnss_driver_status cnss_status;
578
579 switch (status) {
580 case PLD_UNINITIALIZED:
581 cnss_status = CNSS_UNINITIALIZED;
582 break;
583 case PLD_INITIALIZED:
584 cnss_status = CNSS_INITIALIZED;
585 break;
586 default:
587 cnss_status = CNSS_LOAD_UNLOAD;
588 break;
589 }
590 cnss_set_driver_status(cnss_status);
591}
592#endif
593
594#endif