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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/** @file
 *
 * PCI Host Bridge Library instance for Bcm2711 ARM SOC
 *
 * Copyright (c) 2019, Jeremy Linton
 * Copyright (c) 2017, Linaro Ltd. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-2-Clause-Patent
 *
 * This module initializes the Pci as close to a standard
 * PCI root complex as possible. The general information
 * for this driver was sourced from.
 *
 * See https://github.com/raspberrypi/linux/blob/rpi-5.3.y/drivers/pci/controller/pcie-brcmstb.c
 * and https://github.com/raspberrypi/linux/blob/rpi-5.3.y/arch/arm/boot/dts/bcm2838.dtsi
 *
 **/
 
#include <IndustryStandard/Bcm2711.h>
#include <IndustryStandard/Pci22.h>
#include <Library/ArmLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/PcdLib.h>
#include <Library/PciHostBridgeLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <PiDxe.h>
#include <Protocol/PciHostBridgeResourceAllocation.h>
 
STATIC
UINT32
RdRegister (
  UINT32                Offset
  )
{
  ArmDataMemoryBarrier ();
  return MmioRead32 (PCIE_REG_BASE + Offset);
}
 
 
STATIC
VOID
RMWRegister (
  UINT32                Offset,
  UINT32                Mask,
  UINT32                In
  )
{
  EFI_PHYSICAL_ADDRESS  Addr;
  UINT32                Data;
  UINT32                Shift;
 
  Addr = PCIE_REG_BASE + Offset;
  Shift = 1;
  if (In) {
    while (!(Mask & Shift))
      Shift <<= 1;
    Data = (MmioRead32 (Addr) & ~Mask) | ((In * Shift) & Mask);
  } else {
    Data = MmioRead32 (Addr) & ~Mask;
  }
 
  MmioWrite32 (Addr, Data);
 
  ArmDataMemoryBarrier ();
}
 
 
STATIC
VOID
WdRegister (
  UINT32                Offset,
  UINT32                In
  )
{
  MmioWrite32 (PCIE_REG_BASE + Offset, In);
  ArmDataMemoryBarrier ();
}
 
 
EFI_STATUS
EFIAPI
Bcm2711PciHostBridgeLibConstructor (
  IN EFI_HANDLE       ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  INTN                  Timeout;
  UINT32                Data;
  EFI_PHYSICAL_ADDRESS  TopOfPciMap;
 
  DEBUG ((DEBUG_VERBOSE, "PCIe RootBridge constructor\n"));
 
  Timeout = 100;
 
  // Reset controller
  RMWRegister (PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK, 1);
  // PERST
  RMWRegister (PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK, 1);
 
  gBS->Stall (1000);
  // take the bridge out of reset
  RMWRegister (PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK, 0);
 
 
  RMWRegister (PCIE_MISC_HARD_PCIE_HARD_DEBUG,
    PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK, 0);
  RdRegister (PCIE_MISC_HARD_PCIE_HARD_DEBUG);
  // Wait for SerDes to be stable
  gBS->Stall (1000);
 
  // Read revision
  Data = RdRegister (PCIE_MISC_REVISION);
  DEBUG ((DEBUG_INFO, "RootBridge: Revision %x\n", Data & PCIE_MISC_REVISION_MAJMIN_MASK));
 
  RMWRegister (PCIE_MISC_MISC_CTRL, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK, 1);
  RMWRegister (PCIE_MISC_MISC_CTRL, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK, 1);
  RMWRegister (PCIE_MISC_MISC_CTRL, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK, BURST_SIZE_128);
 
  //
  // "RC_BAR2" is the inbound TLP window.
  // Having non RAM regions in the window is ok (and encouraged? for PtP?)
  // so lets just map the entire address space.
  //
  // For regions > 64K then the pci->mem window size = log2(size)-15
  // which is dumped into the low bits of the offset and written to
  // the "LO" register with the high bits of the offset written into
  // the "HI" part. The Linux driver makes the point that the offset
  // must be aligned to its size aka a 1G region must start on a 1G
  // boundary. The size parms are 1GB=0xf=log2(size)-15), or 4G=0x11
  //
 
  DEBUG ((DEBUG_VERBOSE, "RootBridge: Program bottom 4G of ram\n"));
 
  // lets assume a start addr of 0, size 4G
  WdRegister (PCIE_MISC_RC_BAR2_CONFIG_LO, 0x11);   /* Size = 4G */
  WdRegister (PCIE_MISC_RC_BAR2_CONFIG_HI, 0);      /* Start at addr0 */
  RMWRegister (PCIE_MISC_MISC_CTRL, PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK, 0x11);
 
  // RC_BAR1 pcie->gisb disable
  WdRegister (PCIE_MISC_RC_BAR1_CONFIG_LO, 0);
  // RC_BAR3 pcie->scb disable
  WdRegister (PCIE_MISC_RC_BAR3_CONFIG_LO, 0);
 
  TopOfPciMap = PCIE_TOP_OF_MEM_WIN;
 
  DEBUG ((DEBUG_VERBOSE, "RootBridge: MMIO PCIe addr %llx\n", TopOfPciMap));
 
  //
  // Setup the PCI side of the MMIO window.
  //
  // All the _WIN0_ values make one think there can be more than one
  // mapping, which might mean it's possible to program a prefetchable
  // window, or a PIO window...
  //
  WdRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO, TopOfPciMap);
  WdRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI, TopOfPciMap >> 32);
 
  //
  // Set up the CPU MMIO addresses. The BASE_LIMIT register holds the
  // bottom part of the start and end addresses in a 16-bit field (64k)
  // aligned on a 1M boundary (aka only 12 bit active) the top 32-bits
  // are then in their own registers. Further these address ranges are
  // setup to match the Linux driver and seem less than ideal on the RPi
  //
  // The mapping should be 1:1 if possible
  //
  EFI_PHYSICAL_ADDRESS    CpuAddrStart = PCIE_CPU_MMIO_WINDOW;
  EFI_PHYSICAL_ADDRESS    CpuAddrEnd   = CpuAddrStart + PCIE_BRIDGE_MMIO_LEN;
 
  DEBUG ((DEBUG_VERBOSE, "RootBridge: MMIO CPU addr %llx\n", CpuAddrStart));
 
  RMWRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT,
    PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK, CpuAddrStart >> 20);
  RMWRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT,
    PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK, CpuAddrEnd >> 20);
  RMWRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI,
    PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK, CpuAddrStart >> 32);
  RMWRegister (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI,
    PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK, CpuAddrEnd >> 32);
 
  //
  // Consider MSI setup here, not that it matters much its likely the legacy intX
  // is as fast or faster...
  //
 
  // Clear and mask interrupts.
  WdRegister (PCIE_INTR2_CPU_MASK_CLR, 0xffffffff);
  WdRegister (PCIE_INTR2_CPU_MASK_SET, 0xffffffff);
 
  // Set link cap & link ctl?
  //RMWRegister (BRCM_PCIE_CAP_REGS+PCI_LNKCAP, LNKCAP, pen);
  //RMWRegister (BRCM_PCIE_CTL_REGS+PCI_LNKCAP, LNKCAP, pen);
 
  // De-assert PERST
  RMWRegister (PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK, 0);
  DEBUG ((DEBUG_VERBOSE, "RootBridge: Reset done\n"));
 
  // Wait for linkup
  do {
      Data = RdRegister (PCIE_MISC_PCIE_STATUS);
      gBS->Stall (1000);
      Timeout --;
  } while (((Data & 0x30) != 0x030) && (Timeout));
  DEBUG ((DEBUG_VERBOSE, "PCIe link ready (status=%x) Timeout=%d\n", Data, Timeout));
 
  if ((Data & 0x80) != 0x80) {
    DEBUG ((DEBUG_ERROR, "PCIe link not in RC mode (status=%x)\n", Data));
    return EFI_UNSUPPORTED;
  }
 
  // Change class code of the root port
  RMWRegister(BRCM_PCIE_CLASS, PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK, 0x60400);
 
  //
  // PCIe->SCB endian mode for BAR
  // field ENDIAN_MODE_BAR2 = little endian = 0
  //
  RMWRegister (PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1,
    PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK, 0);
 
  RMWRegister (PCIE_MISC_HARD_PCIE_HARD_DEBUG,
    PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK, 1);
 
  return EFI_SUCCESS;
}