Enable AHCI interrupts.

* Implement MSI style interrupts
* Move interrupt handling to device_manager for IRQs
* Give device_manager the ability to allocate IRQs
* Move achi::port to an interrupt-based scheme
This commit is contained in:
Justin C. Miller
2018-05-12 18:27:13 -07:00
parent c9277e4b12
commit 289104cde0
12 changed files with 267 additions and 47 deletions

View File

@@ -14,6 +14,15 @@ ahci_driver::register_device(pci_device *device)
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
device->bus(), device->device(), device->function());
m_devices.emplace(device);
ahci::hba &hba = m_devices.emplace(device);
}
ahci::port *
ahci_driver::find_disk()
{
for (auto &hba : m_devices) {
ahci::port *d = hba.find_disk();
if (d) return d;
}
return nullptr;
}

View File

@@ -22,6 +22,9 @@ public:
/// \arg device The PCI device to remove
void unregister_device(pci_device *device);
/// Debug: find the first disk
ahci::port * find_disk();
private:
kutil::vector<ahci::hba> m_devices;
};

View File

@@ -1,6 +1,7 @@
#include <stdint.h>
#include "ahci/ata.h"
#include "ahci/hba.h"
#include "console.h"
#include "device_manager.h"
#include "log.h"
#include "page_manager.h"
#include "pci.h"
@@ -46,6 +47,13 @@ struct hba_data
} __attribute__ ((packed));
void irq_cb(void *data)
{
hba *h = reinterpret_cast<hba *>(data);
h->handle_interrupt();
}
hba::hba(pci_device *device)
{
page_manager *pm = page_manager::get();
@@ -54,6 +62,9 @@ hba::hba(pci_device *device)
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
m_data->host_control |= 0x80000000; // Enable AHCI mode
uint32_t icap = static_cast<uint32_t>(m_data->cap);
unsigned ports = (icap & 0xf) + 1;
@@ -65,24 +76,42 @@ hba::hba(pci_device *device)
port_data *pd = reinterpret_cast<port_data *>(
kutil::offset_pointer(m_data, 0x100));
bool needs_interrupt = false;
m_ports.ensure_capacity(ports);
for (unsigned i = 0; i < ports; ++i) {
bool impl = ((m_data->port_impl & (1 << i)) != 0);
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
if (p.get_state() == port::state::active) {
uint8_t buf[512];
p.read(1, sizeof(buf), buf);
if (p.get_state() == port::state::active)
needs_interrupt = true;
}
console *cons = console::get();
uint8_t *p = &buf[0];
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 16; ++j) {
cons->printf(" %02x", *p++);
}
cons->putc('\n');
}
if (needs_interrupt) {
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
m_data->host_control |= 0x02; // enable interrupts
}
}
port *
hba::find_disk()
{
for (auto &port : m_ports) {
if (port.get_state() == port::state::active &&
port.get_type() == sata_signature::sata_drive)
return &port;
}
return nullptr;
}
void
hba::handle_interrupt()
{
for (auto &port : m_ports) {
if (m_data->int_status & (1 << port.index())) {
port.handle_interrupt();
}
}
m_data->int_status = 0;
}
} // namespace ahci

View File

@@ -22,6 +22,12 @@ public:
/// \arg device The PCI device for this HBA
hba(pci_device *device);
/// Interrupt handler.
void handle_interrupt();
/// Debug: find the first disk
port * find_disk();
private:
pci_device *m_device;
hba_data *m_data;

View File

@@ -154,6 +154,7 @@ port::update()
rebase();
m_pending.set_size(32);
m_data->interrupt_enable = 1;
} else {
m_state = state::inactive;
}
@@ -290,25 +291,20 @@ port::issue_command(int slot)
// Set bit in CI. Note that only new bits should be written, not
// previous state.
m_data->cmd_issue = (1 << slot);
return true;
}
// TODO: interrupt-based
while (true) {
if ((m_data->cmd_issue & (1 << slot)) == 0) break;
if (m_data->interrupt_status & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
// TODO: clean up!
return false;
}
io_wait();
}
void
port::handle_interrupt()
{
log::debug(logs::driver, "AHCI port %d got an interrupt");
// This is where interrupt handler would begin
// TODO: handle other states in interrupt_status
if (m_data->interrupt_status & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
// TODO: clean up!
return false;
return;
}
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
@@ -329,8 +325,7 @@ port::issue_command(int slot)
p.type = command_type::none;
p.data = nullptr;
}
return true;
m_data->interrupt_status = m_data->interrupt_status;
}
void

View File

@@ -27,15 +27,19 @@ public:
/// Destructor
~port();
/// Get the index of this port on the HBA
/// \returns The port index
inline uint8_t index() const { return m_index; }
enum class state : uint8_t { unimpl, inactive, active };
/// Get the current state of this device
/// \returns An enum representing the state
state get_state() const { return m_state; }
inline state get_state() const { return m_state; }
/// Get the type signature of this device
/// \returns An enum representing the type of device
sata_signature get_type() const { return m_type; }
inline sata_signature get_type() const { return m_type; }
/// Update the state of this object from the register data
void update();
@@ -56,6 +60,9 @@ public:
/// \returns True if the command succeeded
bool read(uint64_t sector, size_t length, void *dest);
/// Handle an incoming interrupt
void handle_interrupt();
private:
/// Rebase the port command structures to a new location in system
/// memory, to be allocated from the page manager.