blob: ada4fd1126b5767b066421111d9703836381e00a [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
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080038#include "pld_internal.h"
Jeff Johnsonbbaf4e42016-10-07 12:33:22 -070039#include "pld_pcie.h"
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080040
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;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080063 int ret = 0;
64
65 pld_context = pld_get_global_context();
66 if (!pld_context) {
67 ret = -ENODEV;
68 goto out;
69 }
70
Yuanyuan Liu5e25f532016-05-25 16:26:40 -070071 ret = pld_add_dev(pld_context, &pdev->dev, PLD_BUS_TYPE_PCIE);
72 if (ret)
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080073 goto out;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080074
75 return pld_context->ops->probe(&pdev->dev,
76 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
77
78out:
79 return ret;
80}
81
82
83/**
84 * pld_pcie_remove() - Remove function for PCIE device
85 * @pdev: PCIE device
86 *
87 * The remove function will be called when PCIE device is disconnected
88 *
89 * Return: void
90 */
91static void pld_pcie_remove(struct pci_dev *pdev)
92{
93 struct pld_context *pld_context;
Yuanyuan Liud9f7a362016-01-22 14:27:12 -080094
95 pld_context = pld_get_global_context();
96
97 if (!pld_context)
98 return;
99
Yuanyuan Liu960fa212016-05-24 17:15:43 -0700100 pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_PCIE);
101
Yuanyuan Liu5e25f532016-05-25 16:26:40 -0700102 pld_del_dev(pld_context, &pdev->dev);
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800103}
104
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700105#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800106/**
107 * pld_pcie_reinit() - SSR re-initialize function for PCIE device
108 * @pdev: PCIE device
109 * @id: PCIE device ID
110 *
111 * During subsystem restart(SSR), this function will be called to
112 * re-initialize PCIE device.
113 *
114 * Return: int
115 */
116static int pld_pcie_reinit(struct pci_dev *pdev,
117 const struct pci_device_id *id)
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(&pdev->dev,
124 PLD_BUS_TYPE_PCIE, pdev, (void *)id);
125
126 return -ENODEV;
127}
128
129/**
130 * pld_pcie_shutdown() - SSR shutdown function for PCIE device
131 * @pdev: PCIE device
132 *
133 * During SSR, this function will be called to shutdown PCIE device.
134 *
135 * Return: void
136 */
137static void pld_pcie_shutdown(struct pci_dev *pdev)
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(&pdev->dev, PLD_BUS_TYPE_PCIE);
144}
145
146/**
147 * pld_pcie_crash_shutdown() - Crash shutdown function for PCIE device
148 * @pdev: PCIE device
149 *
150 * This function will be called when a crash is detected, it will shutdown
151 * the PCIE device.
152 *
153 * Return: void
154 */
155static void pld_pcie_crash_shutdown(struct pci_dev *pdev)
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(&pdev->dev, PLD_BUS_TYPE_PCIE);
162}
163
164/**
165 * pld_pcie_notify_handler() - Modem state notification callback function
166 * @pdev: PCIE device
167 * @state: modem power state
168 *
169 * This function will be called when there's a modem power state change.
170 *
171 * Return: void
172 */
173static void pld_pcie_notify_handler(struct pci_dev *pdev, int state)
174{
175 struct pld_context *pld_context;
176
177 pld_context = pld_get_global_context();
178 if (pld_context->ops->modem_status)
179 pld_context->ops->modem_status(&pdev->dev,
180 PLD_BUS_TYPE_PCIE, state);
181}
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700182
183#ifdef FEATURE_RUNTIME_PM
184/**
185 * pld_pcie_runtime_suspend() - PM runtime suspend
186 * @pdev: PCIE device
187 *
188 * PM runtime suspend callback function.
189 *
190 * Return: int
191 */
192static int pld_pcie_runtime_suspend(struct pci_dev *pdev)
193{
194 struct pld_context *pld_context;
195
196 pld_context = pld_get_global_context();
197 if (pld_context->ops->runtime_suspend)
198 return pld_context->ops->runtime_suspend(&pdev->dev,
199 PLD_BUS_TYPE_PCIE);
200
201 return -ENODEV;
202}
203
204/**
205 * pld_pcie_runtime_resume() - PM runtime resume
206 * @pdev: PCIE device
207 *
208 * PM runtime resume callback function.
209 *
210 * Return: int
211 */
212static int pld_pcie_runtime_resume(struct pci_dev *pdev)
213{
214 struct pld_context *pld_context;
215
216 pld_context = pld_get_global_context();
217 if (pld_context->ops->runtime_resume)
218 return pld_context->ops->runtime_resume(&pdev->dev,
219 PLD_BUS_TYPE_PCIE);
220
221 return -ENODEV;
222}
223#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800224#endif
225
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700226#ifdef CONFIG_PM
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800227/**
228 * pld_pcie_suspend() - Suspend callback function for power management
229 * @pdev: PCIE device
230 * @state: power state
231 *
232 * This function is to suspend the PCIE device when power management is
233 * enabled.
234 *
235 * Return: void
236 */
237static int pld_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
238{
239 struct pld_context *pld_context;
240
241 pld_context = pld_get_global_context();
242 return pld_context->ops->suspend(&pdev->dev,
243 PLD_BUS_TYPE_PCIE, state);
244}
245
246/**
247 * pld_pcie_resume() - Resume callback function for power management
248 * @pdev: PCIE device
249 *
250 * This function is to resume the PCIE device when power management is
251 * enabled.
252 *
253 * Return: void
254 */
255static int pld_pcie_resume(struct pci_dev *pdev)
256{
257 struct pld_context *pld_context;
258
259 pld_context = pld_get_global_context();
260 return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_PCIE);
261}
Yuanyuan Liu25c66e12016-05-13 10:17:46 -0700262#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800263
264static struct pci_device_id pld_pcie_id_table[] = {
265 { 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID },
266 { 0x168c, 0x003e, PCI_ANY_ID, PCI_ANY_ID },
267 { 0x168c, 0x0041, PCI_ANY_ID, PCI_ANY_ID },
268 { 0x168c, 0xabcd, PCI_ANY_ID, PCI_ANY_ID },
269 { 0x168c, 0x7021, PCI_ANY_ID, PCI_ANY_ID },
270 { 0 }
271};
272
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700273#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700274#ifdef FEATURE_RUNTIME_PM
275struct cnss_wlan_runtime_ops runtime_pm_ops = {
276 .runtime_suspend = pld_pcie_runtime_suspend,
277 .runtime_resume = pld_pcie_runtime_resume,
278};
279#endif
280
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800281struct cnss_wlan_driver pld_pcie_ops = {
282 .name = "pld_pcie",
283 .id_table = pld_pcie_id_table,
284 .probe = pld_pcie_probe,
285 .remove = pld_pcie_remove,
286 .reinit = pld_pcie_reinit,
287 .shutdown = pld_pcie_shutdown,
288 .crash_shutdown = pld_pcie_crash_shutdown,
289 .modem_status = pld_pcie_notify_handler,
290#ifdef CONFIG_PM
291 .suspend = pld_pcie_suspend,
292 .resume = pld_pcie_resume,
293#endif
Yuanyuan Liud698eb62016-04-11 11:43:30 -0700294#ifdef FEATURE_RUNTIME_PM
295 .runtime_ops = &runtime_pm_ops,
296#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800297};
298
299/**
300 * pld_pcie_register_driver() - Register PCIE device callback functions
301 *
302 * Return: int
303 */
304int pld_pcie_register_driver(void)
305{
306 return cnss_wlan_register_driver(&pld_pcie_ops);
307}
308
309/**
310 * pld_pcie_unregister_driver() - Unregister PCIE device callback functions
311 *
312 * Return: void
313 */
314void pld_pcie_unregister_driver(void)
315{
316 cnss_wlan_unregister_driver(&pld_pcie_ops);
317}
318#else
319struct pci_driver pld_pcie_ops = {
320 .name = "pld_pcie",
321 .id_table = pld_pcie_id_table,
322 .probe = pld_pcie_probe,
323 .remove = pld_pcie_remove,
324#ifdef CONFIG_PM
325 .suspend = pld_pcie_suspend,
326 .resume = pld_pcie_resume,
327#endif
328};
329
330int pld_pcie_register_driver(void)
331{
332 return pci_register_driver(&pld_pcie_ops);
333}
334
335void pld_pcie_unregister_driver(void)
336{
337 pci_unregister_driver(&pld_pcie_ops);
338}
339#endif
340
341/**
342 * pld_pcie_get_ce_id() - Get CE number for the provided IRQ
343 * @irq: IRQ number
344 *
345 * Return: CE number
346 */
347int pld_pcie_get_ce_id(int irq)
348{
349 int ce_id = irq - 100;
350 if (ce_id < CE_COUNT_MAX && ce_id >= 0)
351 return ce_id;
352
353 return -EINVAL;
354}
355
Yuanyuan Liu11f526a2016-05-18 10:22:07 -0700356#ifdef CONFIG_PLD_PCIE_CNSS
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800357#ifdef QCA_WIFI_3_0_ADRASTEA
358/**
359 * pld_pcie_wlan_enable() - Enable WLAN
360 * @config: WLAN configuration data
361 * @mode: WLAN mode
362 * @host_version: host software version
363 *
364 * This function enables WLAN FW. It passed WLAN configuration data,
365 * WLAN mode and host software version to FW.
366 *
367 * Return: 0 for success
368 * Non zero failure code for errors
369 */
370int pld_pcie_wlan_enable(struct pld_wlan_enable_cfg *config,
371 enum pld_driver_mode mode, const char *host_version)
372{
373 struct cnss_wlan_enable_cfg cfg;
374 enum cnss_driver_mode cnss_mode;
375
376 cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
377 cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *)
378 config->ce_tgt_cfg;
379 cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
380 cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *)
381 config->ce_svc_cfg;
382 cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
383 cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *)
384 config->shadow_reg_cfg;
385
386 switch (mode) {
387 case PLD_FTM:
388 cnss_mode = CNSS_FTM;
389 break;
390 case PLD_EPPING:
391 cnss_mode = CNSS_EPPING;
392 break;
393 default:
394 cnss_mode = CNSS_MISSION;
395 break;
396 }
397 return cnss_wlan_enable(&cfg, cnss_mode, host_version);
398}
399
400/**
401 * pld_pcie_wlan_disable() - Disable WLAN
402 * @mode: WLAN mode
403 *
404 * This function disables WLAN FW. It passes WLAN mode to FW.
405 *
406 * Return: 0 for success
407 * Non zero failure code for errors
408 */
409int pld_pcie_wlan_disable(enum pld_driver_mode mode)
410{
411 return cnss_wlan_disable(CNSS_OFF);
412}
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800413#endif
414
415/**
416 * pld_pcie_get_fw_files_for_target() - Get FW file names
417 * @pfw_files: buffer for FW file names
418 * @target_type: target type
419 * @target_version: target version
420 *
421 * Return target specific FW file names to the buffer.
422 *
423 * Return: 0 for success
424 * Non zero failure code for errors
425 */
426int pld_pcie_get_fw_files_for_target(struct pld_fw_files *pfw_files,
427 u32 target_type, u32 target_version)
428{
429 int ret = 0;
430 struct cnss_fw_files cnss_fw_files;
431
432 if (pfw_files == NULL)
433 return -ENODEV;
434
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700435 memset(pfw_files, 0, sizeof(*pfw_files));
436
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800437 ret = cnss_get_fw_files_for_target(&cnss_fw_files,
438 target_type, target_version);
439 if (0 != ret)
440 return ret;
441
Yuanyuan Liu6a313dc2016-05-10 14:19:10 -0700442 strlcpy(pfw_files->image_file, cnss_fw_files.image_file,
443 PLD_MAX_FILE_NAME);
444 strlcpy(pfw_files->board_data, cnss_fw_files.board_data,
445 PLD_MAX_FILE_NAME);
446 strlcpy(pfw_files->otp_data, cnss_fw_files.otp_data,
447 PLD_MAX_FILE_NAME);
448 strlcpy(pfw_files->utf_file, cnss_fw_files.utf_file,
449 PLD_MAX_FILE_NAME);
450 strlcpy(pfw_files->utf_board_data, cnss_fw_files.utf_board_data,
451 PLD_MAX_FILE_NAME);
452 strlcpy(pfw_files->epping_file, cnss_fw_files.epping_file,
453 PLD_MAX_FILE_NAME);
454 strlcpy(pfw_files->evicted_data, cnss_fw_files.evicted_data,
455 PLD_MAX_FILE_NAME);
456
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800457 return 0;
458}
459
460/**
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800461 * pld_pcie_get_codeswap_struct() - Get codeswap structure
462 * @swap_seg: buffer to codeswap information
463 *
464 * Return codeswap structure information to the buffer.
465 *
466 * Return: 0 for success
467 * Non zero failure code for errors
468 */
469int pld_pcie_get_codeswap_struct(struct pld_codeswap_codeseg_info *swap_seg)
470{
471 int ret = 0;
472 struct codeswap_codeseg_info cnss_swap_seg;
473
474 if (swap_seg == NULL)
475 return -ENODEV;
476
477 ret = cnss_get_codeswap_struct(&cnss_swap_seg);
478 if (0 != ret)
479 return ret;
480
481 memcpy(swap_seg, &cnss_swap_seg, sizeof(*swap_seg));
482 return 0;
483}
484
485/**
486 * pld_pcie_get_platform_cap() - Get platform capabilities
487 * @cap: buffer to the capabilities
488 *
489 * Return capabilities to the buffer.
490 *
491 * Return: 0 for success
492 * Non zero failure code for errors
493 */
494int pld_pcie_get_platform_cap(struct pld_platform_cap *cap)
495{
496 int ret = 0;
497 struct cnss_platform_cap cnss_cap;
498
499 if (cap == NULL)
500 return -ENODEV;
501
502 ret = cnss_get_platform_cap(&cnss_cap);
503 if (0 != ret)
504 return ret;
505
506 memcpy(cap, &cnss_cap, sizeof(*cap));
507 return 0;
508}
509
510/**
511 * pld_pcie_set_driver_status() - Set driver status
512 * @status: driver status
513 *
514 * Return: void
515 */
516void pld_pcie_set_driver_status(enum pld_driver_status status)
517{
518 enum cnss_driver_status cnss_status;
519
520 switch (status) {
521 case PLD_UNINITIALIZED:
522 cnss_status = CNSS_UNINITIALIZED;
523 break;
524 case PLD_INITIALIZED:
525 cnss_status = CNSS_INITIALIZED;
526 break;
527 default:
528 cnss_status = CNSS_LOAD_UNLOAD;
529 break;
530 }
531 cnss_set_driver_status(cnss_status);
532}
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800533#endif
Yuanyuan Liud9f7a362016-01-22 14:27:12 -0800534#endif