Kernel: PCI can now get interrupts for devices
This commit is contained in:
parent
f27974dd3c
commit
89c975350d
|
@ -35,6 +35,8 @@ namespace Kernel
|
|||
static void initialize(bool force_pic);
|
||||
static InterruptController& get();
|
||||
|
||||
bool is_using_apic() const { return m_using_apic; }
|
||||
|
||||
void enter_acpi_mode();
|
||||
|
||||
private:
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace Kernel::PCI
|
|||
|
||||
uint8_t header_type() const { return m_header_type; }
|
||||
|
||||
BAN::ErrorOr<uint8_t> get_irq();
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> allocate_bar_region(uint8_t bar_num);
|
||||
|
||||
void enable_bus_mastering();
|
||||
|
@ -105,6 +107,9 @@ namespace Kernel::PCI
|
|||
uint8_t m_prog_if;
|
||||
|
||||
uint8_t m_header_type;
|
||||
|
||||
BAN::Optional<uint8_t> m_offset_msi;
|
||||
BAN::Optional<uint8_t> m_offset_msi_x;
|
||||
};
|
||||
|
||||
class PCIManager
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#define CONFIG_DATA 0xCFC
|
||||
|
||||
#define PCI_REG_COMMAND 0x04
|
||||
#define PCI_REG_STATUS 0x06
|
||||
#define PCI_REG_CAPABILITIES 0x34
|
||||
#define PCI_REG_IRQ_LINE 0x3C
|
||||
#define PCI_REG_IRQ_PIN 0x44
|
||||
|
||||
#define PCI_CMD_IO_SPACE (1 << 0)
|
||||
#define PCI_CMD_MEM_SPACE (1 << 1)
|
||||
#define PCI_CMD_BUS_MASTER (1 << 2)
|
||||
|
@ -187,10 +192,9 @@ namespace Kernel::PCI
|
|||
{
|
||||
ASSERT(device.header_type() == 0x00);
|
||||
|
||||
uint32_t command_status = device.read_dword(0x04);
|
||||
|
||||
// disable io/mem space while reading bar
|
||||
device.write_dword(0x04, command_status & ~3);
|
||||
uint16_t command = device.read_word(PCI_REG_COMMAND);
|
||||
device.write_word(PCI_REG_COMMAND, command & ~(PCI_CMD_IO_SPACE | PCI_CMD_MEM_SPACE));
|
||||
|
||||
uint8_t offset = 0x10 + bar_num * 4;
|
||||
|
||||
|
@ -232,9 +236,9 @@ namespace Kernel::PCI
|
|||
auto region = BAN::UniqPtr<BarRegion>::adopt(region_ptr);
|
||||
TRY(region->initialize());
|
||||
|
||||
// restore old command register and enable correct IO/MEM
|
||||
command_status |= (type == BarType::IO) ? 1 : 2;
|
||||
device.write_dword(0x04, command_status);
|
||||
// restore old command register and enable correct IO/MEM space
|
||||
command |= (type == BarType::IO) ? PCI_CMD_IO_SPACE : PCI_CMD_MEM_SPACE;
|
||||
device.write_word(PCI_REG_COMMAND, command);
|
||||
|
||||
#if DEBUG_PCI
|
||||
dprintln("created BAR region for PCI {}:{}.{}",
|
||||
|
@ -373,19 +377,67 @@ namespace Kernel::PCI
|
|||
|
||||
void PCI::Device::enumerate_capabilites()
|
||||
{
|
||||
uint16_t status = read_word(0x06);
|
||||
uint16_t status = read_word(PCI_REG_STATUS);
|
||||
if (!(status & (1 << 4)))
|
||||
return;
|
||||
|
||||
uint8_t capabilities = read_byte(0x34) & 0xFC;
|
||||
while (capabilities)
|
||||
uint8_t capability_offset = read_byte(PCI_REG_CAPABILITIES) & 0xFC;
|
||||
while (capability_offset)
|
||||
{
|
||||
uint16_t next = read_word(capabilities);
|
||||
dprintln(" cap {2H}", next & 0xFF);
|
||||
capabilities = (next >> 8) & 0xFC;
|
||||
uint16_t capability_info = read_word(capability_offset);
|
||||
|
||||
switch (capability_info & 0xFF)
|
||||
{
|
||||
case 0x05:
|
||||
m_offset_msi = capability_offset;
|
||||
dprintln("{}:{}.{} has MSI", m_bus, m_dev, m_func);
|
||||
break;
|
||||
case 0x11:
|
||||
m_offset_msi_x = capability_offset;
|
||||
dprintln("{}:{}.{} has MSI-X", m_bus, m_dev, m_func);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
capability_offset = (capability_info >> 8) & 0xFC;
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<uint8_t> PCI::Device::get_irq()
|
||||
{
|
||||
// Legacy PIC just uses the interrupt line field
|
||||
if (!InterruptController::get().is_using_apic())
|
||||
return read_byte(PCI_REG_IRQ_LINE);
|
||||
|
||||
// TODO: use MSI and MSI-X if supported
|
||||
|
||||
if (m_offset_msi.has_value())
|
||||
{
|
||||
}
|
||||
|
||||
if (m_offset_msi_x.has_value())
|
||||
{
|
||||
}
|
||||
|
||||
for (uint8_t irq_pin = 1; irq_pin <= 4; irq_pin++)
|
||||
{
|
||||
acpi_resource_t dest;
|
||||
auto err = lai_pci_route_pin(&dest, 0, m_bus, m_dev, m_func, irq_pin);
|
||||
if (err != LAI_ERROR_NONE)
|
||||
{
|
||||
dprintln("{}", lai_api_error_to_string(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
write_byte(PCI_REG_IRQ_PIN, irq_pin);
|
||||
return dest.base;
|
||||
}
|
||||
|
||||
dwarnln("Could not allocate interrupt for PCI {}:{}.{}", m_bus, m_dev, m_func);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
void PCI::Device::set_command_bits(uint16_t mask)
|
||||
{
|
||||
write_dword(PCI_REG_COMMAND, read_dword(PCI_REG_COMMAND) | mask);
|
||||
|
|
Loading…
Reference in New Issue