blob: 66dada94dcbe17256bdea5476182c6da648ea0bc [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
Yue Mae6a7a322016-08-31 11:09:23 -070035/*
36 * This is a hack to make sure this change can be merged before the
37 * kernel defconfig change (switching to CNSS2 platform driver) merged,
38 * otherwise the kernel change has to depend on this change to be merged.
39 * Once the kernel change is merged, these CNSS flags can be removed
40 * so that only cnss2.h is needed.
41 */
42#ifdef CONFIG_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080043#include <net/cnss.h>
44#endif
Yue Mae6a7a322016-08-31 11:09:23 -070045#ifdef CONFIG_CNSS2
46#include <net/cnss2.h>
47#endif
48#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080049
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080050#include "pld_internal.h"
Jeff Johnsonbbaf4e42016-10-07 12:33:22 -070051#include "pld_pcie.h"
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080052
53#ifdef CONFIG_PCI
54
55#ifdef QCA_WIFI_3_0_ADRASTEA
56#define CE_COUNT_MAX 12
57#else
58#define CE_COUNT_MAX 8
59#endif
60
61/**
62 * pld_pcie_probe() - Probe function for PCIE platform driver
63 * @pdev: PCIE device
64 * @id: PCIE device ID table
65 *
66 * The probe function will be called when PCIE device provided
67 * in the ID table is detected.
68 *
69 * Return: int
70 */
71static int pld_pcie_probe(struct pci_dev *pdev,
72 const struct pci_device_id *id)
73{
74 struct pld_context *pld_context;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080075 int ret = 0;
76
77 pld_context = pld_get_global_context();
78 if (!pld_context) {
79 ret = -ENODEV;
80 goto out;
81 }
82
Yuanyuan Liu5e25f532016-05-25 16:26:40 -070083 ret = pld_add_dev(pld_context, &pdev->dev, PLD_BUS_TYPE_PCIE);
84 if (ret)
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080085 goto out;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080086
87 return pld_context->ops->probe(&pdev->dev,
88 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
89
90out:
91 return ret;
92}
93
94
95/**
96 * pld_pcie_remove() - Remove function for PCIE device
97 * @pdev: PCIE device
98 *
99 * The remove function will be called when PCIE device is disconnected
100 *
101 * Return: void
102 */
103static void pld_pcie_remove(struct pci_dev *pdev)
104{
105 struct pld_context *pld_context;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800106
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 Liu5e25f532016-05-25 16:26:40 -0700114 pld_del_dev(pld_context, &pdev->dev);
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800115}
116
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700117#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800118/**
119 * pld_pcie_reinit() - SSR re-initialize function for PCIE device
120 * @pdev: PCIE device
121 * @id: PCIE device ID
122 *
123 * During subsystem restart(SSR), this function will be called to
124 * re-initialize PCIE device.
125 *
126 * Return: int
127 */
128static int pld_pcie_reinit(struct pci_dev *pdev,
129 const struct pci_device_id *id)
130{
131 struct pld_context *pld_context;
132
133 pld_context = pld_get_global_context();
134 if (pld_context->ops->reinit)
135 return pld_context->ops->reinit(&pdev->dev,
136 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
137
138 return -ENODEV;
139}
140
141/**
142 * pld_pcie_shutdown() - SSR shutdown function for PCIE device
143 * @pdev: PCIE device
144 *
145 * During SSR, this function will be called to shutdown PCIE device.
146 *
147 * Return: void
148 */
149static void pld_pcie_shutdown(struct pci_dev *pdev)
150{
151 struct pld_context *pld_context;
152
153 pld_context = pld_get_global_context();
154 if (pld_context->ops->shutdown)
155 pld_context->ops->shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE);
156}
157
158/**
159 * pld_pcie_crash_shutdown() - Crash shutdown function for PCIE device
160 * @pdev: PCIE device
161 *
162 * This function will be called when a crash is detected, it will shutdown
163 * the PCIE device.
164 *
165 * Return: void
166 */
167static void pld_pcie_crash_shutdown(struct pci_dev *pdev)
168{
169 struct pld_context *pld_context;
170
171 pld_context = pld_get_global_context();
172 if (pld_context->ops->crash_shutdown)
173 pld_context->ops->crash_shutdown(&pdev->dev, PLD_BUS_TYPE_PCIE);
174}
175
176/**
177 * pld_pcie_notify_handler() - Modem state notification callback function
178 * @pdev: PCIE device
179 * @state: modem power state
180 *
181 * This function will be called when there's a modem power state change.
182 *
183 * Return: void
184 */
185static void pld_pcie_notify_handler(struct pci_dev *pdev, int state)
186{
187 struct pld_context *pld_context;
188
189 pld_context = pld_get_global_context();
190 if (pld_context->ops->modem_status)
191 pld_context->ops->modem_status(&pdev->dev,
192 PLD_BUS_TYPE_PCIE, state);
193}
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700194
195#ifdef FEATURE_RUNTIME_PM
196/**
197 * pld_pcie_runtime_suspend() - PM runtime suspend
198 * @pdev: PCIE device
199 *
200 * PM runtime suspend callback function.
201 *
202 * Return: int
203 */
204static int pld_pcie_runtime_suspend(struct pci_dev *pdev)
205{
206 struct pld_context *pld_context;
207
208 pld_context = pld_get_global_context();
209 if (pld_context->ops->runtime_suspend)
210 return pld_context->ops->runtime_suspend(&pdev->dev,
211 PLD_BUS_TYPE_PCIE);
212
213 return -ENODEV;
214}
215
216/**
217 * pld_pcie_runtime_resume() - PM runtime resume
218 * @pdev: PCIE device
219 *
220 * PM runtime resume callback function.
221 *
222 * Return: int
223 */
224static int pld_pcie_runtime_resume(struct pci_dev *pdev)
225{
226 struct pld_context *pld_context;
227
228 pld_context = pld_get_global_context();
229 if (pld_context->ops->runtime_resume)
230 return pld_context->ops->runtime_resume(&pdev->dev,
231 PLD_BUS_TYPE_PCIE);
232
233 return -ENODEV;
234}
235#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800236#endif
237
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700238#ifdef CONFIG_PM
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800239/**
240 * pld_pcie_suspend() - Suspend callback function for power management
241 * @pdev: PCIE device
242 * @state: power state
243 *
244 * This function is to suspend the PCIE device when power management is
245 * enabled.
246 *
247 * Return: void
248 */
249static int pld_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
250{
251 struct pld_context *pld_context;
252
253 pld_context = pld_get_global_context();
254 return pld_context->ops->suspend(&pdev->dev,
255 PLD_BUS_TYPE_PCIE, state);
256}
257
258/**
259 * pld_pcie_resume() - Resume callback function for power management
260 * @pdev: PCIE device
261 *
262 * This function is to resume the PCIE device when power management is
263 * enabled.
264 *
265 * Return: void
266 */
267static int pld_pcie_resume(struct pci_dev *pdev)
268{
269 struct pld_context *pld_context;
270
271 pld_context = pld_get_global_context();
272 return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_PCIE);
273}
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700274#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800275
276static struct pci_device_id pld_pcie_id_table[] = {
277 { 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID },
278 { 0x168c, 0x003e, PCI_ANY_ID, PCI_ANY_ID },
279 { 0x168c, 0x0041, PCI_ANY_ID, PCI_ANY_ID },
280 { 0x168c, 0xabcd, PCI_ANY_ID, PCI_ANY_ID },
281 { 0x168c, 0x7021, PCI_ANY_ID, PCI_ANY_ID },
282 { 0 }
283};
284
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700285#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700286#ifdef FEATURE_RUNTIME_PM
287struct cnss_wlan_runtime_ops runtime_pm_ops = {
288 .runtime_suspend = pld_pcie_runtime_suspend,
289 .runtime_resume = pld_pcie_runtime_resume,
290};
291#endif
292
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800293struct cnss_wlan_driver pld_pcie_ops = {
294 .name = "pld_pcie",
295 .id_table = pld_pcie_id_table,
296 .probe = pld_pcie_probe,
297 .remove = pld_pcie_remove,
298 .reinit = pld_pcie_reinit,
299 .shutdown = pld_pcie_shutdown,
300 .crash_shutdown = pld_pcie_crash_shutdown,
301 .modem_status = pld_pcie_notify_handler,
302#ifdef CONFIG_PM
303 .suspend = pld_pcie_suspend,
304 .resume = pld_pcie_resume,
305#endif
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700306#ifdef FEATURE_RUNTIME_PM
307 .runtime_ops = &runtime_pm_ops,
308#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800309};
310
311/**
312 * pld_pcie_register_driver() - Register PCIE device callback functions
313 *
314 * Return: int
315 */
316int pld_pcie_register_driver(void)
317{
318 return cnss_wlan_register_driver(&pld_pcie_ops);
319}
320
321/**
322 * pld_pcie_unregister_driver() - Unregister PCIE device callback functions
323 *
324 * Return: void
325 */
326void pld_pcie_unregister_driver(void)
327{
328 cnss_wlan_unregister_driver(&pld_pcie_ops);
329}
330#else
331struct pci_driver pld_pcie_ops = {
332 .name = "pld_pcie",
333 .id_table = pld_pcie_id_table,
334 .probe = pld_pcie_probe,
335 .remove = pld_pcie_remove,
336#ifdef CONFIG_PM
337 .suspend = pld_pcie_suspend,
338 .resume = pld_pcie_resume,
339#endif
340};
341
342int pld_pcie_register_driver(void)
343{
344 return pci_register_driver(&pld_pcie_ops);
345}
346
347void pld_pcie_unregister_driver(void)
348{
349 pci_unregister_driver(&pld_pcie_ops);
350}
351#endif
352
353/**
354 * pld_pcie_get_ce_id() - Get CE number for the provided IRQ
355 * @irq: IRQ number
356 *
357 * Return: CE number
358 */
359int pld_pcie_get_ce_id(int irq)
360{
361 int ce_id = irq - 100;
362 if (ce_id < CE_COUNT_MAX && ce_id >= 0)
363 return ce_id;
364
365 return -EINVAL;
366}
367
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700368#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800369#ifdef QCA_WIFI_3_0_ADRASTEA
370/**
371 * pld_pcie_wlan_enable() - Enable WLAN
372 * @config: WLAN configuration data
373 * @mode: WLAN mode
374 * @host_version: host software version
375 *
376 * This function enables WLAN FW. It passed WLAN configuration data,
377 * WLAN mode and host software version to FW.
378 *
379 * Return: 0 for success
380 * Non zero failure code for errors
381 */
382int pld_pcie_wlan_enable(struct pld_wlan_enable_cfg *config,
383 enum pld_driver_mode mode, const char *host_version)
384{
385 struct cnss_wlan_enable_cfg cfg;
386 enum cnss_driver_mode cnss_mode;
387
388 cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
389 cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *)
390 config->ce_tgt_cfg;
391 cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
392 cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *)
393 config->ce_svc_cfg;
394 cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
395 cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *)
396 config->shadow_reg_cfg;
397
398 switch (mode) {
399 case PLD_FTM:
400 cnss_mode = CNSS_FTM;
401 break;
402 case PLD_EPPING:
403 cnss_mode = CNSS_EPPING;
404 break;
405 default:
406 cnss_mode = CNSS_MISSION;
407 break;
408 }
409 return cnss_wlan_enable(&cfg, cnss_mode, host_version);
410}
411
412/**
413 * pld_pcie_wlan_disable() - Disable WLAN
414 * @mode: WLAN mode
415 *
416 * This function disables WLAN FW. It passes WLAN mode to FW.
417 *
418 * Return: 0 for success
419 * Non zero failure code for errors
420 */
421int pld_pcie_wlan_disable(enum pld_driver_mode mode)
422{
423 return cnss_wlan_disable(CNSS_OFF);
424}
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800425#endif
426
427/**
428 * pld_pcie_get_fw_files_for_target() - Get FW file names
429 * @pfw_files: buffer for FW file names
430 * @target_type: target type
431 * @target_version: target version
432 *
433 * Return target specific FW file names to the buffer.
434 *
435 * Return: 0 for success
436 * Non zero failure code for errors
437 */
438int pld_pcie_get_fw_files_for_target(struct pld_fw_files *pfw_files,
439 u32 target_type, u32 target_version)
440{
441 int ret = 0;
442 struct cnss_fw_files cnss_fw_files;
443
444 if (pfw_files == NULL)
445 return -ENODEV;
446
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700447 memset(pfw_files, 0, sizeof(*pfw_files));
448
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800449 ret = cnss_get_fw_files_for_target(&cnss_fw_files,
450 target_type, target_version);
451 if (0 != ret)
452 return ret;
453
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700454 strlcpy(pfw_files->image_file, cnss_fw_files.image_file,
455 PLD_MAX_FILE_NAME);
456 strlcpy(pfw_files->board_data, cnss_fw_files.board_data,
457 PLD_MAX_FILE_NAME);
458 strlcpy(pfw_files->otp_data, cnss_fw_files.otp_data,
459 PLD_MAX_FILE_NAME);
460 strlcpy(pfw_files->utf_file, cnss_fw_files.utf_file,
461 PLD_MAX_FILE_NAME);
462 strlcpy(pfw_files->utf_board_data, cnss_fw_files.utf_board_data,
463 PLD_MAX_FILE_NAME);
464 strlcpy(pfw_files->epping_file, cnss_fw_files.epping_file,
465 PLD_MAX_FILE_NAME);
466 strlcpy(pfw_files->evicted_data, cnss_fw_files.evicted_data,
467 PLD_MAX_FILE_NAME);
468
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800469 return 0;
470}
471
472/**
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800473 * pld_pcie_get_platform_cap() - Get platform capabilities
474 * @cap: buffer to the capabilities
475 *
476 * Return capabilities to the buffer.
477 *
478 * Return: 0 for success
479 * Non zero failure code for errors
480 */
481int pld_pcie_get_platform_cap(struct pld_platform_cap *cap)
482{
483 int ret = 0;
484 struct cnss_platform_cap cnss_cap;
485
486 if (cap == NULL)
487 return -ENODEV;
488
489 ret = cnss_get_platform_cap(&cnss_cap);
490 if (0 != ret)
491 return ret;
492
493 memcpy(cap, &cnss_cap, sizeof(*cap));
494 return 0;
495}
496
497/**
498 * pld_pcie_set_driver_status() - Set driver status
499 * @status: driver status
500 *
501 * Return: void
502 */
503void pld_pcie_set_driver_status(enum pld_driver_status status)
504{
505 enum cnss_driver_status cnss_status;
506
507 switch (status) {
508 case PLD_UNINITIALIZED:
509 cnss_status = CNSS_UNINITIALIZED;
510 break;
511 case PLD_INITIALIZED:
512 cnss_status = CNSS_INITIALIZED;
513 break;
514 default:
515 cnss_status = CNSS_LOAD_UNLOAD;
516 break;
517 }
518 cnss_set_driver_status(cnss_status);
519}
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800520#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800521#endif