1 /*************************************************************************/ /*!
3 @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
4 @License Dual MIT/GPLv2
6 The contents of this file are subject to the MIT license as set out below.
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
18 Alternatively, the contents of this file may be used under the terms of
19 the GNU General Public License Version 2 ("GPL") in which case the provisions
20 of GPL are applicable instead of those above.
22 If you wish to allow use of your version of this file only under the terms of
23 GPL, and not to allow others to use your version of this file under the terms
24 of the MIT license, indicate your decision by deleting the provisions above
25 and replace them with the notice and other provisions required by GPL as set
26 out in the file called "GPL-COPYING" included in this distribution. If you do
27 not delete the provisions above, a recipient may use your version of this file
28 under the terms of either the MIT license or GPL.
30 This License is also included in this distribution in the file called
33 EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
34 PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
35 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36 PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
37 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
38 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 */ /**************************************************************************/
42 #include <linux/pci.h>
44 #if defined(CONFIG_MTRR)
46 #include <linux/version.h>
49 #include "pci_support.h"
52 typedef struct _PVR_PCI_DEV_TAG
54 struct pci_dev *psPCIDev;
55 HOST_PCI_INIT_FLAGS ePCIFlags;
56 IMG_BOOL abPCIResourceInUse[DEVICE_COUNT_RESOURCE];
59 /*************************************************************************/ /*!
61 @Description Set a PCI device for subsequent use.
62 @Input pvPCICookie Pointer to OS specific PCI structure
64 @Return PVRSRV_PCI_DEV_HANDLE Pointer to PCI device handle
65 */ /**************************************************************************/
66 PVRSRV_PCI_DEV_HANDLE OSPCISetDev(void *pvPCICookie, HOST_PCI_INIT_FLAGS eFlags)
70 PVR_PCI_DEV *psPVRPCI;
72 psPVRPCI = OSAllocMem(sizeof(*psPVRPCI));
75 printk(KERN_ERR "OSPCISetDev: Couldn't allocate PVR PCI structure\n");
79 psPVRPCI->psPCIDev = (struct pci_dev *)pvPCICookie;
80 psPVRPCI->ePCIFlags = eFlags;
82 err = pci_enable_device(psPVRPCI->psPCIDev);
85 printk(KERN_ERR "OSPCISetDev: Couldn't enable device (%d)\n", err);
90 if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) /* PRQA S 3358 */ /* misuse of enums */
92 pci_set_master(psPVRPCI->psPCIDev);
95 if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI) /* PRQA S 3358 */ /* misuse of enums */
97 #if defined(CONFIG_PCI_MSI)
98 err = pci_enable_msi(psPVRPCI->psPCIDev);
101 printk(KERN_ERR "OSPCISetDev: Couldn't enable MSI (%d)", err);
102 psPVRPCI->ePCIFlags &= ~HOST_PCI_INIT_FLAG_MSI; /* PRQA S 1474,3358,4130 */ /* misuse of enums */
105 printk(KERN_ERR "OSPCISetDev: MSI support not enabled in the kernel");
109 /* Initialise the PCI resource tracking array */
110 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
112 psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
115 return (PVRSRV_PCI_DEV_HANDLE)psPVRPCI;
118 /*************************************************************************/ /*!
119 @Function OSPCIAcquireDev
120 @Description Acquire a PCI device for subsequent use.
121 @Input ui16VendorID Vendor PCI ID
122 @Input ui16DeviceID Device PCI ID
124 @Return PVRSRV_PCI_DEV_HANDLE Pointer to PCI device handle
125 */ /**************************************************************************/
126 PVRSRV_PCI_DEV_HANDLE OSPCIAcquireDev(IMG_UINT16 ui16VendorID,
127 IMG_UINT16 ui16DeviceID,
128 HOST_PCI_INIT_FLAGS eFlags)
130 struct pci_dev *psPCIDev;
132 psPCIDev = pci_get_device(ui16VendorID, ui16DeviceID, NULL);
133 if (psPCIDev == NULL)
138 return OSPCISetDev((void *)psPCIDev, eFlags);
141 /*************************************************************************/ /*!
143 @Description Get the PCI device ID.
144 @Input hPVRPCI PCI device handle
145 @Output pui16DeviceID Pointer to where the device ID should
147 @Return PVRSRV_ERROR Services error code
148 */ /**************************************************************************/
149 PVRSRV_ERROR OSPCIDevID(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT16 *pui16DeviceID)
151 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
153 if (pui16DeviceID == NULL)
155 return PVRSRV_ERROR_INVALID_PARAMS;
158 *pui16DeviceID = psPVRPCI->psPCIDev->device;
163 /*************************************************************************/ /*!
165 @Description Get the interrupt number for the device.
166 @Input hPVRPCI PCI device handle
167 @Output pui16DeviceID Pointer to where the interrupt number
169 @Return PVRSRV_ERROR Services error code
170 */ /**************************************************************************/
171 PVRSRV_ERROR OSPCIIRQ(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 *pui32IRQ)
173 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
175 if (pui32IRQ == NULL)
177 return PVRSRV_ERROR_INVALID_PARAMS;
180 *pui32IRQ = psPVRPCI->psPCIDev->irq;
185 /* Functions supported by OSPCIAddrRangeFunc */
186 enum HOST_PCI_ADDR_RANGE_FUNC
188 HOST_PCI_ADDR_RANGE_FUNC_LEN,
189 HOST_PCI_ADDR_RANGE_FUNC_START,
190 HOST_PCI_ADDR_RANGE_FUNC_END,
191 HOST_PCI_ADDR_RANGE_FUNC_REQUEST,
192 HOST_PCI_ADDR_RANGE_FUNC_RELEASE
195 /*************************************************************************/ /*!
196 @Function OSPCIAddrRangeFunc
197 @Description Internal support function for various address range related
199 @Input eFunc Function to perform
200 @Input hPVRPCI PCI device handle
201 @Input ui32Index Address range index
202 @Return IMG_UINT32 Function dependent value
203 */ /**************************************************************************/
204 static IMG_UINT64 OSPCIAddrRangeFunc(enum HOST_PCI_ADDR_RANGE_FUNC eFunc,
205 PVRSRV_PCI_DEV_HANDLE hPVRPCI,
206 IMG_UINT32 ui32Index)
208 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
210 if (ui32Index >= DEVICE_COUNT_RESOURCE)
212 printk(KERN_ERR "OSPCIAddrRangeFunc: Index out of range");
218 case HOST_PCI_ADDR_RANGE_FUNC_LEN:
220 return pci_resource_len(psPVRPCI->psPCIDev, ui32Index);
222 case HOST_PCI_ADDR_RANGE_FUNC_START:
224 return pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
226 case HOST_PCI_ADDR_RANGE_FUNC_END:
228 return pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
230 case HOST_PCI_ADDR_RANGE_FUNC_REQUEST:
232 int err = pci_request_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index, PVRSRV_MODNAME);
235 printk(KERN_ERR "OSPCIAddrRangeFunc: pci_request_region_failed (%d)", err);
238 psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_TRUE;
241 case HOST_PCI_ADDR_RANGE_FUNC_RELEASE:
243 if (psPVRPCI->abPCIResourceInUse[ui32Index])
245 pci_release_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index);
246 psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_FALSE;
252 printk(KERN_ERR "OSPCIAddrRangeFunc: Unknown function");
260 /*************************************************************************/ /*!
261 @Function OSPCIAddrRangeLen
262 @Description Returns length of a given address range
263 @Input hPVRPCI PCI device handle
264 @Input ui32Index Address range index
265 @Return IMG_UINT32 Length of address range or 0 if no
267 */ /**************************************************************************/
268 IMG_UINT64 OSPCIAddrRangeLen(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
270 return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_LEN, hPVRPCI, ui32Index);
273 /*************************************************************************/ /*!
274 @Function OSPCIAddrRangeStart
275 @Description Returns the start of a given address range
276 @Input hPVRPCI PCI device handle
277 @Input ui32Index Address range index
278 @Return IMG_UINT32 Start of address range or 0 if no
280 */ /**************************************************************************/
281 IMG_UINT64 OSPCIAddrRangeStart(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
283 return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_START, hPVRPCI, ui32Index);
286 /*************************************************************************/ /*!
287 @Function OSPCIAddrRangeEnd
288 @Description Returns the end of a given address range
289 @Input hPVRPCI PCI device handle
290 @Input ui32Index Address range index
291 @Return IMG_UINT32 End of address range or 0 if no such
293 */ /**************************************************************************/
294 IMG_UINT64 OSPCIAddrRangeEnd(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
296 return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_END, hPVRPCI, ui32Index);
299 /*************************************************************************/ /*!
300 @Function OSPCIRequestAddrRange
301 @Description Request a given address range index for subsequent use
302 @Input hPVRPCI PCI device handle
303 @Input ui32Index Address range index
304 @Return PVRSRV_ERROR Services error code
305 */ /**************************************************************************/
306 PVRSRV_ERROR OSPCIRequestAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI,
307 IMG_UINT32 ui32Index)
309 if (OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_REQUEST, hPVRPCI, ui32Index) == 0)
311 return PVRSRV_ERROR_PCI_CALL_FAILED;
319 /*************************************************************************/ /*!
320 @Function OSPCIReleaseAddrRange
321 @Description Release a given address range that is no longer being used
322 @Input hPVRPCI PCI device handle
323 @Input ui32Index Address range index
324 @Return PVRSRV_ERROR Services error code
325 */ /**************************************************************************/
326 PVRSRV_ERROR OSPCIReleaseAddrRange(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
328 if (OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_RELEASE, hPVRPCI, ui32Index) == 0)
330 return PVRSRV_ERROR_PCI_CALL_FAILED;
338 /*************************************************************************/ /*!
339 @Function OSPCIRequestAddrRegion
340 @Description Request a given region from an address range for subsequent use
341 @Input hPVRPCI PCI device handle
342 @Input ui32Index Address range index
343 @Input uiOffset Offset into the address range that forms
344 the start of the region
345 @Input uiLength Length of the region
346 @Return PVRSRV_ERROR Services error code
347 */ /**************************************************************************/
348 PVRSRV_ERROR OSPCIRequestAddrRegion(PVRSRV_PCI_DEV_HANDLE hPVRPCI,
349 IMG_UINT32 ui32Index,
353 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
354 resource_size_t start;
357 start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
358 end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
360 /* Check that the requested region is valid */
361 if ((start + uiOffset + uiLength - 1) > end)
363 return PVRSRV_ERROR_BAD_REGION_SIZE_MISMATCH;
366 if (pci_resource_flags(psPVRPCI->psPCIDev, ui32Index) & IORESOURCE_IO)
368 if (request_region(start + uiOffset, uiLength, PVRSRV_MODNAME) == NULL)
370 return PVRSRV_ERROR_PCI_REGION_UNAVAILABLE;
375 if (request_mem_region(start + uiOffset, uiLength, PVRSRV_MODNAME) == NULL)
377 return PVRSRV_ERROR_PCI_REGION_UNAVAILABLE;
384 /*************************************************************************/ /*!
385 @Function OSPCIReleaseAddrRegion
386 @Description Release a given region, from an address range, that is no
388 @Input hPVRPCI PCI device handle
389 @Input ui32Index Address range index
390 @Input ui32Offset Offset into the address range that forms
391 the start of the region
392 @Input ui32Length Length of the region
393 @Return PVRSRV_ERROR Services error code
394 */ /**************************************************************************/
395 PVRSRV_ERROR OSPCIReleaseAddrRegion(PVRSRV_PCI_DEV_HANDLE hPVRPCI,
396 IMG_UINT32 ui32Index,
400 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
401 resource_size_t start;
404 start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
405 end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
407 /* Check that the region is valid */
408 if ((start + uiOffset + uiLength - 1) > end)
410 return PVRSRV_ERROR_BAD_REGION_SIZE_MISMATCH;
413 if (pci_resource_flags(psPVRPCI->psPCIDev, ui32Index) & IORESOURCE_IO)
415 release_region(start + uiOffset, uiLength);
419 release_mem_region(start + uiOffset, uiLength);
425 /*************************************************************************/ /*!
426 @Function OSPCIReleaseDev
427 @Description Release a PCI device that is no longer being used
428 @Input hPVRPCI PCI device handle
429 @Return PVRSRV_ERROR Services error code
430 */ /**************************************************************************/
431 PVRSRV_ERROR OSPCIReleaseDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
433 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
436 /* Release all PCI regions that are currently in use */
437 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
439 if (psPVRPCI->abPCIResourceInUse[i])
441 pci_release_region(psPVRPCI->psPCIDev, i);
442 psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
446 #if defined(CONFIG_PCI_MSI)
447 if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI) /* PRQA S 3358 */ /* misuse of enums */
449 pci_disable_msi(psPVRPCI->psPCIDev);
453 if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) /* PRQA S 3358 */ /* misuse of enums */
455 pci_clear_master(psPVRPCI->psPCIDev);
458 pci_disable_device(psPVRPCI->psPCIDev);
461 /*not nulling pointer, copy on stack*/
466 /*************************************************************************/ /*!
467 @Function OSPCISuspendDev
468 @Description Prepare PCI device to be turned off by power management
469 @Input hPVRPCI PCI device handle
470 @Return PVRSRV_ERROR Services error code
471 */ /**************************************************************************/
472 PVRSRV_ERROR OSPCISuspendDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
474 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
478 /* Release all PCI regions that are currently in use */
479 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
481 if (psPVRPCI->abPCIResourceInUse[i])
483 pci_release_region(psPVRPCI->psPCIDev, i);
487 err = pci_save_state(psPVRPCI->psPCIDev);
490 printk(KERN_ERR "OSPCISuspendDev: pci_save_state_failed (%d)", err);
491 return PVRSRV_ERROR_PCI_CALL_FAILED;
494 pci_disable_device(psPVRPCI->psPCIDev);
496 err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_SUSPEND));
502 printk(KERN_ERR "OSPCISuspendDev: device doesn't support PCI PM");
505 printk(KERN_ERR "OSPCISuspendDev: can't enter requested power state");
508 printk(KERN_ERR "OSPCISuspendDev: pci_set_power_state failed (%d)", err);
515 /*************************************************************************/ /*!
516 @Function OSPCIResumeDev
517 @Description Prepare a PCI device to be resumed by power management
518 @Input hPVRPCI PCI device handle
519 @Return PVRSRV_ERROR Services error code
520 */ /**************************************************************************/
521 PVRSRV_ERROR OSPCIResumeDev(PVRSRV_PCI_DEV_HANDLE hPVRPCI)
523 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
527 err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_ON));
533 printk(KERN_ERR "OSPCIResumeDev: device doesn't support PCI PM");
536 printk(KERN_ERR "OSPCIResumeDev: can't enter requested power state");
537 return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
539 printk(KERN_ERR "OSPCIResumeDev: pci_set_power_state failed (%d)", err);
540 return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
543 pci_restore_state(psPVRPCI->psPCIDev);
545 err = pci_enable_device(psPVRPCI->psPCIDev);
548 printk(KERN_ERR "OSPCIResumeDev: Couldn't enable device (%d)", err);
549 return PVRSRV_ERROR_PCI_CALL_FAILED;
552 if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER) /* PRQA S 3358 */ /* misuse of enums */
553 pci_set_master(psPVRPCI->psPCIDev);
555 /* Restore the PCI resource tracking array */
556 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
558 if (psPVRPCI->abPCIResourceInUse[i])
560 err = pci_request_region(psPVRPCI->psPCIDev, i, PVRSRV_MODNAME);
563 printk(KERN_ERR "OSPCIResumeDev: pci_request_region_failed (region %d, error %d)", i, err);
571 #if defined(CONFIG_MTRR)
573 /*************************************************************************/ /*!
574 @Function OSPCIClearResourceMTRRs
575 @Description Clear any BIOS-configured MTRRs for a PCI memory region
576 @Input hPVRPCI PCI device handle
577 @Input ui32Index Address range index
578 @Return PVRSRV_ERROR Services error code
579 */ /**************************************************************************/
580 PVRSRV_ERROR OSPCIClearResourceMTRRs(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
582 PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
583 resource_size_t start, end;
586 start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
587 end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index) + 1;
589 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
590 err = arch_phys_wc_add(start, end - start);
593 return PVRSRV_ERROR_PCI_CALL_FAILED;
597 err = mtrr_add(start, end - start, MTRR_TYPE_UNCACHABLE, 0);
600 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
601 return PVRSRV_ERROR_PCI_CALL_FAILED;
604 err = mtrr_del(err, start, end - start);
607 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_del failed (%d)", err);
608 return PVRSRV_ERROR_PCI_CALL_FAILED;
611 /* Workaround for overlapping MTRRs. */
613 IMG_BOOL bGotMTRR0 = IMG_FALSE;
615 /* Current mobo BIOSes will normally set up a WRBACK MTRR spanning
616 * 0->4GB, and then another 4GB->6GB. If the PCI card's automatic &
617 * overlapping UNCACHABLE MTRR is deleted, we see WRBACK behaviour.
619 * WRBACK is incompatible with some PCI devices, so try to split
620 * the UNCACHABLE regions up and insert a WRCOMB region instead.
622 err = mtrr_add(start, end - start, MTRR_TYPE_WRBACK, 0);
625 /* If this fails, services has probably run before and created
626 * a write-combined MTRR for the test chip. Assume it has, and
627 * don't return an error here.
633 bGotMTRR0 = IMG_TRUE;
635 err = mtrr_del(err, start, end - start);
638 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_del failed (%d)", err);
639 return PVRSRV_ERROR_PCI_CALL_FAILED;
644 /* Replace 0 with a non-overlapping WRBACK MTRR */
645 err = mtrr_add(0, start, MTRR_TYPE_WRBACK, 0);
648 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
649 return PVRSRV_ERROR_PCI_CALL_FAILED;
652 /* Add a WRCOMB MTRR for the PCI device memory bar */
653 err = mtrr_add(start, end - start, MTRR_TYPE_WRCOMB, 0);
656 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
657 return PVRSRV_ERROR_PCI_CALL_FAILED;
666 #endif /* defined(CONFIG_MTRR) */