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