Commit | Line | Data |
---|---|---|
84813812 LJ |
1 | /* |
2 | * Linux DHD Bus Module for PCIE | |
3 | * | |
4 | * Copyright (C) 1999-2017, Broadcom Corporation | |
5 | * | |
6 | * Unless you and Broadcom execute a separate written software license | |
7 | * agreement governing use of this software, this software is licensed to you | |
8 | * under the terms of the GNU General Public License version 2 (the "GPL"), | |
9 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the | |
10 | * following added to such license: | |
11 | * | |
12 | * As a special exception, the copyright holders of this software give you | |
13 | * permission to link this software with independent modules, and to copy and | |
14 | * distribute the resulting executable under terms of your choice, provided that | |
15 | * you also meet, for each linked independent module, the terms and conditions of | |
16 | * the license of that module. An independent module is a module which is not | |
17 | * derived from this software. The special exception does not apply to any | |
18 | * modifications of the software. | |
19 | * | |
20 | * Notwithstanding the above, under no circumstances may you combine this | |
21 | * software in any way with any other Broadcom software provided under a license | |
22 | * other than the GPL, without Broadcom's express prior written consent. | |
23 | * | |
24 | * | |
25 | * <<Broadcom-WL-IPTag/Open:>> | |
26 | * | |
27 | * $Id: dhd_pcie_linux.c 707536 2017-06-28 04:23:48Z $ | |
28 | */ | |
29 | ||
30 | ||
31 | /* include files */ | |
32 | #include <typedefs.h> | |
33 | #include <bcmutils.h> | |
34 | #include <bcmdevs.h> | |
35 | #include <siutils.h> | |
36 | #include <hndsoc.h> | |
37 | #include <hndpmu.h> | |
38 | #include <sbchipc.h> | |
39 | #if defined(DHD_DEBUG) | |
40 | #include <hnd_armtrap.h> | |
41 | #include <hnd_cons.h> | |
42 | #endif /* defined(DHD_DEBUG) */ | |
43 | #include <dngl_stats.h> | |
44 | #include <pcie_core.h> | |
45 | #include <dhd.h> | |
46 | #include <dhd_bus.h> | |
47 | #include <dhd_proto.h> | |
48 | #include <dhd_dbg.h> | |
49 | #include <dhdioctl.h> | |
50 | #include <bcmmsgbuf.h> | |
51 | #include <pcicfg.h> | |
52 | #include <dhd_pcie.h> | |
53 | #include <dhd_linux.h> | |
54 | #ifdef CONFIG_ARCH_MSM | |
55 | #if defined(CONFIG_PCI_MSM) || defined(CONFIG_ARCH_MSM8996) | |
56 | #include <linux/msm_pcie.h> | |
57 | #else | |
58 | #include <mach/msm_pcie.h> | |
59 | #endif /* CONFIG_PCI_MSM */ | |
60 | #endif /* CONFIG_ARCH_MSM */ | |
61 | #ifdef PCIE_OOB | |
62 | #include "ftdi_sio_external.h" | |
63 | #endif /* PCIE_OOB */ | |
64 | #include <linux/irq.h> | |
65 | #ifdef USE_SMMU_ARCH_MSM | |
66 | #include <asm/dma-iommu.h> | |
67 | #include <linux/iommu.h> | |
68 | #include <linux/of.h> | |
69 | #include <linux/platform_device.h> | |
70 | #endif /* USE_SMMU_ARCH_MSM */ | |
71 | ||
72 | #define PCI_CFG_RETRY 10 | |
73 | #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */ | |
74 | #define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */ | |
75 | ||
76 | #define OSL_PKTTAG_CLEAR(p) \ | |
77 | do { \ | |
78 | struct sk_buff *s = (struct sk_buff *)(p); \ | |
79 | ASSERT(OSL_PKTTAG_SZ == 32); \ | |
80 | *(uint32 *)(&s->cb[0]) = 0; *(uint32 *)(&s->cb[4]) = 0; \ | |
81 | *(uint32 *)(&s->cb[8]) = 0; *(uint32 *)(&s->cb[12]) = 0; \ | |
82 | *(uint32 *)(&s->cb[16]) = 0; *(uint32 *)(&s->cb[20]) = 0; \ | |
83 | *(uint32 *)(&s->cb[24]) = 0; *(uint32 *)(&s->cb[28]) = 0; \ | |
84 | } while (0) | |
85 | ||
86 | #ifdef PCIE_OOB | |
87 | #define HOST_WAKE 4 /* GPIO_0 (HOST_WAKE) - Output from WLAN */ | |
88 | #define DEVICE_WAKE 5 /* GPIO_1 (DEVICE_WAKE) - Input to WLAN */ | |
89 | #define BIT_WL_REG_ON 6 | |
90 | #define BIT_BT_REG_ON 7 | |
91 | ||
92 | int gpio_handle_val = 0; | |
93 | unsigned char gpio_port = 0; | |
94 | unsigned char gpio_direction = 0; | |
95 | #define OOB_PORT "ttyUSB0" | |
96 | #endif /* PCIE_OOB */ | |
97 | ||
98 | /* user defined data structures */ | |
99 | ||
100 | typedef struct dhd_pc_res { | |
101 | uint32 bar0_size; | |
102 | void* bar0_addr; | |
103 | uint32 bar1_size; | |
104 | void* bar1_addr; | |
105 | } pci_config_res, *pPci_config_res; | |
106 | ||
107 | typedef bool (*dhdpcie_cb_fn_t)(void *); | |
108 | ||
109 | typedef struct dhdpcie_info | |
110 | { | |
111 | dhd_bus_t *bus; | |
112 | osl_t *osh; | |
113 | struct pci_dev *dev; /* pci device handle */ | |
114 | volatile char *regs; /* pci device memory va */ | |
115 | volatile char *tcm; /* pci device memory va */ | |
116 | uint32 tcm_size; /* pci device memory size */ | |
117 | struct pcos_info *pcos_info; | |
118 | uint16 last_intrstatus; /* to cache intrstatus */ | |
119 | int irq; | |
120 | char pciname[32]; | |
121 | struct pci_saved_state* default_state; | |
122 | struct pci_saved_state* state; | |
123 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
124 | void *os_cxt; /* Pointer to per-OS private data */ | |
125 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
126 | #ifdef DHD_WAKE_STATUS | |
127 | spinlock_t pcie_lock; | |
128 | unsigned int total_wake_count; | |
129 | int pkt_wake; | |
130 | int wake_irq; | |
131 | #endif /* DHD_WAKE_STATUS */ | |
132 | #ifdef USE_SMMU_ARCH_MSM | |
133 | void *smmu_cxt; | |
134 | #endif /* USE_SMMU_ARCH_MSM */ | |
135 | } dhdpcie_info_t; | |
136 | ||
137 | ||
138 | struct pcos_info { | |
139 | dhdpcie_info_t *pc; | |
140 | spinlock_t lock; | |
141 | wait_queue_head_t intr_wait_queue; | |
142 | struct timer_list tuning_timer; | |
143 | int tuning_timer_exp; | |
144 | atomic_t timer_enab; | |
145 | struct tasklet_struct tuning_tasklet; | |
146 | }; | |
147 | ||
148 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
149 | typedef struct dhdpcie_os_info { | |
150 | int oob_irq_num; /* valid when hardware or software oob in use */ | |
151 | unsigned long oob_irq_flags; /* valid when hardware or software oob in use */ | |
152 | bool oob_irq_registered; | |
153 | bool oob_irq_enabled; | |
154 | bool oob_irq_wake_enabled; | |
155 | spinlock_t oob_irq_spinlock; | |
156 | void *dev; /* handle to the underlying device */ | |
157 | } dhdpcie_os_info_t; | |
158 | static irqreturn_t wlan_oob_irq(int irq, void *data); | |
159 | #if defined(CUSTOMER_HW2) && defined(CONFIG_ARCH_APQ8084) | |
160 | extern struct brcm_pcie_wake brcm_pcie_wake; | |
161 | #endif /* CUSTOMER_HW2 && CONFIG_ARCH_APQ8084 */ | |
162 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
163 | ||
164 | #ifdef USE_SMMU_ARCH_MSM | |
165 | typedef struct dhdpcie_smmu_info { | |
166 | struct dma_iommu_mapping *smmu_mapping; | |
167 | dma_addr_t smmu_iova_start; | |
168 | size_t smmu_iova_len; | |
169 | } dhdpcie_smmu_info_t; | |
170 | #endif /* USE_SMMU_ARCH_MSM */ | |
171 | ||
172 | /* function declarations */ | |
173 | static int __devinit | |
174 | dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); | |
175 | static void __devexit | |
176 | dhdpcie_pci_remove(struct pci_dev *pdev); | |
177 | static int dhdpcie_init(struct pci_dev *pdev); | |
178 | static irqreturn_t dhdpcie_isr(int irq, void *arg); | |
179 | /* OS Routine functions for PCI suspend/resume */ | |
180 | ||
181 | static int dhdpcie_set_suspend_resume(dhd_bus_t *bus, bool state); | |
182 | static int dhdpcie_resume_host_dev(dhd_bus_t *bus); | |
183 | static int dhdpcie_suspend_host_dev(dhd_bus_t *bus); | |
184 | static int dhdpcie_resume_dev(struct pci_dev *dev); | |
185 | static int dhdpcie_suspend_dev(struct pci_dev *dev); | |
186 | #ifdef DHD_PCIE_RUNTIMEPM | |
187 | static int dhdpcie_pm_suspend(struct device *dev); | |
188 | static int dhdpcie_pm_prepare(struct device *dev); | |
189 | static int dhdpcie_pm_resume(struct device *dev); | |
190 | static void dhdpcie_pm_complete(struct device *dev); | |
191 | #else | |
192 | static int dhdpcie_pci_suspend(struct pci_dev *dev, pm_message_t state); | |
193 | static int dhdpcie_pci_resume(struct pci_dev *dev); | |
194 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
195 | ||
196 | static struct pci_device_id dhdpcie_pci_devid[] __devinitdata = { | |
197 | { vendor: 0x14e4, | |
198 | device: PCI_ANY_ID, | |
199 | subvendor: PCI_ANY_ID, | |
200 | subdevice: PCI_ANY_ID, | |
201 | class: PCI_CLASS_NETWORK_OTHER << 8, | |
202 | class_mask: 0xffff00, | |
203 | driver_data: 0, | |
204 | }, | |
205 | { 0, 0, 0, 0, 0, 0, 0} | |
206 | }; | |
207 | MODULE_DEVICE_TABLE(pci, dhdpcie_pci_devid); | |
208 | ||
209 | /* Power Management Hooks */ | |
210 | #ifdef DHD_PCIE_RUNTIMEPM | |
211 | static const struct dev_pm_ops dhd_pcie_pm_ops = { | |
212 | .prepare = dhdpcie_pm_prepare, | |
213 | .suspend = dhdpcie_pm_suspend, | |
214 | .resume = dhdpcie_pm_resume, | |
215 | .complete = dhdpcie_pm_complete, | |
216 | }; | |
217 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
218 | ||
219 | static struct pci_driver dhdpcie_driver = { | |
220 | node: {&dhdpcie_driver.node, &dhdpcie_driver.node}, | |
221 | name: "pcieh", | |
222 | id_table: dhdpcie_pci_devid, | |
223 | probe: dhdpcie_pci_probe, | |
224 | remove: dhdpcie_pci_remove, | |
225 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) | |
226 | save_state: NULL, | |
227 | #endif | |
228 | #ifdef DHD_PCIE_RUNTIMEPM | |
229 | .driver.pm = &dhd_pcie_pm_ops, | |
230 | #else | |
231 | suspend: dhdpcie_pci_suspend, | |
232 | resume: dhdpcie_pci_resume, | |
233 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
234 | }; | |
235 | ||
236 | int dhdpcie_init_succeeded = FALSE; | |
237 | ||
238 | #ifdef USE_SMMU_ARCH_MSM | |
239 | static int dhdpcie_smmu_init(struct pci_dev *pdev, void *smmu_cxt) | |
240 | { | |
241 | struct dma_iommu_mapping *mapping; | |
242 | struct device_node *root_node = NULL; | |
243 | dhdpcie_smmu_info_t *smmu_info = (dhdpcie_smmu_info_t *)smmu_cxt; | |
244 | int smmu_iova_address[2]; | |
245 | char *wlan_node = "android,bcmdhd_wlan"; | |
246 | char *wlan_smmu_node = "wlan-smmu-iova-address"; | |
247 | int atomic_ctx = 1; | |
248 | int s1_bypass = 1; | |
249 | int ret = 0; | |
250 | ||
251 | DHD_ERROR(("%s: SMMU initialize\n", __FUNCTION__)); | |
252 | ||
253 | root_node = of_find_compatible_node(NULL, NULL, wlan_node); | |
254 | if (!root_node) { | |
255 | WARN(1, "failed to get device node of BRCM WLAN\n"); | |
256 | return -ENODEV; | |
257 | } | |
258 | ||
259 | if (of_property_read_u32_array(root_node, wlan_smmu_node, | |
260 | smmu_iova_address, 2) == 0) { | |
261 | DHD_ERROR(("%s : get SMMU start address 0x%x, size 0x%x\n", | |
262 | __FUNCTION__, smmu_iova_address[0], smmu_iova_address[1])); | |
263 | smmu_info->smmu_iova_start = smmu_iova_address[0]; | |
264 | smmu_info->smmu_iova_len = smmu_iova_address[1]; | |
265 | } else { | |
266 | printf("%s : can't get smmu iova address property\n", | |
267 | __FUNCTION__); | |
268 | return -ENODEV; | |
269 | } | |
270 | ||
271 | if (smmu_info->smmu_iova_len <= 0) { | |
272 | DHD_ERROR(("%s: Invalid smmu iova len %d\n", | |
273 | __FUNCTION__, (int)smmu_info->smmu_iova_len)); | |
274 | return -EINVAL; | |
275 | } | |
276 | ||
277 | DHD_ERROR(("%s : SMMU init start\n", __FUNCTION__)); | |
278 | mapping = arm_iommu_create_mapping(&platform_bus_type, | |
279 | smmu_info->smmu_iova_start, smmu_info->smmu_iova_len); | |
280 | if (IS_ERR(mapping)) { | |
281 | DHD_ERROR(("%s: create mapping failed, err = %d\n", | |
282 | __FUNCTION__, ret)); | |
283 | ret = PTR_ERR(mapping); | |
284 | goto map_fail; | |
285 | } | |
286 | ||
287 | ret = iommu_domain_set_attr(mapping->domain, | |
288 | DOMAIN_ATTR_ATOMIC, &atomic_ctx); | |
289 | if (ret) { | |
290 | DHD_ERROR(("%s: set atomic_ctx attribute failed, err = %d\n", | |
291 | __FUNCTION__, ret)); | |
292 | goto set_attr_fail; | |
293 | } | |
294 | ||
295 | ret = iommu_domain_set_attr(mapping->domain, | |
296 | DOMAIN_ATTR_S1_BYPASS, &s1_bypass); | |
297 | if (ret < 0) { | |
298 | DHD_ERROR(("%s: set s1_bypass attribute failed, err = %d\n", | |
299 | __FUNCTION__, ret)); | |
300 | goto set_attr_fail; | |
301 | } | |
302 | ||
303 | ret = arm_iommu_attach_device(&pdev->dev, mapping); | |
304 | if (ret) { | |
305 | DHD_ERROR(("%s: attach device failed, err = %d\n", | |
306 | __FUNCTION__, ret)); | |
307 | goto attach_fail; | |
308 | } | |
309 | ||
310 | smmu_info->smmu_mapping = mapping; | |
311 | ||
312 | return ret; | |
313 | ||
314 | attach_fail: | |
315 | set_attr_fail: | |
316 | arm_iommu_release_mapping(mapping); | |
317 | map_fail: | |
318 | return ret; | |
319 | } | |
320 | ||
321 | static void dhdpcie_smmu_remove(struct pci_dev *pdev, void *smmu_cxt) | |
322 | { | |
323 | dhdpcie_smmu_info_t *smmu_info; | |
324 | ||
325 | if (!smmu_cxt) { | |
326 | return; | |
327 | } | |
328 | ||
329 | smmu_info = (dhdpcie_smmu_info_t *)smmu_cxt; | |
330 | if (smmu_info->smmu_mapping) { | |
331 | arm_iommu_detach_device(&pdev->dev); | |
332 | arm_iommu_release_mapping(smmu_info->smmu_mapping); | |
333 | smmu_info->smmu_mapping = NULL; | |
334 | } | |
335 | } | |
336 | #endif /* USE_SMMU_ARCH_MSM */ | |
337 | ||
338 | #ifdef DHD_PCIE_RUNTIMEPM | |
339 | static int dhdpcie_pm_suspend(struct device *dev) | |
340 | { | |
341 | int ret = 0; | |
342 | struct pci_dev *pdev = to_pci_dev(dev); | |
343 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
344 | dhd_bus_t *bus = NULL; | |
345 | unsigned long flags; | |
346 | ||
347 | if (pch) { | |
348 | bus = pch->bus; | |
349 | } | |
350 | if (!bus) { | |
351 | return ret; | |
352 | } | |
353 | ||
354 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
355 | if (!DHD_BUS_BUSY_CHECK_IDLE(bus->dhd)) { | |
356 | DHD_ERROR(("%s: Bus not IDLE!! dhd_bus_busy_state = 0x%x\n", | |
357 | __FUNCTION__, bus->dhd->dhd_bus_busy_state)); | |
358 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
359 | return -EBUSY; | |
360 | } | |
361 | DHD_BUS_BUSY_SET_SUSPEND_IN_PROGRESS(bus->dhd); | |
362 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
363 | ||
364 | if (!bus->dhd->dongle_reset) | |
365 | ret = dhdpcie_set_suspend_resume(bus, TRUE); | |
366 | ||
367 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
368 | DHD_BUS_BUSY_CLEAR_SUSPEND_IN_PROGRESS(bus->dhd); | |
369 | dhd_os_busbusy_wake(bus->dhd); | |
370 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
371 | ||
372 | return ret; | |
373 | ||
374 | } | |
375 | ||
376 | static int dhdpcie_pm_prepare(struct device *dev) | |
377 | { | |
378 | struct pci_dev *pdev = to_pci_dev(dev); | |
379 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
380 | dhd_bus_t *bus = NULL; | |
381 | ||
382 | if (pch) { | |
383 | bus = pch->bus; | |
384 | DHD_DISABLE_RUNTIME_PM(bus->dhd); | |
385 | } | |
386 | ||
387 | bus->chk_pm = TRUE; | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static int dhdpcie_pm_resume(struct device *dev) | |
392 | { | |
393 | int ret = 0; | |
394 | struct pci_dev *pdev = to_pci_dev(dev); | |
395 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
396 | dhd_bus_t *bus = NULL; | |
397 | unsigned long flags; | |
398 | ||
399 | if (pch) { | |
400 | bus = pch->bus; | |
401 | } | |
402 | if (!bus) { | |
403 | return ret; | |
404 | } | |
405 | ||
406 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
407 | DHD_BUS_BUSY_SET_RESUME_IN_PROGRESS(bus->dhd); | |
408 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
409 | ||
410 | if (!bus->dhd->dongle_reset) { | |
411 | ret = dhdpcie_set_suspend_resume(bus, FALSE); | |
412 | bus->chk_pm = FALSE; | |
413 | } | |
414 | ||
415 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
416 | DHD_BUS_BUSY_CLEAR_RESUME_IN_PROGRESS(bus->dhd); | |
417 | dhd_os_busbusy_wake(bus->dhd); | |
418 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
419 | ||
420 | return ret; | |
421 | } | |
422 | ||
423 | static void dhdpcie_pm_complete(struct device *dev) | |
424 | { | |
425 | struct pci_dev *pdev = to_pci_dev(dev); | |
426 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
427 | dhd_bus_t *bus = NULL; | |
428 | ||
429 | if (pch) { | |
430 | bus = pch->bus; | |
431 | DHD_ENABLE_RUNTIME_PM(bus->dhd); | |
432 | } | |
433 | ||
434 | return; | |
435 | } | |
436 | #else | |
437 | static int dhdpcie_pci_suspend(struct pci_dev * pdev, pm_message_t state) | |
438 | { | |
439 | int ret = 0; | |
440 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
441 | dhd_bus_t *bus = NULL; | |
442 | unsigned long flags; | |
443 | ||
444 | if (pch) { | |
445 | bus = pch->bus; | |
446 | } | |
447 | if (!bus) { | |
448 | return ret; | |
449 | } | |
450 | ||
451 | BCM_REFERENCE(state); | |
452 | ||
453 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
454 | if (!DHD_BUS_BUSY_CHECK_IDLE(bus->dhd)) { | |
455 | DHD_ERROR(("%s: Bus not IDLE!! dhd_bus_busy_state = 0x%x\n", | |
456 | __FUNCTION__, bus->dhd->dhd_bus_busy_state)); | |
457 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
458 | return -EBUSY; | |
459 | } | |
460 | DHD_BUS_BUSY_SET_SUSPEND_IN_PROGRESS(bus->dhd); | |
461 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
462 | ||
463 | if (!bus->dhd->dongle_reset) | |
464 | ret = dhdpcie_set_suspend_resume(bus, TRUE); | |
465 | ||
466 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
467 | DHD_BUS_BUSY_CLEAR_SUSPEND_IN_PROGRESS(bus->dhd); | |
468 | dhd_os_busbusy_wake(bus->dhd); | |
469 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
470 | ||
471 | return ret; | |
472 | } | |
473 | ||
474 | static int dhdpcie_pci_resume(struct pci_dev *pdev) | |
475 | { | |
476 | int ret = 0; | |
477 | dhdpcie_info_t *pch = pci_get_drvdata(pdev); | |
478 | dhd_bus_t *bus = NULL; | |
479 | unsigned long flags; | |
480 | ||
481 | if (pch) { | |
482 | bus = pch->bus; | |
483 | } | |
484 | if (!bus) { | |
485 | return ret; | |
486 | } | |
487 | ||
488 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
489 | DHD_BUS_BUSY_SET_RESUME_IN_PROGRESS(bus->dhd); | |
490 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
491 | ||
492 | if (!bus->dhd->dongle_reset) | |
493 | ret = dhdpcie_set_suspend_resume(bus, FALSE); | |
494 | ||
495 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
496 | DHD_BUS_BUSY_CLEAR_RESUME_IN_PROGRESS(bus->dhd); | |
497 | dhd_os_busbusy_wake(bus->dhd); | |
498 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
499 | ||
500 | return ret; | |
501 | } | |
502 | ||
503 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
504 | ||
505 | static int dhdpcie_set_suspend_resume(dhd_bus_t *bus, bool state) | |
506 | { | |
507 | int ret = 0; | |
508 | ||
509 | ASSERT(bus && !bus->dhd->dongle_reset); | |
510 | ||
511 | #ifdef DHD_PCIE_RUNTIMEPM | |
512 | /* if wakelock is held during suspend, return failed */ | |
513 | if (state == TRUE && dhd_os_check_wakelock_all(bus->dhd)) { | |
514 | return -EBUSY; | |
515 | } | |
516 | mutex_lock(&bus->pm_lock); | |
517 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
518 | ||
519 | /* When firmware is not loaded do the PCI bus */ | |
520 | /* suspend/resume only */ | |
521 | if (bus->dhd->busstate == DHD_BUS_DOWN) { | |
522 | ret = dhdpcie_pci_suspend_resume(bus, state); | |
523 | #ifdef DHD_PCIE_RUNTIMEPM | |
524 | mutex_unlock(&bus->pm_lock); | |
525 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
526 | return ret; | |
527 | } | |
528 | ||
529 | ret = dhdpcie_bus_suspend(bus, state); | |
530 | ||
531 | #ifdef DHD_PCIE_RUNTIMEPM | |
532 | mutex_unlock(&bus->pm_lock); | |
533 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
534 | ||
535 | return ret; | |
536 | } | |
537 | ||
538 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
539 | extern void dhd_dpc_tasklet_kill(dhd_pub_t *dhdp); | |
540 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
541 | ||
542 | static int dhdpcie_suspend_dev(struct pci_dev *dev) | |
543 | { | |
544 | int ret; | |
545 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
546 | dhdpcie_info_t *pch = pci_get_drvdata(dev); | |
547 | dhd_bus_t *bus = pch->bus; | |
548 | ||
549 | if (bus->is_linkdown) { | |
550 | DHD_ERROR(("%s: PCIe link is down\n", __FUNCTION__)); | |
551 | return BCME_ERROR; | |
552 | } | |
553 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
554 | DHD_TRACE_HW4(("%s: Enter\n", __FUNCTION__)); | |
555 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
556 | dhd_dpc_tasklet_kill(bus->dhd); | |
557 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
558 | pci_save_state(dev); | |
559 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
560 | pch->state = pci_store_saved_state(dev); | |
561 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
562 | pci_enable_wake(dev, PCI_D0, TRUE); | |
563 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) | |
564 | if (pci_is_enabled(dev)) | |
565 | #endif | |
566 | pci_disable_device(dev); | |
567 | ||
568 | ret = pci_set_power_state(dev, PCI_D3hot); | |
569 | if (ret) { | |
570 | DHD_ERROR(("%s: pci_set_power_state error %d\n", | |
571 | __FUNCTION__, ret)); | |
572 | } | |
573 | dev->state_saved = FALSE; | |
574 | return ret; | |
575 | } | |
576 | ||
577 | #ifdef DHD_WAKE_STATUS | |
578 | int bcmpcie_get_total_wake(struct dhd_bus *bus) | |
579 | { | |
580 | dhdpcie_info_t *pch = pci_get_drvdata(bus->dev); | |
581 | ||
582 | return pch->total_wake_count; | |
583 | } | |
584 | ||
585 | int bcmpcie_set_get_wake(struct dhd_bus *bus, int flag) | |
586 | { | |
587 | dhdpcie_info_t *pch = pci_get_drvdata(bus->dev); | |
588 | unsigned long flags; | |
589 | int ret; | |
590 | ||
591 | spin_lock_irqsave(&pch->pcie_lock, flags); | |
592 | ||
593 | ret = pch->pkt_wake; | |
594 | pch->total_wake_count += flag; | |
595 | pch->pkt_wake = flag; | |
596 | ||
597 | spin_unlock_irqrestore(&pch->pcie_lock, flags); | |
598 | return ret; | |
599 | } | |
600 | #endif /* DHD_WAKE_STATUS */ | |
601 | ||
602 | static int dhdpcie_resume_dev(struct pci_dev *dev) | |
603 | { | |
604 | int err = 0; | |
605 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
606 | dhdpcie_info_t *pch = pci_get_drvdata(dev); | |
607 | pci_load_and_free_saved_state(dev, &pch->state); | |
608 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
609 | DHD_TRACE_HW4(("%s: Enter\n", __FUNCTION__)); | |
610 | dev->state_saved = TRUE; | |
611 | pci_restore_state(dev); | |
612 | err = pci_enable_device(dev); | |
613 | if (err) { | |
614 | printf("%s:pci_enable_device error %d \n", __FUNCTION__, err); | |
615 | goto out; | |
616 | } | |
617 | pci_set_master(dev); | |
618 | err = pci_set_power_state(dev, PCI_D0); | |
619 | if (err) { | |
620 | printf("%s:pci_set_power_state error %d \n", __FUNCTION__, err); | |
621 | goto out; | |
622 | } | |
623 | ||
624 | out: | |
625 | return err; | |
626 | } | |
627 | ||
628 | static int dhdpcie_resume_host_dev(dhd_bus_t *bus) | |
629 | { | |
630 | int bcmerror = 0; | |
631 | #ifdef USE_EXYNOS_PCIE_RC_PMPATCH | |
632 | bcmerror = exynos_pcie_pm_resume(SAMSUNG_PCIE_CH_NUM); | |
633 | #endif /* USE_EXYNOS_PCIE_RC_PMPATCH */ | |
634 | #ifdef CONFIG_ARCH_MSM | |
635 | bcmerror = dhdpcie_start_host_pcieclock(bus); | |
636 | #endif /* CONFIG_ARCH_MSM */ | |
637 | #ifdef CONFIG_ARCH_TEGRA | |
638 | bcmerror = tegra_pcie_pm_resume(); | |
639 | #endif /* CONFIG_ARCH_TEGRA */ | |
640 | if (bcmerror < 0) { | |
641 | DHD_ERROR(("%s: PCIe RC resume failed!!! (%d)\n", | |
642 | __FUNCTION__, bcmerror)); | |
643 | bus->is_linkdown = 1; | |
644 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
645 | #ifdef CONFIG_ARCH_MSM | |
646 | bus->no_cfg_restore = 1; | |
647 | #endif /* CONFIG_ARCH_MSM */ | |
648 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
649 | } | |
650 | ||
651 | return bcmerror; | |
652 | } | |
653 | ||
654 | static int dhdpcie_suspend_host_dev(dhd_bus_t *bus) | |
655 | { | |
656 | int bcmerror = 0; | |
657 | #ifdef USE_EXYNOS_PCIE_RC_PMPATCH | |
658 | if (bus->rc_dev) { | |
659 | pci_save_state(bus->rc_dev); | |
660 | } else { | |
661 | DHD_ERROR(("%s: RC %x:%x handle is NULL\n", | |
662 | __FUNCTION__, PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID)); | |
663 | } | |
664 | exynos_pcie_pm_suspend(SAMSUNG_PCIE_CH_NUM); | |
665 | #endif /* USE_EXYNOS_PCIE_RC_PMPATCH */ | |
666 | #ifdef CONFIG_ARCH_MSM | |
667 | bcmerror = dhdpcie_stop_host_pcieclock(bus); | |
668 | #endif /* CONFIG_ARCH_MSM */ | |
669 | #ifdef CONFIG_ARCH_TEGRA | |
670 | bcmerror = tegra_pcie_pm_suspend(); | |
671 | #endif /* CONFIG_ARCH_TEGRA */ | |
672 | return bcmerror; | |
673 | } | |
674 | ||
675 | #if defined(PCIE_RC_VENDOR_ID) && defined(PCIE_RC_DEVICE_ID) | |
676 | uint32 | |
677 | dhdpcie_rc_config_read(dhd_bus_t *bus, uint offset) | |
678 | { | |
679 | uint val = -1; /* Initialise to 0xfffffff */ | |
680 | if (bus->rc_dev) { | |
681 | pci_read_config_dword(bus->rc_dev, offset, &val); | |
682 | OSL_DELAY(100); | |
683 | } else { | |
684 | DHD_ERROR(("%s: RC %x:%x handle is NULL\n", | |
685 | __FUNCTION__, PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID)); | |
686 | } | |
687 | DHD_ERROR(("%s: RC %x:%x offset 0x%x val 0x%x\n", | |
688 | __FUNCTION__, PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID, offset, val)); | |
689 | return (val); | |
690 | } | |
691 | ||
692 | /* | |
693 | * Reads/ Writes the value of capability register | |
694 | * from the given CAP_ID section of PCI Root Port | |
695 | * | |
696 | * Arguements | |
697 | * @bus current dhd_bus_t pointer | |
698 | * @cap Capability or Extended Capability ID to get | |
699 | * @offset offset of Register to Read | |
700 | * @is_ext TRUE if @cap is given for Extended Capability | |
701 | * @is_write is set to TRUE to indicate write | |
702 | * @val value to write | |
703 | * | |
704 | * Return Value | |
705 | * Returns 0xffffffff on error | |
706 | * on write success returns BCME_OK (0) | |
707 | * on Read Success returns the value of register requested | |
708 | * Note: caller shoud ensure valid capability ID and Ext. Capability ID. | |
709 | */ | |
710 | ||
711 | uint32 | |
712 | dhdpcie_rc_access_cap(dhd_bus_t *bus, int cap, uint offset, bool is_ext, bool is_write, | |
713 | uint32 writeval) | |
714 | { | |
715 | int cap_ptr = 0; | |
716 | uint32 ret = -1; | |
717 | uint32 readval; | |
718 | ||
719 | if (!(bus->rc_dev)) { | |
720 | DHD_ERROR(("%s: RC %x:%x handle is NULL\n", | |
721 | __FUNCTION__, PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID)); | |
722 | return ret; | |
723 | } | |
724 | ||
725 | /* Find Capability offset */ | |
726 | if (is_ext) { | |
727 | /* removing max EXT_CAP_ID check as | |
728 | * linux kernel definition's max value is not upadted yet as per spec | |
729 | */ | |
730 | cap_ptr = pci_find_ext_capability(bus->rc_dev, cap); | |
731 | ||
732 | } else { | |
733 | /* removing max PCI_CAP_ID_MAX check as | |
734 | * pervious kernel versions dont have this definition | |
735 | */ | |
736 | cap_ptr = pci_find_capability(bus->rc_dev, cap); | |
737 | } | |
738 | ||
739 | /* Return if capability with given ID not found */ | |
740 | if (cap_ptr == 0) { | |
741 | DHD_ERROR(("%s: RC %x:%x PCI Cap(0x%02x) not supported.\n", | |
742 | __FUNCTION__, PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID, cap)); | |
743 | return BCME_ERROR; | |
744 | } | |
745 | ||
746 | if (is_write) { | |
747 | ret = pci_write_config_dword(bus->rc_dev, (cap_ptr + offset), writeval); | |
748 | if (ret) { | |
749 | DHD_ERROR(("%s: pci_write_config_dword failed. cap=%d offset=%d\n", | |
750 | __FUNCTION__, cap, offset)); | |
751 | return BCME_ERROR; | |
752 | } | |
753 | ret = BCME_OK; | |
754 | ||
755 | } else { | |
756 | ||
757 | ret = pci_read_config_dword(bus->rc_dev, (cap_ptr + offset), &readval); | |
758 | ||
759 | if (ret) { | |
760 | DHD_ERROR(("%s: pci_read_config_dword failed. cap=%d offset=%d\n", | |
761 | __FUNCTION__, cap, offset)); | |
762 | return BCME_ERROR; | |
763 | } | |
764 | ret = readval; | |
765 | } | |
766 | ||
767 | return ret; | |
768 | } | |
769 | ||
770 | /* API wrapper to read Root Port link capability | |
771 | * Returns 2 = GEN2 1 = GEN1 BCME_ERR on linkcap not found | |
772 | */ | |
773 | ||
774 | uint32 dhd_debug_get_rc_linkcap(dhd_bus_t *bus) | |
775 | { | |
776 | uint32 linkcap = -1; | |
777 | linkcap = dhdpcie_rc_access_cap(bus, PCIE_CAP_ID_EXP, | |
778 | PCIE_CAP_LINKCAP_OFFSET, FALSE, FALSE, 0); | |
779 | linkcap &= PCIE_CAP_LINKCAP_LNKSPEED_MASK; | |
780 | return linkcap; | |
781 | } | |
782 | #endif | |
783 | ||
784 | int dhdpcie_pci_suspend_resume(dhd_bus_t *bus, bool state) | |
785 | { | |
786 | int rc; | |
787 | ||
788 | struct pci_dev *dev = bus->dev; | |
789 | ||
790 | if (state) { | |
791 | #ifndef BCMPCIE_OOB_HOST_WAKE | |
792 | dhdpcie_pme_active(bus->osh, state); | |
793 | #endif /* !BCMPCIE_OOB_HOST_WAKE */ | |
794 | rc = dhdpcie_suspend_dev(dev); | |
795 | if (!rc) { | |
796 | dhdpcie_suspend_host_dev(bus); | |
797 | } | |
798 | } else { | |
799 | dhdpcie_resume_host_dev(bus); | |
800 | rc = dhdpcie_resume_dev(dev); | |
801 | #ifndef BCMPCIE_OOB_HOST_WAKE | |
802 | dhdpcie_pme_active(bus->osh, state); | |
803 | #endif /* !BCMPCIE_OOB_HOST_WAKE */ | |
804 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) | |
805 | #if defined(DHD_HANG_SEND_UP_TEST) | |
806 | if (bus->is_linkdown || | |
807 | bus->dhd->req_hang_type == HANG_REASON_PCIE_RC_LINK_UP_FAIL) | |
808 | #else /* DHD_HANG_SEND_UP_TEST */ | |
809 | if (bus->is_linkdown) | |
810 | #endif /* DHD_HANG_SEND_UP_TEST */ | |
811 | { | |
812 | bus->dhd->hang_reason = HANG_REASON_PCIE_RC_LINK_UP_FAIL; | |
813 | dhd_os_send_hang_message(bus->dhd); | |
814 | } | |
815 | #endif | |
816 | } | |
817 | return rc; | |
818 | } | |
819 | ||
820 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) | |
821 | static int dhdpcie_device_scan(struct device *dev, void *data) | |
822 | { | |
823 | struct pci_dev *pcidev; | |
824 | int *cnt = data; | |
825 | ||
826 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
827 | #pragma GCC diagnostic push | |
828 | #pragma GCC diagnostic ignored "-Wcast-qual" | |
829 | #endif | |
830 | pcidev = container_of(dev, struct pci_dev, dev); | |
831 | #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) | |
832 | #pragma GCC diagnostic pop | |
833 | #endif | |
834 | if (pcidev->vendor != 0x14e4) | |
835 | return 0; | |
836 | ||
837 | DHD_INFO(("Found Broadcom PCI device 0x%04x\n", pcidev->device)); | |
838 | *cnt += 1; | |
839 | if (pcidev->driver && strcmp(pcidev->driver->name, dhdpcie_driver.name)) | |
840 | DHD_ERROR(("Broadcom PCI Device 0x%04x has allocated with driver %s\n", | |
841 | pcidev->device, pcidev->driver->name)); | |
842 | ||
843 | return 0; | |
844 | } | |
845 | #endif /* LINUX_VERSION >= 2.6.0 */ | |
846 | ||
847 | int | |
848 | dhdpcie_bus_register(void) | |
849 | { | |
850 | int error = 0; | |
851 | ||
852 | ||
853 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) | |
854 | if (!(error = pci_module_init(&dhdpcie_driver))) | |
855 | return 0; | |
856 | ||
857 | DHD_ERROR(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error)); | |
858 | #else | |
859 | if (!(error = pci_register_driver(&dhdpcie_driver))) { | |
860 | bus_for_each_dev(dhdpcie_driver.driver.bus, NULL, &error, dhdpcie_device_scan); | |
861 | if (!error) { | |
862 | DHD_ERROR(("No Broadcom PCI device enumerated!\n")); | |
863 | } else if (!dhdpcie_init_succeeded) { | |
864 | DHD_ERROR(("%s: dhdpcie initialize failed.\n", __FUNCTION__)); | |
865 | } else { | |
866 | return 0; | |
867 | } | |
868 | ||
869 | pci_unregister_driver(&dhdpcie_driver); | |
870 | error = BCME_ERROR; | |
871 | } | |
872 | #endif /* LINUX_VERSION < 2.6.0 */ | |
873 | ||
874 | return error; | |
875 | } | |
876 | ||
877 | ||
878 | void | |
879 | dhdpcie_bus_unregister(void) | |
880 | { | |
881 | pci_unregister_driver(&dhdpcie_driver); | |
882 | } | |
883 | ||
884 | int __devinit | |
885 | dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |
886 | { | |
887 | DHD_MUTEX_LOCK(); | |
888 | ||
889 | if (dhdpcie_chipmatch (pdev->vendor, pdev->device)) { | |
890 | DHD_ERROR(("%s: chipmatch failed!!\n", __FUNCTION__)); | |
891 | return -ENODEV; | |
892 | } | |
893 | printf("PCI_PROBE: bus %X, slot %X,vendor %X, device %X" | |
894 | "(good PCI location)\n", pdev->bus->number, | |
895 | PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device); | |
896 | ||
897 | if (dhdpcie_init (pdev)) { | |
898 | DHD_ERROR(("%s: PCIe Enumeration failed\n", __FUNCTION__)); | |
899 | return -ENODEV; | |
900 | } | |
901 | ||
902 | #ifdef BCMPCIE_DISABLE_ASYNC_SUSPEND | |
903 | /* disable async suspend */ | |
904 | device_disable_async_suspend(&pdev->dev); | |
905 | #endif /* BCMPCIE_DISABLE_ASYNC_SUSPEND */ | |
906 | ||
907 | DHD_TRACE(("%s: PCIe Enumeration done!!\n", __FUNCTION__)); | |
908 | DHD_MUTEX_UNLOCK(); | |
909 | return 0; | |
910 | } | |
911 | ||
912 | int | |
913 | dhdpcie_detach(dhdpcie_info_t *pch) | |
914 | { | |
915 | if (pch) { | |
916 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
917 | if (!dhd_download_fw_on_driverload) { | |
918 | pci_load_and_free_saved_state(pch->dev, &pch->default_state); | |
919 | } | |
920 | #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
921 | MFREE(pch->osh, pch, sizeof(dhdpcie_info_t)); | |
922 | } | |
923 | return 0; | |
924 | } | |
925 | ||
926 | ||
927 | void __devexit | |
928 | dhdpcie_pci_remove(struct pci_dev *pdev) | |
929 | { | |
930 | osl_t *osh = NULL; | |
931 | dhdpcie_info_t *pch = NULL; | |
932 | dhd_bus_t *bus = NULL; | |
933 | ||
934 | DHD_TRACE(("%s Enter\n", __FUNCTION__)); | |
935 | ||
936 | DHD_MUTEX_LOCK(); | |
937 | ||
938 | pch = pci_get_drvdata(pdev); | |
939 | bus = pch->bus; | |
940 | osh = pch->osh; | |
941 | ||
942 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
943 | if (bus) { | |
944 | #ifdef CONFIG_ARCH_MSM | |
945 | msm_pcie_deregister_event(&bus->pcie_event); | |
946 | #endif /* CONFIG_ARCH_MSM */ | |
947 | #ifdef EXYNOS_PCIE_LINKDOWN_RECOVERY | |
948 | #ifdef CONFIG_SOC_EXYNOS8890 | |
949 | exynos_pcie_deregister_event(&bus->pcie_event); | |
950 | #endif /* CONFIG_SOC_EXYNOS8890 */ | |
951 | #endif /* EXYNOS_PCIE_LINKDOWN_RECOVERY */ | |
952 | } | |
953 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
954 | ||
955 | bus->rc_dev = NULL; | |
956 | ||
957 | dhdpcie_bus_release(bus); | |
958 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) | |
959 | if (pci_is_enabled(pdev)) | |
960 | #endif | |
961 | pci_disable_device(pdev); | |
962 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
963 | /* pcie os info detach */ | |
964 | MFREE(osh, pch->os_cxt, sizeof(dhdpcie_os_info_t)); | |
965 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
966 | #ifdef USE_SMMU_ARCH_MSM | |
967 | /* smmu info detach */ | |
968 | dhdpcie_smmu_remove(pdev, pch->smmu_cxt); | |
969 | MFREE(osh, pch->smmu_cxt, sizeof(dhdpcie_smmu_info_t)); | |
970 | #endif /* USE_SMMU_ARCH_MSM */ | |
971 | /* pcie info detach */ | |
972 | dhdpcie_detach(pch); | |
973 | /* osl detach */ | |
974 | osl_detach(osh); | |
975 | ||
976 | #if defined(BCMPCIE_OOB_HOST_WAKE) && defined(CUSTOMER_HW2) && \ | |
977 | defined(CONFIG_ARCH_APQ8084) | |
978 | brcm_pcie_wake.wake_irq = NULL; | |
979 | brcm_pcie_wake.data = NULL; | |
980 | #endif /* BCMPCIE_OOB_HOST_WAKE && CUSTOMR_HW2 && CONFIG_ARCH_APQ8084 */ | |
981 | ||
982 | dhdpcie_init_succeeded = FALSE; | |
983 | ||
984 | DHD_MUTEX_UNLOCK(); | |
985 | ||
986 | DHD_TRACE(("%s Exit\n", __FUNCTION__)); | |
987 | ||
988 | return; | |
989 | } | |
990 | ||
991 | /* Free Linux irq */ | |
992 | int | |
993 | dhdpcie_request_irq(dhdpcie_info_t *dhdpcie_info) | |
994 | { | |
995 | dhd_bus_t *bus = dhdpcie_info->bus; | |
996 | struct pci_dev *pdev = dhdpcie_info->bus->dev; | |
997 | int err = 0; | |
998 | ||
999 | if (!bus->irq_registered) { | |
1000 | snprintf(dhdpcie_info->pciname, sizeof(dhdpcie_info->pciname), | |
1001 | "dhdpcie:%s", pci_name(pdev)); | |
1002 | #ifdef DHD_USE_MSI | |
1003 | printf("%s: MSI enabled\n", __FUNCTION__); | |
1004 | err = pci_enable_msi(pdev); | |
1005 | if (err < 0) { | |
1006 | DHD_ERROR(("%s: pci_enable_msi() failed, %d, fall back to INTx\n", __FUNCTION__, err)); | |
1007 | } | |
1008 | #else | |
1009 | printf("%s: MSI not enabled\n", __FUNCTION__); | |
1010 | #endif /* DHD_USE_MSI */ | |
1011 | err = request_irq(pdev->irq, dhdpcie_isr, IRQF_SHARED, | |
1012 | dhdpcie_info->pciname, bus); | |
1013 | if (err) { | |
1014 | DHD_ERROR(("%s: request_irq() failed\n", __FUNCTION__)); | |
1015 | #ifdef DHD_USE_MSI | |
1016 | pci_disable_msi(pdev); | |
1017 | #endif /* DHD_USE_MSI */ | |
1018 | return -1; | |
1019 | } else { | |
1020 | bus->irq_registered = TRUE; | |
1021 | } | |
1022 | } else { | |
1023 | DHD_ERROR(("%s: PCI IRQ is already registered\n", __FUNCTION__)); | |
1024 | } | |
1025 | ||
1026 | if (!dhdpcie_irq_enabled(bus)) { | |
1027 | DHD_ERROR(("%s: PCIe IRQ was disabled, so, enabled it again\n", __FUNCTION__)); | |
1028 | dhdpcie_enable_irq(bus); | |
1029 | } | |
1030 | ||
1031 | DHD_TRACE(("%s %s\n", __FUNCTION__, dhdpcie_info->pciname)); | |
1032 | ||
1033 | ||
1034 | return 0; /* SUCCESS */ | |
1035 | } | |
1036 | ||
1037 | /** | |
1038 | * dhdpcie_get_pcieirq - return pcie irq number to linux-dhd | |
1039 | */ | |
1040 | int | |
1041 | dhdpcie_get_pcieirq(struct dhd_bus *bus, unsigned int *irq) | |
1042 | { | |
1043 | struct pci_dev *pdev = bus->dev; | |
1044 | ||
1045 | if (!pdev) { | |
1046 | DHD_ERROR(("%s : bus->dev is NULL\n", __FUNCTION__)); | |
1047 | return -ENODEV; | |
1048 | } | |
1049 | ||
1050 | *irq = pdev->irq; | |
1051 | ||
1052 | return 0; /* SUCCESS */ | |
1053 | } | |
1054 | ||
1055 | #ifdef CONFIG_PHYS_ADDR_T_64BIT | |
1056 | #define PRINTF_RESOURCE "0x%016llx" | |
1057 | #else | |
1058 | #define PRINTF_RESOURCE "0x%08x" | |
1059 | #endif | |
1060 | ||
1061 | /* | |
1062 | ||
1063 | Name: osl_pci_get_resource | |
1064 | ||
1065 | Parametrs: | |
1066 | ||
1067 | 1: struct pci_dev *pdev -- pci device structure | |
1068 | 2: pci_res -- structure containing pci configuration space values | |
1069 | ||
1070 | ||
1071 | Return value: | |
1072 | ||
1073 | int - Status (TRUE or FALSE) | |
1074 | ||
1075 | Description: | |
1076 | Access PCI configuration space, retrieve PCI allocated resources , updates in resource structure. | |
1077 | ||
1078 | */ | |
1079 | int dhdpcie_get_resource(dhdpcie_info_t *dhdpcie_info) | |
1080 | { | |
1081 | phys_addr_t bar0_addr, bar1_addr; | |
1082 | ulong bar1_size; | |
1083 | struct pci_dev *pdev = NULL; | |
1084 | pdev = dhdpcie_info->dev; | |
1085 | #ifdef EXYNOS_PCIE_MODULE_PATCH | |
1086 | pci_restore_state(pdev); | |
1087 | #endif /* EXYNOS_MODULE_PATCH */ | |
1088 | do { | |
1089 | if (pci_enable_device(pdev)) { | |
1090 | printf("%s: Cannot enable PCI device\n", __FUNCTION__); | |
1091 | break; | |
1092 | } | |
1093 | pci_set_master(pdev); | |
1094 | bar0_addr = pci_resource_start(pdev, 0); /* Bar-0 mapped address */ | |
1095 | bar1_addr = pci_resource_start(pdev, 2); /* Bar-1 mapped address */ | |
1096 | ||
1097 | /* read Bar-1 mapped memory range */ | |
1098 | bar1_size = pci_resource_len(pdev, 2); | |
1099 | ||
1100 | if ((bar1_size == 0) || (bar1_addr == 0)) { | |
1101 | printf("%s: BAR1 Not enabled for this device size(%ld)," | |
1102 | " addr(0x"PRINTF_RESOURCE")\n", | |
1103 | __FUNCTION__, bar1_size, bar1_addr); | |
1104 | goto err; | |
1105 | } | |
1106 | ||
1107 | dhdpcie_info->regs = (volatile char *) REG_MAP(bar0_addr, DONGLE_REG_MAP_SIZE); | |
1108 | dhdpcie_info->tcm_size = | |
1109 | (bar1_size > DONGLE_TCM_MAP_SIZE) ? bar1_size : DONGLE_TCM_MAP_SIZE; | |
1110 | dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, dhdpcie_info->tcm_size); | |
1111 | ||
1112 | if (!dhdpcie_info->regs || !dhdpcie_info->tcm) { | |
1113 | DHD_ERROR(("%s:ioremap() failed\n", __FUNCTION__)); | |
1114 | break; | |
1115 | } | |
1116 | ||
1117 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
1118 | if (!dhd_download_fw_on_driverload) { | |
1119 | /* Backup PCIe configuration so as to use Wi-Fi on/off process | |
1120 | * in case of built in driver | |
1121 | */ | |
1122 | pci_save_state(pdev); | |
1123 | dhdpcie_info->default_state = pci_store_saved_state(pdev); | |
1124 | ||
1125 | if (dhdpcie_info->default_state == NULL) { | |
1126 | DHD_ERROR(("%s pci_store_saved_state returns NULL\n", | |
1127 | __FUNCTION__)); | |
1128 | REG_UNMAP(dhdpcie_info->regs); | |
1129 | REG_UNMAP(dhdpcie_info->tcm); | |
1130 | pci_disable_device(pdev); | |
1131 | break; | |
1132 | } | |
1133 | } | |
1134 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
1135 | ||
1136 | #ifdef EXYNOS_PCIE_MODULE_PATCH | |
1137 | pci_save_state(pdev); | |
1138 | #endif /* EXYNOS_MODULE_PATCH */ | |
1139 | ||
1140 | DHD_TRACE(("%s:Phys addr : reg space = %p base addr 0x"PRINTF_RESOURCE" \n", | |
1141 | __FUNCTION__, dhdpcie_info->regs, bar0_addr)); | |
1142 | DHD_TRACE(("%s:Phys addr : tcm_space = %p base addr 0x"PRINTF_RESOURCE" \n", | |
1143 | __FUNCTION__, dhdpcie_info->tcm, bar1_addr)); | |
1144 | ||
1145 | return 0; /* SUCCESS */ | |
1146 | } while (0); | |
1147 | err: | |
1148 | return -1; /* FAILURE */ | |
1149 | } | |
1150 | ||
1151 | int dhdpcie_scan_resource(dhdpcie_info_t *dhdpcie_info) | |
1152 | { | |
1153 | ||
1154 | DHD_TRACE(("%s: ENTER\n", __FUNCTION__)); | |
1155 | ||
1156 | do { | |
1157 | /* define it here only!! */ | |
1158 | if (dhdpcie_get_resource (dhdpcie_info)) { | |
1159 | DHD_ERROR(("%s: Failed to get PCI resources\n", __FUNCTION__)); | |
1160 | break; | |
1161 | } | |
1162 | DHD_TRACE(("%s:Exit - SUCCESS \n", | |
1163 | __FUNCTION__)); | |
1164 | ||
1165 | return 0; /* SUCCESS */ | |
1166 | ||
1167 | } while (0); | |
1168 | ||
1169 | DHD_TRACE(("%s:Exit - FAILURE \n", __FUNCTION__)); | |
1170 | ||
1171 | return -1; /* FAILURE */ | |
1172 | ||
1173 | } | |
1174 | ||
1175 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1176 | #if defined(CONFIG_ARCH_MSM) || (defined(EXYNOS_PCIE_LINKDOWN_RECOVERY) && \ | |
1177 | (defined(CONFIG_SOC_EXYNOS8890) || defined(CONFIG_SOC_EXYNOS8895))) | |
1178 | void dhdpcie_linkdown_cb(struct_pcie_notify *noti) | |
1179 | { | |
1180 | struct pci_dev *pdev = (struct pci_dev *)noti->user; | |
1181 | dhdpcie_info_t *pch = NULL; | |
1182 | ||
1183 | if (pdev) { | |
1184 | pch = pci_get_drvdata(pdev); | |
1185 | if (pch) { | |
1186 | dhd_bus_t *bus = pch->bus; | |
1187 | if (bus) { | |
1188 | dhd_pub_t *dhd = bus->dhd; | |
1189 | if (dhd) { | |
1190 | DHD_ERROR(("%s: Event HANG send up " | |
1191 | "due to PCIe linkdown\n", | |
1192 | __FUNCTION__)); | |
1193 | #ifdef CONFIG_ARCH_MSM | |
1194 | bus->no_cfg_restore = 1; | |
1195 | #endif /* CONFIG_ARCH_MSM */ | |
1196 | bus->is_linkdown = 1; | |
1197 | DHD_OS_WAKE_LOCK(dhd); | |
1198 | dhd->hang_reason = HANG_REASON_PCIE_LINK_DOWN; | |
1199 | dhd_os_send_hang_message(dhd); | |
1200 | } | |
1201 | } | |
1202 | } | |
1203 | } | |
1204 | ||
1205 | } | |
1206 | #endif /* CONFIG_ARCH_MSM || (EXYNOS_PCIE_LINKDOWN_RECOVERY && | |
1207 | * (CONFIG_SOC_EXYNOS8890 || CONFIG_SOC_EXYNOS8895)) | |
1208 | */ | |
1209 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1210 | ||
1211 | int dhdpcie_init(struct pci_dev *pdev) | |
1212 | { | |
1213 | ||
1214 | osl_t *osh = NULL; | |
1215 | dhd_bus_t *bus = NULL; | |
1216 | dhdpcie_info_t *dhdpcie_info = NULL; | |
1217 | wifi_adapter_info_t *adapter = NULL; | |
1218 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
1219 | dhdpcie_os_info_t *dhdpcie_osinfo = NULL; | |
1220 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
1221 | #ifdef USE_SMMU_ARCH_MSM | |
1222 | dhdpcie_smmu_info_t *dhdpcie_smmu_info = NULL; | |
1223 | #endif /* USE_SMMU_ARCH_MSM */ | |
1224 | ||
1225 | do { | |
1226 | /* osl attach */ | |
1227 | if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { | |
1228 | DHD_ERROR(("%s: osl_attach failed\n", __FUNCTION__)); | |
1229 | break; | |
1230 | } | |
1231 | ||
1232 | /* initialize static buffer */ | |
1233 | adapter = dhd_wifi_platform_get_adapter(PCI_BUS, pdev->bus->number, | |
1234 | PCI_SLOT(pdev->devfn)); | |
1235 | if (adapter != NULL) { | |
1236 | DHD_ERROR(("%s: found adapter info '%s'\n", __FUNCTION__, adapter->name)); | |
1237 | #ifdef BUS_POWER_RESTORE | |
1238 | adapter->pci_dev = pdev; | |
1239 | #endif | |
1240 | } else | |
1241 | DHD_ERROR(("%s: can't find adapter info for this chip\n", __FUNCTION__)); | |
1242 | osl_static_mem_init(osh, adapter); | |
1243 | ||
1244 | /* Set ACP coherence flag */ | |
1245 | if (OSL_ACP_WAR_ENAB() || OSL_ARCH_IS_COHERENT()) | |
1246 | osl_flag_set(osh, OSL_ACP_COHERENCE); | |
1247 | ||
1248 | /* allocate linux spcific pcie structure here */ | |
1249 | if (!(dhdpcie_info = MALLOC(osh, sizeof(dhdpcie_info_t)))) { | |
1250 | DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__)); | |
1251 | break; | |
1252 | } | |
1253 | bzero(dhdpcie_info, sizeof(dhdpcie_info_t)); | |
1254 | dhdpcie_info->osh = osh; | |
1255 | dhdpcie_info->dev = pdev; | |
1256 | ||
1257 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
1258 | /* allocate OS speicific structure */ | |
1259 | dhdpcie_osinfo = MALLOC(osh, sizeof(dhdpcie_os_info_t)); | |
1260 | if (dhdpcie_osinfo == NULL) { | |
1261 | DHD_ERROR(("%s: MALLOC of dhdpcie_os_info_t failed\n", | |
1262 | __FUNCTION__)); | |
1263 | break; | |
1264 | } | |
1265 | bzero(dhdpcie_osinfo, sizeof(dhdpcie_os_info_t)); | |
1266 | dhdpcie_info->os_cxt = (void *)dhdpcie_osinfo; | |
1267 | ||
1268 | /* Initialize host wake IRQ */ | |
1269 | spin_lock_init(&dhdpcie_osinfo->oob_irq_spinlock); | |
1270 | /* Get customer specific host wake IRQ parametres: IRQ number as IRQ type */ | |
1271 | dhdpcie_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter, | |
1272 | &dhdpcie_osinfo->oob_irq_flags); | |
1273 | if (dhdpcie_osinfo->oob_irq_num < 0) { | |
1274 | DHD_ERROR(("%s: Host OOB irq is not defined\n", __FUNCTION__)); | |
1275 | } | |
1276 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
1277 | ||
1278 | #ifdef USE_SMMU_ARCH_MSM | |
1279 | /* allocate private structure for using SMMU */ | |
1280 | dhdpcie_smmu_info = MALLOC(osh, sizeof(dhdpcie_smmu_info_t)); | |
1281 | if (dhdpcie_smmu_info == NULL) { | |
1282 | DHD_ERROR(("%s: MALLOC of dhdpcie_smmu_info_t failed\n", | |
1283 | __FUNCTION__)); | |
1284 | break; | |
1285 | } | |
1286 | bzero(dhdpcie_smmu_info, sizeof(dhdpcie_smmu_info_t)); | |
1287 | dhdpcie_info->smmu_cxt = (void *)dhdpcie_smmu_info; | |
1288 | ||
1289 | /* Initialize smmu structure */ | |
1290 | if (dhdpcie_smmu_init(pdev, dhdpcie_info->smmu_cxt) < 0) { | |
1291 | DHD_ERROR(("%s: Failed to initialize SMMU\n", | |
1292 | __FUNCTION__)); | |
1293 | break; | |
1294 | } | |
1295 | #endif /* USE_SMMU_ARCH_MSM */ | |
1296 | ||
1297 | #ifdef DHD_WAKE_STATUS | |
1298 | /* Initialize pcie_lock */ | |
1299 | spin_lock_init(&dhdpcie_info->pcie_lock); | |
1300 | #endif /* DHD_WAKE_STATUS */ | |
1301 | ||
1302 | /* Find the PCI resources, verify the */ | |
1303 | /* vendor and device ID, map BAR regions and irq, update in structures */ | |
1304 | if (dhdpcie_scan_resource(dhdpcie_info)) { | |
1305 | DHD_ERROR(("%s: dhd_Scan_PCI_Res failed\n", __FUNCTION__)); | |
1306 | ||
1307 | break; | |
1308 | } | |
1309 | ||
1310 | /* Bus initialization */ | |
1311 | bus = dhdpcie_bus_attach(osh, dhdpcie_info->regs, dhdpcie_info->tcm, pdev); | |
1312 | if (!bus) { | |
1313 | DHD_ERROR(("%s:dhdpcie_bus_attach() failed\n", __FUNCTION__)); | |
1314 | break; | |
1315 | } | |
1316 | ||
1317 | dhdpcie_info->bus = bus; | |
1318 | bus->is_linkdown = 0; | |
1319 | ||
1320 | /* Get RC Device Handle */ | |
1321 | #if defined(PCIE_RC_VENDOR_ID) && defined(PCIE_RC_DEVICE_ID) | |
1322 | bus->rc_dev = pci_get_device(PCIE_RC_VENDOR_ID, PCIE_RC_DEVICE_ID, NULL); | |
1323 | #else | |
1324 | bus->rc_dev = NULL; | |
1325 | #endif | |
1326 | ||
1327 | #if defined(BCMPCIE_OOB_HOST_WAKE) && defined(CUSTOMER_HW2) && \ | |
1328 | defined(CONFIG_ARCH_APQ8084) | |
1329 | brcm_pcie_wake.wake_irq = wlan_oob_irq; | |
1330 | brcm_pcie_wake.data = bus; | |
1331 | #endif /* BCMPCIE_OOB_HOST_WAKE && CUSTOMR_HW2 && CONFIG_ARCH_APQ8084 */ | |
1332 | ||
1333 | #ifdef DONGLE_ENABLE_ISOLATION | |
1334 | bus->dhd->dongle_isolation = TRUE; | |
1335 | #endif /* DONGLE_ENABLE_ISOLATION */ | |
1336 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1337 | #ifdef CONFIG_ARCH_MSM | |
1338 | bus->pcie_event.events = MSM_PCIE_EVENT_LINKDOWN; | |
1339 | bus->pcie_event.user = pdev; | |
1340 | bus->pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; | |
1341 | bus->pcie_event.callback = dhdpcie_linkdown_cb; | |
1342 | bus->pcie_event.options = MSM_PCIE_CONFIG_NO_RECOVERY; | |
1343 | msm_pcie_register_event(&bus->pcie_event); | |
1344 | bus->no_cfg_restore = 0; | |
1345 | #endif /* CONFIG_ARCH_MSM */ | |
1346 | #ifdef EXYNOS_PCIE_LINKDOWN_RECOVERY | |
1347 | #if defined(CONFIG_SOC_EXYNOS8890) || defined(CONFIG_SOC_EXYNOS8895) | |
1348 | bus->pcie_event.events = EXYNOS_PCIE_EVENT_LINKDOWN; | |
1349 | bus->pcie_event.user = pdev; | |
1350 | bus->pcie_event.mode = EXYNOS_PCIE_TRIGGER_CALLBACK; | |
1351 | bus->pcie_event.callback = dhdpcie_linkdown_cb; | |
1352 | exynos_pcie_register_event(&bus->pcie_event); | |
1353 | #endif /* CONFIG_SOC_EXYNOS8890 || CONFIG_SOC_EXYNOS8895 */ | |
1354 | #endif /* EXYNOS_PCIE_LINKDOWN_RECOVERY */ | |
1355 | bus->read_shm_fail = FALSE; | |
1356 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1357 | ||
1358 | if (bus->intr) { | |
1359 | /* Register interrupt callback, but mask it (not operational yet). */ | |
1360 | DHD_INTR(("%s: Registering and masking interrupts\n", __FUNCTION__)); | |
1361 | dhdpcie_bus_intr_disable(bus); | |
1362 | ||
1363 | if (dhdpcie_request_irq(dhdpcie_info)) { | |
1364 | DHD_ERROR(("%s: request_irq() failed\n", __FUNCTION__)); | |
1365 | break; | |
1366 | } | |
1367 | } else { | |
1368 | bus->pollrate = 1; | |
1369 | DHD_INFO(("%s: PCIe interrupt function is NOT registered " | |
1370 | "due to polling mode\n", __FUNCTION__)); | |
1371 | } | |
1372 | ||
1373 | #if defined(BCM_REQUEST_FW) | |
1374 | if (dhd_bus_download_firmware(bus, osh, NULL, NULL) < 0) { | |
1375 | DHD_ERROR(("%s: failed to download firmware\n", __FUNCTION__)); | |
1376 | } | |
1377 | bus->nv_path = NULL; | |
1378 | bus->fw_path = NULL; | |
1379 | #endif /* BCM_REQUEST_FW */ | |
1380 | ||
1381 | /* set private data for pci_dev */ | |
1382 | pci_set_drvdata(pdev, dhdpcie_info); | |
1383 | ||
1384 | if (dhd_download_fw_on_driverload) { | |
1385 | if (dhd_bus_start(bus->dhd)) { | |
1386 | DHD_ERROR(("%s: dhd_bud_start() failed\n", __FUNCTION__)); | |
1387 | if (!allow_delay_fwdl) | |
1388 | break; | |
1389 | } | |
1390 | } else { | |
1391 | /* Set ramdom MAC address during boot time */ | |
1392 | get_random_bytes(&bus->dhd->mac.octet[3], 3); | |
1393 | /* Adding BRCM OUI */ | |
1394 | bus->dhd->mac.octet[0] = 0; | |
1395 | bus->dhd->mac.octet[1] = 0x90; | |
1396 | bus->dhd->mac.octet[2] = 0x4C; | |
1397 | } | |
1398 | ||
1399 | /* Attach to the OS network interface */ | |
1400 | DHD_TRACE(("%s(): Calling dhd_register_if() \n", __FUNCTION__)); | |
1401 | if (dhd_register_if(bus->dhd, 0, TRUE)) { | |
1402 | DHD_ERROR(("%s(): ERROR.. dhd_register_if() failed\n", __FUNCTION__)); | |
1403 | break; | |
1404 | } | |
1405 | ||
1406 | dhdpcie_init_succeeded = TRUE; | |
1407 | ||
1408 | #if defined(MULTIPLE_SUPPLICANT) | |
1409 | wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe | |
1410 | #endif /* MULTIPLE_SUPPLICANT */ | |
1411 | ||
1412 | DHD_TRACE(("%s:Exit - SUCCESS \n", __FUNCTION__)); | |
1413 | return 0; /* return SUCCESS */ | |
1414 | ||
1415 | } while (0); | |
1416 | /* reverse the initialization in order in case of error */ | |
1417 | ||
1418 | if (bus) | |
1419 | dhdpcie_bus_release(bus); | |
1420 | ||
1421 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
1422 | if (dhdpcie_osinfo) { | |
1423 | MFREE(osh, dhdpcie_osinfo, sizeof(dhdpcie_os_info_t)); | |
1424 | } | |
1425 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
1426 | ||
1427 | #ifdef USE_SMMU_ARCH_MSM | |
1428 | if (dhdpcie_smmu_info) { | |
1429 | MFREE(osh, dhdpcie_smmu_info, sizeof(dhdpcie_smmu_info_t)); | |
1430 | dhdpcie_info->smmu_cxt = NULL; | |
1431 | } | |
1432 | #endif /* USE_SMMU_ARCH_MSM */ | |
1433 | ||
1434 | if (dhdpcie_info) | |
1435 | dhdpcie_detach(dhdpcie_info); | |
1436 | pci_disable_device(pdev); | |
1437 | if (osh) | |
1438 | osl_detach(osh); | |
1439 | ||
1440 | dhdpcie_init_succeeded = FALSE; | |
1441 | ||
1442 | DHD_TRACE(("%s:Exit - FAILURE \n", __FUNCTION__)); | |
1443 | ||
1444 | return -1; /* return FAILURE */ | |
1445 | } | |
1446 | ||
1447 | /* Free Linux irq */ | |
1448 | void | |
1449 | dhdpcie_free_irq(dhd_bus_t *bus) | |
1450 | { | |
1451 | struct pci_dev *pdev = NULL; | |
1452 | ||
1453 | DHD_TRACE(("%s: freeing up the IRQ\n", __FUNCTION__)); | |
1454 | if (bus) { | |
1455 | pdev = bus->dev; | |
1456 | if (bus->irq_registered) { | |
1457 | free_irq(pdev->irq, bus); | |
1458 | bus->irq_registered = FALSE; | |
1459 | #ifdef DHD_USE_MSI | |
1460 | pci_disable_msi(pdev); | |
1461 | #endif /* DHD_USE_MSI */ | |
1462 | } else { | |
1463 | DHD_ERROR(("%s: PCIe IRQ is not registered\n", __FUNCTION__)); | |
1464 | } | |
1465 | } | |
1466 | DHD_TRACE(("%s: Exit\n", __FUNCTION__)); | |
1467 | return; | |
1468 | } | |
1469 | ||
1470 | /* | |
1471 | ||
1472 | Name: dhdpcie_isr | |
1473 | ||
1474 | Parametrs: | |
1475 | ||
1476 | 1: IN int irq -- interrupt vector | |
1477 | 2: IN void *arg -- handle to private data structure | |
1478 | ||
1479 | Return value: | |
1480 | ||
1481 | Status (TRUE or FALSE) | |
1482 | ||
1483 | Description: | |
1484 | Interrupt Service routine checks for the status register, | |
1485 | disable interrupt and queue DPC if mail box interrupts are raised. | |
1486 | */ | |
1487 | ||
1488 | ||
1489 | irqreturn_t | |
1490 | dhdpcie_isr(int irq, void *arg) | |
1491 | { | |
1492 | dhd_bus_t *bus = (dhd_bus_t*)arg; | |
1493 | if (dhdpcie_bus_isr(bus)) | |
1494 | return TRUE; | |
1495 | else | |
1496 | return FALSE; | |
1497 | } | |
1498 | ||
1499 | int | |
1500 | dhdpcie_disable_irq_nosync(dhd_bus_t *bus) | |
1501 | { | |
1502 | struct pci_dev *dev; | |
1503 | if ((bus == NULL) || (bus->dev == NULL)) { | |
1504 | DHD_ERROR(("%s: bus or bus->dev is NULL\n", __FUNCTION__)); | |
1505 | return BCME_ERROR; | |
1506 | } | |
1507 | ||
1508 | dev = bus->dev; | |
1509 | disable_irq_nosync(dev->irq); | |
1510 | return BCME_OK; | |
1511 | } | |
1512 | ||
1513 | int | |
1514 | dhdpcie_disable_irq(dhd_bus_t *bus) | |
1515 | { | |
1516 | struct pci_dev *dev; | |
1517 | if ((bus == NULL) || (bus->dev == NULL)) { | |
1518 | DHD_ERROR(("%s: bus or bus->dev is NULL\n", __FUNCTION__)); | |
1519 | return BCME_ERROR; | |
1520 | } | |
1521 | ||
1522 | dev = bus->dev; | |
1523 | disable_irq(dev->irq); | |
1524 | return BCME_OK; | |
1525 | } | |
1526 | ||
1527 | int | |
1528 | dhdpcie_enable_irq(dhd_bus_t *bus) | |
1529 | { | |
1530 | struct pci_dev *dev; | |
1531 | if ((bus == NULL) || (bus->dev == NULL)) { | |
1532 | DHD_ERROR(("%s: bus or bus->dev is NULL\n", __FUNCTION__)); | |
1533 | return BCME_ERROR; | |
1534 | } | |
1535 | ||
1536 | dev = bus->dev; | |
1537 | enable_irq(dev->irq); | |
1538 | return BCME_OK; | |
1539 | } | |
1540 | ||
1541 | bool | |
1542 | dhdpcie_irq_enabled(dhd_bus_t *bus) | |
1543 | { | |
1544 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
1545 | struct irq_desc *desc = irq_to_desc(bus->dev->irq); | |
1546 | /* depth will be zero, if enabled */ | |
1547 | if (!desc->depth) { | |
1548 | DHD_ERROR(("%s: depth:%d\n", __FUNCTION__, desc->depth)); | |
1549 | } | |
1550 | return desc->depth ? FALSE : TRUE; | |
1551 | #else | |
1552 | /* return TRUE by default as there is no support for lower versions */ | |
1553 | return TRUE; | |
1554 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
1555 | } | |
1556 | ||
1557 | int | |
1558 | dhdpcie_start_host_pcieclock(dhd_bus_t *bus) | |
1559 | { | |
1560 | int ret = 0; | |
1561 | #ifdef CONFIG_ARCH_MSM | |
1562 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1563 | int options = 0; | |
1564 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1565 | #endif /* CONFIG_ARCH_MSM */ | |
1566 | DHD_TRACE(("%s Enter:\n", __FUNCTION__)); | |
1567 | ||
1568 | if (bus == NULL) { | |
1569 | return BCME_ERROR; | |
1570 | } | |
1571 | ||
1572 | if (bus->dev == NULL) { | |
1573 | return BCME_ERROR; | |
1574 | } | |
1575 | ||
1576 | #ifdef CONFIG_ARCH_MSM | |
1577 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1578 | if (bus->no_cfg_restore) { | |
1579 | options = MSM_PCIE_CONFIG_NO_CFG_RESTORE; | |
1580 | } | |
1581 | ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number, | |
1582 | bus->dev, NULL, options); | |
1583 | if (bus->no_cfg_restore && !ret) { | |
1584 | msm_pcie_recover_config(bus->dev); | |
1585 | bus->no_cfg_restore = 0; | |
1586 | } | |
1587 | #else | |
1588 | ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number, | |
1589 | bus->dev, NULL, 0); | |
1590 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1591 | if (ret) { | |
1592 | DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__)); | |
1593 | goto done; | |
1594 | } | |
1595 | ||
1596 | done: | |
1597 | #endif /* CONFIG_ARCH_MSM */ | |
1598 | DHD_TRACE(("%s Exit:\n", __FUNCTION__)); | |
1599 | return ret; | |
1600 | } | |
1601 | ||
1602 | int | |
1603 | dhdpcie_stop_host_pcieclock(dhd_bus_t *bus) | |
1604 | { | |
1605 | int ret = 0; | |
1606 | #ifdef CONFIG_ARCH_MSM | |
1607 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1608 | int options = 0; | |
1609 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1610 | #endif /* CONFIG_ARCH_MSM */ | |
1611 | ||
1612 | DHD_TRACE(("%s Enter:\n", __FUNCTION__)); | |
1613 | ||
1614 | if (bus == NULL) { | |
1615 | return BCME_ERROR; | |
1616 | } | |
1617 | ||
1618 | if (bus->dev == NULL) { | |
1619 | return BCME_ERROR; | |
1620 | } | |
1621 | ||
1622 | #ifdef CONFIG_ARCH_MSM | |
1623 | #ifdef SUPPORT_LINKDOWN_RECOVERY | |
1624 | /* Always reset the PCIe host when wifi off */ | |
1625 | bus->no_cfg_restore = 1; | |
1626 | ||
1627 | if (bus->no_cfg_restore) { | |
1628 | options = MSM_PCIE_CONFIG_NO_CFG_RESTORE | MSM_PCIE_CONFIG_LINKDOWN; | |
1629 | } | |
1630 | ||
1631 | ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus->dev->bus->number, | |
1632 | bus->dev, NULL, options); | |
1633 | #else | |
1634 | ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus->dev->bus->number, | |
1635 | bus->dev, NULL, 0); | |
1636 | #endif /* SUPPORT_LINKDOWN_RECOVERY */ | |
1637 | if (ret) { | |
1638 | DHD_ERROR(("Failed to stop PCIe link\n")); | |
1639 | goto done; | |
1640 | } | |
1641 | done: | |
1642 | #endif /* CONFIG_ARCH_MSM */ | |
1643 | DHD_TRACE(("%s Exit:\n", __FUNCTION__)); | |
1644 | return ret; | |
1645 | } | |
1646 | ||
1647 | int | |
1648 | dhdpcie_disable_device(dhd_bus_t *bus) | |
1649 | { | |
1650 | DHD_TRACE(("%s Enter:\n", __FUNCTION__)); | |
1651 | ||
1652 | if (bus == NULL) { | |
1653 | return BCME_ERROR; | |
1654 | } | |
1655 | ||
1656 | if (bus->dev == NULL) { | |
1657 | return BCME_ERROR; | |
1658 | } | |
1659 | ||
1660 | pci_disable_device(bus->dev); | |
1661 | ||
1662 | return 0; | |
1663 | } | |
1664 | ||
1665 | int | |
1666 | dhdpcie_enable_device(dhd_bus_t *bus) | |
1667 | { | |
1668 | int ret = BCME_ERROR; | |
1669 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
1670 | dhdpcie_info_t *pch; | |
1671 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ | |
1672 | ||
1673 | DHD_TRACE(("%s Enter:\n", __FUNCTION__)); | |
1674 | ||
1675 | if (bus == NULL) { | |
1676 | return BCME_ERROR; | |
1677 | } | |
1678 | ||
1679 | if (bus->dev == NULL) { | |
1680 | return BCME_ERROR; | |
1681 | } | |
1682 | ||
1683 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) | |
1684 | pch = pci_get_drvdata(bus->dev); | |
1685 | if (pch == NULL) { | |
1686 | return BCME_ERROR; | |
1687 | } | |
1688 | ||
1689 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) && (LINUX_VERSION_CODE < \ | |
1690 | KERNEL_VERSION(3, 19, 0)) && !defined(CONFIG_SOC_EXYNOS8890) | |
1691 | /* Updated with pci_load_and_free_saved_state to compatible | |
1692 | * with Kernel version 3.14.0 to 3.18.41. | |
1693 | */ | |
1694 | pci_load_and_free_saved_state(bus->dev, &pch->default_state); | |
1695 | pch->default_state = pci_store_saved_state(bus->dev); | |
1696 | #else | |
1697 | pci_load_saved_state(bus->dev, pch->default_state); | |
1698 | #endif /* LINUX_VERSION >= 3.14.0 && LINUX_VERSION < 3.19.0 && !CONFIG_SOC_EXYNOS8890 */ | |
1699 | ||
1700 | pci_restore_state(bus->dev); | |
1701 | #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) */ | |
1702 | ||
1703 | ret = pci_enable_device(bus->dev); | |
1704 | if (ret) { | |
1705 | pci_disable_device(bus->dev); | |
1706 | } else { | |
1707 | pci_set_master(bus->dev); | |
1708 | } | |
1709 | ||
1710 | return ret; | |
1711 | } | |
1712 | ||
1713 | int | |
1714 | dhdpcie_alloc_resource(dhd_bus_t *bus) | |
1715 | { | |
1716 | dhdpcie_info_t *dhdpcie_info; | |
1717 | phys_addr_t bar0_addr, bar1_addr; | |
1718 | ulong bar1_size; | |
1719 | ||
1720 | do { | |
1721 | if (bus == NULL) { | |
1722 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1723 | break; | |
1724 | } | |
1725 | ||
1726 | if (bus->dev == NULL) { | |
1727 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1728 | break; | |
1729 | } | |
1730 | ||
1731 | dhdpcie_info = pci_get_drvdata(bus->dev); | |
1732 | if (dhdpcie_info == NULL) { | |
1733 | DHD_ERROR(("%s: dhdpcie_info is NULL\n", __FUNCTION__)); | |
1734 | break; | |
1735 | } | |
1736 | ||
1737 | bar0_addr = pci_resource_start(bus->dev, 0); /* Bar-0 mapped address */ | |
1738 | bar1_addr = pci_resource_start(bus->dev, 2); /* Bar-1 mapped address */ | |
1739 | ||
1740 | /* read Bar-1 mapped memory range */ | |
1741 | bar1_size = pci_resource_len(bus->dev, 2); | |
1742 | ||
1743 | if ((bar1_size == 0) || (bar1_addr == 0)) { | |
1744 | printf("%s: BAR1 Not enabled for this device size(%ld)," | |
1745 | " addr(0x"PRINTF_RESOURCE")\n", | |
1746 | __FUNCTION__, bar1_size, bar1_addr); | |
1747 | break; | |
1748 | } | |
1749 | ||
1750 | dhdpcie_info->regs = (volatile char *) REG_MAP(bar0_addr, DONGLE_REG_MAP_SIZE); | |
1751 | if (!dhdpcie_info->regs) { | |
1752 | DHD_ERROR(("%s: ioremap() for regs is failed\n", __FUNCTION__)); | |
1753 | break; | |
1754 | } | |
1755 | ||
1756 | bus->regs = dhdpcie_info->regs; | |
1757 | dhdpcie_info->tcm_size = | |
1758 | (bar1_size > DONGLE_TCM_MAP_SIZE) ? bar1_size : DONGLE_TCM_MAP_SIZE; | |
1759 | dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, dhdpcie_info->tcm_size); | |
1760 | if (!dhdpcie_info->tcm) { | |
1761 | DHD_ERROR(("%s: ioremap() for regs is failed\n", __FUNCTION__)); | |
1762 | REG_UNMAP(dhdpcie_info->regs); | |
1763 | bus->regs = NULL; | |
1764 | break; | |
1765 | } | |
1766 | ||
1767 | bus->tcm = dhdpcie_info->tcm; | |
1768 | ||
1769 | DHD_TRACE(("%s:Phys addr : reg space = %p base addr 0x"PRINTF_RESOURCE" \n", | |
1770 | __FUNCTION__, dhdpcie_info->regs, bar0_addr)); | |
1771 | DHD_TRACE(("%s:Phys addr : tcm_space = %p base addr 0x"PRINTF_RESOURCE" \n", | |
1772 | __FUNCTION__, dhdpcie_info->tcm, bar1_addr)); | |
1773 | ||
1774 | return 0; | |
1775 | } while (0); | |
1776 | ||
1777 | return BCME_ERROR; | |
1778 | } | |
1779 | ||
1780 | void | |
1781 | dhdpcie_free_resource(dhd_bus_t *bus) | |
1782 | { | |
1783 | dhdpcie_info_t *dhdpcie_info; | |
1784 | ||
1785 | if (bus == NULL) { | |
1786 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1787 | return; | |
1788 | } | |
1789 | ||
1790 | if (bus->dev == NULL) { | |
1791 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1792 | return; | |
1793 | } | |
1794 | ||
1795 | dhdpcie_info = pci_get_drvdata(bus->dev); | |
1796 | if (dhdpcie_info == NULL) { | |
1797 | DHD_ERROR(("%s: dhdpcie_info is NULL\n", __FUNCTION__)); | |
1798 | return; | |
1799 | } | |
1800 | ||
1801 | if (bus->regs) { | |
1802 | REG_UNMAP(dhdpcie_info->regs); | |
1803 | bus->regs = NULL; | |
1804 | } | |
1805 | ||
1806 | if (bus->tcm) { | |
1807 | REG_UNMAP(dhdpcie_info->tcm); | |
1808 | bus->tcm = NULL; | |
1809 | } | |
1810 | } | |
1811 | ||
1812 | int | |
1813 | dhdpcie_bus_request_irq(struct dhd_bus *bus) | |
1814 | { | |
1815 | dhdpcie_info_t *dhdpcie_info; | |
1816 | int ret = 0; | |
1817 | ||
1818 | if (bus == NULL) { | |
1819 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1820 | return BCME_ERROR; | |
1821 | } | |
1822 | ||
1823 | if (bus->dev == NULL) { | |
1824 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1825 | return BCME_ERROR; | |
1826 | } | |
1827 | ||
1828 | dhdpcie_info = pci_get_drvdata(bus->dev); | |
1829 | if (dhdpcie_info == NULL) { | |
1830 | DHD_ERROR(("%s: dhdpcie_info is NULL\n", __FUNCTION__)); | |
1831 | return BCME_ERROR; | |
1832 | } | |
1833 | ||
1834 | if (bus->intr) { | |
1835 | /* Register interrupt callback, but mask it (not operational yet). */ | |
1836 | DHD_INTR(("%s: Registering and masking interrupts\n", __FUNCTION__)); | |
1837 | dhdpcie_bus_intr_disable(bus); | |
1838 | ret = dhdpcie_request_irq(dhdpcie_info); | |
1839 | if (ret) { | |
1840 | DHD_ERROR(("%s: request_irq() failed, ret=%d\n", | |
1841 | __FUNCTION__, ret)); | |
1842 | return ret; | |
1843 | } | |
1844 | } | |
1845 | ||
1846 | return ret; | |
1847 | } | |
1848 | ||
1849 | #ifdef BCMPCIE_OOB_HOST_WAKE | |
1850 | void dhdpcie_oob_intr_set(dhd_bus_t *bus, bool enable) | |
1851 | { | |
1852 | unsigned long flags; | |
1853 | dhdpcie_info_t *pch; | |
1854 | dhdpcie_os_info_t *dhdpcie_osinfo; | |
1855 | ||
1856 | if (bus == NULL) { | |
1857 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1858 | return; | |
1859 | } | |
1860 | ||
1861 | if (bus->dev == NULL) { | |
1862 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1863 | return; | |
1864 | } | |
1865 | ||
1866 | pch = pci_get_drvdata(bus->dev); | |
1867 | if (pch == NULL) { | |
1868 | DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); | |
1869 | return; | |
1870 | } | |
1871 | ||
1872 | dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; | |
1873 | spin_lock_irqsave(&dhdpcie_osinfo->oob_irq_spinlock, flags); | |
1874 | if ((dhdpcie_osinfo->oob_irq_enabled != enable) && | |
1875 | (dhdpcie_osinfo->oob_irq_num > 0)) { | |
1876 | if (enable) { | |
1877 | enable_irq(dhdpcie_osinfo->oob_irq_num); | |
1878 | } else { | |
1879 | disable_irq_nosync(dhdpcie_osinfo->oob_irq_num); | |
1880 | } | |
1881 | dhdpcie_osinfo->oob_irq_enabled = enable; | |
1882 | } | |
1883 | spin_unlock_irqrestore(&dhdpcie_osinfo->oob_irq_spinlock, flags); | |
1884 | } | |
1885 | ||
1886 | static irqreturn_t wlan_oob_irq(int irq, void *data) | |
1887 | { | |
1888 | dhd_bus_t *bus; | |
1889 | DHD_TRACE(("%s: IRQ Triggered\n", __FUNCTION__)); | |
1890 | bus = (dhd_bus_t *)data; | |
1891 | dhdpcie_oob_intr_set(bus, FALSE); | |
1892 | #ifdef DHD_WAKE_STATUS | |
1893 | #ifdef DHD_PCIE_RUNTIMEPM | |
1894 | /* This condition is for avoiding counting of wake up from Runtime PM */ | |
1895 | if (bus->chk_pm) | |
1896 | #endif /* DHD_PCIE_RUNTIMPM */ | |
1897 | { | |
1898 | bcmpcie_set_get_wake(bus, 1); | |
1899 | } | |
1900 | #endif /* DHD_WAKE_STATUS */ | |
1901 | #ifdef DHD_PCIE_RUNTIMEPM | |
1902 | dhdpcie_runtime_bus_wake(bus->dhd, FALSE, wlan_oob_irq); | |
1903 | #endif /* DHD_PCIE_RUNTIMPM */ | |
1904 | if (bus->dhd->up && bus->oob_presuspend) { | |
1905 | DHD_OS_OOB_IRQ_WAKE_LOCK_TIMEOUT(bus->dhd, OOB_WAKE_LOCK_TIMEOUT); | |
1906 | } | |
1907 | return IRQ_HANDLED; | |
1908 | } | |
1909 | ||
1910 | int dhdpcie_oob_intr_register(dhd_bus_t *bus) | |
1911 | { | |
1912 | int err = 0; | |
1913 | dhdpcie_info_t *pch; | |
1914 | dhdpcie_os_info_t *dhdpcie_osinfo; | |
1915 | ||
1916 | DHD_TRACE(("%s: Enter\n", __FUNCTION__)); | |
1917 | if (bus == NULL) { | |
1918 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1919 | return -EINVAL; | |
1920 | } | |
1921 | ||
1922 | if (bus->dev == NULL) { | |
1923 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1924 | return -EINVAL; | |
1925 | } | |
1926 | ||
1927 | pch = pci_get_drvdata(bus->dev); | |
1928 | if (pch == NULL) { | |
1929 | DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); | |
1930 | return -EINVAL; | |
1931 | } | |
1932 | ||
1933 | dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; | |
1934 | if (dhdpcie_osinfo->oob_irq_registered) { | |
1935 | DHD_ERROR(("%s: irq is already registered\n", __FUNCTION__)); | |
1936 | return -EBUSY; | |
1937 | } | |
1938 | ||
1939 | if (dhdpcie_osinfo->oob_irq_num > 0) { | |
1940 | printf("%s OOB irq=%d flags=0x%X\n", __FUNCTION__, | |
1941 | (int)dhdpcie_osinfo->oob_irq_num, | |
1942 | (int)dhdpcie_osinfo->oob_irq_flags); | |
1943 | err = request_irq(dhdpcie_osinfo->oob_irq_num, wlan_oob_irq, | |
1944 | dhdpcie_osinfo->oob_irq_flags, "dhdpcie_host_wake", | |
1945 | bus); | |
1946 | if (err) { | |
1947 | DHD_ERROR(("%s: request_irq failed with %d\n", | |
1948 | __FUNCTION__, err)); | |
1949 | return err; | |
1950 | } | |
1951 | #if defined(DISABLE_WOWLAN) | |
1952 | printf("%s: disable_irq_wake\n", __FUNCTION__); | |
1953 | dhdpcie_osinfo->oob_irq_wake_enabled = FALSE; | |
1954 | #else | |
1955 | printf("%s: enable_irq_wake\n", __FUNCTION__); | |
1956 | err = enable_irq_wake(dhdpcie_osinfo->oob_irq_num); | |
1957 | if (!err) { | |
1958 | dhdpcie_osinfo->oob_irq_wake_enabled = TRUE; | |
1959 | } else | |
1960 | printf("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err); | |
1961 | #endif | |
1962 | dhdpcie_osinfo->oob_irq_enabled = TRUE; | |
1963 | } | |
1964 | ||
1965 | dhdpcie_osinfo->oob_irq_registered = TRUE; | |
1966 | ||
1967 | return err; | |
1968 | } | |
1969 | ||
1970 | void dhdpcie_oob_intr_unregister(dhd_bus_t *bus) | |
1971 | { | |
1972 | int err = 0; | |
1973 | dhdpcie_info_t *pch; | |
1974 | dhdpcie_os_info_t *dhdpcie_osinfo; | |
1975 | ||
1976 | DHD_TRACE(("%s: Enter\n", __FUNCTION__)); | |
1977 | if (bus == NULL) { | |
1978 | DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); | |
1979 | return; | |
1980 | } | |
1981 | ||
1982 | if (bus->dev == NULL) { | |
1983 | DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); | |
1984 | return; | |
1985 | } | |
1986 | ||
1987 | pch = pci_get_drvdata(bus->dev); | |
1988 | if (pch == NULL) { | |
1989 | DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); | |
1990 | return; | |
1991 | } | |
1992 | ||
1993 | dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; | |
1994 | if (!dhdpcie_osinfo->oob_irq_registered) { | |
1995 | DHD_ERROR(("%s: irq is not registered\n", __FUNCTION__)); | |
1996 | return; | |
1997 | } | |
1998 | if (dhdpcie_osinfo->oob_irq_num > 0) { | |
1999 | if (dhdpcie_osinfo->oob_irq_wake_enabled) { | |
2000 | err = disable_irq_wake(dhdpcie_osinfo->oob_irq_num); | |
2001 | if (!err) { | |
2002 | dhdpcie_osinfo->oob_irq_wake_enabled = FALSE; | |
2003 | } | |
2004 | } | |
2005 | if (dhdpcie_osinfo->oob_irq_enabled) { | |
2006 | disable_irq(dhdpcie_osinfo->oob_irq_num); | |
2007 | dhdpcie_osinfo->oob_irq_enabled = FALSE; | |
2008 | } | |
2009 | free_irq(dhdpcie_osinfo->oob_irq_num, bus); | |
2010 | } | |
2011 | dhdpcie_osinfo->oob_irq_registered = FALSE; | |
2012 | } | |
2013 | #endif /* BCMPCIE_OOB_HOST_WAKE */ | |
2014 | ||
2015 | #ifdef PCIE_OOB | |
2016 | void dhdpcie_oob_init(dhd_bus_t *bus) | |
2017 | { | |
2018 | gpio_handle_val = get_handle(OOB_PORT); | |
2019 | if (gpio_handle_val < 0) | |
2020 | { | |
2021 | DHD_ERROR(("%s: Could not get GPIO handle.\n", __FUNCTION__)); | |
2022 | ASSERT(FALSE); | |
2023 | } | |
2024 | ||
2025 | gpio_direction = 0; | |
2026 | ftdi_set_bitmode(gpio_handle_val, 0, BITMODE_BITBANG); | |
2027 | ||
2028 | /* Note BT core is also enabled here */ | |
2029 | gpio_port = 1 << BIT_WL_REG_ON | 1 << BIT_BT_REG_ON | 1 << DEVICE_WAKE; | |
2030 | gpio_write_port(gpio_handle_val, gpio_port); | |
2031 | ||
2032 | gpio_direction = 1 << BIT_WL_REG_ON | 1 << BIT_BT_REG_ON | 1 << DEVICE_WAKE; | |
2033 | ftdi_set_bitmode(gpio_handle_val, gpio_direction, BITMODE_BITBANG); | |
2034 | ||
2035 | bus->oob_enabled = TRUE; | |
2036 | bus->oob_presuspend = FALSE; | |
2037 | ||
2038 | /* drive the Device_Wake GPIO low on startup */ | |
2039 | bus->device_wake_state = TRUE; | |
2040 | dhd_bus_set_device_wake(bus, FALSE); | |
2041 | dhd_bus_doorbell_timeout_reset(bus); | |
2042 | ||
2043 | } | |
2044 | ||
2045 | void | |
2046 | dhd_oob_set_bt_reg_on(struct dhd_bus *bus, bool val) | |
2047 | { | |
2048 | DHD_INFO(("Set Device_Wake to %d\n", val)); | |
2049 | if (val) | |
2050 | { | |
2051 | gpio_port = gpio_port | (1 << BIT_BT_REG_ON); | |
2052 | gpio_write_port(gpio_handle_val, gpio_port); | |
2053 | } else { | |
2054 | gpio_port = gpio_port & (0xff ^ (1 << BIT_BT_REG_ON)); | |
2055 | gpio_write_port(gpio_handle_val, gpio_port); | |
2056 | } | |
2057 | } | |
2058 | ||
2059 | int | |
2060 | dhd_oob_get_bt_reg_on(struct dhd_bus *bus) | |
2061 | { | |
2062 | int ret; | |
2063 | uint8 val; | |
2064 | ret = gpio_read_port(gpio_handle_val, &val); | |
2065 | ||
2066 | if (ret < 0) { | |
2067 | DHD_ERROR(("gpio_read_port returns %d\n", ret)); | |
2068 | return ret; | |
2069 | } | |
2070 | ||
2071 | if (val & (1 << BIT_BT_REG_ON)) | |
2072 | { | |
2073 | ret = 1; | |
2074 | } else { | |
2075 | ret = 0; | |
2076 | } | |
2077 | ||
2078 | return ret; | |
2079 | } | |
2080 | ||
2081 | int | |
2082 | dhd_os_oob_set_device_wake(struct dhd_bus *bus, bool val) | |
2083 | { | |
2084 | if (bus->device_wake_state != val) | |
2085 | { | |
2086 | DHD_INFO(("Set Device_Wake to %d\n", val)); | |
2087 | ||
2088 | if (bus->oob_enabled && !bus->oob_presuspend) | |
2089 | { | |
2090 | if (val) | |
2091 | { | |
2092 | gpio_port = gpio_port | (1 << DEVICE_WAKE); | |
2093 | gpio_write_port_non_block(gpio_handle_val, gpio_port); | |
2094 | } else { | |
2095 | gpio_port = gpio_port & (0xff ^ (1 << DEVICE_WAKE)); | |
2096 | gpio_write_port_non_block(gpio_handle_val, gpio_port); | |
2097 | } | |
2098 | } | |
2099 | ||
2100 | bus->device_wake_state = val; | |
2101 | } | |
2102 | return BCME_OK; | |
2103 | } | |
2104 | ||
2105 | INLINE void | |
2106 | dhd_os_ib_set_device_wake(struct dhd_bus *bus, bool val) | |
2107 | { | |
2108 | /* TODO: Currently Inband implementation of Device_Wake is not supported, | |
2109 | * so this function is left empty later this can be used to support the same. | |
2110 | */ | |
2111 | } | |
2112 | #endif /* PCIE_OOB */ | |
2113 | ||
2114 | #ifdef DHD_PCIE_RUNTIMEPM | |
2115 | bool dhd_runtimepm_state(dhd_pub_t *dhd) | |
2116 | { | |
2117 | dhd_bus_t *bus; | |
2118 | unsigned long flags; | |
2119 | bus = dhd->bus; | |
2120 | ||
2121 | DHD_GENERAL_LOCK(dhd, flags); | |
2122 | ||
2123 | bus->idlecount++; | |
2124 | ||
2125 | DHD_TRACE(("%s : Enter \n", __FUNCTION__)); | |
2126 | if ((bus->idletime > 0) && (bus->idlecount >= bus->idletime)) { | |
2127 | bus->idlecount = 0; | |
2128 | if (DHD_BUS_BUSY_CHECK_IDLE(dhd) && !DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(dhd)) { | |
2129 | bus->bus_wake = 0; | |
2130 | DHD_BUS_BUSY_SET_RPM_SUSPEND_IN_PROGRESS(dhd); | |
2131 | bus->runtime_resume_done = FALSE; | |
2132 | /* stop all interface network queue. */ | |
2133 | dhd_bus_stop_queue(bus); | |
2134 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2135 | DHD_ERROR(("%s: DHD Idle state!! - idletime :%d, wdtick :%d \n", | |
2136 | __FUNCTION__, bus->idletime, dhd_runtimepm_ms)); | |
2137 | /* RPM suspend is failed, return FALSE then re-trying */ | |
2138 | if (dhdpcie_set_suspend_resume(bus, TRUE)) { | |
2139 | DHD_ERROR(("%s: exit with wakelock \n", __FUNCTION__)); | |
2140 | DHD_GENERAL_LOCK(dhd, flags); | |
2141 | DHD_BUS_BUSY_CLEAR_RPM_SUSPEND_IN_PROGRESS(dhd); | |
2142 | dhd_os_busbusy_wake(bus->dhd); | |
2143 | bus->runtime_resume_done = TRUE; | |
2144 | /* It can make stuck NET TX Queue without below */ | |
2145 | dhd_bus_start_queue(bus); | |
2146 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2147 | smp_wmb(); | |
2148 | wake_up_interruptible(&bus->rpm_queue); | |
2149 | return FALSE; | |
2150 | } | |
2151 | ||
2152 | DHD_GENERAL_LOCK(dhd, flags); | |
2153 | DHD_BUS_BUSY_CLEAR_RPM_SUSPEND_IN_PROGRESS(dhd); | |
2154 | DHD_BUS_BUSY_SET_RPM_SUSPEND_DONE(dhd); | |
2155 | /* For making sure NET TX Queue active */ | |
2156 | dhd_bus_start_queue(bus); | |
2157 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2158 | ||
2159 | wait_event_interruptible(bus->rpm_queue, bus->bus_wake); | |
2160 | ||
2161 | DHD_GENERAL_LOCK(dhd, flags); | |
2162 | DHD_BUS_BUSY_CLEAR_RPM_SUSPEND_DONE(dhd); | |
2163 | DHD_BUS_BUSY_SET_RPM_RESUME_IN_PROGRESS(dhd); | |
2164 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2165 | ||
2166 | dhdpcie_set_suspend_resume(bus, FALSE); | |
2167 | ||
2168 | DHD_GENERAL_LOCK(dhd, flags); | |
2169 | DHD_BUS_BUSY_CLEAR_RPM_RESUME_IN_PROGRESS(dhd); | |
2170 | dhd_os_busbusy_wake(bus->dhd); | |
2171 | /* Inform the wake up context that Resume is over */ | |
2172 | bus->runtime_resume_done = TRUE; | |
2173 | /* For making sure NET TX Queue active */ | |
2174 | dhd_bus_start_queue(bus); | |
2175 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2176 | ||
2177 | smp_wmb(); | |
2178 | wake_up_interruptible(&bus->rpm_queue); | |
2179 | DHD_ERROR(("%s : runtime resume ended \n", __FUNCTION__)); | |
2180 | return TRUE; | |
2181 | } else { | |
2182 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2183 | /* Since one of the contexts are busy (TX, IOVAR or RX) | |
2184 | * we should not suspend | |
2185 | */ | |
2186 | DHD_ERROR(("%s : bus is active with dhd_bus_busy_state = 0x%x\n", | |
2187 | __FUNCTION__, dhd->dhd_bus_busy_state)); | |
2188 | return FALSE; | |
2189 | } | |
2190 | } | |
2191 | ||
2192 | DHD_GENERAL_UNLOCK(dhd, flags); | |
2193 | return FALSE; | |
2194 | } /* dhd_runtimepm_state */ | |
2195 | ||
2196 | /* | |
2197 | * dhd_runtime_bus_wake | |
2198 | * TRUE - related with runtime pm context | |
2199 | * FALSE - It isn't invloved in runtime pm context | |
2200 | */ | |
2201 | bool dhd_runtime_bus_wake(dhd_bus_t *bus, bool wait, void *func_addr) | |
2202 | { | |
2203 | unsigned long flags; | |
2204 | bus->idlecount = 0; | |
2205 | DHD_TRACE(("%s : enter\n", __FUNCTION__)); | |
2206 | if (bus->dhd->up == FALSE) { | |
2207 | DHD_INFO(("%s : dhd is not up\n", __FUNCTION__)); | |
2208 | return FALSE; | |
2209 | } | |
2210 | ||
2211 | DHD_GENERAL_LOCK(bus->dhd, flags); | |
2212 | if (DHD_BUS_BUSY_CHECK_RPM_ALL(bus->dhd)) { | |
2213 | /* Wake up RPM state thread if it is suspend in progress or suspended */ | |
2214 | if (DHD_BUS_BUSY_CHECK_RPM_SUSPEND_IN_PROGRESS(bus->dhd) || | |
2215 | DHD_BUS_BUSY_CHECK_RPM_SUSPEND_DONE(bus->dhd)) { | |
2216 | bus->bus_wake = 1; | |
2217 | ||
2218 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
2219 | ||
2220 | DHD_ERROR(("Runtime Resume is called in %pf\n", func_addr)); | |
2221 | smp_wmb(); | |
2222 | wake_up_interruptible(&bus->rpm_queue); | |
2223 | /* No need to wake up the RPM state thread */ | |
2224 | } else if (DHD_BUS_BUSY_CHECK_RPM_RESUME_IN_PROGRESS(bus->dhd)) { | |
2225 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
2226 | } | |
2227 | ||
2228 | /* If wait is TRUE, function with wait = TRUE will be wait in here */ | |
2229 | if (wait) { | |
2230 | wait_event_interruptible(bus->rpm_queue, bus->runtime_resume_done); | |
2231 | } else { | |
2232 | DHD_INFO(("%s: bus wakeup but no wait until resume done\n", __FUNCTION__)); | |
2233 | } | |
2234 | /* If it is called from RPM context, it returns TRUE */ | |
2235 | return TRUE; | |
2236 | } | |
2237 | ||
2238 | DHD_GENERAL_UNLOCK(bus->dhd, flags); | |
2239 | ||
2240 | return FALSE; | |
2241 | } | |
2242 | ||
2243 | bool dhdpcie_runtime_bus_wake(dhd_pub_t *dhdp, bool wait, void* func_addr) | |
2244 | { | |
2245 | dhd_bus_t *bus = dhdp->bus; | |
2246 | return dhd_runtime_bus_wake(bus, wait, func_addr); | |
2247 | } | |
2248 | ||
2249 | void dhdpcie_block_runtime_pm(dhd_pub_t *dhdp) | |
2250 | { | |
2251 | dhd_bus_t *bus = dhdp->bus; | |
2252 | bus->idletime = 0; | |
2253 | } | |
2254 | ||
2255 | bool dhdpcie_is_resume_done(dhd_pub_t *dhdp) | |
2256 | { | |
2257 | dhd_bus_t *bus = dhdp->bus; | |
2258 | return bus->runtime_resume_done; | |
2259 | } | |
2260 | #endif /* DHD_PCIE_RUNTIMEPM */ | |
2261 | ||
2262 | struct device * dhd_bus_to_dev(dhd_bus_t *bus) | |
2263 | { | |
2264 | struct pci_dev *pdev; | |
2265 | pdev = bus->dev; | |
2266 | ||
2267 | if (pdev) | |
2268 | return &pdev->dev; | |
2269 | else | |
2270 | return NULL; | |
2271 | } | |
2272 | ||
2273 | #ifdef HOFFLOAD_MODULES | |
2274 | void | |
2275 | dhd_free_module_memory(struct dhd_bus *bus, struct module_metadata *hmem) | |
2276 | { | |
2277 | struct device *dev = &bus->dev->dev; | |
2278 | if (hmem) { | |
2279 | dma_unmap_single(dev, (dma_addr_t) hmem->data_addr, hmem->size, DMA_TO_DEVICE); | |
2280 | kfree(hmem->data); | |
2281 | hmem->data = NULL; | |
2282 | hmem->size = 0; | |
2283 | } else { | |
2284 | DHD_ERROR(("dev:%p pci unmapping error\n", dev)); | |
2285 | } | |
2286 | } | |
2287 | ||
2288 | void * | |
2289 | dhd_alloc_module_memory(struct dhd_bus *bus, uint32_t size, struct module_metadata *hmem) | |
2290 | { | |
2291 | struct device *dev = &bus->dev->dev; | |
2292 | if (!hmem->data) { | |
2293 | hmem->data = kzalloc(size, GFP_KERNEL); | |
2294 | if (!hmem->data) { | |
2295 | DHD_ERROR(("dev:%p mem alloc failure\n", dev)); | |
2296 | return NULL; | |
2297 | } | |
2298 | } | |
2299 | hmem->size = size; | |
2300 | DHD_INFO(("module size: 0x%x \n", hmem->size)); | |
2301 | hmem->data_addr = (u64) dma_map_single(dev, hmem->data, hmem->size, DMA_TO_DEVICE); | |
2302 | if (dma_mapping_error(dev, hmem->data_addr)) { | |
2303 | DHD_ERROR(("dev:%p dma mapping error\n", dev)); | |
2304 | kfree(hmem->data); | |
2305 | hmem->data = NULL; | |
2306 | return hmem->data; | |
2307 | } | |
2308 | return hmem->data; | |
2309 | } | |
2310 | #endif /* HOFFLOAD_MODULES */ |