| // Copyright 2018 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 "config.h" |
| #include "common.h" |
| |
| #include <assert.h> |
| #include <ddk/debug.h> |
| #include <ddktl/protocol/pciroot.h> |
| #include <fbl/alloc_checker.h> |
| #include <inttypes.h> |
| #include <pretty/hexdump.h> |
| |
| namespace pci { |
| |
| // Storage for register constexprs |
| constexpr PciReg16 Config::kVendorId; |
| constexpr PciReg16 Config::kDeviceId; |
| constexpr PciReg16 Config::kCommand; |
| constexpr PciReg16 Config::kStatus; |
| constexpr PciReg8 Config::kRevisionId; |
| constexpr PciReg8 Config::kProgramInterface; |
| constexpr PciReg8 Config::kSubClass; |
| constexpr PciReg8 Config::kBaseClass; |
| constexpr PciReg8 Config::kCacheLineSize; |
| constexpr PciReg8 Config::kLatencyTimer; |
| constexpr PciReg8 Config::kHeaderType; |
| constexpr PciReg8 Config::kBist; |
| constexpr PciReg32 Config::kCardbusCisPtr; |
| constexpr PciReg16 Config::kSubsystemVendorId; |
| constexpr PciReg16 Config::kSubsystemId; |
| constexpr PciReg32 Config::kExpansionRomAddress; |
| constexpr PciReg8 Config::kCapabilitiesPtr; |
| constexpr PciReg8 Config::kInterruptLine; |
| constexpr PciReg8 Config::kInterruptPin; |
| constexpr PciReg8 Config::kMinGrant; |
| constexpr PciReg8 Config::kMaxLatency; |
| constexpr PciReg8 Config::kPrimaryBusId; |
| constexpr PciReg8 Config::kSecondaryBusId; |
| constexpr PciReg8 Config::kSubordinateBusId; |
| constexpr PciReg8 Config::kSecondaryLatencyTimer; |
| constexpr PciReg8 Config::kIoBase; |
| constexpr PciReg8 Config::kIoLimit; |
| constexpr PciReg16 Config::kSecondaryStatus; |
| constexpr PciReg16 Config::kMemoryBase; |
| constexpr PciReg16 Config::kMemoryLimit; |
| constexpr PciReg16 Config::kPrefetchableMemoryBase; |
| constexpr PciReg16 Config::kPrefetchableMemoryLimit; |
| constexpr PciReg32 Config::kPrefetchableMemoryBaseUpper; |
| constexpr PciReg32 Config::kPrefetchableMemoryLimitUpper; |
| constexpr PciReg16 Config::kIoBaseUpper; |
| constexpr PciReg16 Config::kIoLimitUpper; |
| constexpr PciReg32 Config::kBridgeExpansionRomAddress; |
| constexpr PciReg16 Config::kBridgeControl; |
| |
| // MMIO Config Implementation |
| zx_status_t MmioConfig::Create(pci_bdf_t bdf, |
| ddk::MmioBuffer* ecam, |
| uint8_t start_bus, |
| uint8_t end_bus, |
| fbl::RefPtr<Config>* config) { |
| if (bdf.bus_id < start_bus || |
| bdf.bus_id > end_bus || |
| bdf.device_id >= PCI_MAX_DEVICES_PER_BUS || |
| bdf.function_id >= PCI_MAX_FUNCTIONS_PER_DEVICE) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_paddr_t bdf_start = reinterpret_cast<zx_paddr_t>(ecam->get()); |
| // Find the offset into the ecam region for the given bdf address. Every bus |
| // has 32 devices, every device has 8 functions, and each function has an |
| // extended config space of 4096 bytes. The base address of the vmo provided |
| // to the bus driver corresponds to the start_bus_num, so offset the bdf address |
| // based on the bottom of our ecam. |
| bdf_start += (bdf.bus_id - start_bus) * PCIE_ECAM_BYTES_PER_BUS; |
| bdf_start += bdf.device_id * PCI_MAX_FUNCTIONS_PER_DEVICE * PCIE_EXTENDED_CONFIG_SIZE; |
| bdf_start += bdf.function_id * PCIE_EXTENDED_CONFIG_SIZE; |
| |
| fbl::AllocChecker ac; |
| *config = AdoptRef(new (&ac) MmioConfig(bdf, bdf_start)); |
| if (!ac.check()) { |
| zxlogf(ERROR, "failed to allocate memory for PciConfig!\n"); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| return ZX_OK; |
| } |
| |
| uint8_t MmioConfig::Read(const PciReg8 addr) const { |
| auto reg = reinterpret_cast<const volatile uint8_t*>(base_ + addr.offset()); |
| return *reg; |
| } |
| |
| uint16_t MmioConfig::Read(const PciReg16 addr) const { |
| auto reg = reinterpret_cast<const volatile uint16_t*>(base_ + addr.offset()); |
| return htole16(*reg); |
| } |
| |
| uint32_t MmioConfig::Read(const PciReg32 addr) const { |
| auto reg = reinterpret_cast<const volatile uint32_t*>(base_ + addr.offset()); |
| return htole32(*reg); |
| } |
| |
| void MmioConfig::Write(PciReg8 addr, uint8_t val) const { |
| auto reg = reinterpret_cast<volatile uint8_t*>(base_ + addr.offset()); |
| *reg = val; |
| } |
| |
| void MmioConfig::Write(PciReg16 addr, uint16_t val) const { |
| auto reg = reinterpret_cast<volatile uint16_t*>(base_ + addr.offset()); |
| *reg = htole16(val); |
| } |
| |
| void MmioConfig::Write(PciReg32 addr, uint32_t val) const { |
| auto reg = reinterpret_cast<volatile uint32_t*>(base_ + addr.offset()); |
| *reg = htole32(val); |
| } |
| |
| const char* MmioConfig::type(void) const { |
| return "mmio"; |
| } |
| |
| // Proxy Config Implementation |
| zx_status_t ProxyConfig::Create(pci_bdf_t bdf, |
| ddk::PcirootProtocolClient* proto, |
| fbl::RefPtr<Config>* config) { |
| fbl::AllocChecker ac; |
| *config = AdoptRef(new (&ac) ProxyConfig(bdf, proto)); |
| if (!ac.check()) { |
| zxlogf(ERROR, "failed to allocate memory for PciConfig!\n"); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| return ZX_OK; |
| } |
| |
| uint8_t ProxyConfig::Read(const PciReg8 addr) const { |
| uint8_t tmp; |
| ZX_ASSERT(pciroot_->ConfigRead8(&bdf_, addr.offset(), &tmp) == ZX_OK); |
| return tmp; |
| } |
| |
| uint16_t ProxyConfig::Read(const PciReg16 addr) const { |
| uint16_t tmp; |
| ZX_ASSERT(pciroot_->ConfigRead16(&bdf_, addr.offset(), &tmp) == ZX_OK); |
| return tmp; |
| } |
| |
| uint32_t ProxyConfig::Read(const PciReg32 addr) const { |
| uint32_t tmp; |
| ZX_ASSERT(pciroot_->ConfigRead32(&bdf_, addr.offset(), &tmp) == ZX_OK); |
| return tmp; |
| } |
| |
| void ProxyConfig::Write(PciReg8 addr, uint8_t val) const { |
| ZX_ASSERT(pciroot_->ConfigWrite8(&bdf_, addr.offset(), val) == ZX_OK); |
| } |
| |
| void ProxyConfig::Write(PciReg16 addr, uint16_t val) const { |
| ZX_ASSERT(pciroot_->ConfigWrite16(&bdf_, addr.offset(), val) == ZX_OK); |
| } |
| |
| void ProxyConfig::Write(PciReg32 addr, uint32_t val) const { |
| ZX_ASSERT(pciroot_->ConfigWrite32(&bdf_, addr.offset(), val) == ZX_OK); |
| } |
| |
| const char* ProxyConfig::type(void) const { |
| return "proxy"; |
| } |
| |
| void Config::DumpConfig(uint16_t len) const { |
| printf("%u bytes of raw config (type: %s)\n", len, type()); |
| // 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, pos); |
| pos += row_len; |
| } while (pos < PCI_BASE_CONFIG_SIZE); |
| } |
| |
| } // namespace pci |