| // Copyright 2017 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <assert.h> |
| #include <debug.h> |
| #include <inttypes.h> |
| #include <lib/pci/pio.h> |
| #include <trace.h> |
| |
| #include <dev/pci_config.h> |
| #include <fbl/alloc_checker.h> |
| #include <pretty/hexdump.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| // Storage for register constexprs |
| constexpr PciReg16 PciConfig::kVendorId; |
| constexpr PciReg16 PciConfig::kDeviceId; |
| constexpr PciReg16 PciConfig::kCommand; |
| constexpr PciReg16 PciConfig::kStatus; |
| constexpr PciReg8 PciConfig::kRevisionId; |
| constexpr PciReg8 PciConfig::kProgramInterface; |
| constexpr PciReg8 PciConfig::kSubClass; |
| constexpr PciReg8 PciConfig::kBaseClass; |
| constexpr PciReg8 PciConfig::kCacheLineSize; |
| constexpr PciReg8 PciConfig::kLatencyTimer; |
| constexpr PciReg8 PciConfig::kHeaderType; |
| constexpr PciReg8 PciConfig::kBist; |
| constexpr PciReg32 PciConfig::kCardbusCisPtr; |
| constexpr PciReg16 PciConfig::kSubsystemVendorId; |
| constexpr PciReg16 PciConfig::kSubsystemId; |
| constexpr PciReg32 PciConfig::kExpansionRomAddress; |
| constexpr PciReg8 PciConfig::kCapabilitiesPtr; |
| constexpr PciReg8 PciConfig::kInterruptLine; |
| constexpr PciReg8 PciConfig::kInterruptPin; |
| constexpr PciReg8 PciConfig::kMinGrant; |
| constexpr PciReg8 PciConfig::kMaxLatency; |
| constexpr PciReg8 PciConfig::kPrimaryBusId; |
| constexpr PciReg8 PciConfig::kSecondaryBusId; |
| constexpr PciReg8 PciConfig::kSubordinateBusId; |
| constexpr PciReg8 PciConfig::kSecondaryLatencyTimer; |
| constexpr PciReg8 PciConfig::kIoBase; |
| constexpr PciReg8 PciConfig::kIoLimit; |
| constexpr PciReg16 PciConfig::kSecondaryStatus; |
| constexpr PciReg16 PciConfig::kMemoryBase; |
| constexpr PciReg16 PciConfig::kMemoryLimit; |
| constexpr PciReg16 PciConfig::kPrefetchableMemoryBase; |
| constexpr PciReg16 PciConfig::kPrefetchableMemoryLimit; |
| constexpr PciReg32 PciConfig::kPrefetchableMemoryBaseUpper; |
| constexpr PciReg32 PciConfig::kPrefetchableMemoryLimitUpper; |
| constexpr PciReg16 PciConfig::kIoBaseUpper; |
| constexpr PciReg16 PciConfig::kIoLimitUpper; |
| constexpr PciReg32 PciConfig::kBridgeExpansionRomAddress; |
| constexpr PciReg16 PciConfig::kBridgeControl; |
| |
| /* |
| * Derived classes should not be in the global namespace, all users |
| * of PciConfig should only have the base interface available |
| */ |
| namespace { |
| |
| class PciPioConfig : public PciConfig { |
| public: |
| PciPioConfig(uintptr_t base) : PciConfig(base, PciAddrSpace::PIO) {} |
| uint8_t Read(const PciReg8 addr) const override; |
| uint16_t Read(const PciReg16 addr) const override; |
| uint32_t Read(const PciReg32 addr) const override; |
| void Write(const PciReg8 addr, uint8_t val) const override; |
| void Write(const PciReg16 addr, uint16_t val) const override; |
| void Write(const PciReg32 addr, uint32_t val) const override; |
| |
| private: |
| friend PciConfig; |
| }; |
| |
| uint8_t PciPioConfig::Read(const PciReg8 addr) const { |
| uint32_t val; |
| zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 8u); |
| DEBUG_ASSERT(status == ZX_OK); |
| return static_cast<uint8_t>(val & 0xFF); |
| } |
| uint16_t PciPioConfig::Read(const PciReg16 addr) const { |
| uint32_t val; |
| zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 16u); |
| DEBUG_ASSERT(status == ZX_OK); |
| return static_cast<uint16_t>(val & 0xFFFF); |
| } |
| uint32_t PciPioConfig::Read(const PciReg32 addr) const { |
| uint32_t val; |
| zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 32u); |
| DEBUG_ASSERT(status == ZX_OK); |
| return val; |
| } |
| void PciPioConfig::Write(const PciReg8 addr, uint8_t val) const { |
| zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 8u); |
| DEBUG_ASSERT(status == ZX_OK); |
| } |
| void PciPioConfig::Write(const PciReg16 addr, uint16_t val) const { |
| zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 16u); |
| DEBUG_ASSERT(status == ZX_OK); |
| } |
| void PciPioConfig::Write(const PciReg32 addr, uint32_t val) const { |
| zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 32u); |
| DEBUG_ASSERT(status == ZX_OK); |
| } |
| |
| class PciMmioConfig : public PciConfig { |
| public: |
| PciMmioConfig(uintptr_t base) : PciConfig(base, PciAddrSpace::MMIO) {} |
| uint8_t Read(const PciReg8 addr) const override; |
| uint16_t Read(const PciReg16 addr) const override; |
| uint32_t Read(const PciReg32 addr) const override; |
| void Write(const PciReg8 addr, uint8_t val) const override; |
| void Write(const PciReg16 addr, uint16_t val) const override; |
| void Write(const PciReg32 addr, uint32_t val) const override; |
| |
| private: |
| friend PciConfig; |
| }; |
| |
| // MMIO Config Implementation |
| uint8_t PciMmioConfig::Read(const PciReg8 addr) const { |
| auto reg = reinterpret_cast<const volatile uint8_t*>(base_ + addr.offset()); |
| return *reg; |
| } |
| |
| uint16_t PciMmioConfig::Read(const PciReg16 addr) const { |
| auto reg = reinterpret_cast<const volatile uint16_t*>(base_ + addr.offset()); |
| return LE16(*reg); |
| } |
| |
| uint32_t PciMmioConfig::Read(const PciReg32 addr) const { |
| auto reg = reinterpret_cast<const volatile uint32_t*>(base_ + addr.offset()); |
| return LE32(*reg); |
| } |
| |
| void PciMmioConfig::Write(PciReg8 addr, uint8_t val) const { |
| auto reg = reinterpret_cast<volatile uint8_t*>(base_ + addr.offset()); |
| *reg = val; |
| } |
| |
| void PciMmioConfig::Write(PciReg16 addr, uint16_t val) const { |
| auto reg = reinterpret_cast<volatile uint16_t*>(base_ + addr.offset()); |
| *reg = LE16(val); |
| } |
| |
| void PciMmioConfig::Write(PciReg32 addr, uint32_t val) const { |
| auto reg = reinterpret_cast<volatile uint32_t*>(base_ + addr.offset()); |
| *reg = LE32(val); |
| } |
| |
| } // namespace |
| |
| fbl::RefPtr<PciConfig> PciConfig::Create(uintptr_t base, PciAddrSpace addr_type) { |
| fbl::AllocChecker ac; |
| fbl::RefPtr<PciConfig> cfg = nullptr; |
| |
| LTRACEF("base %#" PRIxPTR ", type %s\n", base, (addr_type == PciAddrSpace::PIO) ? "PIO" : "MIO"); |
| |
| if (addr_type == PciAddrSpace::PIO) { |
| cfg = fbl::AdoptRef(new (&ac) PciPioConfig(base)); |
| } else { |
| cfg = fbl::AdoptRef(new (&ac) PciMmioConfig(base)); |
| } |
| |
| if (!ac.check()) { |
| TRACEF("failed to allocate memory for PciConfig!\n"); |
| } |
| |
| return cfg; |
| } |
| |
| void PciConfig::DumpConfig(uint16_t len) const { |
| printf("%u bytes of raw config (base %s:%#" PRIxPTR ")\n", len, |
| (addr_space_ == PciAddrSpace::MMIO) ? "MMIO" : "PIO", base_); |
| if (addr_space_ == PciAddrSpace::MMIO) { |
| hexdump8(reinterpret_cast<const void*>(base_), len); |
| } else { |
| // PIO space can't be dumped directly so we read a row at a time |
| constexpr uint8_t row_len = 16; |
| uint32_t pos = 0; |
| uint8_t buf[row_len]; |
| |
| do { |
| for (uint16_t i = 0; i < row_len; i++) { |
| buf[i] = Read(PciReg8(static_cast<uint8_t>(pos + i))); |
| } |
| |
| hexdump8_ex(buf, row_len, base_ + pos); |
| pos += row_len; |
| } while (pos < PCIE_BASE_CONFIG_SIZE); |
| } |
| } |