blob: 1081255270e3f9e088271df77fddfcf6d39606e7 [file] [log] [blame]
// 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