From b2f3ab1c7f9dee3db3430521cb48364b59bffeef Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Thu, 13 Feb 2025 16:26:07 +0200 Subject: [PATCH] pci: better capability handling (todo: split/merge into master separately) --- .../driver/bus/pci/src/capability/express.rs | 177 ++++++++++ kernel/driver/bus/pci/src/capability/mod.rs | 14 + .../src/{capability.rs => capability/msi.rs} | 313 +----------------- kernel/driver/bus/pci/src/capability/power.rs | 82 +++++ .../driver/bus/pci/src/capability/virtio.rs | 175 ++++++++++ kernel/driver/bus/pci/src/device.rs | 19 +- kernel/driver/net/rtl81xx/src/lib.rs | 11 +- .../driver/sound/intel-hda/src/codec/mod.rs | 29 +- .../sound/intel-hda/src/codec/parameter.rs | 1 + kernel/driver/sound/intel-hda/src/lib.rs | 11 +- kernel/driver/sound/intel-hda/src/regs.rs | 40 ++- kernel/driver/sound/intel-hda/src/ring.rs | 4 +- kernel/driver/usb/xhci/src/lib.rs | 2 +- .../driver/virtio/core/src/transport/pci.rs | 2 +- 14 files changed, 551 insertions(+), 329 deletions(-) create mode 100644 kernel/driver/bus/pci/src/capability/express.rs create mode 100644 kernel/driver/bus/pci/src/capability/mod.rs rename kernel/driver/bus/pci/src/{capability.rs => capability/msi.rs} (54%) create mode 100644 kernel/driver/bus/pci/src/capability/power.rs create mode 100644 kernel/driver/bus/pci/src/capability/virtio.rs diff --git a/kernel/driver/bus/pci/src/capability/express.rs b/kernel/driver/bus/pci/src/capability/express.rs new file mode 100644 index 00000000..ee6103e4 --- /dev/null +++ b/kernel/driver/bus/pci/src/capability/express.rs @@ -0,0 +1,177 @@ +use core::time::Duration; + +use libk::{error::Error, task::runtime::psleep}; +use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy}; + +use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace}; + +// bitflags! { +// pub struct PcieLinkControl: u16 { +// const ASPM_DISABLE = 0 << 0; +// // Active state power management control +// const ASPM_MASK = 0x3 << 0; +// // Enable clock power management +// const ECPM = 1 << 8; +// } +// } + +register_bitfields! { + u32, + pub DeviceCapabilities [ + MAX_PAYLOAD_SIZE OFFSET(0) NUMBITS(3) [], + PHANTOM_FUNCTIONS OFFSET(3) NUMBITS(2) [], + L0S_ACCEPTABLE_LATENCY OFFSET(6) NUMBITS(3) [], + L1_ACCEPTABLE_LATENCY OFFSET(9) NUMBITS(3) [], + ROLE_ERROR_REPORTING OFFSET(15) NUMBITS(1) [], + CAPTURED_SLOT_POWER_LIMIT OFFSET(18) NUMBITS(8) [], + CAPTURED_SLOT_POWER_SCALE OFFSET(26) NUMBITS(3) [], + FUNCTION_LEVEL_RESET OFFSET(28) NUMBITS(1) [], + ], + pub LinkCapabilities [ + MAX_LINK_SPEED OFFSET(0) NUMBITS(4) [], + MAX_LINK_WIDTH OFFSET(4) NUMBITS(6) [], + ASPM OFFSET(10) NUMBITS(2) [], + L0S_EXIT_LATENCY OFFSET(12) NUMBITS(3) [], + L1_EXIT_LATENCY OFFSET(15) NUMBITS(3) [], + CLOCK_PM OFFSET(18) NUMBITS(1) [], + SURPRISE_DOWN_ERROR OFFSET(19) NUMBITS(1) [], + DATA_LINK_ACTIVE_REPORTING OFFSET(20) NUMBITS(1) [], + LINK_BANDWIDTH_NOTIFICATION OFFSET(21) NUMBITS(1) [], + ASPM_COMPLIANCE OFFSET(22) NUMBITS(1) [], + PORT_NUMBER OFFSET(24) NUMBITS(8) [], + ], +} + +register_bitfields! { + u16, + pub DeviceControl [ + CORRECTABLE_ERROR_REPORTING OFFSET(0) NUMBITS(1) [], + NONFATAL_ERROR_REPORTING OFFSET(1) NUMBITS(1) [], + FATAL_ERROR_REPORTING OFFSET(2) NUMBITS(1) [], + UNSUPPORTED_REQ_REPORTING OFFSET(3) NUMBITS(1) [], + RELAXED_ORDERING OFFSET(4) NUMBITS(1) [], + MAX_PAYLOAD_SIZE OFFSET(5) NUMBITS(3) [], + EXTENDED_TAG_FIELD OFFSET(8) NUMBITS(1) [], + PHANTOM_FUNCTIONS OFFSET(9) NUMBITS(1) [], + AUX_POWER_PM_ENABLE OFFSET(10) NUMBITS(1) [], + NO_SNOOP OFFSET(11) NUMBITS(1) [], + MAX_READ_REQ_SIZE OFFSET(12) NUMBITS(3) [], + FUNCTION_LEVEL_RESET OFFSET(15) NUMBITS(1) [], + ], + pub LinkControl [ + ASPM OFFSET(0) NUMBITS(2) [ + Disabled = 0, + L0sEntryEnabled = 1, + L1EntryEnabled = 2, + L0sL1EntryEnabled = 3, + ], + READ_COMPLETION_BOUNDARY OFFSET(3) NUMBITS(1) [], + LINK_DISABLE OFFSET(4) NUMBITS(1) [], + RETRAIN_LINK OFFSET(5) NUMBITS(1) [], + COMMON_CLOCK_CONFIG OFFSET(6) NUMBITS(1) [], + EXTENDED_SYNCH OFFSET(7) NUMBITS(1) [], + CLOCK_PM OFFSET(8) NUMBITS(1) [], + HARDWARE_ABW_DISABLE OFFSET(9) NUMBITS(1) [], + LINK_BW_MANAGEMENT_IRQ OFFSET(10) NUMBITS(1) [], + LINK_ABW_IRQ OFFSET(11) NUMBITS(1) [], + ], +} + +/// PCIe capability +pub struct PciExpressCapability; + +/// PCI Express capability data structure +pub struct PciExpressData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +impl PciCapability for PciExpressCapability { + const ID: PciCapabilityId = PciCapabilityId::PciExpress; + type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PciExpressData<'a, S>; + + fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( + space: &'s S, + offset: usize, + _len: usize, + ) -> Self::CapabilityData<'s, S> { + PciExpressData { space, offset } + } +} + +macro_rules! reg_read { + ($self:expr, $offset:literal, u32) => { + $self.space.read_u32($self.offset + $offset) + }; + ($self:expr, $offset:literal, u16) => { + $self.space.read_u16($self.offset + $offset) + }; +} +macro_rules! reg_write { + ($self:expr, $offset:literal, u32, $value:expr) => { + $self.space.write_u32($self.offset + $offset, $value) + }; + ($self:expr, $offset:literal, u16, $value:expr) => { + $self.space.write_u16($self.offset + $offset, $value) + }; +} +macro_rules! make_register { + ( + $reg:ident : $ty:ident @ $offset:literal { + $get:ident + $(, $set:ident, $modify:ident)? + $(,)? + } + ) => { + pub fn $get(&self) -> LocalRegisterCopy<$ty, $reg::Register> { + LocalRegisterCopy::new(reg_read!(self, $offset, $ty)) + } + + $( + pub fn $set(&mut self, value: $ty) { + reg_write!(self, $offset, $ty, value) + } + + pub fn $modify(&mut self, field: FieldValue<$ty, $reg::Register>) { + let mut value = self.$get(); + value.modify(field); + self.$set(value.get()); + } + )? + }; +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> PciExpressData<'s, S> { + make_register!(DeviceCapabilities : u32 @ 0x04 { device_capabilities }); + make_register!(DeviceControl : u16 @ 0x08 { + device_control, + set_device_control, + modify_device_control, + }); + make_register!(LinkCapabilities : u32 @ 0x0C { link_capabilities }); + make_register!(LinkControl : u16 @ 0x10 { + link_control, + set_link_control, + modify_link_control, + }); + + pub fn function_level_reset(&mut self) -> Result<(), Error> { + if self + .device_capabilities() + .matches_all(DeviceCapabilities::FUNCTION_LEVEL_RESET::SET) + { + self.modify_device_control(DeviceControl::FUNCTION_LEVEL_RESET::SET); + psleep(Duration::from_millis(10)); + Ok(()) + } else { + Err(Error::NotImplemented) + } + } + + pub fn hot_link_reset(&mut self) { + self.modify_link_control(LinkControl::LINK_DISABLE::SET); + psleep(Duration::from_millis(10)); + self.modify_link_control(LinkControl::LINK_DISABLE::CLEAR); + psleep(Duration::from_millis(100)); + } +} diff --git a/kernel/driver/bus/pci/src/capability/mod.rs b/kernel/driver/bus/pci/src/capability/mod.rs new file mode 100644 index 00000000..61a25ae9 --- /dev/null +++ b/kernel/driver/bus/pci/src/capability/mod.rs @@ -0,0 +1,14 @@ +//! PCI capability structures and queries + +pub mod express; +pub mod msi; +pub mod power; +pub mod virtio; + +pub use express::PciExpressCapability; +pub use msi::{MsiCapability, MsiXCapability}; +pub use power::PowerManagementCapability; +pub use virtio::{ + VirtioCapability, VirtioCommonConfigCapability, VirtioDeviceConfigCapability, + VirtioInterruptStatusCapability, VirtioNotifyConfigCapability, +}; diff --git a/kernel/driver/bus/pci/src/capability.rs b/kernel/driver/bus/pci/src/capability/msi.rs similarity index 54% rename from kernel/driver/bus/pci/src/capability.rs rename to kernel/driver/bus/pci/src/capability/msi.rs index 8e53e948..94541d93 100644 --- a/kernel/driver/bus/pci/src/capability.rs +++ b/kernel/driver/bus/pci/src/capability/msi.rs @@ -1,91 +1,21 @@ -//! PCI capability structures and queries - use alloc::{sync::Arc, vec, vec::Vec}; -use bitflags::bitflags; + use device_api::interrupt::{ InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo, }; +use libk::error::Error; use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut}; use tock_registers::{ interfaces::{Readable, Writeable}, registers::{ReadWrite, WriteOnly}, }; -use yggdrasil_abi::error::Error; - -use crate::PciBaseAddress; - -use super::{PciCapability, PciCapabilityId, PciConfigurationSpace}; - -bitflags! { - pub struct PcieLinkControl: u16 { - const ASPM_DISABLE = 0 << 0; - // Active state power management control - const ASPM_MASK = 0x3 << 0; - // Enable clock power management - const ECPM = 1 << 8; - } -} #[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))] use core::mem::offset_of; #[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))] use kernel_arch_x86::intrinsics; -pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized { - fn from_space_offset(space: &'s S, offset: usize) -> Self; - - fn space(&self) -> &'s S; - fn offset(&self) -> usize; - - fn bar_index(&self) -> Option { - let value = self.space().read_u8(self.offset() + 4); - (value <= 0x5).then_some(value as _) - } - - fn bar_offset(&self) -> usize { - let value = self.space().read_u32(self.offset() + 8); - value as _ - } - - fn length(&self) -> usize { - let value = self.space().read_u32(self.offset() + 12); - value as _ - } -} - -pub trait VirtioCapability { - const CFG_TYPE: u8; - const MIN_LEN: usize = 0; - type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>; -} - -/// Power management capability entry -pub struct PowerManagementCapability; -/// MSI-X capability query -pub struct MsiXCapability; -/// MSI capability query -pub struct MsiCapability; -/// PCIe capability -pub struct PciExpressCapability; - -// VirtIO-over-PCI capabilities -/// VirtIO PCI configuration access -pub struct VirtioDeviceConfigCapability; -/// VirtIO common configuration -pub struct VirtioCommonConfigCapability; -/// VirtIO notify configuration -pub struct VirtioNotifyConfigCapability; -/// VirtIO interrupt status -pub struct VirtioInterruptStatusCapability; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DevicePowerState { - D0, - D1, - D2, - D3Cold, - D3Hot, -} +use crate::{PciBaseAddress, PciCapability, PciCapabilityId, PciConfigurationSpace}; /// Represents an entry in MSI-X vector table #[repr(C)] @@ -109,11 +39,10 @@ pub struct MsiXVectorTable<'a> { len: usize, } -/// PCI Power Management capability data structure -pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} +/// MSI-X capability query +pub struct MsiXCapability; +/// MSI capability query +pub struct MsiCapability; /// MSI-X capability data structure pub struct MsiXData<'s, S: PciConfigurationSpace + ?Sized + 's> { @@ -127,63 +56,6 @@ pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> { offset: usize, } -/// PCI Express capability data structure -pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} - -pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} - -pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} - -pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} - -pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> { - space: &'s S, - offset: usize, -} - -impl PciCapability for T { - const ID: PciCapabilityId = PciCapabilityId::VendorSpecific; - type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>; - - fn check(space: &S, offset: usize, len: usize) -> bool { - let cfg_type = space.read_u8(offset + 3); - cfg_type == T::CFG_TYPE && len >= T::MIN_LEN - } - - fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( - space: &'s S, - offset: usize, - _len: usize, - ) -> Self::CapabilityData<'s, S> { - T::Output::from_space_offset(space, offset) - } -} - -impl PciCapability for PowerManagementCapability { - const ID: PciCapabilityId = PciCapabilityId::PowerManagement; - type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>; - - fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( - space: &'s S, - offset: usize, - _len: usize, - ) -> Self::CapabilityData<'s, S> { - PowerManagementData { space, offset } - } -} - impl PciCapability for MsiXCapability { const ID: PciCapabilityId = PciCapabilityId::MsiX; type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>; @@ -210,167 +82,6 @@ impl PciCapability for MsiCapability { } } -impl PciCapability for PciExpressCapability { - const ID: PciCapabilityId = PciCapabilityId::PciExpress; - type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>; - - fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( - space: &'s S, - offset: usize, - _len: usize, - ) -> Self::CapabilityData<'s, S> { - PcieData { space, offset } - } -} - -impl VirtioCapability for VirtioDeviceConfigCapability { - const CFG_TYPE: u8 = 0x04; - type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>; -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> - for VirtioDeviceConfigData<'s, S> -{ - fn from_space_offset(space: &'s S, offset: usize) -> Self { - Self { space, offset } - } - - fn space(&self) -> &'s S { - self.space - } - - fn offset(&self) -> usize { - self.offset - } -} - -impl VirtioCapability for VirtioCommonConfigCapability { - const CFG_TYPE: u8 = 0x01; - type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>; -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> - for VirtioCommonConfigData<'s, S> -{ - fn from_space_offset(space: &'s S, offset: usize) -> Self { - Self { space, offset } - } - - fn space(&self) -> &'s S { - self.space - } - - fn offset(&self) -> usize { - self.offset - } -} - -impl VirtioCapability for VirtioNotifyConfigCapability { - const CFG_TYPE: u8 = 0x02; - const MIN_LEN: usize = 0x14; - type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>; -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> { - pub fn offset_multiplier(&self) -> usize { - self.space.read_u32(self.offset + 16) as usize - } -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> - for VirtioNotifyConfigData<'s, S> -{ - fn from_space_offset(space: &'s S, offset: usize) -> Self { - Self { space, offset } - } - - fn space(&self) -> &'s S { - self.space - } - - fn offset(&self) -> usize { - self.offset - } -} - -impl VirtioCapability for VirtioInterruptStatusCapability { - const CFG_TYPE: u8 = 0x03; - const MIN_LEN: usize = 1; - type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>; -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> { - pub fn read_status(&self) -> (bool, bool) { - todo!() - } -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> - for VirtioInterruptStatusData<'s, S> -{ - fn from_space_offset(space: &'s S, offset: usize) -> Self { - Self { space, offset } - } - - fn space(&self) -> &'s S { - self.space - } - - fn offset(&self) -> usize { - self.offset - } -} - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> { - pub fn set_device_power_state(&self, state: DevicePowerState) { - let pmcsr = self.space.read_u16(self.offset + 4) & !0x3; - let current = self.get_device_power_state(); - - if state == current { - return; - } - - log::info!("Set device power state: {state:?}"); - - match state { - DevicePowerState::D0 => { - // power = 0b00 | PME_EN - self.space.write_u16(self.offset + 4, pmcsr); - } - _ => { - log::warn!("TODO: {state:?} power state"); - } - } - } - - pub fn set_pme_en(&self, state: bool) { - let pmcsr = self.space.read_u16(self.offset + 4); - let new = if state { - pmcsr | (1 << 8) - } else { - pmcsr & !(1 << 8) - }; - if pmcsr == new { - return; - } - - log::info!("Set PMCSR.PME_En = {state}"); - - self.space.write_u16(self.offset + 4, new); - } - - pub fn get_device_power_state(&self) -> DevicePowerState { - let pmcsr = self.space.read_u16(self.offset + 4); - match pmcsr & 0x3 { - 0b00 => DevicePowerState::D0, - 0b01 => DevicePowerState::D1, - 0b10 => DevicePowerState::D2, - 0b11 => DevicePowerState::D3Hot, - _ => unreachable!(), - } - } -} - impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> { // TODO use pending bits as well /// Maps and returns the vector table associated with the device's MSI-X capability @@ -601,13 +312,3 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> { Ok(info) } } - -impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> { - pub fn link_control(&self) -> PcieLinkControl { - PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10)) - } - - pub fn set_link_control(&mut self, value: PcieLinkControl) { - self.space.write_u16(self.offset + 0x10, value.bits()); - } -} diff --git a/kernel/driver/bus/pci/src/capability/power.rs b/kernel/driver/bus/pci/src/capability/power.rs new file mode 100644 index 00000000..1a6fd5f4 --- /dev/null +++ b/kernel/driver/bus/pci/src/capability/power.rs @@ -0,0 +1,82 @@ +use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DevicePowerState { + D0, + D1, + D2, + D3Cold, + D3Hot, +} + +/// Power management capability entry +pub struct PowerManagementCapability; + +/// PCI Power Management capability data structure +pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +impl PciCapability for PowerManagementCapability { + const ID: PciCapabilityId = PciCapabilityId::PowerManagement; + type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>; + + fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( + space: &'s S, + offset: usize, + _len: usize, + ) -> Self::CapabilityData<'s, S> { + PowerManagementData { space, offset } + } +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> { + pub fn set_device_power_state(&self, state: DevicePowerState) { + let pmcsr = self.space.read_u16(self.offset + 4) & !0x3; + let current = self.get_device_power_state(); + + if state == current { + return; + } + + log::info!("Set device power state: {state:?}"); + + match state { + DevicePowerState::D0 => { + // power = 0b00 | PME_EN + self.space.write_u16(self.offset + 4, pmcsr); + } + _ => { + log::warn!("TODO: {state:?} power state"); + } + } + } + + pub fn set_pme_en(&self, state: bool) { + let pmcsr = self.space.read_u16(self.offset + 4); + let new = if state { + pmcsr | (1 << 8) + } else { + pmcsr & !(1 << 8) + }; + if pmcsr == new { + return; + } + + log::info!("Set PMCSR.PME_En = {state}"); + + self.space.write_u16(self.offset + 4, new); + } + + pub fn get_device_power_state(&self) -> DevicePowerState { + let pmcsr = self.space.read_u16(self.offset + 4); + match pmcsr & 0x3 { + 0b00 => DevicePowerState::D0, + 0b01 => DevicePowerState::D1, + 0b10 => DevicePowerState::D2, + 0b11 => DevicePowerState::D3Hot, + _ => unreachable!(), + } + } +} diff --git a/kernel/driver/bus/pci/src/capability/virtio.rs b/kernel/driver/bus/pci/src/capability/virtio.rs new file mode 100644 index 00000000..cf852e11 --- /dev/null +++ b/kernel/driver/bus/pci/src/capability/virtio.rs @@ -0,0 +1,175 @@ +use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace}; + +pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized { + fn from_space_offset(space: &'s S, offset: usize) -> Self; + + fn space(&self) -> &'s S; + fn offset(&self) -> usize; + + fn bar_index(&self) -> Option { + let value = self.space().read_u8(self.offset() + 4); + (value <= 0x5).then_some(value as _) + } + + fn bar_offset(&self) -> usize { + let value = self.space().read_u32(self.offset() + 8); + value as _ + } + + fn length(&self) -> usize { + let value = self.space().read_u32(self.offset() + 12); + value as _ + } +} + +pub trait VirtioCapability { + const CFG_TYPE: u8; + const MIN_LEN: usize = 0; + type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>; +} + +// VirtIO-over-PCI capabilities +/// VirtIO PCI configuration access +pub struct VirtioDeviceConfigCapability; +/// VirtIO common configuration +pub struct VirtioCommonConfigCapability; +/// VirtIO notify configuration +pub struct VirtioNotifyConfigCapability; +/// VirtIO interrupt status +pub struct VirtioInterruptStatusCapability; + +pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + +impl PciCapability for T { + const ID: PciCapabilityId = PciCapabilityId::VendorSpecific; + type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>; + + fn check(space: &S, offset: usize, len: usize) -> bool { + let cfg_type = space.read_u8(offset + 3); + cfg_type == T::CFG_TYPE && len >= T::MIN_LEN + } + + fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( + space: &'s S, + offset: usize, + _len: usize, + ) -> Self::CapabilityData<'s, S> { + T::Output::from_space_offset(space, offset) + } +} + +impl VirtioCapability for VirtioDeviceConfigCapability { + const CFG_TYPE: u8 = 0x04; + type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>; +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> + for VirtioDeviceConfigData<'s, S> +{ + fn from_space_offset(space: &'s S, offset: usize) -> Self { + Self { space, offset } + } + + fn space(&self) -> &'s S { + self.space + } + + fn offset(&self) -> usize { + self.offset + } +} + +impl VirtioCapability for VirtioCommonConfigCapability { + const CFG_TYPE: u8 = 0x01; + type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>; +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> + for VirtioCommonConfigData<'s, S> +{ + fn from_space_offset(space: &'s S, offset: usize) -> Self { + Self { space, offset } + } + + fn space(&self) -> &'s S { + self.space + } + + fn offset(&self) -> usize { + self.offset + } +} + +impl VirtioCapability for VirtioNotifyConfigCapability { + const CFG_TYPE: u8 = 0x02; + const MIN_LEN: usize = 0x14; + type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>; +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> { + pub fn offset_multiplier(&self) -> usize { + self.space.read_u32(self.offset + 16) as usize + } +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> + for VirtioNotifyConfigData<'s, S> +{ + fn from_space_offset(space: &'s S, offset: usize) -> Self { + Self { space, offset } + } + + fn space(&self) -> &'s S { + self.space + } + + fn offset(&self) -> usize { + self.offset + } +} + +impl VirtioCapability for VirtioInterruptStatusCapability { + const CFG_TYPE: u8 = 0x03; + const MIN_LEN: usize = 1; + type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>; +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> { + pub fn read_status(&self) -> (bool, bool) { + todo!() + } +} + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S> + for VirtioInterruptStatusData<'s, S> +{ + fn from_space_offset(space: &'s S, offset: usize) -> Self { + Self { space, offset } + } + + fn space(&self) -> &'s S { + self.space + } + + fn offset(&self) -> usize { + self.offset + } +} diff --git a/kernel/driver/bus/pci/src/device.rs b/kernel/driver/bus/pci/src/device.rs index eb124996..62acf8b9 100644 --- a/kernel/driver/bus/pci/src/device.rs +++ b/kernel/driver/bus/pci/src/device.rs @@ -13,7 +13,7 @@ use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit}; use yggdrasil_abi::error::Error; use crate::{ - capability::{MsiCapability, MsiXCapability, MsiXVectorTable}, + capability::{msi::MsiXVectorTable, MsiCapability, MsiXCapability, PciExpressCapability}, driver::PciDriver, PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo, }; @@ -142,6 +142,23 @@ impl PciDeviceInfo { } } + pub fn function_level_reset(&self) -> Result<(), Error> { + if let Some(mut pcie) = self.config_space.capability::() { + pcie.function_level_reset() + } else { + Err(Error::NotImplemented) + } + } + + pub fn hot_link_reset(&self) -> Result<(), Error> { + if let Some(mut pcie) = self.config_space.capability::() { + pcie.hot_link_reset(); + Ok(()) + } else { + Err(Error::NotImplemented) + } + } + pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> { self.interrupt_config .try_init_with(|| { diff --git a/kernel/driver/net/rtl81xx/src/lib.rs b/kernel/driver/net/rtl81xx/src/lib.rs index 57d5f038..1e0bc74c 100644 --- a/kernel/driver/net/rtl81xx/src/lib.rs +++ b/kernel/driver/net/rtl81xx/src/lib.rs @@ -7,7 +7,8 @@ use rtl8139::Rtl8139; use rtl8168::Rtl8168; use ygg_driver_pci::{ capability::{ - DevicePowerState, PciExpressCapability, PcieLinkControl, PowerManagementCapability, + express::LinkControl, power::DevicePowerState, PciExpressCapability, + PowerManagementCapability, }, device::{PciDeviceInfo, PreferredInterruptMode}, macros::pci_driver, @@ -30,9 +31,7 @@ pci_driver! { info.init_interrupts(PreferredInterruptMode::Msi(false))?; if let Some(mut pcie) = info.config_space.capability::() { - let mut lcr = pcie.link_control(); - lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM); - pcie.set_link_control(lcr); + pcie.modify_link_control(LinkControl::ASPM::CLEAR + LinkControl::CLOCK_PM::CLEAR); } // Enable MMIO + interrupts + bus mastering @@ -76,9 +75,7 @@ pci_driver! { } if let Some(mut pcie) = info.config_space.capability::() { - let mut lcr = pcie.link_control(); - lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM); - pcie.set_link_control(lcr); + pcie.modify_link_control(LinkControl::ASPM::CLEAR + LinkControl::CLOCK_PM::CLEAR); } let device = Rtl8168::new(dma.clone(), base, info.clone())?; diff --git a/kernel/driver/sound/intel-hda/src/codec/mod.rs b/kernel/driver/sound/intel-hda/src/codec/mod.rs index c93375c7..ec1286e8 100644 --- a/kernel/driver/sound/intel-hda/src/codec/mod.rs +++ b/kernel/driver/sound/intel-hda/src/codec/mod.rs @@ -37,10 +37,13 @@ pub struct PinComplexWidget { default_config: PinDefaultConfig, } #[derive(Debug)] +pub struct MixerWidget {} +#[derive(Debug)] pub enum AudioWidget { AudioOutput(AudioOutputWidget), AudioInput(AudioInputWidget), PinComplex(PinComplexWidget), + Mixer(MixerWidget), Other, } @@ -198,9 +201,13 @@ impl Node { pub async fn set_unsolicited_response( &self, hda: &HdAudio, - enabled: bool, + tag: Option, ) -> Result<(), Error> { - self.perform_command(hda, Verb::SetUnsolicitedResponse, enabled as u32) + let value = match tag { + Some(tag) => (tag as u32) | (1 << 7), + None => 0, + }; + self.perform_command(hda, Verb::SetUnsolicitedResponse, value) .await?; Ok(()) } @@ -290,6 +297,9 @@ impl AudioWidgetNode { let default_config = node.get_pin_default_config(hda).await?; AudioWidget::PinComplex(PinComplexWidget { default_config }) } + Some(AudioWidgetCapabilities::TYPE::Value::AudioMixer) => { + AudioWidget::Mixer(MixerWidget {}) + } v => { log::warn!("hda: unknown audio widget: {v:?}"); AudioWidget::Other @@ -469,15 +479,24 @@ impl Codec { AudioWidget::PinComplex(_) => { log::info!(" Configure pin @ {wid:?}"); wid.set_power_state(&*self.hda, true).await?; - wid.set_unsolicited_response(&*self.hda, false).await?; - wid.modify_pin_control(&*self.hda, PinControl::OutEnable::SET) + // TODO only set for jacks + wid.set_unsolicited_response(&*self.hda, Some(wid.nid)) .await?; + wid.modify_pin_control( + &*self.hda, + PinControl::OutEnable::SET + PinControl::HPhnEnable::SET, + ) + .await?; } AudioWidget::AudioOutput(out) => { log::info!(" Configure audio output @ {wid:?}"); audio_widget = Some(widget.node); wid.set_power_state(&*self.hda, true).await?; - wid.set_unsolicited_response(&*self.hda, false).await?; + wid.set_unsolicited_response(&*self.hda, None).await?; + } + AudioWidget::Mixer(_) => { + log::info!(" Configure mixer @ {wid:?}"); + wid.set_power_state(&*self.hda, true).await?; } _ => (), } diff --git a/kernel/driver/sound/intel-hda/src/codec/parameter.rs b/kernel/driver/sound/intel-hda/src/codec/parameter.rs index b7ab2d8c..2f5f9ccc 100644 --- a/kernel/driver/sound/intel-hda/src/codec/parameter.rs +++ b/kernel/driver/sound/intel-hda/src/codec/parameter.rs @@ -51,6 +51,7 @@ register_bitfields! { Power = 0x5, VolumeKnob = 0x6, BeepGenerator = 0x7, + VendorDefined = 0xF, ], ], } diff --git a/kernel/driver/sound/intel-hda/src/lib.rs b/kernel/driver/sound/intel-hda/src/lib.rs index c252a460..447d8489 100644 --- a/kernel/driver/sound/intel-hda/src/lib.rs +++ b/kernel/driver/sound/intel-hda/src/lib.rs @@ -18,7 +18,7 @@ use futures_util::task::AtomicWaker; use libk::{ dma::DmaBuffer, error::Error, - task::runtime::{self, with_timeout}, + task::runtime::{self, psleep, with_timeout}, time::monotonic_time, }; use libk_mm::device::DeviceMemoryIo; @@ -33,6 +33,7 @@ use sink::HdAudioSink; use stream::{BufferDescriptorList, OutputStream}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use ygg_driver_pci::{ + capability::{power::DevicePowerState, PowerManagementCapability}, device::{PciDeviceInfo, PreferredInterruptMode}, macros::pci_driver, PciBaseAddress, PciConfigurationSpace, @@ -163,6 +164,7 @@ impl HdAudio { match with_timeout(self.softirq_event.wait(), Duration::from_millis(100)).await { // IRQs happened Ok(events) => { + log::info!("softirq()"); for sink_index in self.input_stream_count..self.input_stream_count + self.output_stream_count { @@ -233,6 +235,8 @@ impl InterruptHandler for HdAudio { impl Device for HdAudio { unsafe fn init(self: Arc, _cx: DeviceInitContext) -> Result<(), Error> { + // FLR if capable + self.pci.function_level_reset().ok(); self.pci .map_interrupt(InterruptAffinity::Any, self.clone())?; @@ -250,7 +254,10 @@ impl Device for HdAudio { self.bidi_stream_count ); - regs.reset(Duration::from_millis(100))?; + regs.reset( + Duration::from_millis(100), + self.input_stream_count + self.output_stream_count + self.bidi_stream_count, + )?; regs.disable_interrupts(); regs.disable_control(); diff --git a/kernel/driver/sound/intel-hda/src/regs.rs b/kernel/driver/sound/intel-hda/src/regs.rs index 8b6b69f8..d90a6b0b 100644 --- a/kernel/driver/sound/intel-hda/src/regs.rs +++ b/kernel/driver/sound/intel-hda/src/regs.rs @@ -94,6 +94,10 @@ register_bitfields! { /// 64-bit addresses supported OK64 OFFSET(0) NUMBITS(1) [], ], + pub GSTS [ + /// Flush status bit + FSTS OFFSET(1) NUMBITS(1) [], + ], pub CORBRP [ /// CORB read pointer reset CORBRPRST OFFSET(15) NUMBITS(1) [], @@ -159,7 +163,7 @@ register_structs! { (0x0008 => pub GCTL: ReadWrite), (0x000C => pub WAKEEN: ReadWrite), (0x000E => pub WAKESTS: ReadWrite), - (0x0010 => pub GSTS: ReadOnly), + (0x0010 => pub GSTS: ReadWrite), (0x0012 => _0), (0x0018 => pub OUTSTRMPAY: ReadOnly), (0x001A => pub INSTRMPAY: ReadOnly), @@ -235,17 +239,39 @@ register_structs! { } impl Regs { - pub fn reset(&self, timeout: Duration) -> Result<(), Error> { + pub fn reset(&self, timeout: Duration, stream_count: usize) -> Result<(), Error> { + // Go through all the streams, disable their DMA + for i in 0..stream_count { + let stream = &self.STREAMS[i]; + stream + .SDxCTL0 + .write(SDxCTL0::RUN::CLEAR + SDxCTL0::SRST::SET); + } + + self.CORBCTL.write(CORBCTL::CORBRUN::CLEAR); + self.RIRBCTL.write(RIRBCTL::RIRBDMAEN::CLEAR); + + // Flush the FIFO + self.GCTL.modify(GCTL::FCNTRL::SET); + psleep(Duration::from_millis(10)); + pwait(timeout, Duration::from_millis(10), || { + self.GSTS.matches_all(GSTS::FSTS::SET) + })?; + self.GCTL.modify(GCTL::FCNTRL::CLEAR); + self.GSTS.write(GSTS::FSTS::SET); + // TODO does FIFO need to be flushed here as well? // Begin controller reset self.GCTL.write(GCTL::CRST::CLEAR); - psleep(Duration::from_millis(100)); + psleep(Duration::from_millis(10)); // End controller reset self.GCTL.write(GCTL::CRST::SET); - psleep(Duration::from_millis(100)); + psleep(Duration::from_millis(10)); pwait(timeout, Duration::from_millis(10), || { self.GCTL.matches_all(GCTL::CRST::SET) - }) + })?; + + Ok(()) } pub fn disable_interrupts(&self) { @@ -253,6 +279,8 @@ impl Regs { } pub fn enable_interrupts(&self) { + // Also accept unsolicited responses from codecs + self.GCTL.modify(GCTL::UNSOL::SET); self.INTCTL.write(INTCTL::GIE::SET + INTCTL::CIE::SET); } @@ -325,6 +353,8 @@ impl Regs { // Reset write/read pointers self.CORBWP.set(0); self.CORBRP.write(CORBRP::CORBRPRST::SET); + self.RIRBSTS + .write(RIRBSTS::RIRBOIS::SET + RIRBSTS::RINTFL::SET); self.RINTCNT.set(255); // Ensure CORBRP is reset properly diff --git a/kernel/driver/sound/intel-hda/src/ring.rs b/kernel/driver/sound/intel-hda/src/ring.rs index 6846b0ac..5aa4183b 100644 --- a/kernel/driver/sound/intel-hda/src/ring.rs +++ b/kernel/driver/sound/intel-hda/src/ring.rs @@ -262,7 +262,9 @@ impl CommandRing { pub(super) fn process_completions(&self, corb_tail: u8, rirb_head: u8) -> usize { let mut inner = self.inner.lock(); inner.process_responses( - |_, _| (), + |codec, message| { + log::info!("Unsolicited message: {message:#x} from codec {codec:#x}"); + }, |codec| self.codec_notify[codec as usize].wake(), corb_tail as u32, rirb_head as u32, diff --git a/kernel/driver/usb/xhci/src/lib.rs b/kernel/driver/usb/xhci/src/lib.rs index 6bbb0c78..ec8b8bad 100644 --- a/kernel/driver/usb/xhci/src/lib.rs +++ b/kernel/driver/usb/xhci/src/lib.rs @@ -9,7 +9,7 @@ use controller::Xhci; use device_api::{device::Device, dma::DmaAllocator, interrupt::InterruptAffinity}; use regs::Regs; use ygg_driver_pci::{ - capability::{DevicePowerState, PowerManagementCapability}, + capability::{power::DevicePowerState, PowerManagementCapability}, device::{PciDeviceInfo, PreferredInterruptMode}, macros::pci_driver, PciCommandRegister, PciConfigurationSpace, diff --git a/kernel/driver/virtio/core/src/transport/pci.rs b/kernel/driver/virtio/core/src/transport/pci.rs index 2965fa8b..ed3076f2 100644 --- a/kernel/driver/virtio/core/src/transport/pci.rs +++ b/kernel/driver/virtio/core/src/transport/pci.rs @@ -5,7 +5,7 @@ use tock_registers::{ }; use ygg_driver_pci::{ capability::{ - VirtioCapabilityData, VirtioCommonConfigCapability, VirtioDeviceConfigCapability, + virtio::VirtioCapabilityData, VirtioCommonConfigCapability, VirtioDeviceConfigCapability, VirtioInterruptStatusCapability, VirtioNotifyConfigCapability, }, PciCommandRegister, PciConfigurationSpace,