1 /** @file
2 
3   Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
4 
5 
6   This program and the accompanying materials are licensed and made available under
7 
8   the terms and conditions of the BSD License that accompanies this distribution.
9 
10   The full text of the license may be found at
11 
12   http://opensource.org/licenses/bsd-license.php.
13 
14 
15 
16   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17 
18   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 
20 
21 
22 
23 Module Name:
24 
25 
26   PciDevice.c
27 
28 Abstract:
29 
30   Platform Initialization Driver.
31 
32 Revision History
33 
34 --*/
35 
36 #include "PlatformDxe.h"
37 #include "Library/DxeServicesTableLib.h"
38 #include "PciBus.h"
39 #include "Guid/PciLanInfo.h"
40 
41 extern  VOID    *mPciLanInfo;
42 extern  UINTN   mPciLanCount;
43 
44 extern  EFI_HANDLE  mImageHandle;
45 extern  SYSTEM_CONFIGURATION    mSystemConfiguration;
46 
47 
48 VOID       *mPciRegistration;
49 #define NCR_VENDOR_ID  0x1000
50 #define ATI_VENDOR_ID  0x1002
51 #define INTEL_VENDOR_ID 0x8086
52 #define ATI_RV423_ID   0x5548
53 #define ATI_RV423_ID2  0x5d57
54 #define ATI_RV380_ID   0x3e50
55 #define ATI_RV370_ID   0x5b60
56 #define SI_VENDOR_ID   0x1095
57 #define SI_SISATA_ID   0x3114
58 #define SI_SIRAID_PCIUNL 0x40
59 #define INTEL_82573E_IDER 0x108D
60 
61 typedef struct {
62   UINT8               ClassCode;
63   UINT8               SubClassCode;
64   UINT16              VendorId;
65   UINT16              DeviceId;
PciBusDriverHook()66 } BAD_DEVICE_TABLE;
67 
68 BAD_DEVICE_TABLE BadDeviceTable[] = {
69                     {(UINT8)PCI_CLASS_MASS_STORAGE,(UINT8)PCI_CLASS_MASS_STORAGE_SCSI,(UINT16)NCR_VENDOR_ID, (UINT16)0xffff}, // Any NCR cards
70                     {(UINT8)PCI_CLASS_MASS_STORAGE,(UINT8)PCI_CLASS_MASS_STORAGE_IDE,(UINT16)INTEL_VENDOR_ID, (UINT16)INTEL_82573E_IDER},  // Intel i82573E Tekoa GBit Lan IDE-R
71                     {(UINT8)0xff,(UINT8)0xff,(UINT16)0xffff,(UINT16)0xffff}
72                   };
73 
74 EFI_STATUS
75 PciBusDriverHook (
76   )
77 {
78   EFI_STATUS                Status;
79   EFI_EVENT                 FilterEvent;
80 
81   //
82   // Register for callback to PCI I/O protocol
83   //
84   Status = gBS->CreateEvent (
85                   EVT_NOTIFY_SIGNAL,
86                   TPL_CALLBACK,
87                   PciBusEvent,
88                   NULL,
89                   &FilterEvent
90                   );
91   ASSERT_EFI_ERROR(Status);
92 
93   //
94   // Register for protocol notifications on this event
95   //
96   Status = gBS->RegisterProtocolNotify (
97                   &gEfiPciIoProtocolGuid,
InitBadBars(IN EFI_PCI_IO_PROTOCOL * PciIo,IN UINT16 VendorId,IN UINT16 DeviceId)98                   FilterEvent,
99                   &mPciRegistration
100                   );
101   ASSERT_EFI_ERROR (Status);
102 
103   return  EFI_SUCCESS;
104 }
105 
106 VOID
107 InitBadBars(
108   IN    EFI_PCI_IO_PROTOCOL           *PciIo,
109   IN    UINT16                        VendorId,
110   IN    UINT16                        DeviceId
111   )
112 {
113 
114   EFI_STATUS                          Status;
115   PCI_IO_DEVICE                       *PciIoDevice;
116   UINT64                              BaseAddress = 0;
117   UINT64                              TempBaseAddress = 0;
118   UINT8                               RevId = 0;
119   UINT32                              Bar;
120   UINT64                              IoSize;
121   UINT64                              MemSize;
122   UINTN                               MemSizeBits;
123 
124 
125   PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
126   switch ( VendorId) {
127     case ATI_VENDOR_ID:
128       //
129       //  ATI fix-ups. At this time all ATI cards in BadDeviceTable
130       //  have same problem in that OPROM BAR needs to be increased.
131       //
132       Bar = 0x30 ;
133       //
134       // Get original BAR address
135       //
136       Status = PciIo->Pci.Read (
137                             PciIo,
138                             EfiPciIoWidthUint32,
139                             Bar,
140                             1,
141                                 (VOID *) &BaseAddress
142                             );
143       //
144       // Find BAR size
145       //
146       TempBaseAddress = 0xffffffff;
147       Status = PciIo->Pci.Write (
148                             PciIo,
149                             EfiPciIoWidthUint32,
150                             Bar,
151                             1,
152                                  (VOID *) &TempBaseAddress
153                             );
154       Status = PciIo->Pci.Read (
155                             PciIo,
156                             EfiPciIoWidthUint32,
157                             Bar,
158                             1,
159                                 (VOID *) &TempBaseAddress
160                             );
161       TempBaseAddress &= 0xfffffffe;
162       MemSize = 1;
163       while ((TempBaseAddress & 0x01) == 0) {
164         TempBaseAddress = TempBaseAddress >> 1;
165         MemSize = MemSize << 1;
166       }
167 
168       //
169       // Free up allocated memory memory and re-allocate with increased size.
170       //
171       Status = gDS->FreeMemorySpace (
172                       BaseAddress,
173                       MemSize
174                       );
175       //
176       // Force new alignment
177       //
178       MemSize = 0x8000000;
179       MemSizeBits = 28;
180 
181       Status = gDS->AllocateMemorySpace (
182                       EfiGcdAllocateAnySearchBottomUp,
183                       EfiGcdMemoryTypeMemoryMappedIo,
184                       MemSizeBits,           // Alignment
185                       MemSize,
186                       &BaseAddress,
187                       mImageHandle,
188                       NULL
189                       );
190       Status = PciIo->Pci.Write (
191                             PciIo,
192                             EfiPciIoWidthUint32,
193                             Bar,
194                             1,
195                                  (VOID *) &BaseAddress
196                             );
197 
198       break;
199     case    NCR_VENDOR_ID:
200 #define MIN_NCR_IO_SIZE  0x800
201 #define NCR_GRAN  11  // 2**11 = 0x800
202   //
203   // NCR SCSI cards like 8250S lie about IO needed. Assign as least 0x80.
204   //
205   for (Bar = 0x10; Bar < 0x28; Bar+= 4) {
206 
207     Status = PciIo->Pci.Read (
208                           PciIo,
209                           EfiPciIoWidthUint32,
210                           Bar,
211                           1,
212                               (VOID *) &BaseAddress
213                           );
214     if (BaseAddress && 0x01) {
215       TempBaseAddress = 0xffffffff;
216       Status = PciIo->Pci.Write (
217                             PciIo,
218                             EfiPciIoWidthUint32,
219                             Bar,
220                             1,
221                                  (VOID *) &TempBaseAddress
222                             );
223       TempBaseAddress &= 0xfffffffc;
224       IoSize = 1;
225       while ((TempBaseAddress & 0x01) == 0) {
226         TempBaseAddress = TempBaseAddress >> 1;
227         IoSize = IoSize << 1;
228       }
229       if (IoSize < MIN_NCR_IO_SIZE) {
230         Status = gDS->FreeIoSpace (
231                         BaseAddress,
232                         IoSize
233                         );
234 
235         Status = gDS->AllocateIoSpace (
236                         EfiGcdAllocateAnySearchTopDown,
237                         EfiGcdIoTypeIo,
238                         NCR_GRAN,           // Alignment
239                         MIN_NCR_IO_SIZE,
240                         &BaseAddress,
241                         mImageHandle,
242                         NULL
243                         );
244         TempBaseAddress = BaseAddress + 1;
245         Status = PciIo->Pci.Write (
246                               PciIo,
247                               EfiPciIoWidthUint32,
248                               Bar,
249                               1,
250                                    (VOID *) &TempBaseAddress
251                               );
252       }
253     }
254   }
255 
256       break;
257 
258     case INTEL_VENDOR_ID:
259       if (DeviceId == INTEL_82573E_IDER) {
260         //
261         //  Tekoa i82573E IDE-R fix-ups. At this time A2 step and earlier parts do not
262         //  support any BARs except BAR0. Other BARS will actualy map to BAR0 so disable
263         //  them all for Control Blocks and Bus mastering ops as well as Secondary IDE
264         //  Controller.
265         //  All Tekoa A2 or earlier step chips for now.
266         //
267         Status = PciIo->Pci.Read (
268                               PciIo,
269                               EfiPciIoWidthUint8,
270                               PCI_REVISION_ID_OFFSET,
271                               1,
272                               &RevId
273                               );
274         if (RevId <= 0x02) {
275           for (Bar = 0x14; Bar < 0x24; Bar+= 4) {
276             //
277             // Maybe want to clean this up a bit later but for now just clear out the secondary
278             // Bars don't worry aboyut freeing up thge allocs.
279             //
280             TempBaseAddress = 0x0;
281             Status = PciIo->Pci.Write (
282                                   PciIo,
283                                   EfiPciIoWidthUint32,
284                                   Bar,
285                                   1,
286                                        (VOID *) &TempBaseAddress
287                                   );
288           } // end for
289         }
290         else
291         {
292         	//
293           //Tekoa A3 or above:
294           //Clear bus master base address (PCI register 0x20)
295           //since Tekoa does not fully support IDE Bus Mastering
296           //
297           TempBaseAddress = 0x0;
298           Status = PciIo->Pci.Write (
299                                 PciIo,
300                                 EfiPciIoWidthUint32,
301                                 0x20,
302                                 1,
303                                      (VOID *) &TempBaseAddress
304                                 );
305         }
306       }
ProgramPciLatency(IN EFI_PCI_IO_PROTOCOL * PciIo)307       break;
308 
309     default:
310       break;
311   }
312   return;
313 }
314 
315 VOID
316 ProgramPciLatency(
317   IN    EFI_PCI_IO_PROTOCOL           *PciIo
318   )
319 {
320   EFI_STATUS                          Status;
321 
322   //
323   // Program Master Latency Timer
324   //
325   if (mSystemConfiguration.PciLatency != 0) {
326      Status = PciIo->Pci.Write (
327                            PciIo,
328                            EfiPciIoWidthUint8,
329                            PCI_LATENCY_TIMER_OFFSET,
330                            1,
331                            &mSystemConfiguration.PciLatency
332                            );
333   }
SavePciLanAddress(IN EFI_PCI_IO_PROTOCOL * PciIo)334   return;
335 }
336 
337 /**
338 During S5 shutdown, we need to program PME in all LAN devices.
339 Here we identify LAN devices and save their bus/dev/func.
340 
341 **/
342 VOID
343 SavePciLanAddress(
344   IN EFI_PCI_IO_PROTOCOL    *PciIo
345   )
346 {
347   EFI_STATUS        Status;
348   UINTN             PciSegment,
349                     PciBus,
350                     PciDevice,
351                     PciFunction;
352   VOID              *NewBuffer;
353   PCI_LAN_INFO      *x;
354 
355   Status = PciIo->GetLocation (
356                     PciIo,
357                     &PciSegment,
358                     &PciBus,
359                     &PciDevice,
360                     &PciFunction
361                     );
362   if (EFI_ERROR (Status)) {
363     return;
364   }
365 
366   mPciLanCount ++;
367   Status = gBS->AllocatePool (
368                   EfiBootServicesData,
369                   mPciLanCount * sizeof(PCI_LAN_INFO),
370                   &NewBuffer
371                   );
372   if (EFI_ERROR (Status)) {
373     return;
374   }
375 
376   if (mPciLanCount > 1) {
377     //
378     // copy old data into new, larger buffer
379     //
380     gBS->CopyMem (
381            NewBuffer,
382            mPciLanInfo,
383            (mPciLanCount - 1) * sizeof(PCI_LAN_INFO)
384            );
385 
386     //
387     // free the old memory buffer
388     //
389     gBS->FreePool (mPciLanInfo);
390 
391   }
392 
393   //
394   // init the new entry
395   //
396   x = (PCI_LAN_INFO *)NewBuffer + (mPciLanCount - 1);
397   x->PciBus = (UINT8)PciBus;
398   x->PciDevice = (UINT8)PciDevice;
399   x->PciFunction = (UINT8)PciFunction;
400 
401   mPciLanInfo = NewBuffer;
402 
403   return;
404 }
PciBusEvent(IN EFI_EVENT Event,IN VOID * Context)405 
406 /**
407   @param  Event          the event that is signaled.
408   @param Context        not used here.
409 
410 
411 **/
412 VOID
413 EFIAPI
414 PciBusEvent (
415   IN EFI_EVENT    Event,
416   IN VOID*        Context
417   )
418 {
419 
420   EFI_STATUS                    Status;
421   UINTN                         BufferSize;
422   EFI_HANDLE                    Handle;
423   EFI_PCI_IO_PROTOCOL           *PciIo;
424   PCI_IO_DEVICE                 *PciIoDevice;
425   UINT64                        Supports;
426   UINTN                         Index;
427   UINT8                         mCacheLineSize = 0x10;
428 
429   while (TRUE) {
430     BufferSize = sizeof (EFI_HANDLE);
431     Status = gBS->LocateHandle (
432                     ByRegisterNotify,
433                     NULL,
434                     mPciRegistration,
435                     &BufferSize,
436                     &Handle
437                     );
438     if (EFI_ERROR (Status)) {
439       //
440       // If no more notification events exist
441       //
442       return;
443     }
444 
445     Status = gBS->HandleProtocol (
446                     Handle,
447                     &gEfiPciIoProtocolGuid,
448                     (void **)&PciIo
449                     );
450 
451     PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
452 
453     //
454     // Enable I/O for bridge so port 0x80 codes will come out
455     //
456     if (PciIoDevice->Pci.Hdr.VendorId == V_PCH_INTEL_VENDOR_ID)
457     {
458       Status = PciIo->Attributes(
459                         PciIo,
460                         EfiPciIoAttributeOperationSupported,
461                         0,
462                         &Supports
463                         );
464       Supports &= EFI_PCI_DEVICE_ENABLE;
465       Status = PciIo->Attributes (
466                         PciIo,
467                         EfiPciIoAttributeOperationEnable,
468                         Supports,
469                         NULL
470                         );
471       break;
472     }
473 
474     //
475     // Program PCI Latency Timer
476     //
477     ProgramPciLatency(PciIo);
478 
479     //
480     // Program Cache Line Size to 64 bytes (0x10 DWORDs)
481     //
482     Status = PciIo->Pci.Write (
483                           PciIo,
484                           EfiPciIoWidthUint8,
485                           PCI_CACHELINE_SIZE_OFFSET,
486                           1,
487                           &mCacheLineSize
488                           );
489 
490     //
491     // If PCI LAN device, save bus/dev/func info
492     // so we can program PME during S5 shutdown
493     //
494     if (PciIoDevice->Pci.Hdr.ClassCode[2] == PCI_CLASS_NETWORK) {
495       SavePciLanAddress(PciIo);
496       break;
497     }
498 
499     //
500     // Workaround for cards with bad BARs
501     //
502     Index = 0;
503     while (BadDeviceTable[Index].ClassCode != 0xff) {
504       if (BadDeviceTable[Index].DeviceId == 0xffff) {
505         if ((PciIoDevice->Pci.Hdr.ClassCode[2] == BadDeviceTable[Index].ClassCode) &&
506             (PciIoDevice->Pci.Hdr.ClassCode[1] == BadDeviceTable[Index].SubClassCode) &&
507             (PciIoDevice->Pci.Hdr.VendorId == BadDeviceTable[Index].VendorId)) {
508           InitBadBars(PciIo,BadDeviceTable[Index].VendorId,BadDeviceTable[Index].DeviceId);
509         }
510       } else {
511         if ((PciIoDevice->Pci.Hdr.ClassCode[2] == BadDeviceTable[Index].ClassCode) &&
512             (PciIoDevice->Pci.Hdr.ClassCode[1] == BadDeviceTable[Index].SubClassCode) &&
513             (PciIoDevice->Pci.Hdr.VendorId == BadDeviceTable[Index].VendorId) &&
514             (PciIoDevice->Pci.Hdr.DeviceId == BadDeviceTable[Index].DeviceId)) {
515 
516           InitBadBars(PciIo,BadDeviceTable[Index].VendorId,BadDeviceTable[Index].DeviceId);
517         }
518       }
519       ++Index;
520     }
521       break;
522     }
523 
524   return;
525 }
526 
527