#![allow(non_snake_case, clippy::upper_case_acronyms)]
use core::fmt;
use bitfield_struct::bitfield;
use volatile::{VolatileFieldAccess, access::ReadOnly};
#[derive(VolatileFieldAccess)]
#[repr(C)]
pub struct AhciMmio {
pub host: GenericHostControl,
_res: [u8; 0xd8],
pub ports: [PortRegisters; 32],
}
const _: () = assert!(core::mem::offset_of!(AhciMmio, ports) == 0x100);
#[derive(VolatileFieldAccess)]
#[repr(C)]
pub struct GenericHostControl {
pub cap: CAP,
pub ghc: GHC,
pub is: u32,
pub pi: u32,
#[access(ReadOnly)]
pub vs: VS,
pub ccc_ctl: u32,
pub ccc_ports: u32,
#[access(ReadOnly)]
pub em_loc: u32,
pub em_ctl: u32,
#[access(ReadOnly)]
pub cap2: CAP2,
}
#[bitfield(u32, order = Msb)]
pub struct CAP {
pub S64A: bool,
pub SNCQ: bool,
pub SSNTF: bool,
pub SMPS: bool,
pub SSS: bool,
pub SALP: bool,
pub SAL: bool,
pub SCLO: bool,
#[bits(4)]
pub ISS: ISS,
pub __: bool,
pub SAM: bool,
pub SPM: bool,
pub FBSS: bool,
pub PMD: bool,
pub SSC: bool,
pub PSC: bool,
#[bits(5)]
pub NCS: u8,
pub CCCS: bool,
pub EMS: bool,
pub SXS: bool,
#[bits(5)]
pub NP: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ISS {
Reserved = 0,
Gen1 = 1,
Gen2 = 2,
Gen3 = 3,
}
impl ISS {
pub const fn into_bits(self) -> u8 {
self as _
}
pub const fn from_bits(bits: u8) -> Self {
match bits {
1 => ISS::Gen1,
2 => ISS::Gen2,
3 => ISS::Gen3,
_ => ISS::Reserved,
}
}
}
impl fmt::Display for ISS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ISS::Reserved => write!(f, "Reserved"),
ISS::Gen1 => write!(f, "Gen 1 (1.5 Gbps)"),
ISS::Gen2 => write!(f, "Gen 2 (3 Gbps)"),
ISS::Gen3 => write!(f, "Gen 3 (6 Gbps)"),
}
}
}
impl fmt::Display for CAP {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.S64A() {
write!(f, "64bit, ")?;
}
if self.SNCQ() {
write!(f, "Native Command Queuing, ")?;
}
if self.SSNTF() {
write!(f, "SNotification, ")?;
}
if self.SMPS() {
write!(f, "Mechanical Presence Switch, ")?;
}
if self.SSS() {
write!(f, "Staggered Spin-up, ")?;
}
if self.SALP() {
write!(f, "Aggressive Link Power Management, ")?;
}
if self.SAL() {
write!(f, "Activity LED, ")?;
}
if self.SCLO() {
write!(f, "Command List Override, ")?;
}
if self.SAM() {
write!(f, "AHCI only, ")?;
}
if self.SPM() {
write!(f, "Port Multiplier, ")?;
}
if self.FBSS() {
write!(f, "FIS-based Switching, ")?;
}
if self.PMD() {
write!(f, "Multiple DRQ Block, ")?;
}
if self.SSC() {
write!(f, "Slumber State Capable, ")?;
}
if self.PSC() {
write!(f, "Partial State Capable, ")?;
}
if self.CCCS() {
write!(f, "Command Completion Coalescing, ")?;
}
if self.EMS() {
write!(f, "Enclosure Management, ")?;
}
if self.SXS() {
write!(f, "External SATA, ")?;
}
write!(f, "Command slots: {}, ", self.NCS() + 1)?;
write!(f, "Ports: {}", self.NP() + 1)?;
Ok(())
}
}
#[bitfield(u32, order = Msb)]
pub struct GHC {
pub AE: bool,
#[bits(28)]
__: u32,
pub MRSM: bool,
pub IE: bool,
pub HR: bool,
}
#[bitfield(u32, order = Msb)]
pub struct VS {
major_h: u8,
major_l: u8,
minor_h: u8,
minor_l: u8,
}
impl fmt::Display for VS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let major = self.major_h() * 0x10 + self.major_l();
let minor = self.minor_h() * 0x10 + self.minor_l();
write!(f, "{major:x}.{minor:x}")
}
}
#[bitfield(u32, order = Msb)]
pub struct CAP2 {
#[bits(26)]
__: u32,
pub DESO: bool,
pub SADM: bool,
pub SDS: bool,
pub APST: bool,
pub NVMP: bool,
pub BOH: bool,
}
#[derive(VolatileFieldAccess)]
#[repr(C)]
pub struct PortRegisters {
CLB: u32,
CLBU: u32,
FB: u32,
FBU: u32,
pub IS: PxI,
pub IE: PxI,
pub CMD: PxCMD,
_res0: u32,
#[access(ReadOnly)]
pub TFD: PxTFD,
#[access(ReadOnly)]
pub SIG: PxSIG,
pub SSTS: PxSSTS,
pub SCTL: u32,
pub SERR: PxSERR,
pub SACT: u32,
pub CI: u32,
pub SNTF: u32,
pub FBS: u32,
pub DEVSLP: u8,
_reserved1: [u8; 0x28],
pub vs: u128,
}
const _: () = assert!(size_of::<PortRegisters>() == 0x80);
#[bitfield(u32, order = Msb)]
pub struct PxI {
pub CPD: bool,
pub TFE: bool,
pub HBF: bool,
pub HBD: bool,
pub IF: bool,
pub INF: bool,
__: bool,
pub OF: bool,
pub IPM: bool,
pub PRC: bool,
#[bits(14)]
__: u16,
pub DMP: bool,
pub PC: bool,
pub DP: bool,
pub UF: bool,
pub SDB: bool,
pub DS: bool,
pub PS: bool,
pub DHR: bool,
}
impl PxI {
pub fn default_enable() -> Self {
Self::new()
.with_TFE(true)
.with_HBF(true)
.with_HBD(true)
.with_IF(true)
.with_IPM(true)
.with_PRC(true)
.with_PC(true)
.with_UF(true)
.with_SDB(true)
.with_DS(true)
.with_PS(true)
.with_DHR(true)
}
}
#[bitfield(u32, order = Msb)]
pub struct PxCMD {
#[bits(4)]
pub ICC: ICC,
pub ASP: bool,
pub ALPE: bool,
pub DLAE: bool,
pub ATAPI: bool,
pub APSTE: bool,
#[bits(access = RO)]
pub FBSCP: bool,
#[bits(access = RO)]
pub ESP: bool,
#[bits(access = RO)]
pub CPD: bool,
#[bits(access = RO)]
pub MPSP: bool,
#[bits(access = RO)]
pub HPCP: bool,
pub PMA: bool,
#[bits(access = RO)]
pub CPS: bool,
pub CR: bool,
pub FR: bool,
#[bits(access = RO)]
pub MPSS: bool,
#[bits(5, access = RO)]
pub CCS: u8,
#[bits(3)]
pub __: u8,
pub FRE: bool,
pub CLO: bool,
pub POD: bool,
pub SUD: bool,
pub ST: bool,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ICC {
#[default]
Idle = 0x0,
Active = 0x1,
Partial = 0x2,
Slumber = 0x6,
DevSleep = 0x8,
Reserved = 0xf,
}
impl ICC {
pub const fn into_bits(self) -> u8 {
self as _
}
pub const fn from_bits(bits: u8) -> Self {
match bits {
0x0 => Self::Idle,
0x1 => Self::Active,
0x2 => Self::Partial,
0x6 => Self::Slumber,
0x8 => Self::DevSleep,
0xf => Self::Reserved,
_ => Self::Reserved,
}
}
}
#[bitfield(u32, order = Msb)]
pub struct PxTFD {
__: u16,
pub ERR: u8,
pub STS_BSY: bool,
#[bits(3)]
__: u8,
pub STS_DRQ: bool,
#[bits(2)]
__: u8,
pub STS_ERR: bool,
}
#[bitfield(u32, order = Msb)]
pub struct PxSIG {
pub high: u8,
pub mid: u8,
pub low: u8,
pub count: u8,
}
#[bitfield(u32, order = Msb)]
pub struct PxSSTS {
#[bits(20)]
__: u32,
#[bits(4)]
pub IPM: u8,
#[bits(4)]
pub SPD: u8,
#[bits(4)]
pub DET: u8,
}
#[bitfield(u32, order = Msb)]
pub struct PxSERR {
#[bits(5)]
__: u8,
pub DIAG_X: bool,
pub DIAG_F: bool,
pub DIAG_T: bool,
pub DIAG_S: bool,
pub DIAG_H: bool,
pub DIAG_C: bool,
pub DIAG_D: bool,
pub DIAG_B: bool,
pub DIAG_W: bool,
pub DIAG_I: bool,
pub DIAG_N: bool,
#[bits(4)]
__: u8,
pub ERR_E: bool,
pub ERR_P: bool,
pub ERR_C: bool,
pub ERR_T: bool,
#[bits(6)]
__: u8,
pub ERR_M: bool,
pub ERR_I: bool,
}