316 lines
7.8 KiB
Rust
316 lines
7.8 KiB
Rust
use hwio::{Io, Pio};
|
|
|
|
use crate::{
|
|
Error,
|
|
Spi,
|
|
SpiTarget,
|
|
SuperIo,
|
|
Timeout,
|
|
timeout
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[repr(u8)]
|
|
pub enum Cmd {
|
|
None = 0,
|
|
Probe = 1,
|
|
Board = 2,
|
|
Version = 3,
|
|
Print = 4,
|
|
Spi = 5,
|
|
Reset = 6,
|
|
FanGet = 7,
|
|
FanSet = 8,
|
|
KeymapGet = 9,
|
|
KeymapSet = 10,
|
|
}
|
|
|
|
pub const CMD_SPI_FLAG_READ: u8 = 1 << 0;
|
|
pub const CMD_SPI_FLAG_DISABLE: u8 = 1 << 1;
|
|
pub const CMD_SPI_FLAG_SCRATCH: u8 = 1 << 2;
|
|
pub const CMD_SPI_FLAG_BACKUP: u8 = 1 << 3;
|
|
|
|
pub struct Ec<T: Timeout> {
|
|
cmd: u16,
|
|
dbg: u16,
|
|
timeout: T,
|
|
}
|
|
|
|
impl<T: Timeout> Ec<T> {
|
|
/// Probes for a compatible EC
|
|
pub unsafe fn new(timeout: T) -> Result<Self, Error> {
|
|
let mut sio = SuperIo::new(0x2E);
|
|
|
|
let id =
|
|
(sio.read(0x20) as u16) << 8 |
|
|
(sio.read(0x21) as u16);
|
|
|
|
match id {
|
|
0x5570 | 0x8587 => (),
|
|
_ => return Err(Error::SuperIoId(id)),
|
|
}
|
|
|
|
let mut ec = Ec {
|
|
cmd: 0xE00,
|
|
dbg: 0xF00,
|
|
timeout,
|
|
};
|
|
|
|
ec.probe()?;
|
|
|
|
Ok(ec)
|
|
}
|
|
|
|
/// Read from the command space
|
|
pub unsafe fn read(&mut self, addr: u8) -> u8 {
|
|
Pio::<u8>::new(
|
|
self.cmd + (addr as u16)
|
|
).read()
|
|
}
|
|
|
|
/// Write to the command space
|
|
pub unsafe fn write(&mut self, addr: u8, data: u8) {
|
|
Pio::<u8>::new(
|
|
self.cmd + (addr as u16)
|
|
).write(data)
|
|
}
|
|
|
|
/// Read from the debug space
|
|
pub unsafe fn debug(&mut self, addr: u8) -> u8 {
|
|
Pio::<u8>::new(
|
|
self.dbg + (addr as u16)
|
|
).read()
|
|
}
|
|
|
|
/// Returns Ok if a command can be sent
|
|
unsafe fn command_check(&mut self) -> Result<(), Error> {
|
|
if self.read(0) == Cmd::None as u8 {
|
|
Ok(())
|
|
} else {
|
|
Err(Error::WouldBlock)
|
|
}
|
|
}
|
|
|
|
/// Wait until a command can be sent
|
|
unsafe fn command_wait(&mut self) -> Result<(), Error> {
|
|
self.timeout.reset();
|
|
timeout!(self.timeout, self.command_check())
|
|
}
|
|
|
|
/// Run an EC command
|
|
pub unsafe fn command(&mut self, cmd: Cmd) -> Result<(), Error> {
|
|
// All previous commands should be finished
|
|
self.command_check()?;
|
|
// Write command byte
|
|
self.write(0, cmd as u8);
|
|
// Wait for command to finish
|
|
self.command_wait()?;
|
|
// Read response byte and test for error
|
|
match self.read(1) {
|
|
0 => Ok(()),
|
|
err => Err(Error::Protocol(err)),
|
|
}
|
|
}
|
|
|
|
/// Probe for EC
|
|
pub unsafe fn probe(&mut self) -> Result<u8, Error> {
|
|
self.command(Cmd::Probe)?;
|
|
let signature = (
|
|
self.read(2),
|
|
self.read(3)
|
|
);
|
|
if signature == (0x76, 0xEC) {
|
|
let version = self.read(4);
|
|
Ok(version)
|
|
} else {
|
|
Err(Error::Signature(signature))
|
|
}
|
|
}
|
|
|
|
/// Read board from EC
|
|
pub unsafe fn board(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
|
self.command(Cmd::Board)?;
|
|
let mut i = 0;
|
|
while i < data.len() && (i + 2) < 256 {
|
|
data[i] = self.read((i + 2) as u8);
|
|
if data[i] == 0 {
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
Ok(i)
|
|
}
|
|
|
|
/// Read version from EC
|
|
pub unsafe fn version(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
|
self.command(Cmd::Version)?;
|
|
let mut i = 0;
|
|
while i < data.len() && (i + 2) < 256 {
|
|
data[i] = self.read((i + 2) as u8);
|
|
if data[i] == 0 {
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
Ok(i)
|
|
}
|
|
|
|
pub unsafe fn print(&mut self, data: &[u8]) -> Result<usize, Error> {
|
|
let flags = 0;
|
|
for chunk in data.chunks(256 - 4) {
|
|
for i in 0..chunk.len() {
|
|
self.write(i as u8 + 4, chunk[i]);
|
|
}
|
|
|
|
self.write(2, flags);
|
|
self.write(3, chunk.len() as u8);
|
|
self.command(Cmd::Print)?;
|
|
if self.read(3) != chunk.len() as u8 {
|
|
return Err(Error::Verify);
|
|
}
|
|
}
|
|
Ok(data.len())
|
|
}
|
|
|
|
pub unsafe fn spi(&mut self, target: SpiTarget, scratch: bool) -> Result<EcSpi<T>, Error> {
|
|
let mut spi = EcSpi {
|
|
ec: self,
|
|
target,
|
|
scratch,
|
|
};
|
|
spi.reset()?;
|
|
Ok(spi)
|
|
}
|
|
|
|
pub unsafe fn reset(&mut self) -> Result<(), Error> {
|
|
self.command(Cmd::Reset)
|
|
}
|
|
|
|
pub unsafe fn fan_get(&mut self, index: u8) -> Result<u8, Error> {
|
|
self.write(2, index);
|
|
self.command(Cmd::FanGet)?;
|
|
Ok(self.read(3))
|
|
}
|
|
|
|
pub unsafe fn fan_set(&mut self, index: u8, duty: u8) -> Result<(), Error> {
|
|
self.write(2, index);
|
|
self.write(3, duty);
|
|
self.command(Cmd::FanSet)
|
|
}
|
|
|
|
pub unsafe fn keymap_get(&mut self, layer: u8, output: u8, input: u8) -> Result<u16, Error> {
|
|
self.write(2, layer);
|
|
self.write(3, output);
|
|
self.write(4, input);
|
|
self.command(Cmd::KeymapGet)?;
|
|
Ok(
|
|
(self.read(5) as u16) |
|
|
((self.read(6) as u16) << 8)
|
|
)
|
|
}
|
|
|
|
pub unsafe fn keymap_set(&mut self, layer: u8, output: u8, input: u8, value: u16) -> Result<(), Error> {
|
|
self.write(2, layer);
|
|
self.write(3, output);
|
|
self.write(4, input);
|
|
self.write(5, value as u8);
|
|
self.write(6, (value >> 8) as u8);
|
|
self.command(Cmd::KeymapSet)
|
|
}
|
|
}
|
|
|
|
pub struct EcSpi<'a, T: Timeout> {
|
|
ec: &'a mut Ec<T>,
|
|
target: SpiTarget,
|
|
scratch: bool,
|
|
}
|
|
|
|
impl<'a, T: Timeout> EcSpi<'a, T> {
|
|
fn flags(&self, read: bool, disable: bool) -> u8 {
|
|
let mut flags = 0;
|
|
|
|
if read {
|
|
flags |= CMD_SPI_FLAG_READ;
|
|
}
|
|
|
|
if disable {
|
|
flags |= CMD_SPI_FLAG_DISABLE;
|
|
}
|
|
|
|
if self.scratch {
|
|
flags |= CMD_SPI_FLAG_SCRATCH;
|
|
}
|
|
|
|
match self.target {
|
|
SpiTarget::Main => (),
|
|
SpiTarget::Backup => {
|
|
flags |= CMD_SPI_FLAG_BACKUP;
|
|
},
|
|
}
|
|
|
|
flags
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Timeout> Spi for EcSpi<'a, T> {
|
|
fn target(&self) -> SpiTarget {
|
|
self.target
|
|
}
|
|
|
|
/// Disable SPI chip, must be done before and after a transaction
|
|
unsafe fn reset(&mut self) -> Result<(), Error> {
|
|
let flags = self.flags(false, true);
|
|
self.ec.write(2, flags);
|
|
self.ec.write(3, 0);
|
|
self.ec.command(Cmd::Spi)?;
|
|
if self.ec.read(3) != 0 {
|
|
return Err(Error::Verify);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// SPI read
|
|
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
|
let flags = self.flags(true, false);
|
|
for chunk in data.chunks_mut(256 - 4) {
|
|
self.ec.write(2, flags);
|
|
self.ec.write(3, chunk.len() as u8);
|
|
self.ec.command(Cmd::Spi)?;
|
|
if self.ec.read(3) != chunk.len() as u8 {
|
|
return Err(Error::Verify);
|
|
}
|
|
|
|
for i in 0..chunk.len() {
|
|
chunk[i] = self.ec.read(i as u8 + 4);
|
|
}
|
|
}
|
|
Ok(data.len())
|
|
}
|
|
|
|
/// SPI write
|
|
unsafe fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
|
|
let flags = self.flags(false, false);
|
|
for chunk in data.chunks(256 - 4) {
|
|
for i in 0..chunk.len() {
|
|
self.ec.write(i as u8 + 4, chunk[i]);
|
|
}
|
|
|
|
self.ec.write(2, flags);
|
|
self.ec.write(3, chunk.len() as u8);
|
|
self.ec.command(Cmd::Spi)?;
|
|
if self.ec.read(3) != chunk.len() as u8 {
|
|
return Err(Error::Verify);
|
|
}
|
|
}
|
|
Ok(data.len())
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Timeout> Drop for EcSpi<'a, T> {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
let _ = self.reset();
|
|
}
|
|
}
|
|
}
|