Files
RedBear-OS/drivers/graphics/ihdgd/src/device/power.rs
T

324 lines
10 KiB
Rust

use common::{
io::{Io, MmioPtr},
timeout::Timeout,
};
use syscall::error::{Error, Result, EIO};
use super::MmioRegion;
#[derive(Clone, Copy)]
pub struct PowerWell {
pub name: &'static str,
pub depends: &'static [&'static str],
pub ddis: &'static [&'static str],
pub pipes: &'static [&'static str],
pub transcoders: &'static [&'static str],
pub request: u32,
pub state: u32,
pub fuse_status: u32,
}
pub struct PowerWells {
pub ctl: MmioPtr<u32>,
pub ctl_aux: MmioPtr<u32>,
pub ctl_ddi: MmioPtr<u32>,
pub fuse_status: MmioPtr<u32>,
pub fuse_status_pg0: u32,
pub wells: Vec<PowerWell>,
}
impl PowerWells {
//TODO: return guard?
pub fn enable_well(&mut self, name: &'static str) -> Result<()> {
// Wait 20us for distribution of PG0
{
let timeout = Timeout::from_micros(20);
while !self.fuse_status.readf(self.fuse_status_pg0) {
timeout.run().map_err(|()| {
log::warn!("timeout on distribution of power well 0");
Error::new(EIO)
})?;
}
}
// self.wells iter copied to allow mutable self.enable_well later
for well in self.wells.iter().copied() {
if well.name == name {
// Enable dependent wells
for depend in well.depends.iter() {
self.enable_well(depend)?;
}
if !self.ctl.readf(well.request) {
log::info!("enabling power well {}", well.name);
}
// Set request bit
self.ctl.writef(well.request, true);
// Wait 100us for enabled state
{
let timeout = Timeout::from_micros(100);
while !self.ctl.readf(well.state) {
timeout.run().map_err(|()| {
log::warn!("timeout enabling power well {}", well.name);
Error::new(EIO)
})?;
}
}
// Wait 20us for distribution
{
let timeout = Timeout::from_micros(20);
while !self.fuse_status.readf(well.fuse_status) {
timeout.run().map_err(|()| {
log::warn!("timeout on distribution of power well {}", well.name);
Error::new(EIO)
})?;
}
}
return Ok(());
}
}
log::warn!("power well {} not found", name);
Err(Error::new(EIO))
}
pub fn enable_well_by_ddi(&mut self, name: &'static str) -> Result<()> {
for well in self.wells.iter() {
if well.ddis.contains(&name) {
return self.enable_well(well.name);
}
}
log::warn!("power well for DDI {} not found", name);
Err(Error::new(EIO))
}
pub fn enable_well_by_pipe(&mut self, name: &'static str) -> Result<()> {
for well in self.wells.iter() {
if well.pipes.contains(&name) {
return self.enable_well(well.name);
}
}
log::warn!("power well for pipe {} not found", name);
Err(Error::new(EIO))
}
pub fn enable_well_by_transcoder(&mut self, name: &'static str) -> Result<()> {
for well in self.wells.iter() {
if well.transcoders.contains(&name) {
return self.enable_well(well.name);
}
}
log::warn!("power well for transcoder {} not found", name);
Err(Error::new(EIO))
}
pub fn kabylake(gttmm: &MmioRegion) -> Result<Self> {
// IHD-OS-KBL-Vol 2c-1.17 PWR_WELL_CTL
let ctl = unsafe { gttmm.mmio(0x45404)? };
// Hack since these power ctl registers are combined
let ctl_aux = unsafe { gttmm.mmio(0x45404)? };
let ctl_ddi = unsafe { gttmm.mmio(0x45404)? };
// IHD-OS-KBL-Vol 2c-1.17 FUSE_STATUS
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
let fuse_status_pg0 = 1 << 27;
let wells = vec![
PowerWell {
name: "1",
depends: &[],
ddis: &["A"],
pipes: &["A"],
transcoders: &["EDP"],
request: 1 << 29,
state: 1 << 28,
fuse_status: 1 << 26,
},
PowerWell {
name: "2",
depends: &["1"],
ddis: &["B", "C", "D", "E"],
pipes: &["B", "C"],
transcoders: &["A", "B", "C"],
request: 1 << 31,
state: 1 << 30,
fuse_status: 1 << 25,
},
];
Ok(Self {
ctl,
ctl_aux,
ctl_ddi,
fuse_status,
fuse_status_pg0,
wells,
})
}
pub fn tigerlake(gttmm: &MmioRegion) -> Result<Self> {
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL
let ctl = unsafe { gttmm.mmio(0x45404)? };
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_AUX
let ctl_aux = unsafe { gttmm.mmio(0x45444)? };
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_DDI
let ctl_ddi = unsafe { gttmm.mmio(0x45454)? };
// IHD-OS-TGL-Vol 2c-12.21 FUSE_STATUS
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
let fuse_status_pg0 = 1 << 27;
let wells = vec![
// DBUF functionality, Pipe A, Transcoder A and DSI, DDI A-C, FBC, DSS
PowerWell {
name: "1",
depends: &[],
ddis: &["A", "B", "C"],
pipes: &["A"],
transcoders: &["A"],
request: 1 << 1,
state: 1 << 0,
fuse_status: 1 << 26,
},
// VDSC for pipe A
PowerWell {
name: "2",
depends: &["1"],
ddis: &[],
pipes: &[],
transcoders: &[],
request: 1 << 3,
state: 1 << 2,
fuse_status: 1 << 25,
},
// Pipe B, Audio, Transcoder WD, VGA, Transcoder B, DDI USBC1-6, KVMR
PowerWell {
name: "3",
depends: &["2"],
ddis: &["USBC1", "USBC2", "USBC3", "USBC4", "USBC5", "USBC6"],
pipes: &["B"],
transcoders: &["B"],
request: 1 << 5,
state: 1 << 4,
fuse_status: 1 << 24,
},
// Pipe C, Transcoder C
PowerWell {
name: "4",
depends: &["3"],
ddis: &[],
pipes: &["C"],
transcoders: &["C"],
request: 1 << 7,
state: 1 << 6,
fuse_status: 1 << 23,
},
// Pipe D, Transcoder D
PowerWell {
name: "5",
depends: &["4"],
ddis: &[],
pipes: &["D"],
transcoders: &["D"],
request: 1 << 9,
state: 1 << 8,
fuse_status: 1 << 22,
},
];
Ok(Self {
ctl,
ctl_aux,
ctl_ddi,
fuse_status,
fuse_status_pg0,
wells,
})
}
pub fn alchemist(gttmm: &MmioRegion) -> Result<Self> {
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL
let ctl = unsafe { gttmm.mmio(0x45404)? };
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_AUX
let ctl_aux = unsafe { gttmm.mmio(0x45444)? };
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_DDI
let ctl_ddi = unsafe { gttmm.mmio(0x45454)? };
// IHD-OS-ACM-Vol 2c-3.23 FUSE_STATUS
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
let fuse_status_pg0 = 1 << 27;
let wells = vec![
// DBUF functionality, Transcoder A, DDI A-B
PowerWell {
name: "1",
depends: &[],
ddis: &["A", "B"],
pipes: &[],
transcoders: &["A"],
request: 1 << 1,
state: 1 << 0,
fuse_status: 1 >> 26,
},
// Audio playback, Transcoder WD, VGA, DDI C-E, Type-C, KVMR
PowerWell {
name: "2",
depends: &["1"],
ddis: &["C", "D", "E", "USBC1", "USBC2", "USBC3", "USBC4"],
pipes: &[],
transcoders: &[],
request: 1 << 3,
state: 1 << 2,
fuse_status: 1 << 25,
},
// Pipe A, FBC
PowerWell {
name: "A",
depends: &["1"],
ddis: &[],
pipes: &["A"],
transcoders: &[],
request: 1 << 11,
state: 1 << 10,
fuse_status: 1 << 21,
},
// Pipe B, Transcoder B
PowerWell {
name: "B",
depends: &["2"],
ddis: &[],
pipes: &["B"],
transcoders: &["B"],
request: 1 << 13,
state: 1 << 12,
fuse_status: 1 << 20,
},
// Pipe C, Transcoder C
PowerWell {
name: "C",
depends: &["2"],
ddis: &[],
pipes: &["C"],
transcoders: &["C"],
request: 1 << 15,
state: 1 << 14,
fuse_status: 1 << 19,
},
// Pipe D, Transcoder D
PowerWell {
name: "D",
depends: &["2"],
ddis: &[],
pipes: &["D"],
transcoders: &["D"],
request: 1 << 17,
state: 1 << 16,
fuse_status: 1 << 18,
},
];
Ok(Self {
ctl,
ctl_aux,
ctl_ddi,
fuse_status,
fuse_status_pg0,
wells,
})
}
}