192 lines
5.1 KiB
Rust

use libk_mm::address::PhysicalAddress;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::error::Error;
use crate::error::AhciError;
register_bitfields! {
u32,
pub CAP [
NP OFFSET(0) NUMBITS(5) [],
NCS OFFSET(8) NUMBITS(5) [],
SAM OFFSET(18) NUMBITS(1) [],
S64A OFFSET(31) NUMBITS(1) [],
],
pub GHC [
HR OFFSET(0) NUMBITS(1) [],
IE OFFSET(1) NUMBITS(1) [],
AE OFFSET(31) NUMBITS(1) [],
],
// Read/write 1 to clear
pub IS [
TFES OFFSET(30) NUMBITS(1) [],
HBFS OFFSET(29) NUMBITS(1) [],
HBDS OFFSET(28) NUMBITS(1) [],
IFS OFFSET(27) NUMBITS(1) [],
OFS OFFSET(24) NUMBITS(1) [],
],
pub IE [
TFEE OFFSET(30) NUMBITS(1) [],
HBFE OFFSET(29) NUMBITS(1) [],
HBDE OFFSET(28) NUMBITS(1) [],
IFE OFFSET(27) NUMBITS(1) [],
OFE OFFSET(24) NUMBITS(1) [],
DPE OFFSET(5) NUMBITS(1) [],
DHRE OFFSET(0) NUMBITS(1) [],
],
pub CMD [
CR OFFSET(15) NUMBITS(1) [],
FR OFFSET(14) NUMBITS(1) [],
CCS OFFSET(8) NUMBITS(5) [],
FRE OFFSET(4) NUMBITS(1) [],
POD OFFSET(2) NUMBITS(1) [],
ST OFFSET(0) NUMBITS(1) [],
],
pub SSTS [
IPM OFFSET(8) NUMBITS(4) [
NotPresent = 0,
Active = 1,
],
DET OFFSET(0) NUMBITS(4) [
NotPresent = 0,
Online = 3,
],
],
pub TFD [
BSY OFFSET(7) NUMBITS(1) [],
DRQ OFFSET(3) NUMBITS(1) [],
ERR OFFSET(0) NUMBITS(1) [],
]
}
register_structs! {
#[allow(non_snake_case)]
pub Regs {
(0x0000 => pub CAP: ReadOnly<u32, CAP::Register>),
(0x0004 => pub GHC: ReadWrite<u32, GHC::Register>),
(0x0008 => pub IS: ReadWrite<u32>),
(0x000C => pub PI: ReadOnly<u32>),
(0x0010 => pub VS: ReadOnly<u32>),
(0x0014 => _0),
(0x0100 => pub PORTS: [PortRegs; 30]),
(0x1000 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub PortRegs {
(0x00 => pub CLB: ReadWrite<u32>),
(0x04 => pub CLBU: ReadWrite<u32>),
(0x08 => pub FB: ReadWrite<u32>),
(0x0C => pub FBU: ReadWrite<u32>),
(0x10 => pub IS: ReadWrite<u32, IS::Register>),
(0x14 => pub IE: ReadWrite<u32, IE::Register>),
(0x18 => pub CMD: ReadWrite<u32, CMD::Register>),
(0x1C => _0),
(0x20 => pub TFD: ReadWrite<u32, TFD::Register>),
(0x24 => pub SIG: ReadOnly<u32>),
(0x28 => pub SSTS: ReadOnly<u32, SSTS::Register>),
(0x2C => pub SCTL: ReadOnly<u32>),
(0x30 => pub SERR: ReadOnly<u32>),
(0x34 => pub SACT: ReadOnly<u32>),
(0x38 => pub CI: ReadWrite<u32>),
(0x3C => pub SNTF: ReadOnly<u32>),
(0x40 => _1),
(0x80 => @END),
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Version {
V0_95,
V1_0,
V1_1,
V1_2,
V1_3,
V1_3_1,
}
pub const CMD_PENDING: u32 = 1;
pub const CMD_READY: u32 = 0;
pub const CMD_ERROR: u32 = 2;
impl PortRegs {
pub const SIG_SATA: u32 = 0x101;
// NOTE: usually doesn't take long, so not async, I guess
pub fn stop(&self) -> Result<(), AhciError> {
self.CMD.modify(CMD::ST::CLEAR + CMD::FRE::CLEAR);
// TODO timeout here
while self.CMD.matches_any(&[CMD::FR::SET, CMD::CR::SET]) {
core::hint::spin_loop();
}
Ok(())
}
pub fn start(&self) -> Result<(), AhciError> {
while self.CMD.matches_all(CMD::CR::SET) {
core::hint::spin_loop();
}
self.CMD.modify(CMD::ST::SET + CMD::FRE::SET);
Ok(())
}
pub fn set_received_fis_address_64(&self, address: PhysicalAddress) {
let address: u64 = address.into();
self.FB.set(address as u32);
self.FBU.set((address >> 32) as u32);
}
pub fn set_command_list_address_64(&self, address: PhysicalAddress) {
let address: u64 = address.into();
self.CLB.set(address as u32);
self.CLBU.set((address >> 32) as u32);
}
pub fn clear_interrupt(&self) -> Option<u32> {
let is = self.IS.extract();
if is.get() == 0 {
return None;
}
self.IS.set(u32::MAX);
if is.matches_any(&[IS::HBDS::SET, IS::HBFS::SET]) {
todo!("Host communication error unhandled");
}
if is.matches_any(&[IS::TFES::SET, IS::IFS::SET, IS::OFS::SET]) {
Some(CMD_ERROR)
} else {
Some(CMD_READY)
}
}
}
impl TryFrom<u32> for Version {
type Error = Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0x00000905 => Ok(Self::V0_95),
0x00010000 => Ok(Self::V1_0),
0x00010100 => Ok(Self::V1_1),
0x00010200 => Ok(Self::V1_2),
0x00010300 => Ok(Self::V1_3),
0x00010301 => Ok(Self::V1_3_1),
_ => Err(Error::InvalidArgument),
}
}
}