mirror of
https://github.com/justinian/jsix.git
synced 2025-12-12 09:24:31 -08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user