1/*++
2
3Copyright (c) 2004 - 2006, Intel Corporation. All rights reserved.<BR>
4This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution.  The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13
14  PlatformIoLib.c
15
16Abstract:
17
18--*/
19
20#include "Tiano.h"
21#include "EfiRuntimeLib.h"
22#include EFI_PROTOCOL_DEFINITION (CpuIo)
23
24#define PCI_CONFIG_INDEX_PORT    0xcf8
25#define PCI_CONFIG_DATA_PORT     0xcfc
26#define REFRESH_CYCLE_TOGGLE_BIT 0x10
27
28UINT32
29GetPciAddress (
30  UINT8   Segment,
31  UINT8   Bus,
32  UINT8   DevFunc,
33  UINT8   Register
34  )
35/*++
36
37Routine Description:
38  Constructs PCI Address 32 bits
39
40Arguments:
41  Segment   - PCI Segment ACPI _SEG
42  Bus       - PCI Bus
43  DevFunc   - PCI Device(7:3) and Func(2:0)
44  Register  - PCI config space register
45
46Returns:
47  PciAddress to be written to Config Port
48
49--*/
50{
51  UINT32  Data;
52
53  Data  = (((UINT32) Segment) << 24);
54  Data |= (((UINT32) Bus) << 16);
55  Data |= (((UINT32) DevFunc) << 8);
56  Data |= (UINT32) Register;
57
58  return Data;
59
60}
61
62UINT8
63PciRead8 (
64  UINT8   Segment,
65  UINT8   Bus,
66  UINT8   DevFunc,
67  UINT8   Register
68  )
69/*++
70
71Routine Description:
72  Perform an one byte PCI config cycle read
73
74Arguments:
75  Segment   - PCI Segment ACPI _SEG
76  Bus       - PCI Bus
77  DevFunc   - PCI Device(7:3) and Func(2:0)
78  Register  - PCI config space register
79
80Returns:
81  Data read from PCI config space
82
83--*/
84{
85  EFI_STATUS  Status;
86  UINT32      PciAddress;
87  UINT32      PciAddress1;
88  UINT8       Data;
89
90  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
91  //
92  // Set bit 31 for PCI config access
93  //
94  PciAddress1 = PciAddress;
95  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
96
97  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
98
99  if (EFI_ERROR (Status)) {
100    return 0;
101  }
102
103  EfiIoRead (EfiCpuIoWidthUint8, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
104
105  return Data;
106}
107
108UINT16
109PciRead16 (
110  UINT8   Segment,
111  UINT8   Bus,
112  UINT8   DevFunc,
113  UINT8   Register
114  )
115/*++
116
117Routine Description:
118  Perform an two byte PCI config cycle read
119
120Arguments:
121  Segment   - PCI Segment ACPI _SEG
122  Bus       - PCI Bus
123  DevFunc   - PCI Device(7:3) and Func(2:0)
124  Register  - PCI config space register
125
126Returns:
127  Data read from PCI config space
128
129--*/
130{
131  EFI_STATUS  Status;
132  UINT32      PciAddress;
133  UINT32      PciAddress1;
134  UINT16      Data;
135
136  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
137  //
138  // Set bit 31 for PCI config access
139  //
140  PciAddress1 = PciAddress;
141  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
142
143  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
144
145  if (EFI_ERROR (Status)) {
146    return 0;
147  }
148
149  EfiIoRead (EfiCpuIoWidthUint16, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
150
151  return Data;
152}
153
154UINT32
155PciRead32 (
156  UINT8   Segment,
157  UINT8   Bus,
158  UINT8   DevFunc,
159  UINT8   Register
160  )
161/*++
162
163Routine Description:
164  Perform an four byte PCI config cycle read
165
166Arguments:
167  Segment   - PCI Segment ACPI _SEG
168  Bus       - PCI Bus
169  DevFunc   - PCI Device(7:3) and Func(2:0)
170  Register  - PCI config space register
171
172Returns:
173  Data read from PCI config space
174
175--*/
176{
177  EFI_STATUS  Status;
178  UINT32      PciAddress;
179  UINT32      PciAddress1;
180  UINT32      Data;
181
182  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
183  //
184  // Set bit 31 for PCI config access
185  //
186  PciAddress1 = PciAddress;
187  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
188
189  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
190
191  if (EFI_ERROR (Status)) {
192    return 0;
193  }
194
195  EfiIoRead (EfiCpuIoWidthUint32, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
196
197  return Data;
198}
199
200VOID
201PciWrite8 (
202  UINT8   Segment,
203  UINT8   Bus,
204  UINT8   DevFunc,
205  UINT8   Register,
206  UINT8   Data
207  )
208/*++
209
210Routine Description:
211  Perform an one byte PCI config cycle write
212
213Arguments:
214  Segment   - PCI Segment ACPI _SEG
215  Bus       - PCI Bus
216  DevFunc   - PCI Device(7:3) and Func(2:0)
217  Register  - PCI config space register
218  Data      - Data to write
219
220Returns:
221  NONE
222
223--*/
224{
225  EFI_STATUS  Status;
226  UINT32      PciAddress;
227  UINT32      PciAddress1;
228
229  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
230  //
231  // Set bit 31 for PCI config access
232  //
233  PciAddress1 = PciAddress;
234  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
235
236  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
237
238  if (EFI_ERROR (Status)) {
239    return ;
240  }
241
242  EfiIoWrite (EfiCpuIoWidthUint8, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
243}
244
245VOID
246PciWrite16 (
247  UINT8   Segment,
248  UINT8   Bus,
249  UINT8   DevFunc,
250  UINT8   Register,
251  UINT16  Data
252  )
253/*++
254
255Routine Description:
256  Perform an two byte PCI config cycle write
257
258Arguments:
259  Segment   - PCI Segment ACPI _SEG
260  Bus       - PCI Bus
261  DevFunc   - PCI Device(7:3) and Func(2:0)
262  Register  - PCI config space register
263  Data      - Data to write
264
265Returns:
266  NONE
267
268--*/
269{
270  EFI_STATUS  Status;
271  UINT32      PciAddress;
272  UINT32      PciAddress1;
273
274  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
275  //
276  // Set bit 31 for PCI config access
277  //
278  PciAddress1 = PciAddress;
279  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
280
281  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
282
283  if (EFI_ERROR (Status)) {
284    return ;
285  }
286
287  EfiIoWrite (EfiCpuIoWidthUint16, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
288}
289
290VOID
291PciWrite32 (
292  UINT8   Segment,
293  UINT8   Bus,
294  UINT8   DevFunc,
295  UINT8   Register,
296  UINT32  Data
297  )
298/*++
299
300Routine Description:
301  Perform an four byte PCI config cycle write
302
303Arguments:
304  Segment   - PCI Segment ACPI _SEG
305  Bus       - PCI Bus
306  DevFunc   - PCI Device(7:3) and Func(2:0)
307  Register  - PCI config space register
308  Data      - Data to write
309
310Returns:
311  NONE
312
313--*/
314{
315  EFI_STATUS  Status;
316  UINT32      PciAddress;
317  UINT32      PciAddress1;
318
319  PciAddress = GetPciAddress (Segment, Bus, DevFunc, Register);
320  //
321  // Set bit 31 for PCI config access
322  //
323  PciAddress1 = PciAddress;
324  PciAddress  = ((PciAddress & 0xFFFFFFFC) | (0x80000000));
325
326  Status      = EfiIoWrite (EfiCpuIoWidthUint32, PCI_CONFIG_INDEX_PORT, 1, &PciAddress);
327
328  if (EFI_ERROR (Status)) {
329    return ;
330  }
331
332  EfiIoWrite (EfiCpuIoWidthUint32, (PCI_CONFIG_DATA_PORT + (PciAddress1 & 0x3)), 1, &Data);
333}
334//
335// Delay Primative
336//
337VOID
338EfiStall (
339  IN  UINTN   Microseconds
340  )
341/*++
342
343Routine Description:
344 Delay for at least the request number of microseconds
345
346Arguments:
347  Microseconds - Number of microseconds to delay.
348
349Returns:
350  NONE
351
352--*/
353{
354  UINT8 Data;
355  UINT8 InitialState;
356  UINTN CycleIterations;
357
358  CycleIterations = 0;
359  Data            = 0;
360  InitialState    = 0;
361
362  if (EfiAtRuntime ()) {
363    //
364    // The time-source is 30 us granular, so calibrate the timing loop
365    // based on this baseline
366    // Error is possible 30us.
367    //
368    CycleIterations = (Microseconds - 1) / 30 + 1;
369
370    //
371    // Use the DMA Refresh timer in port 0x61.  Cheap but effective.
372    // The only issue is that the granularity is 30us, and we want to
373    // guarantee "at least" one full transition to avoid races.
374    //
375    //
376    //   _____________/----------\__________/--------
377    //
378    //                |<--15us-->|<--15us-->|
379    //
380    // --------------------------------------------------> Time (us)
381    //
382    while (CycleIterations--) {
383      EfiIoRead (EfiCpuIoWidthUint8, 0x61, 1, &Data);
384      Data &= REFRESH_CYCLE_TOGGLE_BIT;
385      InitialState = Data;
386
387      //
388      // Capture first transition (strictly less than one period)
389      //
390      while (InitialState == Data) {
391        EfiIoRead (EfiCpuIoWidthUint8, 0x61, 1, &Data);
392        Data &= REFRESH_CYCLE_TOGGLE_BIT;
393      }
394
395      InitialState = Data;
396      //
397      // Capture next transition (guarantee at least one full pulse)
398      //
399      while (InitialState == Data) {
400        EfiIoRead (EfiCpuIoWidthUint8, 0x61, 1, &Data);
401        Data &= REFRESH_CYCLE_TOGGLE_BIT;
402      }
403    }
404  } else {
405    gBS->Stall (Microseconds);
406  }
407}
408