1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE
4
5#include <dev/pci/designware/dw-pcie.h>
6
7#include <zircon/syscalls.h>
8
9#include "dw-pcie-hw.h"
10
11namespace {
12
13const uint64_t kMask32 = 0xffffffff;
14
15inline uint32_t lo32(const uint64_t v) {
16    return static_cast<uint32_t>(v & kMask32);
17}
18
19inline uint32_t hi32(const uint64_t v) {
20    return static_cast<uint32_t>((v >> 32));
21}
22
23}  // namespace
24
25namespace pcie {
26
27namespace designware {
28
29bool DwPcie::IsLinkUp() {
30    volatile uint8_t* dbi = reinterpret_cast<volatile uint8_t*>(dbi_);
31    hwreg::RegisterIo phyDebugR1mmio(dbi + PortLogic::DebugR1Offset);
32
33    auto phyDebugR1 = PortLogic::DebugR1::Get().ReadFrom(&phyDebugR1mmio);
34
35    const bool isLinkUp = phyDebugR1.link_up();
36    const bool isLinkTraining = phyDebugR1.link_in_training();
37
38    return isLinkUp && !isLinkTraining;
39}
40
41uint32_t DwPcie::ReadRC(const uint32_t offset) {
42    volatile uint8_t* dbi = reinterpret_cast<volatile uint8_t*>(dbi_);
43    return readl(dbi + offset);
44}
45
46void DwPcie::WriteRC(const uint32_t offset, const uint32_t val) {
47    volatile uint8_t* dbi = reinterpret_cast<volatile uint8_t*>(dbi_);
48    writel(val, dbi + offset);
49}
50
51
52/*
53 * Program a region into the outbound ATU
54 * The ATU supports 16 regions that can be programmed independently.
55 *   pcie,              PCIe Device Struct
56 *   index,             Which iATU region are we programming?
57 *   type,              Type of PCIe txn being generated on the PCIe bus
58 *   cpu_addr,          Physical source address to translate in the CPU's address space
59 *   pci_addr,          Destination Address in the PCIe address space
60 *   size               Size of the aperature that we're translating.
61 */
62zx_status_t DwPcie::ProgramOutboundAtu(const uint32_t index,
63                                       const uint32_t type,
64                                       const zx_paddr_t cpu_addr,
65                                       const uintptr_t pci_addr,
66                                       const size_t size) {
67    // The ATU supports a limited number of regions.
68    ZX_DEBUG_ASSERT(index < kAtuRegionCount);
69
70    // Each ATU region has its own bank of registers at this offset from the
71    // DBI base
72    const size_t bank_offset = (0x3 << 20) | (index << 9);
73    volatile uint8_t* atu_base =
74        reinterpret_cast<volatile uint8_t*>(dbi_) + bank_offset;
75
76    volatile atu_ctrl_regs_t* regs =
77        reinterpret_cast<volatile atu_ctrl_regs_t*>(atu_base);
78
79    // Memory transactions that are in the following range will get translated
80    // to PCI bus transactions:
81    //
82    // [cpu_addr, cpu_addr + size - 1]
83    regs->unroll_lower_base = lo32(cpu_addr);
84    regs->unroll_upper_base = hi32(cpu_addr);
85
86    regs->unroll_limit = lo32(cpu_addr + size - 1);
87
88    // Target of the transactions above.
89    regs->unroll_lower_target = lo32(pci_addr);
90    regs->unroll_upper_target = hi32(pci_addr);
91
92    // Region Ctrl 1 contains a number of fields. The Low 5 bits of the field
93    // indicate the type of transaction to dispatch onto the PCIe bus.
94    regs->region_ctrl1 = type;
95
96    // Each region can individually be marked as Enabled or Disabled.
97    regs->region_ctrl2 |= kAtuRegionCtrlEnable;
98    regs->region_ctrl2 |= kAtuCfgShiftMode;
99
100    // Wait for the enable to take effect.
101    for (unsigned int i = 0; i < kAtuProgramRetries; ++i) {
102        if (regs->region_ctrl2 & kAtuRegionCtrlEnable) {
103            return ZX_OK;
104        }
105
106        // Wait a little bit before trying again.
107        zx_nanosleep(zx_deadline_after(ZX_USEC(kAtuWaitEnableTimeoutUs)));
108    }
109
110    return ZX_ERR_TIMED_OUT;
111}
112
113void DwPcie::LinkSpeedChange() {
114    volatile uint8_t* elbi = reinterpret_cast<volatile uint8_t*>(dbi_);
115    volatile uint8_t* reg = elbi + GEN2_CTRL_OFF;
116
117    uint32_t val = readl(reg);
118    val |= G2_CTRL_DIRECT_SPEED_CHANGE;
119    writel(val, reg);
120}
121
122zx_status_t DwPcie::SetupRootComplex(
123    const iatu_translation_entry_t* cfg,
124    const iatu_translation_entry_t* io,
125    const iatu_translation_entry_t* mem
126) {
127    uint32_t val;
128    uint32_t portLinkMode = 0;
129    uint32_t g2ctrlNoOfLanes = G2_CTRL_NO_OF_LANES(nLanes_);
130
131    switch (nLanes_) {
132        case 1:
133            portLinkMode = PLC_LINK_CAPABLE_X1;
134            break;
135        case 2:
136            portLinkMode = PLC_LINK_CAPABLE_X2;
137            break;
138        case 4:
139            portLinkMode = PLC_LINK_CAPABLE_X4;
140            break;
141        case 8:
142            portLinkMode = PLC_LINK_CAPABLE_X8;
143            break;
144        default:
145            return ZX_ERR_INVALID_ARGS;
146    }
147
148    val = ReadRC(PORT_LINK_CTRL_OFF);
149    val &= ~PLC_LINK_CAPABLE_MASK;
150    val |= portLinkMode;
151    WriteRC(PORT_LINK_CTRL_OFF, val);
152
153    val = ReadRC(GEN2_CTRL_OFF);
154    val &= ~G2_CTRL_NUM_OF_LANES_MASK;
155    val |= g2ctrlNoOfLanes;
156    WriteRC(GEN2_CTRL_OFF, g2ctrlNoOfLanes);
157
158    WriteRC(PCI_TYPE1_BAR0, 0x4);
159    WriteRC(PCI_TYPE1_BAR1, 0x0);
160
161    uint32_t idx = 0;
162    if (cfg) {
163        ProgramOutboundAtu(idx, PCIE_TLP_TYPE_CFG0, cfg->cpu_addr,
164                           cfg->pci_addr, cfg->length);
165        idx++;
166    }
167
168    if (io) {
169        ProgramOutboundAtu(idx, PCIE_TLP_TYPE_IO_RW, cfg->cpu_addr,
170                           cfg->pci_addr, cfg->length);
171        idx++;
172    }
173
174    if (mem) {
175        ProgramOutboundAtu(idx, PCIE_TLP_TYPE_MEM_RW, cfg->cpu_addr,
176                           cfg->pci_addr, cfg->length);
177        idx++;
178    }
179
180    LinkSpeedChange();
181
182    return ZX_OK;
183
184}
185
186} // namespace designware
187} // namespace pcie