2023-06-08 02:36:59 -07:00
|
|
|
#include "ahci/ahci_driver.h"
|
|
|
|
|
2023-06-21 18:28:54 -07:00
|
|
|
#include <glacier/status/error.h>
|
2023-06-21 18:38:11 -07:00
|
|
|
#include <glacier/status/error_or.h>
|
2023-06-08 02:36:59 -07:00
|
|
|
#include <mammoth/debug.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <zcall.h>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
const uint64_t kSataPciPhys = 0xB00FA000;
|
|
|
|
const uint64_t kPciSize = 0x1000;
|
|
|
|
|
2023-06-12 19:20:51 -07:00
|
|
|
const uint64_t kGhc_InteruptEnable = 0x2;
|
|
|
|
|
|
|
|
void interrupt_thread(void* void_driver) {
|
|
|
|
AhciDriver* driver = static_cast<AhciDriver*>(void_driver);
|
|
|
|
|
|
|
|
driver->InterruptLoop();
|
|
|
|
|
2023-06-21 18:28:54 -07:00
|
|
|
crash("Driver returned from interrupt loop", glcr::INTERNAL);
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
|
2023-06-08 02:36:59 -07:00
|
|
|
} // namespace
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::Init() {
|
2023-06-08 02:36:59 -07:00
|
|
|
RET_ERR(LoadPciDeviceHeader());
|
2023-06-12 23:32:24 -07:00
|
|
|
// RET_ERR(LoadCapabilities());
|
2023-06-12 19:20:51 -07:00
|
|
|
RET_ERR(RegisterIrq());
|
2023-06-08 02:36:59 -07:00
|
|
|
RET_ERR(LoadHbaRegisters());
|
2023-06-12 19:20:51 -07:00
|
|
|
ahci_hba_->global_host_control |= kGhc_InteruptEnable;
|
|
|
|
RET_ERR(LoadDevices());
|
2023-06-12 23:32:24 -07:00
|
|
|
// DumpCapabilities();
|
|
|
|
// DumpPorts();
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-08 02:36:59 -07:00
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorOr<AhciDevice*> AhciDriver::GetDevice(uint64_t id) {
|
2023-06-15 16:20:29 -07:00
|
|
|
if (id >= 32) {
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::INVALID_ARGUMENT;
|
2023-06-15 16:20:29 -07:00
|
|
|
}
|
|
|
|
|
2023-06-16 01:31:23 -07:00
|
|
|
if (devices_[id] != nullptr && !devices_[id]->IsInit()) {
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::NOT_FOUND;
|
2023-06-15 16:20:29 -07:00
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
return devices_[id];
|
2023-06-15 16:20:29 -07:00
|
|
|
}
|
|
|
|
|
2023-06-08 02:36:59 -07:00
|
|
|
void AhciDriver::DumpCapabilities() {
|
|
|
|
dbgln("AHCI Capabilities:");
|
|
|
|
uint32_t caps = ahci_hba_->capabilities;
|
|
|
|
|
|
|
|
if (caps & 0x20) {
|
|
|
|
dbgln("External SATA");
|
|
|
|
}
|
|
|
|
if (caps & 0x40) {
|
|
|
|
dbgln("Enclosure Management");
|
|
|
|
}
|
|
|
|
if (caps & 0x80) {
|
|
|
|
dbgln("Command Completion Coalescing");
|
|
|
|
}
|
|
|
|
if (caps & 0x2000) {
|
|
|
|
dbgln("Partial State Capable");
|
|
|
|
}
|
|
|
|
if (caps & 0x4000) {
|
|
|
|
dbgln("Slumber state capable");
|
|
|
|
}
|
|
|
|
if (caps & 0x8000) {
|
|
|
|
dbgln("PIO Multiple DRQ Block");
|
|
|
|
}
|
|
|
|
if (caps & 0x1'0000) {
|
|
|
|
dbgln("FIS-Based Switching");
|
|
|
|
}
|
|
|
|
if (caps & 0x2'0000) {
|
|
|
|
dbgln("Port Multiplier");
|
|
|
|
}
|
|
|
|
if (caps & 0x4'0000) {
|
|
|
|
dbgln("AHCI mode only");
|
|
|
|
}
|
|
|
|
dbgln("Speed support: %u", (caps & 0xF0'0000) >> 20);
|
|
|
|
if (caps & 0x100'0000) {
|
|
|
|
dbgln("Command list override");
|
|
|
|
}
|
|
|
|
if (caps & 0x200'0000) {
|
|
|
|
dbgln("Activity LED");
|
|
|
|
}
|
|
|
|
if (caps & 0x400'0000) {
|
|
|
|
dbgln("Aggresive link power management");
|
|
|
|
}
|
|
|
|
if (caps & 0x800'0000) {
|
|
|
|
dbgln("Staggered spin up");
|
|
|
|
}
|
|
|
|
if (caps & 0x1000'0000) {
|
|
|
|
dbgln("Mechanical Switch Presence");
|
|
|
|
}
|
|
|
|
if (caps & 0x2000'0000) {
|
|
|
|
dbgln("SNotification Register");
|
|
|
|
}
|
|
|
|
if (caps & 0x4000'0000) {
|
|
|
|
dbgln("Native Command Queueing");
|
|
|
|
}
|
|
|
|
if (caps & 0x8000'0000) {
|
|
|
|
dbgln("64bit Addressing");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Secondary.
|
|
|
|
caps = ahci_hba_->capabilities_ext;
|
|
|
|
if (caps & 0x1) {
|
|
|
|
dbgln("BIOS/OS handoff");
|
|
|
|
}
|
|
|
|
if (caps & 0x2) {
|
|
|
|
dbgln("NVMHCI Present");
|
|
|
|
}
|
|
|
|
if (caps & 0x4) {
|
|
|
|
dbgln("Auto partial to slumber tranisitions");
|
|
|
|
}
|
|
|
|
if (caps & 0x8) {
|
|
|
|
dbgln("Device sleep");
|
|
|
|
}
|
|
|
|
if (caps & 0x10) {
|
|
|
|
dbgln("Aggressive device sleep management");
|
|
|
|
}
|
2023-06-12 19:20:51 -07:00
|
|
|
|
|
|
|
dbgln("Control %x", ahci_hba_->global_host_control);
|
2023-06-08 02:36:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void AhciDriver::DumpPorts() {
|
2023-06-12 19:20:51 -07:00
|
|
|
for (uint64_t i = 0; i < 6; i++) {
|
2023-06-16 01:31:23 -07:00
|
|
|
AhciDevice* dev = devices_[i];
|
|
|
|
if (dev == nullptr || !dev->IsInit()) {
|
2023-06-08 02:36:59 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbgln("");
|
2023-06-12 19:20:51 -07:00
|
|
|
dbgln("Port %u:", i);
|
2023-06-16 01:31:23 -07:00
|
|
|
dev->DumpInfo();
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
}
|
2023-06-08 02:36:59 -07:00
|
|
|
|
2023-06-12 19:20:51 -07:00
|
|
|
void AhciDriver::InterruptLoop() {
|
2023-06-16 01:31:23 -07:00
|
|
|
dbgln("Starting interrupt loop");
|
2023-06-12 19:20:51 -07:00
|
|
|
while (true) {
|
2023-06-17 02:01:21 -07:00
|
|
|
uint64_t bytes, caps;
|
2023-06-20 14:55:54 -07:00
|
|
|
check(ZPortRecv(irq_port_cap_, &bytes, nullptr, &caps, nullptr));
|
2023-06-12 23:32:24 -07:00
|
|
|
for (uint64_t i = 0; i < 32; i++) {
|
2023-06-16 01:31:23 -07:00
|
|
|
if (devices_[i] != nullptr && devices_[i]->IsInit() &&
|
|
|
|
(ahci_hba_->interrupt_status & (1 << i))) {
|
|
|
|
dbgln("Interrupt for %u", i);
|
|
|
|
devices_[i]->HandleIrq();
|
2023-06-12 23:32:24 -07:00
|
|
|
ahci_hba_->interrupt_status &= ~(1 << i);
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
}
|
2023-06-08 02:36:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::LoadPciDeviceHeader() {
|
2023-06-12 23:32:24 -07:00
|
|
|
pci_region_ = MappedMemoryRegion::DirectPhysical(kSataPciPhys, kPciSize);
|
|
|
|
pci_device_header_ = reinterpret_cast<PciDeviceHeader*>(pci_region_.vaddr());
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-08 02:36:59 -07:00
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::LoadCapabilities() {
|
2023-06-12 19:20:51 -07:00
|
|
|
if (!(pci_device_header_->status_reg & 0x10)) {
|
|
|
|
dbgln("No caps!");
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::FAILED_PRECONDITION;
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
uint8_t* base = reinterpret_cast<uint8_t*>(pci_device_header_);
|
|
|
|
uint16_t offset = pci_device_header_->cap_ptr;
|
|
|
|
do {
|
|
|
|
uint16_t* cap = reinterpret_cast<uint16_t*>(base + offset);
|
|
|
|
switch (*cap & 0xFF) {
|
|
|
|
case 0x01:
|
|
|
|
dbgln("Power Management");
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
dbgln("MSI");
|
|
|
|
break;
|
|
|
|
case 0x12:
|
|
|
|
dbgln("SATA");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dbgln("Unrecognized cap");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = (*cap & 0xFF00) >> 8;
|
|
|
|
} while (offset);
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::RegisterIrq() {
|
2023-06-12 19:20:51 -07:00
|
|
|
if (pci_device_header_->interrupt_pin == 0) {
|
2023-06-21 18:38:11 -07:00
|
|
|
crash("Can't register IRQ without a pin num", glcr::INVALID_ARGUMENT);
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
uint64_t irq_num = Z_IRQ_PCI_BASE + pci_device_header_->interrupt_pin - 1;
|
|
|
|
RET_ERR(ZIrqRegister(irq_num, &irq_port_cap_));
|
|
|
|
irq_thread_ = Thread(interrupt_thread, this);
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::LoadHbaRegisters() {
|
2023-06-12 23:32:24 -07:00
|
|
|
ahci_region_ =
|
|
|
|
MappedMemoryRegion::DirectPhysical(pci_device_header_->abar, 0x1100);
|
|
|
|
ahci_hba_ = reinterpret_cast<AhciHba*>(ahci_region_.vaddr());
|
|
|
|
num_ports_ = (ahci_hba_->capabilities & 0x1F) + 1;
|
|
|
|
num_commands_ = ((ahci_hba_->capabilities & 0x1F00) >> 8) + 1;
|
2023-06-08 02:36:59 -07:00
|
|
|
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-08 02:36:59 -07:00
|
|
|
}
|
2023-06-12 19:20:51 -07:00
|
|
|
|
2023-06-21 18:38:11 -07:00
|
|
|
glcr::ErrorCode AhciDriver::LoadDevices() {
|
2023-06-12 23:32:24 -07:00
|
|
|
for (uint8_t i = 0; i < 32; i++) {
|
2023-06-12 19:20:51 -07:00
|
|
|
if (!(ahci_hba_->port_implemented & (1 << i))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
uint64_t port_addr =
|
|
|
|
reinterpret_cast<uint64_t>(ahci_hba_) + 0x100 + (0x80 * i);
|
2023-06-16 01:31:23 -07:00
|
|
|
devices_[i] = new AhciDevice(reinterpret_cast<AhciPort*>(port_addr));
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|
2023-06-21 18:28:54 -07:00
|
|
|
return glcr::OK;
|
2023-06-12 19:20:51 -07:00
|
|
|
}
|