devices: pci: add Programming Interface to device

PCI class codes are made up of three fields: class, subclass, and
programming interface.  Some class/subclass combinations do not define
any programming interfaces, so add an optional parameter to specify the
value and use 0 if it is not provided.

Change-Id: Ib4000eafe2d7d003ed5753d7b0ea05e16fd06130
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1237358
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Daniel Verkamp 2018-09-20 17:43:57 -07:00 committed by chrome-bot
parent 0f579cb09c
commit 4f228cb203
3 changed files with 53 additions and 2 deletions

View file

@ -8,7 +8,7 @@ mod pci_configuration;
mod pci_device;
mod pci_root;
pub use self::pci_configuration::{PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciHeaderType, PciSubclass};
pub use self::pci_configuration::{PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSubclass};
pub use self::pci_device::Error as PciDeviceError;
pub use self::pci_device::PciDevice;
pub use self::pci_root::PciRoot;

View file

@ -121,6 +121,15 @@ impl PciSubclass for PciSerialBusSubClass {
}
}
/// A PCI class programming interface. Each combination of `PciClassCode` and
/// `PciSubclass` can specify a set of register-level programming interfaces.
/// This trait is implemented by each programming interface.
/// It allows use of a trait object to generate configurations.
pub trait PciProgrammingInterface {
/// Convert this programming interface to the value used in the PCI specification.
fn get_register_value(&self) -> u8;
}
/// Types of PCI capabilities.
pub enum PciCapabilityID {
ListID = 0,
@ -169,14 +178,21 @@ impl PciConfiguration {
device_id: u16,
class_code: PciClassCode,
subclass: &PciSubclass,
programming_interface: Option<&PciProgrammingInterface>,
header_type: PciHeaderType,
subsystem_vendor_id: u16,
subsystem_id: u16,
) -> Self {
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
let pi = if let Some(pi) = programming_interface {
pi.get_register_value()
} else {
0
};
registers[2] = u32::from(class_code.get_register_value()) << 24
| u32::from(subclass.get_register_value()) << 16;
| u32::from(subclass.get_register_value()) << 16
| u32::from(pi) << 8;
match header_type {
PciHeaderType::Device => (),
PciHeaderType::Bridge => registers[3] = 0x0001_0000,
@ -364,6 +380,7 @@ mod tests {
0x5678,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
None,
PciHeaderType::Device,
0xABCD,
0x2468,
@ -395,4 +412,37 @@ mod tests {
assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
}
#[derive(Copy, Clone)]
enum TestPI {
Test = 0x5a,
}
impl PciProgrammingInterface for TestPI {
fn get_register_value(&self) -> u8 {
*self as u8
}
}
#[test]
fn class_code() {
let cfg = PciConfiguration::new(
0x1234,
0x5678,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
Some(&TestPI::Test),
PciHeaderType::Device,
0xABCD,
0x2468,
);
let class_reg = cfg.read_reg(2);
let class_code = (class_reg >> 24) & 0xFF;
let subclass = (class_reg >> 16) & 0xFF;
let prog_if = (class_reg >> 8) & 0xFF;
assert_eq!(class_code, 0x04);
assert_eq!(subclass, 0x01);
assert_eq!(prog_if, 0x5a);
}
}

View file

@ -52,6 +52,7 @@ impl PciRoot {
0,
PciClassCode::BridgeDevice,
&PciBridgeSubclass::HostBridge,
None,
PciHeaderType::Bridge,
0,
0,