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