| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #ifndef SRC_DEVICES_BUS_DRIVERS_PCI_CONFIG_H_ |
| #define SRC_DEVICES_BUS_DRIVERS_PCI_CONFIG_H_ |
| |
| #include <endian.h> |
| #include <lib/mmio/mmio.h> |
| #include <lib/zx/status.h> |
| #include <stdio.h> |
| #include <zircon/errors.h> |
| #include <zircon/hw/pci.h> |
| #include <zircon/types.h> |
| |
| #include <ddk/mmio-buffer.h> |
| #include <ddktl/protocol/pciroot.h> |
| #include <hwreg/bitfields.h> |
| |
| namespace pci { |
| namespace config { |
| |
| // Fields correspond to the name in the PCI Local Bus Spec section 6.2. |
| struct Command { |
| uint16_t value; |
| // 15-11 are reserved preserve. |
| DEF_SUBBIT(value, 10, interrupt_disable); |
| DEF_SUBBIT(value, 9, fast_back_to_back_enable); |
| DEF_SUBBIT(value, 8, serr_enable); |
| // 7 is reserved preserve. |
| DEF_SUBBIT(value, 6, parity_error_response); |
| DEF_SUBBIT(value, 5, vga_palette_snoop); |
| DEF_SUBBIT(value, 4, memory_write_and_invalidate_enable); |
| DEF_SUBBIT(value, 3, special_cycles); |
| DEF_SUBBIT(value, 2, bus_master); |
| DEF_SUBBIT(value, 1, memory_space); |
| DEF_SUBBIT(value, 0, io_space); |
| }; |
| |
| // The layout of a Base Address Register changes based on its type. |
| // PCI Local Bus Spec section 6.2.5.1. |
| struct IoBaseAddress; |
| struct MmioBaseAddress; |
| struct BaseAddress : public hwreg::RegisterBase<BaseAddress, uint32_t> { |
| DEF_RSVDZ_BIT(1); |
| DEF_BIT(0, is_io_space); |
| |
| IoBaseAddress* io() { return reinterpret_cast<IoBaseAddress*>(this); } |
| MmioBaseAddress* mmio() { return reinterpret_cast<MmioBaseAddress*>(this); } |
| static auto Get() { return hwreg::RegisterAddr<BaseAddress>(0); } |
| }; |
| |
| struct IoBaseAddress : public BaseAddress { |
| DEF_UNSHIFTED_FIELD(31, 2, base_address); |
| }; |
| |
| struct MmioBaseAddress : public BaseAddress { |
| DEF_UNSHIFTED_FIELD(31, 4, base_address); |
| DEF_BIT(3, is_prefetchable); |
| DEF_BIT(2, is_64_bit); |
| }; |
| |
| } // namespace config |
| |
| constexpr zx_paddr_t bdf_to_ecam_offset(pci_bdf_t bdf, uint8_t start_bus) { |
| // 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. |
| zx_vaddr_t 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; |
| return bdf_start; |
| } |
| |
| class PciReg8 { |
| public: |
| constexpr explicit PciReg8(uint16_t offset) : offset_(offset) {} |
| constexpr PciReg8() : offset_(0u) {} |
| constexpr uint16_t offset() const { return offset_; } |
| inline bool operator==(const PciReg8& other) { return (offset() == other.offset()); } |
| |
| private: |
| uint16_t offset_; |
| }; |
| |
| class PciReg16 { |
| public: |
| constexpr explicit PciReg16(uint16_t offset) : offset_(offset) {} |
| constexpr PciReg16() : offset_(0u) {} |
| constexpr uint16_t offset() const { return offset_; } |
| inline bool operator==(const PciReg8& other) { return (offset() == other.offset()); } |
| |
| private: |
| uint16_t offset_; |
| }; |
| |
| class PciReg32 { |
| public: |
| constexpr explicit PciReg32(uint16_t offset) : offset_(offset) {} |
| constexpr PciReg32() : offset_(0u) {} |
| constexpr uint16_t offset() const { return offset_; } |
| inline bool operator==(const PciReg8& other) { return (offset() == other.offset()); } |
| |
| private: |
| uint16_t offset_; |
| }; |
| |
| // Config supplies the factory for creating the appropriate pci config |
| // object based on the address space of the pci device. |
| class Config { |
| public: |
| // Standard PCI configuration space values. Offsets from PCI Firmware Spec ch 6. |
| static constexpr PciReg16 kVendorId = PciReg16(0x0); |
| static constexpr PciReg16 kDeviceId = PciReg16(0x2); |
| static constexpr PciReg16 kCommand = PciReg16(0x4); |
| static constexpr PciReg16 kStatus = PciReg16(0x6); |
| static constexpr PciReg8 kRevisionId = PciReg8(0x8); |
| static constexpr PciReg8 kProgramInterface = PciReg8(0x9); |
| static constexpr PciReg8 kSubClass = PciReg8(0xA); |
| static constexpr PciReg8 kBaseClass = PciReg8(0xB); |
| static constexpr PciReg8 kCacheLineSize = PciReg8(0xC); |
| static constexpr PciReg8 kLatencyTimer = PciReg8(0xD); |
| static constexpr PciReg8 kHeaderType = PciReg8(0xE); |
| static constexpr PciReg8 kBist = PciReg8(0xF); |
| // 0x10 is the address of the first BAR in config space |
| // BAR rather than BaseAddress for space / sanity considerations |
| static constexpr PciReg32 kBar(uint32_t bar) { |
| ZX_ASSERT(bar < PCI_MAX_BAR_REGS); |
| return PciReg32(static_cast<uint16_t>(0x10 + (bar * sizeof(uint32_t)))); |
| } |
| static constexpr PciReg32 kCardbusCisPtr = PciReg32(0x28); |
| static constexpr PciReg16 kSubsystemVendorId = PciReg16(0x2C); |
| static constexpr PciReg16 kSubsystemId = PciReg16(0x2E); |
| static constexpr PciReg32 kExpansionRomAddress = PciReg32(0x30); |
| static constexpr PciReg8 kCapabilitiesPtr = PciReg8(0x34); |
| // 0x35 through 0x3B is reserved |
| static constexpr PciReg8 kInterruptLine = PciReg8(0x3C); |
| static constexpr PciReg8 kInterruptPin = PciReg8(0x3D); |
| static constexpr PciReg8 kMinGrant = PciReg8(0x3E); |
| static constexpr PciReg8 kMaxLatency = PciReg8(0x3F); |
| static constexpr uint8_t kStdCfgEnd = |
| static_cast<uint8_t>(kMaxLatency.offset() + sizeof(uint8_t)); |
| |
| // pci to pci bridge config |
| // Unlike a normal PCI header, a bridge only has two BARs, but the BAR offset in config space |
| // is the same. |
| static constexpr PciReg8 kPrimaryBusId = PciReg8(0x18); |
| static constexpr PciReg8 kSecondaryBusId = PciReg8(0x19); |
| static constexpr PciReg8 kSubordinateBusId = PciReg8(0x1A); |
| static constexpr PciReg8 kSecondaryLatencyTimer = PciReg8(0x1B); |
| static constexpr PciReg8 kIoBase = PciReg8(0x1C); |
| static constexpr PciReg8 kIoLimit = PciReg8(0x1D); |
| static constexpr PciReg16 kSecondaryStatus = PciReg16(0x1E); |
| static constexpr PciReg16 kMemoryBase = PciReg16(0x20); |
| static constexpr PciReg16 kMemoryLimit = PciReg16(0x22); |
| static constexpr PciReg16 kPrefetchableMemoryBase = PciReg16(0x24); |
| static constexpr PciReg16 kPrefetchableMemoryLimit = PciReg16(0x26); |
| static constexpr PciReg32 kPrefetchableMemoryBaseUpper = PciReg32(0x28); |
| static constexpr PciReg32 kPrefetchableMemoryLimitUpper = PciReg32(0x2C); |
| static constexpr PciReg16 kIoBaseUpper = PciReg16(0x30); |
| static constexpr PciReg16 kIoLimitUpper = PciReg16(0x32); |
| // Capabilities Pointer for a bridge matches the standard 0x34 offset |
| // 0x35 through 0x38 is reserved |
| static constexpr PciReg32 kBridgeExpansionRomAddress = PciReg32(0x38); |
| // interrupt line for a bridge matches the standard 0x3C offset |
| // interrupt pin for a bridge matches the standard 0x3D offset |
| static constexpr PciReg16 kBridgeControl = PciReg16(0x3E); |
| |
| inline const pci_bdf_t& bdf() const { return bdf_; } |
| inline const char* addr(void) const { return addr_; } |
| virtual const char* type(void) const = 0; |
| // Return a copy of the MmioView backing the Config's MMIO space, if supported. |
| virtual zx::status<ddk::MmioView> get_view() const { return zx::error(ZX_ERR_NOT_SUPPORTED); } |
| |
| // Virtuals |
| void DumpConfig(uint16_t len) const; |
| virtual uint8_t Read(const PciReg8 addr) const = 0; |
| virtual uint16_t Read(const PciReg16 addr) const = 0; |
| virtual uint32_t Read(const PciReg32 addr) const = 0; |
| virtual void Write(const PciReg8 addr, uint8_t val) const = 0; |
| virtual void Write(const PciReg16 addr, uint16_t val) const = 0; |
| virtual void Write(const PciReg32 addr, uint32_t val) const = 0; |
| virtual ~Config() {} |
| |
| protected: |
| Config(pci_bdf_t bdf) : bdf_(bdf) { |
| snprintf(addr_, sizeof(addr_), "%02x:%02x.%01x", bdf_.bus_id, bdf_.device_id, bdf_.function_id); |
| } |
| const pci_bdf_t bdf_; |
| char addr_[8]; |
| }; |
| |
| // MMIO config is the stardard method for accessing modern pci configuration space. |
| // A device's configuration space is mapped to a specific place in a given pci root's |
| // ecam and can be directly accessed with standard IO operations.t |
| class MmioConfig : public Config { |
| public: |
| static zx_status_t Create(pci_bdf_t bdf, ddk::MmioBuffer* ecam_, uint8_t start_bus, |
| uint8_t end_bus, std::unique_ptr<Config>* config); |
| uint8_t Read(const PciReg8 addr) const final; |
| uint16_t Read(const PciReg16 addr) const final; |
| uint32_t Read(const PciReg32 addr) const final; |
| void Write(const PciReg8 addr, uint8_t val) const final; |
| void Write(const PciReg16 addr, uint16_t val) const final; |
| void Write(const PciReg32 addr, uint32_t val) const override; |
| const char* type(void) const override; |
| virtual zx::status<ddk::MmioView> get_view() const final { return zx::ok(ddk::MmioView(view_)); } |
| |
| private: |
| friend class FakeMmioConfig; |
| MmioConfig(pci_bdf_t bdf, ddk::MmioView&& view) : Config(bdf), view_(std::move(view)) {} |
| const ddk::MmioView view_; |
| }; |
| |
| // ProxyConfig is used with PCI buses that do not support MMIO config space, |
| // or require special controller configuration before config access. Examples |
| // of this are IO config on x64 due to needing to synchronize CF8/CFC with |
| // ACPI, and Designware on ARM where the controller needs to be configured to |
| // map a given device's configuration space in before access. |
| // |
| // For proxy configuration access all operations are passed to the pciroot |
| // protocol implementation hosted in the same devhost as the pci bus driver. |
| class ProxyConfig final : public Config { |
| public: |
| static zx_status_t Create(pci_bdf_t bdf, ddk::PcirootProtocolClient* proto, |
| std::unique_ptr<Config>* config); |
| uint8_t Read(const PciReg8 addr) const final; |
| uint16_t Read(const PciReg16 addr) const final; |
| uint32_t Read(const PciReg32 addr) const final; |
| void Write(const PciReg8 addr, uint8_t val) const final; |
| void Write(const PciReg16 addr, uint16_t val) const final; |
| void Write(const PciReg32 addr, uint32_t val) const final; |
| const char* type(void) const final; |
| |
| private: |
| ProxyConfig(pci_bdf_t bdf, ddk::PcirootProtocolClient* proto) : Config(bdf), pciroot_(proto) {} |
| // The bus driver outlives config objects. |
| ddk::PcirootProtocolClient* const pciroot_; |
| }; |
| |
| } // namespace pci |
| |
| #endif // SRC_DEVICES_BUS_DRIVERS_PCI_CONFIG_H_ |