MdeModulePkg: Fix PciBus hang issue
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciBusDxe / PciCommand.c
1 /** @file
2 PCI command register operations supporting functions implementation for PCI Bus module.
3
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PciBus.h"
16
17 /**
18 Operate the PCI register via PciIo function interface.
19
20 @param PciIoDevice Pointer to instance of PCI_IO_DEVICE.
21 @param Command Operator command.
22 @param Offset The address within the PCI configuration space for the PCI controller.
23 @param Operation Type of Operation.
24 @param PtrCommand Return buffer holding old PCI command, if operation is not EFI_SET_REGISTER.
25
26 @return Status of PciIo operation.
27
28 **/
29 EFI_STATUS
30 PciOperateRegister (
31 IN PCI_IO_DEVICE *PciIoDevice,
32 IN UINT16 Command,
33 IN UINT8 Offset,
34 IN UINT8 Operation,
35 OUT UINT16 *PtrCommand
36 )
37 {
38 UINT16 OldCommand;
39 EFI_STATUS Status;
40 EFI_PCI_IO_PROTOCOL *PciIo;
41
42 OldCommand = 0;
43 PciIo = &PciIoDevice->PciIo;
44
45 if (Operation != EFI_SET_REGISTER) {
46 Status = PciIo->Pci.Read (
47 PciIo,
48 EfiPciIoWidthUint16,
49 Offset,
50 1,
51 &OldCommand
52 );
53
54 if (Operation == EFI_GET_REGISTER) {
55 *PtrCommand = OldCommand;
56 return Status;
57 }
58 }
59
60 if (Operation == EFI_ENABLE_REGISTER) {
61 OldCommand = (UINT16) (OldCommand | Command);
62 } else if (Operation == EFI_DISABLE_REGISTER) {
63 OldCommand = (UINT16) (OldCommand & ~(Command));
64 } else {
65 OldCommand = Command;
66 }
67
68 return PciIo->Pci.Write (
69 PciIo,
70 EfiPciIoWidthUint16,
71 Offset,
72 1,
73 &OldCommand
74 );
75 }
76
77 /**
78 Check the cpability supporting by given device.
79
80 @param PciIoDevice Pointer to instance of PCI_IO_DEVICE.
81
82 @retval TRUE Cpability supportted.
83 @retval FALSE Cpability not supportted.
84
85 **/
86 BOOLEAN
87 PciCapabilitySupport (
88 IN PCI_IO_DEVICE *PciIoDevice
89 )
90 {
91 if ((PciIoDevice->Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {
92 return TRUE;
93 }
94
95 return FALSE;
96 }
97
98 /**
99 Locate capability register block per capability ID.
100
101 @param PciIoDevice A pointer to the PCI_IO_DEVICE.
102 @param CapId The capability ID.
103 @param Offset A pointer to the offset returned.
104 @param NextRegBlock A pointer to the next block returned.
105
106 @retval EFI_SUCCESS Successfuly located capability register block.
107 @retval EFI_UNSUPPORTED Pci device does not support capability.
108 @retval EFI_NOT_FOUND Pci device support but can not find register block.
109
110 **/
111 EFI_STATUS
112 LocateCapabilityRegBlock (
113 IN PCI_IO_DEVICE *PciIoDevice,
114 IN UINT8 CapId,
115 IN OUT UINT8 *Offset,
116 OUT UINT8 *NextRegBlock OPTIONAL
117 )
118 {
119 UINT8 CapabilityPtr;
120 UINT16 CapabilityEntry;
121 UINT8 CapabilityID;
122
123 //
124 // To check the cpability of this device supports
125 //
126 if (!PciCapabilitySupport (PciIoDevice)) {
127 return EFI_UNSUPPORTED;
128 }
129
130 if (*Offset != 0) {
131 CapabilityPtr = *Offset;
132 } else {
133
134 CapabilityPtr = 0;
135 if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) {
136
137 PciIoDevice->PciIo.Pci.Read (
138 &PciIoDevice->PciIo,
139 EfiPciIoWidthUint8,
140 EFI_PCI_CARDBUS_BRIDGE_CAPABILITY_PTR,
141 1,
142 &CapabilityPtr
143 );
144 } else {
145
146 PciIoDevice->PciIo.Pci.Read (
147 &PciIoDevice->PciIo,
148 EfiPciIoWidthUint8,
149 PCI_CAPBILITY_POINTER_OFFSET,
150 1,
151 &CapabilityPtr
152 );
153 }
154 }
155
156 while ((CapabilityPtr >= 0x40) && ((CapabilityPtr & 0x03) == 0x00)) {
157 PciIoDevice->PciIo.Pci.Read (
158 &PciIoDevice->PciIo,
159 EfiPciIoWidthUint16,
160 CapabilityPtr,
161 1,
162 &CapabilityEntry
163 );
164
165 CapabilityID = (UINT8) CapabilityEntry;
166
167 if (CapabilityID == CapId) {
168 *Offset = CapabilityPtr;
169 if (NextRegBlock != NULL) {
170 *NextRegBlock = (UINT8) (CapabilityEntry >> 8);
171 }
172
173 return EFI_SUCCESS;
174 }
175
176 CapabilityPtr = (UINT8) (CapabilityEntry >> 8);
177 }
178
179 return EFI_NOT_FOUND;
180 }
181
182 /**
183 Locate PciExpress capability register block per capability ID.
184
185 @param PciIoDevice A pointer to the PCI_IO_DEVICE.
186 @param CapId The capability ID.
187 @param Offset A pointer to the offset returned.
188 @param NextRegBlock A pointer to the next block returned.
189
190 @retval EFI_SUCCESS Successfuly located capability register block.
191 @retval EFI_UNSUPPORTED Pci device does not support capability.
192 @retval EFI_NOT_FOUND Pci device support but can not find register block.
193
194 **/
195 EFI_STATUS
196 LocatePciExpressCapabilityRegBlock (
197 IN PCI_IO_DEVICE *PciIoDevice,
198 IN UINT16 CapId,
199 IN OUT UINT32 *Offset,
200 OUT UINT32 *NextRegBlock OPTIONAL
201 )
202 {
203 EFI_STATUS Status;
204 UINT32 CapabilityPtr;
205 UINT32 CapabilityEntry;
206 UINT16 CapabilityID;
207
208 //
209 // To check the capability of this device supports
210 //
211 if (!PciIoDevice->IsPciExp) {
212 return EFI_UNSUPPORTED;
213 }
214
215 if (*Offset != 0) {
216 CapabilityPtr = *Offset;
217 } else {
218 CapabilityPtr = EFI_PCIE_CAPABILITY_BASE_OFFSET;
219 }
220
221 while (CapabilityPtr != 0) {
222 //
223 // Mask it to DWORD alignment per PCI spec
224 //
225 CapabilityPtr &= 0xFFC;
226 Status = PciIoDevice->PciIo.Pci.Read (
227 &PciIoDevice->PciIo,
228 EfiPciIoWidthUint32,
229 CapabilityPtr,
230 1,
231 &CapabilityEntry
232 );
233 if (EFI_ERROR (Status)) {
234 break;
235 }
236
237 CapabilityID = (UINT16) CapabilityEntry;
238
239 if (CapabilityID == CapId) {
240 *Offset = CapabilityPtr;
241 if (NextRegBlock != NULL) {
242 *NextRegBlock = (CapabilityEntry >> 20) & 0xFFF;
243 }
244
245 return EFI_SUCCESS;
246 }
247
248 CapabilityPtr = (CapabilityEntry >> 20) & 0xFFF;
249 }
250
251 return EFI_NOT_FOUND;
252 }