hc
2024-03-26 e0728245c89800c2038c23308f2d88969d5b41c8
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
 /** @file
  SynQuacer DXE platform driver - PCIe support
 
  Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR>
 
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
 
#include "PlatformDxe.h"
 
#define ASMEDIA_VID                         0x1b21
#define ASM1061_PID                         0x0612
#define ASM1182E_PID                        0x1182
#define ASM1184E_PID                        0x1184
 
#define ASM1061_SSC_OFFSET                  0xA10
 
#define ASM118x_PCIE_CAPABILITY_OFFSET      0x80
#define ASM118x_PCIE_LINK_CONTROL_OFFSET    (ASM118x_PCIE_CAPABILITY_OFFSET + \
                                             OFFSET_OF (PCI_CAPABILITY_PCIEXP, \
                                                        LinkControl))
 
STATIC VOID         *mPciProtocolNotifyRegistration;
STATIC EFI_EVENT    mPciProtocolNotifyEvent;
 
#pragma pack(1)
typedef struct {
  EFI_PCI_CAPABILITY_HDR    CapHdr;
  PCI_REG_PCIE_CAPABILITY   Pcie;
} PCIE_CAP;
#pragma pack()
 
STATIC
VOID
RetrainAsm1184eDownstreamPort (
  IN  EFI_PCI_IO_PROTOCOL   *PciIo
  )
{
  EFI_STATUS                Status;
  PCIE_CAP                  Cap;
  PCI_REG_PCIE_LINK_CONTROL LinkControl;
  UINTN                     SegmentNumber;
  UINTN                     BusNumber;
  UINTN                     DeviceNumber;
  UINTN                     FunctionNumber;
  UINT8                     MaxSpeed;
 
  //
  // The upstream and downstream ports share the same PID/VID, so check
  // the port type. This assumes the PCIe Express capability block lives
  // at offset 0x80 in the port's config space, which is known to be the
  // case for these particular chips.
  //
  ASSERT (sizeof (Cap) == sizeof (UINT32));
  ASSERT (sizeof (LinkControl) == sizeof (UINT16));
 
  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32,
                        ASM118x_PCIE_CAPABILITY_OFFSET, 1, &Cap);
  ASSERT_EFI_ERROR (Status);
  ASSERT (Cap.CapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP);
 
  if (Cap.Pcie.Bits.DevicePortType != PCIE_DEVICE_PORT_TYPE_DOWNSTREAM_PORT) {
    return;
  }
 
  Status = PciIo->GetLocation (PciIo, &SegmentNumber, &BusNumber, &DeviceNumber,
                    &FunctionNumber);
  ASSERT_EFI_ERROR (Status);
 
  //
  // Check whether this downstream port is described by any of our 'slot'
  // definitions, and get the maximum speed if this is the case.
  //
  switch (SYNQUACER_PCI_LOCATION (SegmentNumber, BusNumber, DeviceNumber)) {
  case SYNQUACER_PCI_SLOT0_LOCATION:
    MaxSpeed = mHiiSettings->PcieSlot0MaxSpeed;
    break;
  case SYNQUACER_PCI_SLOT1_LOCATION:
    MaxSpeed = mHiiSettings->PcieSlot1MaxSpeed;
    break;
  case SYNQUACER_PCI_SLOT2_LOCATION:
    MaxSpeed = mHiiSettings->PcieSlot2MaxSpeed;
    break;
  default:
    MaxSpeed = PCIE_MAX_SPEED_UNLIMITED;
  }
  if (MaxSpeed == PCIE_MAX_SPEED_GEN1) {
    return;
  }
 
  DEBUG ((DEBUG_INFO,
    "%a: retraining ASM118x downstream PCIe port at %04x:%02x:%02x to Gen2\n",
    __FUNCTION__, SegmentNumber, BusNumber, DeviceNumber));
 
  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,
                        ASM118x_PCIE_LINK_CONTROL_OFFSET, 1, &LinkControl);
  ASSERT_EFI_ERROR (Status);
 
  LinkControl.Bits.RetrainLink = 1;
 
  Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16,
                        ASM118x_PCIE_LINK_CONTROL_OFFSET, 1, &LinkControl);
  ASSERT_EFI_ERROR (Status);
}
 
STATIC
VOID
EnableAsm1061SpreadSpectrum (
  IN  EFI_PCI_IO_PROTOCOL   *PciIo
  )
{
  EFI_STATUS  Status;
  UINT8       SscVal;
 
  DEBUG ((DEBUG_INFO, "%a: enabling spread spectrum mode 0 for ASM1061\n",
    __FUNCTION__));
 
  // SSC mode 0~-4000 ppm, 1:1 modulation
 
  SscVal = 0;
  Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ASM1061_SSC_OFFSET, 1,
                        &SscVal);
  ASSERT_EFI_ERROR (Status);
 
  MemoryFence ();
  gBS->Stall (1); // delay at least 100 ns between writes of the same register
 
  SscVal = 1;
  Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ASM1061_SSC_OFFSET, 1,
                        &SscVal);
  ASSERT_EFI_ERROR (Status);
}
 
STATIC
VOID
EFIAPI
OnPciIoProtocolNotify (
  IN EFI_EVENT      Event,
  IN VOID           *Context
  )
{
  EFI_PCI_IO_PROTOCOL       *PciIo;
  EFI_STATUS                Status;
  EFI_HANDLE                HandleBuffer;
  UINTN                     BufferSize;
  UINT16                    PciVidPid[2];
 
  while (TRUE) {
    BufferSize = sizeof (EFI_HANDLE);
    Status = gBS->LocateHandle (ByRegisterNotify, NULL,
                    mPciProtocolNotifyRegistration, &BufferSize, &HandleBuffer);
    if (EFI_ERROR (Status)) {
      break;
    }
 
    Status = gBS->HandleProtocol (HandleBuffer, &gEfiPciIoProtocolGuid,
                    (VOID **)&PciIo);
    ASSERT_EFI_ERROR (Status);
 
    Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,
                          ARRAY_SIZE (PciVidPid), &PciVidPid);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_WARN, "%a: failed to read PCI vendor/product ID - %r\n",
        __FUNCTION__, Status));
      continue;
    }
 
    if (PciVidPid[0] != ASMEDIA_VID) {
      continue;
    }
 
    switch (PciVidPid[1]) {
    case ASM1061_PID:
      //
      // The ASM1061 SATA controller as integrated into the DeveloperBox design
      // emits too much electromagnetic radiation. So enable spread spectrum
      // mode.
      //
      EnableAsm1061SpreadSpectrum (PciIo);
      break;
    case ASM1182E_PID:
    case ASM1184E_PID:
      //
      // The ASM1184E 4-port PCIe switch on the DeveloperBox board (and its
      // 2-port sibling of which samples were used in development) needs a
      // little nudge to get it to train the downstream links at Gen2 speed.
      //
      RetrainAsm1184eDownstreamPort (PciIo);
      break;
    }
  }
}
 
EFI_STATUS
EFIAPI
RegisterPcieNotifier (
  VOID
  )
{
  mPciProtocolNotifyEvent = EfiCreateProtocolNotifyEvent (
                              &gEfiPciIoProtocolGuid,
                              TPL_CALLBACK,
                              OnPciIoProtocolNotify,
                              NULL,
                              &mPciProtocolNotifyRegistration);
 
  return EFI_SUCCESS;
}