I'm a tabs guy. I like tabs, it's an elegant way to represent indentation instead of brute-forcing it. But I have to admit that the world seems to be going towards spaces, and tooling tends not to play nice with tabs. So here we go, changing the whole repo to spaces since I'm getting tired of all the inconsistent formatting.
142 lines
4.2 KiB
C++
142 lines
4.2 KiB
C++
#pragma once
|
|
/// \file pci.h
|
|
/// PCI devices and groups
|
|
|
|
#include <stdint.h>
|
|
#include "kutil/memory.h"
|
|
|
|
struct pci_group;
|
|
enum class isr : uint8_t;
|
|
|
|
struct pci_cap
|
|
{
|
|
enum class type : uint8_t
|
|
{
|
|
msi = 0x05,
|
|
msix = 0x11
|
|
};
|
|
|
|
type id;
|
|
uint8_t next;
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
/// Information about a discovered PCIe device
|
|
class pci_device
|
|
{
|
|
public:
|
|
/// Default constructor creates an empty object.
|
|
pci_device();
|
|
|
|
/// Constructor
|
|
/// \arg group The group of this device's bus
|
|
/// \arg bus The bus number this device is on
|
|
/// \arg device The device number of this device
|
|
/// \arg func The function number of this device
|
|
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
|
|
|
/// Check if this device is multi-function.
|
|
/// \returns True if this device is multi-function
|
|
inline bool multi() const { return m_multi; }
|
|
|
|
/// Get the bus number this device is on.
|
|
/// \returns The bus number
|
|
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
|
|
|
/// Get the device number of this device on its bus
|
|
/// \returns The device number
|
|
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
|
|
|
/// Get the function number of this device on its device
|
|
/// \returns The function number
|
|
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
|
|
|
/// Get the device class
|
|
/// \returns The PCI device class
|
|
inline uint8_t devclass() const { return m_class; }
|
|
|
|
/// Get the device subclass
|
|
/// \returns The PCI device subclass
|
|
inline uint8_t subclass() const { return m_subclass; }
|
|
|
|
/// Get the device program interface
|
|
/// \returns The PCI device program interface
|
|
inline uint8_t progif() const { return m_progif; }
|
|
|
|
/// Read one of the device's Base Address Registers
|
|
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
|
/// \returns The contents of the BAR
|
|
uint32_t get_bar(unsigned i);
|
|
|
|
/// Write one of the device's Base Address Registers
|
|
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
|
/// \arg val The value to write
|
|
void set_bar(unsigned i, uint32_t val);
|
|
|
|
/// Write to the MSI registers
|
|
/// \arg addr The address to write to the MSI address registers
|
|
/// \arg data The value to write to the MSI data register
|
|
void write_msi_regs(uintptr_t addr, uint16_t data);
|
|
|
|
/// Get a bus address, given the bus/device/function numbers.
|
|
/// \arg bus Number of the bus
|
|
/// \arg device Index of the device on the bus
|
|
/// \arg func The function number within the device
|
|
/// \returns The computed bus_addr
|
|
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
|
{
|
|
return bus << 8 | device << 3 | func;
|
|
}
|
|
|
|
private:
|
|
uint32_t *m_base;
|
|
pci_cap *m_msi;
|
|
|
|
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
|
uint16_t m_bus_addr;
|
|
|
|
uint16_t m_vendor;
|
|
uint16_t m_device;
|
|
|
|
uint8_t m_class;
|
|
uint8_t m_subclass;
|
|
uint8_t m_progif;
|
|
uint8_t m_revision;
|
|
bool m_multi;
|
|
|
|
// Might as well cache these to fill out the struct align
|
|
isr m_irq;
|
|
uint8_t m_header_type;
|
|
};
|
|
|
|
|
|
/// Represents data about a PCI bus group from the ACPI MCFG
|
|
struct pci_group
|
|
{
|
|
uint16_t group;
|
|
uint16_t bus_start;
|
|
uint16_t bus_end;
|
|
|
|
uint32_t *base;
|
|
|
|
/// Get the base address of the MMIO configuration registers for a device
|
|
/// \arg bus The bus number of the device (relative to this group)
|
|
/// \arg device The device number on the given bus
|
|
/// \arg func The function number on the device
|
|
/// \returns A pointer to the memory-mapped configuration registers
|
|
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
|
{
|
|
return kutil::offset_pointer(base,
|
|
pci_device::bus_addr(bus, device, func) << 12);
|
|
}
|
|
|
|
/// Check if the given device function is present.
|
|
/// \arg bus The bus number of the device (relative to this group)
|
|
/// \arg device The device number on the given bus
|
|
/// \arg func The function number on the device
|
|
/// \returns True if the device function is present
|
|
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
|
};
|
|
|
|
|