apic/net: better MSI allocation, better PHY setup
This commit is contained in:
parent
3f62374431
commit
2867597c8e
@ -1,6 +1,7 @@
|
||||
//! PCI capability structures and queries
|
||||
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use device_api::interrupt::{
|
||||
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
||||
};
|
||||
@ -15,6 +16,16 @@ 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))]
|
||||
@ -50,12 +61,12 @@ pub trait VirtioCapability {
|
||||
|
||||
/// 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
|
||||
@ -116,6 +127,12 @@ 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,
|
||||
@ -193,6 +210,19 @@ 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>;
|
||||
@ -571,3 +601,13 @@ 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());
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ primitive_enum! {
|
||||
PowerManagement = 0x01,
|
||||
Msi = 0x05,
|
||||
VendorSpecific = 0x09,
|
||||
PciExpress = 0x10,
|
||||
MsiX = 0x11,
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ pub(crate) fn make_sysfs_object(
|
||||
Some(PciCapabilityId::Msi) => write!(output, "msi").ok(),
|
||||
Some(PciCapabilityId::MsiX) => write!(output, "msix").ok(),
|
||||
Some(PciCapabilityId::VendorSpecific) => write!(output, "vendor-specific").ok(),
|
||||
Some(PciCapabilityId::PciExpress) => write!(output, "pcie").ok(),
|
||||
Some(PciCapabilityId::PowerManagement) => {
|
||||
write!(output, "power-management").ok()
|
||||
}
|
||||
|
181
kernel/driver/net/core/src/ephy.rs
Normal file
181
kernel/driver/net/core/src/ephy.rs
Normal file
@ -0,0 +1,181 @@
|
||||
//! Ethernet PHY utilities
|
||||
|
||||
use libk::error::Error;
|
||||
use yggdrasil_abi::bitflags;
|
||||
|
||||
bitflags! {
|
||||
pub struct BMCR: u16 {
|
||||
const RESET: bit 15;
|
||||
const LOOPBACK: bit 14;
|
||||
const SPEED0: bit 13;
|
||||
const ANE: bit 12;
|
||||
const PWD: bit 11;
|
||||
const ISOLATE: bit 10;
|
||||
const RESTART_AN: bit 9;
|
||||
const DUPLEX: bit 8;
|
||||
const SPEED1: bit 6;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct BMSR: u16 {
|
||||
const HAVE_100BASET4: bit 15;
|
||||
const HAVE_100BASETX_FULL: bit 14;
|
||||
const HAVE_100BASETX_HALF: bit 13;
|
||||
const HAVE_10BASET_FULL: bit 12;
|
||||
const HAVE_10BASET_HALF: bit 11;
|
||||
const HAVE_100BASET2_FULL: bit 10;
|
||||
const HAVE_100BASET2_HALF: bit 9;
|
||||
const EXT_STATUS_1000BASET: bit 8;
|
||||
const PREAMBLE_SUPP: bit 6;
|
||||
const AN_COMPLETE: bit 5;
|
||||
const REMOTE_FAULT: bit 4;
|
||||
const AN_AVAILABLE: bit 3;
|
||||
const LINK_STATUS: bit 2;
|
||||
const JABBER: bit 1;
|
||||
const EXTENDED_CAP: bit 0;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct ANAR: u16 {
|
||||
const HAVE_10BASET: bit 5;
|
||||
const HAVE_10BASET_FULL: bit 6;
|
||||
const HAVE_100BASETX: bit 7;
|
||||
const HAVE_100BASETX_FULL: bit 8;
|
||||
const HAVE_100BASET4: bit 9;
|
||||
const HAVE_PAUSE: bit 10;
|
||||
const ASM_DIR: bit 11;
|
||||
const REMOTE_FAULT: bit 13;
|
||||
const NEXT_PAGE: bit 15;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct GBCR: u16 {
|
||||
const HAVE_1000BASET_HALF: bit 8;
|
||||
const HAVE_1000BASET_FULL: bit 9;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct GBESR: u16 {
|
||||
const HAVE_1000BASET_HALF: bit 12;
|
||||
const HAVE_1000BASET_FULL: bit 13;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MdioBus {
|
||||
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error>;
|
||||
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct PhyAccess<'a, M: MdioBus> {
|
||||
address: u8,
|
||||
mii: &'a M,
|
||||
}
|
||||
|
||||
pub const REG_BMCR: u8 = 0x00;
|
||||
pub const REG_BMSR: u8 = 0x01;
|
||||
pub const REG_ANAR: u8 = 0x04;
|
||||
pub const REG_GBCR: u8 = 0x09;
|
||||
pub const REG_GBESR: u8 = 0x0F;
|
||||
|
||||
pub const ANAR_SELECTOR_802_3: u16 = 0b00001;
|
||||
pub const ANAR_SELECTOR_MASK: u16 = 0b11111;
|
||||
|
||||
impl<'a, M: MdioBus> PhyAccess<'a, M> {
|
||||
pub fn new(mii: &'a M, address: u8) -> Self {
|
||||
Self { address, mii }
|
||||
}
|
||||
|
||||
pub fn probe(_mii: &'a M, _address: u8) -> Result<Self, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn read_reg(&self, reg: u8) -> Result<u16, Error> {
|
||||
self.mii.mii_read(self.address, reg)
|
||||
}
|
||||
|
||||
pub fn write_reg(&self, reg: u8, value: u16) -> Result<(), Error> {
|
||||
self.mii.mii_write(self.address, reg, value)
|
||||
}
|
||||
|
||||
pub fn status(&self) -> Result<BMSR, Error> {
|
||||
self.read_reg(REG_BMSR).map(BMSR::from)
|
||||
}
|
||||
|
||||
pub fn reset(&self, mut timeout: u64) -> Result<(), Error> {
|
||||
self.write_reg(REG_BMCR, BMCR::RESET.bits())?;
|
||||
while timeout > 0 && self.read_reg(REG_BMCR)? & BMCR::RESET.bits() != 0 {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
if timeout > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_link(&self, have_pause: bool, force_gbesr: GBESR) -> Result<(), Error> {
|
||||
let bmsr = BMSR::from(self.read_reg(REG_BMSR)?);
|
||||
let mut gbesr = if bmsr.contains(BMSR::EXT_STATUS_1000BASET) {
|
||||
GBESR::from(self.read_reg(REG_GBESR)?)
|
||||
} else {
|
||||
GBESR::empty()
|
||||
};
|
||||
gbesr |= force_gbesr;
|
||||
let mut anar = ANAR::from_capabilities(bmsr);
|
||||
if have_pause {
|
||||
anar |= ANAR::HAVE_PAUSE | ANAR::ASM_DIR;
|
||||
}
|
||||
let mut gbcr = GBCR::empty();
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_HALF) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_HALF;
|
||||
}
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_FULL) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_FULL;
|
||||
}
|
||||
|
||||
self.write_reg(REG_ANAR, anar.bits())?;
|
||||
for _ in 0..10000000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.write_reg(REG_GBCR, gbcr.bits())?;
|
||||
for _ in 0..10000000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.write_reg(REG_BMCR, (BMCR::ANE | BMCR::RESTART_AN).bits())?;
|
||||
for _ in 0..10000000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ANAR {
|
||||
pub fn from_capabilities(bmsr: BMSR) -> ANAR {
|
||||
let mut anar = ANAR::from(ANAR_SELECTOR_802_3);
|
||||
if bmsr.contains(BMSR::HAVE_10BASET_HALF) {
|
||||
anar |= ANAR::HAVE_10BASET;
|
||||
}
|
||||
if bmsr.contains(BMSR::HAVE_10BASET_FULL) {
|
||||
anar |= ANAR::HAVE_10BASET_FULL;
|
||||
}
|
||||
if bmsr.contains(BMSR::HAVE_100BASETX_HALF) {
|
||||
anar |= ANAR::HAVE_100BASETX;
|
||||
}
|
||||
if bmsr.contains(BMSR::HAVE_100BASETX_FULL) {
|
||||
anar |= ANAR::HAVE_100BASETX_FULL;
|
||||
}
|
||||
if bmsr.contains(BMSR::HAVE_100BASET4) {
|
||||
anar |= ANAR::HAVE_100BASET4;
|
||||
}
|
||||
anar
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@ pub mod socket;
|
||||
pub mod interface;
|
||||
pub mod util;
|
||||
|
||||
pub mod ephy;
|
||||
|
||||
pub use interface::register_interface;
|
||||
|
||||
pub struct RxPacket {
|
||||
|
@ -6,6 +6,9 @@ use libk::error::Error;
|
||||
use rtl8139::Rtl8139;
|
||||
use rtl8168::Rtl8168;
|
||||
use ygg_driver_pci::{
|
||||
capability::{
|
||||
DevicePowerState, PciExpressCapability, PcieLinkControl, PowerManagementCapability,
|
||||
},
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
macros::pci_driver,
|
||||
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
||||
@ -26,6 +29,12 @@ pci_driver! {
|
||||
fn probe(&self, info: &PciDeviceInfo, dma: &Arc<dyn DmaAllocator>) -> Result<Arc<dyn Device>, Error> {
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||
|
||||
if let Some(mut pcie) = info.config_space.capability::<PciExpressCapability>() {
|
||||
let mut lcr = pcie.link_control();
|
||||
lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM);
|
||||
pcie.set_link_control(lcr);
|
||||
}
|
||||
|
||||
// Enable MMIO + interrupts + bus mastering
|
||||
let mut command = info.config_space.command();
|
||||
command |= (PciCommandRegister::BUS_MASTER | PciCommandRegister::ENABLE_MEMORY).bits();
|
||||
@ -58,9 +67,9 @@ pci_driver! {
|
||||
.and_then(PciBaseAddress::as_memory)
|
||||
.ok_or(Error::InvalidArgument)?;
|
||||
|
||||
// if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
|
||||
// power.set_device_power_state(DevicePowerState::D0);
|
||||
// }
|
||||
if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
|
||||
power.set_device_power_state(DevicePowerState::D0);
|
||||
}
|
||||
|
||||
// Enable MMIO + interrupts + bus mastering
|
||||
info.set_command(true, true, false, true);
|
||||
|
@ -21,6 +21,7 @@ use tock_registers::{
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
ephy::{MdioBus, PhyAccess, GBESR},
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
RxPacket,
|
||||
};
|
||||
@ -48,6 +49,15 @@ register_bitfields! {
|
||||
NPQ OFFSET(6) NUMBITS(1) [],
|
||||
FSWInt OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
R9346CR [
|
||||
EEM OFFSET(6) NUMBITS(2) [
|
||||
Normal = 0b00,
|
||||
ConfigWriteEnable = 0b11
|
||||
],
|
||||
],
|
||||
CONFIG2 [
|
||||
MSI OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
PHYSTATUS [
|
||||
TXFLOW OFFSET(6) NUMBITS(1) [],
|
||||
RXFLOW OFFSET(5) NUMBITS(1) [],
|
||||
@ -172,10 +182,10 @@ register_structs! {
|
||||
(0x0044 => RCR: ReadWrite<u32, RCR::Register>),
|
||||
(0x0048 => TCTR: ReadWrite<u32>),
|
||||
(0x004C => MPKT: ReadWrite<u32>),
|
||||
(0x0050 => R9346CR: ReadWrite<u8>),
|
||||
(0x0050 => R9346CR: ReadWrite<u8, R9346CR::Register>),
|
||||
(0x0051 => CONFIG0: ReadWrite<u8>),
|
||||
(0x0052 => CONFIG1: ReadWrite<u8>),
|
||||
(0x0053 => CONFIG2: ReadWrite<u8>),
|
||||
(0x0053 => CONFIG2: ReadWrite<u8, CONFIG2::Register>),
|
||||
(0x0054 => CONFIG3: ReadWrite<u8>),
|
||||
(0x0055 => CONFIG4: ReadWrite<u8>),
|
||||
(0x0056 => CONFIG5: ReadWrite<u8>),
|
||||
@ -186,6 +196,8 @@ register_structs! {
|
||||
(0x0064 => _6),
|
||||
(0x006C => PHYSTATUS: ReadOnly<u8, PHYSTATUS::Register>),
|
||||
(0x006D => _7),
|
||||
(0x0082 => LDPS: ReadWrite<u8>),
|
||||
(0x0083 => _13),
|
||||
(0x0084 => WAKEUPn: [ReadWrite<u32>; 16]),
|
||||
(0x00C4 => CRCn: [ReadWrite<u16>; 5]),
|
||||
(0x00CE => _8),
|
||||
@ -420,41 +432,6 @@ impl Descriptor {
|
||||
impl Regs {
|
||||
const TCR_REVISION_MASK: u32 = 0x7C800000;
|
||||
|
||||
pub fn gmii_write(&self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> {
|
||||
self.PHYAR.write(
|
||||
PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write,
|
||||
);
|
||||
while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout == 0 {
|
||||
Err(Error::TimedOut)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn gmii_read(&self, reg: u8, mut timeout: u64) -> Result<u16, Error> {
|
||||
self.PHYAR
|
||||
.write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read);
|
||||
|
||||
loop {
|
||||
if timeout == 0 {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
|
||||
let status = self.PHYAR.extract();
|
||||
if status.matches_all(PHYAR::FLAG::SET) {
|
||||
return Ok(status.read(PHYAR::DATA) as u16);
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_link_state(&self) -> EthernetLinkState {
|
||||
let phystatus = self.PHYSTATUS.extract();
|
||||
if phystatus.matches_all(PHYSTATUS::LINKSTS::SET) {
|
||||
@ -492,6 +469,10 @@ impl Regs {
|
||||
pub fn reset_device(&self, mut timeout: u64) -> Result<(), Error> {
|
||||
self.CR.write(CR::RST::SET);
|
||||
|
||||
for _ in 0..100000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
while timeout > 0 && self.CR.matches_all(CR::RST::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
@ -505,32 +486,51 @@ impl Regs {
|
||||
}
|
||||
|
||||
pub fn reset_phy(&self, timeout: u64) -> Result<(), Error> {
|
||||
// Set the reset bit in bmcr
|
||||
self.gmii_write(0x00, 1 << 15, timeout)?;
|
||||
for _ in 0..timeout {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
// Disable power off mode + start auto-negotiation
|
||||
self.gmii_write(0x00, 1 << 12, timeout)?;
|
||||
for _ in 0..timeout {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let phy = PhyAccess::new(self, 0x00);
|
||||
|
||||
self.gmii_write(0x1F, 0x00, timeout)?;
|
||||
self.gmii_write(0x0E, 0x00, timeout)?;
|
||||
phy.reset(timeout)?;
|
||||
phy.setup_link(true, GBESR::empty())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
// pub fn lock_config(&self) {
|
||||
// self.R9346CR.set(0x00);
|
||||
// }
|
||||
impl MdioBus for Regs {
|
||||
fn mii_read(&self, _phyaddr: u8, reg: u8) -> Result<u16, Error> {
|
||||
let mut timeout = 1000000;
|
||||
self.PHYAR
|
||||
.write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read);
|
||||
|
||||
// #[inline]
|
||||
// pub fn unlock_config(&self) {
|
||||
// self.R9346CR.set(0xC0);
|
||||
// }
|
||||
loop {
|
||||
if timeout == 0 {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
|
||||
let status = self.PHYAR.extract();
|
||||
if status.matches_all(PHYAR::FLAG::SET) {
|
||||
return Ok(status.read(PHYAR::DATA) as u16);
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn mii_write(&self, _phyaddr: u8, reg: u8, value: u16) -> Result<(), Error> {
|
||||
let mut timeout = 1000000;
|
||||
self.PHYAR.write(
|
||||
PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write,
|
||||
);
|
||||
while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout == 0 {
|
||||
Err(Error::TimedOut)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rtl8168 {
|
||||
@ -565,6 +565,7 @@ impl InterruptHandler for Rtl8168 {
|
||||
if status.get() == 0 {
|
||||
return false;
|
||||
}
|
||||
log::info!("RTL8168 IRQ");
|
||||
regs.ISR.set(status.get());
|
||||
|
||||
let mut any = false;
|
||||
@ -609,12 +610,28 @@ impl Device for Rtl8168 {
|
||||
log::info!("Initialize rtl8168");
|
||||
log::info!("MAC: {}", self.mac);
|
||||
|
||||
// Bind IRQ
|
||||
self.pci
|
||||
.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||
let msi_info = self.pci.map_interrupt(Default::default(), self.clone())?;
|
||||
|
||||
let regs = self.regs.lock();
|
||||
|
||||
regs.R9346CR.write(R9346CR::EEM::ConfigWriteEnable);
|
||||
if msi_info.is_some() {
|
||||
log::info!("Switch rtl816x into MSI(-x) mode");
|
||||
regs.CONFIG2.modify(CONFIG2::MSI::SET);
|
||||
} else {
|
||||
log::info!("Switch rtl816x into legacy IRQ mode");
|
||||
regs.CONFIG2.modify(CONFIG2::MSI::CLEAR);
|
||||
}
|
||||
regs.R9346CR.write(R9346CR::EEM::Normal);
|
||||
|
||||
let rx_ring = RxRing::with_capacity(&*self.dma, 256)?;
|
||||
let tx_ring = TxRing::with_capacity(&*self.dma, 256)?;
|
||||
let rx_ring_base = rx_ring.base_address().into_u64();
|
||||
let tx_ring_base = tx_ring.base_address().into_u64();
|
||||
|
||||
let regs = self.regs.lock();
|
||||
let hwrev = regs.get_chip_revision();
|
||||
|
||||
log::info!("Revision: {:?}", hwrev);
|
||||
@ -633,6 +650,9 @@ impl Device for Rtl8168 {
|
||||
}
|
||||
regs.CPCR.write(cpcr);
|
||||
|
||||
regs.IMR.set(0x0000);
|
||||
regs.ISR.set(0xFFFF);
|
||||
|
||||
// Reset the PHY
|
||||
if flags.contains(ChipFlags::NEED_PHY_RESET) {
|
||||
regs.reset_phy(10000000)
|
||||
@ -667,7 +687,10 @@ impl Device for Rtl8168 {
|
||||
regs.CR.write(CR::RE::SET + CR::TE::SET);
|
||||
|
||||
// Setup interrupts
|
||||
regs.IMR.set(0xFFFF);
|
||||
// regs.IMR.set(0xFFFF);
|
||||
regs.IMR.write(
|
||||
IMR_ISR::RDU::SET + IMR_ISR::ROK::SET + IMR_ISR::TOK::SET + IMR_ISR::LINKCHG::SET,
|
||||
);
|
||||
regs.ISR.set(0xFFFF);
|
||||
|
||||
// Clear missed packet counter (?)
|
||||
@ -681,11 +704,6 @@ impl Device for Rtl8168 {
|
||||
self.rx.init(IrqSafeSpinlock::new(rx_ring));
|
||||
self.tx.init(IrqSafeSpinlock::new(tx_ring));
|
||||
|
||||
// Bind IRQ
|
||||
self.pci
|
||||
.init_interrupts(PreferredInterruptMode::Msi(false))?;
|
||||
self.pci.map_interrupt(Default::default(), self.clone())?;
|
||||
|
||||
let interface =
|
||||
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||
self.nic.init(interface.id());
|
||||
|
@ -538,7 +538,6 @@ impl Device for Xhci {
|
||||
|
||||
impl InterruptHandler for Xhci {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
log::info!("xhci irq");
|
||||
let status = self.regs.handle_interrupt(0);
|
||||
|
||||
if status.matches_all(USBSTS::HSE::SET) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! x86-64 Local APIC driver implementation
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
@ -15,7 +15,7 @@ use kernel_arch_x86_64::{mem::table::L3, LocalApicInterface, CPU_COUNT};
|
||||
use libk::arch::Cpu;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, table::EntryLevelExt};
|
||||
use libk_util::{
|
||||
sync::{IrqGuard, IrqSafeSpinlock},
|
||||
sync::{spin_rwlock::IrqSafeRwLock, IrqGuard},
|
||||
OneTimeInit,
|
||||
};
|
||||
use tock_registers::{
|
||||
@ -134,11 +134,91 @@ register_structs! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MsiVector {
|
||||
logical: usize,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
}
|
||||
|
||||
struct MsiVectorTable {
|
||||
// MSI_MAX_VECTORS * vectors per row
|
||||
msi_vectors: IrqSafeRwLock<Vec<Vec<MsiVector>>>,
|
||||
last_msi_allocated: AtomicUsize,
|
||||
}
|
||||
|
||||
impl MsiVectorTable {
|
||||
pub fn new(physical_vectors: usize) -> Self {
|
||||
let rows = vec![Vec::new(); physical_vectors];
|
||||
|
||||
Self {
|
||||
msi_vectors: IrqSafeRwLock::new(rows),
|
||||
last_msi_allocated: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
&self,
|
||||
apic_id: usize,
|
||||
range: &mut [MsiInfo],
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
if range.is_empty() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let seq = self
|
||||
.last_msi_allocated
|
||||
.fetch_add(range.len(), Ordering::AcqRel);
|
||||
let mut rows = self.msi_vectors.write();
|
||||
|
||||
let physical_start = seq % rows.len();
|
||||
let physical_end = seq.wrapping_add(range.len()) % rows.len();
|
||||
if range.len() == 1 {
|
||||
log::info!("Map MSI {:?} -> {}", handler.display_name(), physical_start);
|
||||
} else {
|
||||
log::info!(
|
||||
"Map MSI {:?} -> {:?}",
|
||||
handler.display_name(),
|
||||
physical_start..physical_end - 1
|
||||
);
|
||||
}
|
||||
|
||||
for (logical, info) in range.iter_mut().enumerate() {
|
||||
let physical = logical.wrapping_add(seq) % rows.len();
|
||||
rows[physical].push(MsiVector {
|
||||
logical,
|
||||
handler: handler.clone(),
|
||||
});
|
||||
|
||||
let address = 0xFEE00000 | (apic_id << 12);
|
||||
let value = 32 + APIC_MSI_OFFSET + physical as u32;
|
||||
|
||||
*info = MsiInfo {
|
||||
vector: logical,
|
||||
affinity: InterruptAffinity::Specific(0),
|
||||
address,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke(&self, physical_vector: usize) {
|
||||
let rows = self.msi_vectors.read();
|
||||
let row = &rows[physical_vector];
|
||||
|
||||
for col in row.iter() {
|
||||
col.handler.clone().handle_irq(IrqVector::Msi(col.logical));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-processor local APIC interface
|
||||
pub struct LocalApic {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
id: u32,
|
||||
msi_vectors: Vec<IrqSafeSpinlock<Vec<Arc<dyn InterruptHandler>>>>,
|
||||
msi_table: MsiVectorTable,
|
||||
}
|
||||
|
||||
unsafe impl Send for LocalApic {}
|
||||
@ -210,21 +290,7 @@ impl LocalApicInterface for LocalApic {
|
||||
|
||||
impl MessageInterruptController for LocalApic {
|
||||
fn handle_msi(&self, vector: usize) {
|
||||
// TODO this is ugly
|
||||
let row = &self.msi_vectors[vector];
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
let table = row.lock();
|
||||
let Some(handler) = table.get(i).cloned() else {
|
||||
break;
|
||||
};
|
||||
drop(table);
|
||||
|
||||
handler.handle_irq(IrqVector::Msi(vector));
|
||||
|
||||
i += 1;
|
||||
}
|
||||
self.msi_table.invoke(vector);
|
||||
}
|
||||
|
||||
fn register_msi_range(
|
||||
@ -233,36 +299,7 @@ impl MessageInterruptController for LocalApic {
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
let _guard = IrqGuard::acquire();
|
||||
|
||||
// TODO fill smallest vectors first
|
||||
// TODO don't ignore affinity
|
||||
|
||||
for (i, msi) in range.iter_mut().enumerate() {
|
||||
let row = &self.msi_vectors[i];
|
||||
let mut row = row.lock();
|
||||
|
||||
row.push(handler.clone());
|
||||
|
||||
log::info!(
|
||||
"Bind {}:{} -> apic{}:msi{}",
|
||||
handler.display_name(),
|
||||
i,
|
||||
self.id,
|
||||
i
|
||||
);
|
||||
|
||||
let value = 32 + APIC_MSI_OFFSET + i as u32;
|
||||
let address = 0xFEE00000 | ((self.id as usize) << 12);
|
||||
|
||||
*msi = MsiInfo {
|
||||
address,
|
||||
value,
|
||||
vector: i,
|
||||
affinity: InterruptAffinity::Specific(self.id as _),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
self.msi_table.map(self.id as usize, range, handler)
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,12 +385,12 @@ impl LocalApic {
|
||||
LocalVectorEntry::Mask::Masked + LocalVectorEntry::Vector.val(APIC_LINT1_VECTOR + 32),
|
||||
);
|
||||
|
||||
let msi_vectors = vec![IrqSafeSpinlock::new(Vec::new()); MAX_MSI_VECTORS as _];
|
||||
let msi_table = MsiVectorTable::new(MAX_MSI_VECTORS as _);
|
||||
|
||||
Self {
|
||||
id,
|
||||
regs,
|
||||
msi_vectors,
|
||||
msi_table,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user