summaryrefslogtreecommitdiff
path: root/Silicon/Intel/PurleyRcPkg/Library/UsraAccessLib/PcieAccess.c
blob: 5af2c5953a1220101ec4f14a3b89707b70238fd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/** @file

Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that accompanies this distribution.
The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "UsraAccessLib.h"

#define MAX_IO_PORT_ADDRESS   0xFFFF

//
// Lookup table for increment values based on transfer widths
//
UINT8 mInStride[] = {
  1, // UsraWidth8
  2, // UsraWidth16
  4, // UsraWidth32
  8, // UsraWidth64
  0, // UsraWidthFifo8
  0, // UsraWidthFifo16
  0, // UsraWidthFifo32
  0, // UsraWidthFifo64
  1, // UsraWidthFill8
  2, // UsraWidthFill16
  4, // UsraWidthFill32
  8  // UsraWidthFill64
};

//
// Lookup table for increment values based on transfer widths
//
UINT8 mOutStride[] = {
  1, // UsraWidth8
  2, // UsraWidth16
  4, // UsraWidth32
  8, // UsraWidth64
  1, // UsraWidthFifo8
  2, // UsraWidthFifo16
  4, // UsraWidthFifo32
  8, // UsraWidthFifo64
  0, // UsraWidthFill8
  0, // UsraWidthFill16
  0, // UsraWidthFill32
  0  // UsraWidthFill64
};


/**
  This API gets the Pcie address from the given USRA Address.
  
  @param[in] Global               Global pointer
  @param[in] Virtual              Virtual address
  @param[in] Address              A pointer of the address of the USRA Address Structure
  @param[out] AlignedAddress      A pointer of aligned address converted from USRA address

  @retval NONE
**/
VOID
GetPcieAccessAddress (
  IN VOID                     *Global,
  IN BOOLEAN                  Virtual,
  IN USRA_ADDRESS             *Address,
  OUT UINTN                   *AlignedAddress
  )
{
  INTN                        MmCfgBase;
  
  MmCfgBase = GetPcieSegMmcfgBaseAddress(Address);
  // TODO: add Error Check for NULL later
  *AlignedAddress = MmCfgBase + (UINTN)(Address->Attribute.RawData32[0] & 0x0fffffff);
}

/**
  This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register read operations. 
  It transfers data from a register into a naturally aligned data buffer.
  
  @param[in] Address              A pointer of the address of the USRA Address Structure to be read out
  @param[in] Buffer               A pointer of buffer for the value read from the register

  @retval RETURN_SUCCESS          The function completed successfully.
**/
RETURN_STATUS
PcieRegisterRead (
  IN USRA_ADDRESS             *Address,
  IN VOID                     *Buffer
  )
{
  UINTN                       AlignedAddress;
  
  GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress);
  UsraRegAlignedRead((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer);

  return RETURN_SUCCESS;
}

/**
  Check parameters to PcieBlkRegisterRead() function request.

  The I/O operations are carried out exactly as requested. The caller is responsible 
  for satisfying any alignment and I/O width restrictions that a PI System on a 
  platform might require. For example on some platforms, width requests of 
  UsraWidth64 do not work. Misaligned buffers, on the other hand, will 
  be handled by the driver.
  
  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
  @param[in] Width          Signifies the width of the I/O or Memory operation.
  @param[in] Address        The base address of the I/O operation. 
  @param[in] Count          The number of I/O operations to perform. The number of  
                            bytes moved is Width size * Count, starting at Address.
  @param[in] Buffer         For read operations, the destination buffer to store the results.
                            For write operations, the source buffer from which to write data.

  @retval EFI_SUCCESS            The parameters for this request pass the checks.
  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
  @retval EFI_UNSUPPORTED        The address range specified by Address, Width, 
                                 and Count is not valid for this PI system.

**/
STATIC
RETURN_STATUS
CpuIoCheckParameter (
  IN BOOLEAN                    MmioOperation,
  IN USRA_ACCESS_WIDTH          Width,
  IN UINT64                     Address,
  IN UINTN                      Count,
  IN VOID                       *Buffer
  )
{
  UINT64  MaxCount;
  UINT64  Limit;

  //
  // Check to see if Buffer is NULL
  //
  if (Buffer == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // Check to see if Width is in the valid range
  //
  if ((UINT32)Width >= UsraWidthMaximum) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // For FIFO type, the target address won't increase during the access,
  // so treat Count as 1
  //
  if (Width >= UsraWidthFifo8 && Width <= UsraWidthFifo64) {
    Count = 1;
  }

  //
  // Check to see if Width is in the valid range for I/O Port operations
  //
  Width = (USRA_ACCESS_WIDTH) (Width & 0x03);
  if (!MmioOperation && (Width == UsraWidth64)) {
    return RETURN_INVALID_PARAMETER;
  }
  
  //
  // Check to see if Address is aligned
  //
  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {
    return RETURN_UNSUPPORTED;
  }

  //
  // Check to see if any address associated with this transfer exceeds the maximum 
  // allowed address.  The maximum address implied by the parameters passed in is
  // Address + Size * Count.  If the following condition is met, then the transfer
  // is not supported.
  //
  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
  //
  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count 
  // can also be the maximum integer value supported by the CPU, this range
  // check must be adjusted to avoid all oveflow conditions.
  //   
  // The following form of the range check is equivalent but assumes that 
  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
  //
  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
  if (Count == 0) {
    if (Address > Limit) {
      return RETURN_UNSUPPORTED;
    }
  } else {  
    MaxCount = RShiftU64 (Limit, Width);
    if (MaxCount < (Count - 1)) {
      return RETURN_UNSUPPORTED;
    }
    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
      return RETURN_UNSUPPORTED;
    }
  }

  //
  // Check to see if Buffer is aligned
  // (IA-32 allows UINT64 and INT64 data types to be 32-bit aligned.)
  //
  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {
    return RETURN_UNSUPPORTED;
  }

  return RETURN_SUCCESS;
}

/**
  This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register read operations. 
  It transfers data from a register into a naturally aligned data buffer.
  
  @param[in] Address              A pointer of the address of the USRA Address Structure to be read out
  @param[in] Buffer               A pointer of buffer for the value read from the register

  @retval RETURN_SUCCESS          The function completed successfully.
  @retval Others                  Some error occurs when executing CpuIoCheckParameter function.
**/
RETURN_STATUS
PcieBlkRegisterRead (
  IN USRA_ADDRESS             *Address,
  IN VOID                     *Buffer
  )
{
  UINT8                       InStride;
  UINT8                       OutStride;
  RETURN_STATUS               Status;
  UINTN                       AlignedAddress;
  UINT32                      ReadCount = Address->PcieBlk.Count;
  UINT8                       *UINT8Buffer;
  
  GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress);
  Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, ReadCount, Buffer);
  if (RETURN_ERROR (Status)) {
    return Status;
  }

  InStride = mInStride[Address->Attribute.AccessWidth];
  OutStride = mOutStride[Address->Attribute.AccessWidth];
  for (UINT8Buffer = Buffer; ReadCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, ReadCount--) {
    UsraRegAlignedRead((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer);
  }
  
  return RETURN_SUCCESS;
}

/**
  This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register write operations. 
  It transfers data from a naturally aligned data buffer into a register.
  
  @param[in] Address              A pointer of the address of the USRA Address Structure to be written
  @param[in] Buffer               A pointer of buffer for the value write to the register

  @retval RETURN_SUCCESS          The function completed successfully.
**/
RETURN_STATUS
PcieRegisterWrite (
  IN USRA_ADDRESS             *Address,
  OUT VOID                    *Buffer
  )
{
  UINTN                       AlignedAddress;
  
  GetPcieAccessAddress(NULL, 0, Address, &AlignedAddress);
  UsraRegAlignedWrite((UINT32)Address->Attribute.AccessWidth, AlignedAddress, Buffer);
  
  if (FeaturePcdGet (PcdUsraSupportS3)) 
  {  
    if(Address->Attribute.S3Enable)
    {
      S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)Address->Attribute.AccessWidth, (UINT64)AlignedAddress, 1, Buffer);
    }
  }
  
  return RETURN_SUCCESS;
}

/**
  This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie block silicon register write operations. 
  It transfers data from a naturally aligned data buffer into a register.
  
  @param[in] Address              A pointer of the address of the USRA Address Structure to be written
  @param[in] Buffer               A pointer of buffer for the value write to the register

  @retval RETURN_SUCCESS          The function completed successfully.
  @retval Others                  Some error occurs when executing CpuIoCheckParameter function.  
**/
RETURN_STATUS
PcieBlkRegisterWrite (
  IN USRA_ADDRESS             *Address,
  OUT VOID                    *Buffer
  )
{
  UINT8                       InStride;
  UINT8                       OutStride;
  RETURN_STATUS               Status;
  UINTN                       AlignedAddress;
  UINT32                      WriteCount = Address->PcieBlk.Count;
  UINT8                       *UINT8Buffer;
  
  GetPcieAccessAddress (NULL, 0, Address, &AlignedAddress);
  Status = CpuIoCheckParameter (TRUE, Address->Attribute.AccessWidth, AlignedAddress, WriteCount, Buffer);
  if (RETURN_ERROR (Status)) {
    return Status;
  }

  InStride = mInStride[Address->Attribute.AccessWidth];
  OutStride = mOutStride[Address->Attribute.AccessWidth];
  for (UINT8Buffer = Buffer; WriteCount > 0; AlignedAddress += InStride, UINT8Buffer += OutStride, WriteCount--) {
    UsraRegAlignedWrite((USRA_ACCESS_WIDTH) (Address->Attribute.AccessWidth & 0x03), AlignedAddress, (VOID *)UINT8Buffer);

    if (FeaturePcdGet (PcdUsraSupportS3)) {
      if(Address->Attribute.S3Enable) {
        S3BootScriptSaveMemWrite ((S3_BOOT_SCRIPT_LIB_WIDTH)(Address->Attribute.AccessWidth & 0x03), (UINT64)AlignedAddress, 1, (VOID *)UINT8Buffer);
      }
    }
  }

  return RETURN_SUCCESS;
}

/**
  This API performs 8-bit, 16-bit, 32-bit or 64-bit Pcie silicon register AND then OR operations. It read data from a
  register, And it with the AndBuffer, then Or it with the OrBuffer, and write the result back to the register
  
  @param[in] Address              A pointer of the address of the silicon register to be modified
  @param[in] AndBuffer            A pointer of buffer for the value used for AND operation
                                  A NULL pointer means no AND operation. RegisterModify() equivalents to RegisterOr()
  @param[in] OrBuffer             A pointer of buffer for the value used for OR operation
                                  A NULL pointer means no OR operation. RegisterModify() equivalents to RegisterAnd()

  @retval RETURN_SUCCESS          The function completed successfully.
**/
RETURN_STATUS
PcieRegisterModify (
  IN USRA_ADDRESS             *Address,
  IN VOID                     *AndBuffer,
  IN VOID                     *OrBuffer
  )
{
  UINT64                      Data;
  UINT8                       WidthTable[] = {1,2,4,8};
  
  PcieRegisterRead(Address, &Data);
  DataAndOr (&Data, AndBuffer, OrBuffer, WidthTable[(UINT8)Address->Attribute.AccessWidth]);
  PcieRegisterWrite(Address, &Data);
  
  return RETURN_SUCCESS;
}