192 lines
5.1 KiB
Rust
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),
|
|
}
|
|
}
|
|
}
|