1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <dev/pci_config.h> 8#include <lib/pci/pio.h> 9 10#include <assert.h> 11#include <debug.h> 12#include <inttypes.h> 13#include <trace.h> 14 15#include <fbl/alloc_checker.h> 16 17#define LOCAL_TRACE 0 18 19// Storage for register constexprs 20constexpr PciReg16 PciConfig::kVendorId; 21constexpr PciReg16 PciConfig::kDeviceId; 22constexpr PciReg16 PciConfig::kCommand; 23constexpr PciReg16 PciConfig::kStatus; 24constexpr PciReg8 PciConfig::kRevisionId; 25constexpr PciReg8 PciConfig::kProgramInterface; 26constexpr PciReg8 PciConfig::kSubClass; 27constexpr PciReg8 PciConfig::kBaseClass; 28constexpr PciReg8 PciConfig::kCacheLineSize; 29constexpr PciReg8 PciConfig::kLatencyTimer; 30constexpr PciReg8 PciConfig::kHeaderType; 31constexpr PciReg8 PciConfig::kBist; 32constexpr PciReg32 PciConfig::kCardbusCisPtr; 33constexpr PciReg16 PciConfig::kSubsystemVendorId; 34constexpr PciReg16 PciConfig::kSubsystemId; 35constexpr PciReg32 PciConfig::kExpansionRomAddress; 36constexpr PciReg8 PciConfig::kCapabilitiesPtr; 37constexpr PciReg8 PciConfig::kInterruptLine; 38constexpr PciReg8 PciConfig::kInterruptPin; 39constexpr PciReg8 PciConfig::kMinGrant; 40constexpr PciReg8 PciConfig::kMaxLatency; 41constexpr PciReg8 PciConfig::kPrimaryBusId; 42constexpr PciReg8 PciConfig::kSecondaryBusId; 43constexpr PciReg8 PciConfig::kSubordinateBusId; 44constexpr PciReg8 PciConfig::kSecondaryLatencyTimer; 45constexpr PciReg8 PciConfig::kIoBase; 46constexpr PciReg8 PciConfig::kIoLimit; 47constexpr PciReg16 PciConfig::kSecondaryStatus; 48constexpr PciReg16 PciConfig::kMemoryBase; 49constexpr PciReg16 PciConfig::kMemoryLimit; 50constexpr PciReg16 PciConfig::kPrefetchableMemoryBase; 51constexpr PciReg16 PciConfig::kPrefetchableMemoryLimit; 52constexpr PciReg32 PciConfig::kPrefetchableMemoryBaseUpper; 53constexpr PciReg32 PciConfig::kPrefetchableMemoryLimitUpper; 54constexpr PciReg16 PciConfig::kIoBaseUpper; 55constexpr PciReg16 PciConfig::kIoLimitUpper; 56constexpr PciReg32 PciConfig::kBridgeExpansionRomAddress; 57constexpr PciReg16 PciConfig::kBridgeControl; 58 59/* 60 * Derived classes should not be in the global namespace, all users 61 * of PciConfig should only have the base interface available 62 */ 63namespace { 64 65class PciPioConfig : public PciConfig { 66public: 67 PciPioConfig(uintptr_t base) 68 : PciConfig(base, PciAddrSpace::PIO) {} 69 uint8_t Read(const PciReg8 addr) const override; 70 uint16_t Read(const PciReg16 addr) const override; 71 uint32_t Read(const PciReg32 addr) const override; 72 void Write(const PciReg8 addr, uint8_t val) const override; 73 void Write(const PciReg16 addr, uint16_t val) const override; 74 void Write(const PciReg32 addr, uint32_t val) const override; 75 76private: 77 friend PciConfig; 78}; 79 80uint8_t PciPioConfig::Read(const PciReg8 addr) const { 81 uint32_t val; 82 zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 8u); 83 DEBUG_ASSERT(status == ZX_OK); 84 return static_cast<uint8_t>(val & 0xFF); 85} 86uint16_t PciPioConfig::Read(const PciReg16 addr) const { 87 uint32_t val; 88 zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 16u); 89 DEBUG_ASSERT(status == ZX_OK); 90 return static_cast<uint16_t>(val & 0xFFFF); 91} 92uint32_t PciPioConfig::Read(const PciReg32 addr) const { 93 uint32_t val; 94 zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 32u); 95 DEBUG_ASSERT(status == ZX_OK); 96 return val; 97} 98void PciPioConfig::Write(const PciReg8 addr, uint8_t val) const { 99 zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 8u); 100 DEBUG_ASSERT(status == ZX_OK); 101} 102void PciPioConfig::Write(const PciReg16 addr, uint16_t val) const { 103 zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 16u); 104 DEBUG_ASSERT(status == ZX_OK); 105} 106void PciPioConfig::Write(const PciReg32 addr, uint32_t val) const { 107 zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 32u); 108 DEBUG_ASSERT(status == ZX_OK); 109} 110 111class PciMmioConfig : public PciConfig { 112public: 113 PciMmioConfig(uintptr_t base) 114 : PciConfig(base, PciAddrSpace::MMIO) {} 115 uint8_t Read(const PciReg8 addr) const override; 116 uint16_t Read(const PciReg16 addr) const override; 117 uint32_t Read(const PciReg32 addr) const override; 118 void Write(const PciReg8 addr, uint8_t val) const override; 119 void Write(const PciReg16 addr, uint16_t val) const override; 120 void Write(const PciReg32 addr, uint32_t val) const override; 121 122private: 123 friend PciConfig; 124}; 125 126// MMIO Config Implementation 127uint8_t PciMmioConfig::Read(const PciReg8 addr) const { 128 auto reg = reinterpret_cast<const volatile uint8_t*>(base_ + addr.offset()); 129 return *reg; 130} 131 132uint16_t PciMmioConfig::Read(const PciReg16 addr) const { 133 auto reg = reinterpret_cast<const volatile uint16_t*>(base_ + addr.offset()); 134 return LE16(*reg); 135} 136 137uint32_t PciMmioConfig::Read(const PciReg32 addr) const { 138 auto reg = reinterpret_cast<const volatile uint32_t*>(base_ + addr.offset()); 139 return LE32(*reg); 140} 141 142void PciMmioConfig::Write(PciReg8 addr, uint8_t val) const { 143 auto reg = reinterpret_cast<volatile uint8_t*>(base_ + addr.offset()); 144 *reg = val; 145} 146 147void PciMmioConfig::Write(PciReg16 addr, uint16_t val) const { 148 auto reg = reinterpret_cast<volatile uint16_t*>(base_ + addr.offset()); 149 *reg = LE16(val); 150} 151 152void PciMmioConfig::Write(PciReg32 addr, uint32_t val) const { 153 auto reg = reinterpret_cast<volatile uint32_t*>(base_ + addr.offset()); 154 *reg = LE32(val); 155} 156 157} // anon namespace 158 159fbl::RefPtr<PciConfig> PciConfig::Create(uintptr_t base, PciAddrSpace addr_type) { 160 fbl::AllocChecker ac; 161 fbl::RefPtr<PciConfig> cfg = nullptr; 162 163 LTRACEF("base %#" PRIxPTR ", type %s\n", base, (addr_type == PciAddrSpace::PIO) ? "PIO" : "MIO"); 164 165 if (addr_type == PciAddrSpace::PIO) { 166 cfg = fbl::AdoptRef(new (&ac) PciPioConfig(base)); 167 } else { 168 cfg = fbl::AdoptRef(new (&ac) PciMmioConfig(base)); 169 } 170 171 if (!ac.check()) { 172 TRACEF("failed to allocate memory for PciConfig!\n"); 173 } 174 175 return cfg; 176} 177 178void PciConfig::DumpConfig(uint16_t len) const { 179 printf("%u bytes of raw config (base %s:%#" PRIxPTR ")\n", 180 len, (addr_space_ == PciAddrSpace::MMIO) ? "MMIO" : "PIO", base_); 181 if (addr_space_ == PciAddrSpace::MMIO) { 182 hexdump8(reinterpret_cast<const void*>(base_), len); 183 } else { 184 // PIO space can't be dumped directly so we read a row at a time 185 constexpr uint8_t row_len = 16; 186 uint32_t pos = 0; 187 uint8_t buf[row_len]; 188 189 do { 190 for (uint16_t i = 0; i < row_len; i++) { 191 buf[i] = Read(PciReg8(static_cast<uint8_t>(pos + i))); 192 } 193 194 hexdump8_ex(buf, row_len, base_ + pos); 195 pos += row_len; 196 } while (pos < PCIE_BASE_CONFIG_SIZE); 197 } 198} 199