RK3368 GPU: Rogue N Init.
[firefly-linux-kernel-4.4.55.git] / drivers / staging / imgtec / apollo / pci_support.c
1 /*************************************************************************/ /*!
2 @File
3 @Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
4 @License        Dual MIT/GPLv2
5
6 The contents of this file are subject to the MIT license as set out below.
7
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:
14
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17
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.
21
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.
29
30 This License is also included in this distribution in the file called
31 "MIT-COPYING".
32
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 */ /**************************************************************************/
41
42 #include <linux/pci.h>
43
44 #if defined(CONFIG_MTRR)
45 #include <asm/mtrr.h>
46 #include <linux/version.h>
47 #endif
48
49 #include "pci_support.h"
50 #include "allocmem.h"
51
52 typedef struct _PVR_PCI_DEV_TAG
53 {
54         struct pci_dev          *psPCIDev;
55         HOST_PCI_INIT_FLAGS     ePCIFlags;
56         IMG_BOOL                abPCIResourceInUse[DEVICE_COUNT_RESOURCE];
57 } PVR_PCI_DEV;
58
59 /*************************************************************************/ /*!
60 @Function       OSPCISetDev
61 @Description    Set a PCI device for subsequent use.
62 @Input          pvPCICookie             Pointer to OS specific PCI structure
63 @Input          eFlags                  Flags
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)
67 {
68         int err;
69         IMG_UINT32 i;
70         PVR_PCI_DEV *psPVRPCI;
71
72         psPVRPCI = OSAllocMem(sizeof(*psPVRPCI));
73         if (psPVRPCI == NULL)
74         {
75                 printk(KERN_ERR "OSPCISetDev: Couldn't allocate PVR PCI structure\n");
76                 return NULL;
77         }
78
79         psPVRPCI->psPCIDev = (struct pci_dev *)pvPCICookie;
80         psPVRPCI->ePCIFlags = eFlags;
81
82         err = pci_enable_device(psPVRPCI->psPCIDev);
83         if (err != 0)
84         {
85                 printk(KERN_ERR "OSPCISetDev: Couldn't enable device (%d)\n", err);
86                 OSFreeMem(psPVRPCI);
87                 return NULL;
88         }
89
90         if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)        /* PRQA S 3358 */ /* misuse of enums */
91         {
92                 pci_set_master(psPVRPCI->psPCIDev);
93         }
94
95         if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI)               /* PRQA S 3358 */ /* misuse of enums */
96         {
97 #if defined(CONFIG_PCI_MSI)
98                 err = pci_enable_msi(psPVRPCI->psPCIDev);
99                 if (err != 0)
100                 {
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 */
103                 }
104 #else
105                 printk(KERN_ERR "OSPCISetDev: MSI support not enabled in the kernel");
106 #endif
107 }
108
109         /* Initialise the PCI resource tracking array */
110         for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
111         {
112                 psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
113         }
114
115         return (PVRSRV_PCI_DEV_HANDLE)psPVRPCI;
116 }
117
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
123 @Input          eFlags                  Flags
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)
129 {
130         struct pci_dev *psPCIDev;
131
132         psPCIDev = pci_get_device(ui16VendorID, ui16DeviceID, NULL);
133         if (psPCIDev == NULL)
134         {
135                 return NULL;
136         }
137
138         return OSPCISetDev((void *)psPCIDev, eFlags);
139 }
140
141 /*************************************************************************/ /*!
142 @Function       OSPCIDevID
143 @Description    Get the PCI device ID.
144 @Input          hPVRPCI                 PCI device handle
145 @Output         pui16DeviceID           Pointer to where the device ID should 
146                                         be returned
147 @Return         PVRSRV_ERROR            Services error code
148 */ /**************************************************************************/
149 PVRSRV_ERROR OSPCIDevID(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT16 *pui16DeviceID)
150 {
151         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
152
153         if (pui16DeviceID == NULL)
154         {
155                 return PVRSRV_ERROR_INVALID_PARAMS;
156         }
157
158         *pui16DeviceID = psPVRPCI->psPCIDev->device;
159
160         return PVRSRV_OK;
161 }
162
163 /*************************************************************************/ /*!
164 @Function       OSPCIIRQ
165 @Description    Get the interrupt number for the device.
166 @Input          hPVRPCI                 PCI device handle
167 @Output         pui16DeviceID           Pointer to where the interrupt number 
168                                         should be returned
169 @Return         PVRSRV_ERROR            Services error code
170 */ /**************************************************************************/
171 PVRSRV_ERROR OSPCIIRQ(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 *pui32IRQ)
172 {
173         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
174
175         if (pui32IRQ == NULL)
176         {
177                 return PVRSRV_ERROR_INVALID_PARAMS;
178         }
179
180         *pui32IRQ = psPVRPCI->psPCIDev->irq;
181
182         return PVRSRV_OK;
183 }
184
185 /* Functions supported by OSPCIAddrRangeFunc */
186 enum HOST_PCI_ADDR_RANGE_FUNC
187 {
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
193 };
194
195 /*************************************************************************/ /*!
196 @Function       OSPCIAddrRangeFunc
197 @Description    Internal support function for various address range related 
198                 functions
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)
207 {
208         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
209
210         if (ui32Index >= DEVICE_COUNT_RESOURCE)
211         {
212                 printk(KERN_ERR "OSPCIAddrRangeFunc: Index out of range");
213                 return 0;
214         }
215
216         switch (eFunc)
217         {
218                 case HOST_PCI_ADDR_RANGE_FUNC_LEN:
219                 {
220                         return pci_resource_len(psPVRPCI->psPCIDev, ui32Index);
221                 }
222                 case HOST_PCI_ADDR_RANGE_FUNC_START:
223                 {
224                         return pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
225                 }
226                 case HOST_PCI_ADDR_RANGE_FUNC_END:
227                 {
228                         return pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
229                 }
230                 case HOST_PCI_ADDR_RANGE_FUNC_REQUEST:
231                 {
232                         int err = pci_request_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index, PVRSRV_MODNAME);
233                         if (err != 0)
234                         {
235                                 printk(KERN_ERR "OSPCIAddrRangeFunc: pci_request_region_failed (%d)", err);
236                                 return 0;
237                         }
238                         psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_TRUE;
239                         return 1;
240                 }
241                 case HOST_PCI_ADDR_RANGE_FUNC_RELEASE:
242                 {
243                         if (psPVRPCI->abPCIResourceInUse[ui32Index])
244                         {
245                                 pci_release_region(psPVRPCI->psPCIDev, (IMG_INT)ui32Index);
246                                 psPVRPCI->abPCIResourceInUse[ui32Index] = IMG_FALSE;
247                         }
248                         return 1;
249                 }
250                 default:
251                 {
252                         printk(KERN_ERR "OSPCIAddrRangeFunc: Unknown function");
253                         break;
254                 }
255         }
256
257         return 0;
258 }
259
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 
266                                         such range
267 */ /**************************************************************************/
268 IMG_UINT64 OSPCIAddrRangeLen(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
269 {
270         return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_LEN, hPVRPCI, ui32Index);
271 }
272
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 
279                                         such range
280 */ /**************************************************************************/
281 IMG_UINT64 OSPCIAddrRangeStart(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
282 {
283         return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_START, hPVRPCI, ui32Index); 
284 }
285
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
292                                         range
293 */ /**************************************************************************/
294 IMG_UINT64 OSPCIAddrRangeEnd(PVRSRV_PCI_DEV_HANDLE hPVRPCI, IMG_UINT32 ui32Index)
295 {
296         return OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_END, hPVRPCI, ui32Index); 
297 }
298
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)
308 {
309         if (OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_REQUEST, hPVRPCI, ui32Index) == 0)
310         {
311                 return PVRSRV_ERROR_PCI_CALL_FAILED;
312         }
313         else
314         {
315                 return PVRSRV_OK;
316         }
317 }
318
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)
327 {
328         if (OSPCIAddrRangeFunc(HOST_PCI_ADDR_RANGE_FUNC_RELEASE, hPVRPCI, ui32Index) == 0)
329         {
330                 return PVRSRV_ERROR_PCI_CALL_FAILED;
331         }
332         else
333         {
334                 return PVRSRV_OK;
335         }
336 }
337
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,
350                                                                         IMG_UINT64 uiOffset,
351                                                                         IMG_UINT64 uiLength)
352 {
353         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
354         resource_size_t start;
355         resource_size_t end;
356
357         start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
358         end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
359
360         /* Check that the requested region is valid */
361         if ((start + uiOffset + uiLength - 1) > end)
362         {
363                 return PVRSRV_ERROR_BAD_REGION_SIZE_MISMATCH;
364         }
365
366         if (pci_resource_flags(psPVRPCI->psPCIDev, ui32Index) & IORESOURCE_IO)
367         {
368                 if (request_region(start + uiOffset, uiLength, PVRSRV_MODNAME) == NULL)
369                 {
370                         return PVRSRV_ERROR_PCI_REGION_UNAVAILABLE;
371                 }
372         }
373         else
374         {
375                 if (request_mem_region(start + uiOffset, uiLength, PVRSRV_MODNAME) == NULL)
376                 {
377                         return PVRSRV_ERROR_PCI_REGION_UNAVAILABLE;
378                 }
379         }
380
381         return PVRSRV_OK;
382 }
383
384 /*************************************************************************/ /*!
385 @Function       OSPCIReleaseAddrRegion
386 @Description    Release a given region, from an address range, that is no 
387                 longer in use
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,
397                                                                         IMG_UINT64 uiOffset,
398                                                                         IMG_UINT64 uiLength)
399 {
400         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
401         resource_size_t start;
402         resource_size_t end;
403
404         start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
405         end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index);
406
407         /* Check that the region is valid */
408         if ((start + uiOffset + uiLength - 1) > end)
409         {
410                 return PVRSRV_ERROR_BAD_REGION_SIZE_MISMATCH;
411         }
412
413         if (pci_resource_flags(psPVRPCI->psPCIDev, ui32Index) & IORESOURCE_IO)
414         {
415                 release_region(start + uiOffset, uiLength);
416         }
417         else
418         {
419                 release_mem_region(start + uiOffset, uiLength);
420         }
421
422         return PVRSRV_OK;
423 }
424
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)
432 {
433         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
434         int i;
435
436         /* Release all PCI regions that are currently in use */
437         for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
438         {
439                 if (psPVRPCI->abPCIResourceInUse[i])
440                 {
441                         pci_release_region(psPVRPCI->psPCIDev, i);
442                         psPVRPCI->abPCIResourceInUse[i] = IMG_FALSE;
443                 }
444         }
445
446 #if defined(CONFIG_PCI_MSI)
447         if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_MSI)               /* PRQA S 3358 */ /* misuse of enums */
448         {
449                 pci_disable_msi(psPVRPCI->psPCIDev);
450         }
451 #endif
452
453         if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)        /* PRQA S 3358 */ /* misuse of enums */
454         {
455                 pci_clear_master(psPVRPCI->psPCIDev);
456         }
457
458         pci_disable_device(psPVRPCI->psPCIDev);
459
460         OSFreeMem(psPVRPCI);
461         /*not nulling pointer, copy on stack*/
462
463         return PVRSRV_OK;
464 }
465
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)
473 {
474         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
475         int i;
476         int err;
477
478         /* Release all PCI regions that are currently in use */
479         for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
480         {
481                 if (psPVRPCI->abPCIResourceInUse[i])
482                 {
483                         pci_release_region(psPVRPCI->psPCIDev, i);
484                 }
485         }
486
487         err = pci_save_state(psPVRPCI->psPCIDev);
488         if (err != 0)
489         {
490                 printk(KERN_ERR "OSPCISuspendDev: pci_save_state_failed (%d)", err);
491                 return PVRSRV_ERROR_PCI_CALL_FAILED;
492         }
493
494         pci_disable_device(psPVRPCI->psPCIDev);
495
496         err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_SUSPEND));
497         switch(err)
498         {
499                 case 0:
500                         break;
501                 case -EIO:
502                         printk(KERN_ERR "OSPCISuspendDev: device doesn't support PCI PM");
503                         break;
504                 case -EINVAL:
505                         printk(KERN_ERR "OSPCISuspendDev: can't enter requested power state");
506                         break;
507                 default:
508                         printk(KERN_ERR "OSPCISuspendDev: pci_set_power_state failed (%d)", err);
509                         break;
510         }
511
512         return PVRSRV_OK;
513 }
514
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)
522 {
523         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
524         int err;
525         int i;
526
527         err = pci_set_power_state(psPVRPCI->psPCIDev, pci_choose_state(psPVRPCI->psPCIDev, PMSG_ON));
528         switch(err)
529         {
530                 case 0:
531                         break;
532                 case -EIO:
533                         printk(KERN_ERR "OSPCIResumeDev: device doesn't support PCI PM");
534                         break;
535                 case -EINVAL:
536                         printk(KERN_ERR "OSPCIResumeDev: can't enter requested power state");
537                         return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
538                 default:
539                         printk(KERN_ERR "OSPCIResumeDev: pci_set_power_state failed (%d)", err);
540                         return PVRSRV_ERROR_UNKNOWN_POWER_STATE;
541         }
542
543         pci_restore_state(psPVRPCI->psPCIDev);
544
545         err = pci_enable_device(psPVRPCI->psPCIDev);
546         if (err != 0)
547         {
548                 printk(KERN_ERR "OSPCIResumeDev: Couldn't enable device (%d)", err);
549                 return PVRSRV_ERROR_PCI_CALL_FAILED;
550         }
551
552         if (psPVRPCI->ePCIFlags & HOST_PCI_INIT_FLAG_BUS_MASTER)        /* PRQA S 3358 */ /* misuse of enums */
553                 pci_set_master(psPVRPCI->psPCIDev);
554
555         /* Restore the PCI resource tracking array */
556         for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
557         {
558                 if (psPVRPCI->abPCIResourceInUse[i])
559                 {
560                         err = pci_request_region(psPVRPCI->psPCIDev, i, PVRSRV_MODNAME);
561                         if (err != 0)
562                         {
563                                 printk(KERN_ERR "OSPCIResumeDev: pci_request_region_failed (region %d, error %d)", i, err);
564                         }
565                 }
566         }
567
568         return PVRSRV_OK;
569 }
570
571 #if defined(CONFIG_MTRR)
572
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)
581 {
582         PVR_PCI_DEV *psPVRPCI = (PVR_PCI_DEV *)hPVRPCI;
583         resource_size_t start, end;
584         int err;
585
586         start = pci_resource_start(psPVRPCI->psPCIDev, ui32Index);
587         end = pci_resource_end(psPVRPCI->psPCIDev, ui32Index) + 1;
588
589 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
590         err = arch_phys_wc_add(start, end - start);
591         if (err < 0)
592         {
593                 return PVRSRV_ERROR_PCI_CALL_FAILED;
594         }
595 #else
596
597         err = mtrr_add(start, end - start, MTRR_TYPE_UNCACHABLE, 0);
598         if (err < 0)
599         {
600                 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
601                 return PVRSRV_ERROR_PCI_CALL_FAILED;
602         }
603
604         err = mtrr_del(err, start, end - start);
605         if (err < 0)
606         {
607                 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_del failed (%d)", err);
608                 return PVRSRV_ERROR_PCI_CALL_FAILED;
609         }
610
611         /* Workaround for overlapping MTRRs. */
612         {
613                 IMG_BOOL bGotMTRR0 = IMG_FALSE;
614
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.
618                  *
619                  * WRBACK is incompatible with some PCI devices, so try to split
620                  * the UNCACHABLE regions up and insert a WRCOMB region instead.
621                  */
622                 err = mtrr_add(start, end - start, MTRR_TYPE_WRBACK, 0);
623                 if (err < 0)
624                 {
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.
628                          */
629                         return PVRSRV_OK;
630                 }
631
632                 if(err == 0)
633                         bGotMTRR0 = IMG_TRUE;
634
635                 err = mtrr_del(err, start, end - start);
636                 if(err < 0)
637                 {
638                         printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_del failed (%d)", err);
639                         return PVRSRV_ERROR_PCI_CALL_FAILED;
640                 }
641
642                 if(bGotMTRR0)
643                 {
644                         /* Replace 0 with a non-overlapping WRBACK MTRR */
645                         err = mtrr_add(0, start, MTRR_TYPE_WRBACK, 0);
646                         if(err < 0)
647                         {
648                                 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
649                                 return PVRSRV_ERROR_PCI_CALL_FAILED;
650                         }
651
652                         /* Add a WRCOMB MTRR for the PCI device memory bar */
653                         err = mtrr_add(start, end - start, MTRR_TYPE_WRCOMB, 0);
654                         if(err < 0)
655                         {
656                                 printk(KERN_ERR "OSPCIClearResourceMTRRs: mtrr_add failed (%d)", err);
657                                 return PVRSRV_ERROR_PCI_CALL_FAILED;
658                         }
659                 }
660         }
661 #endif
662
663         return PVRSRV_OK;
664 }
665
666 #endif /* defined(CONFIG_MTRR) */