From be56097fe4fce697bd3e4c4c291c6a362ff3eae6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 7 Oct 2024 22:52:55 +0800 Subject: [PATCH] add axp2101 driver skeleton --- libs/cramium-hal/src/axp2101.rs | 308 ++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 libs/cramium-hal/src/axp2101.rs diff --git a/libs/cramium-hal/src/axp2101.rs b/libs/cramium-hal/src/axp2101.rs new file mode 100644 index 000000000..f01a10444 --- /dev/null +++ b/libs/cramium-hal/src/axp2101.rs @@ -0,0 +1,308 @@ +use crate::udma::i2c; + +pub const AXP2101_DEV: u8 = 0x34; + +const REG_DCDC_ENA: usize = 0x80; +const REG_DCDC1_V: usize = 0x82; +const REG_DCDC2_V: usize = 0x83; +const REG_DCDC3_V: usize = 0x84; +const REG_DCDC4_V: usize = 0x85; +#[allow(dead_code)] +const REG_DCDC5_V: usize = 0x86; +const REG_LDO1_ENA: usize = 0x90; +const REG_LDO2_ENA: usize = 0x91; +#[allow(dead_code)] +const REG_ALDO1_V: usize = 0x92; +#[allow(dead_code)] +const REG_ALDO2_V: usize = 0x93; +#[allow(dead_code)] +const REG_ALDO3_V: usize = 0x94; +#[allow(dead_code)] +const REG_ALDO4_V: usize = 0x95; +#[allow(dead_code)] +const REG_BLDO1_V: usize = 0x96; +#[allow(dead_code)] +const REG_BLDO2_V: usize = 0x97; +#[allow(dead_code)] +const REG_CPUSLDO_V: usize = 0x98; +#[allow(dead_code)] +const REG_DLDO1_V: usize = 0x99; +#[allow(dead_code)] +const REG_DLDO2_V: usize = 0x9A; + +#[repr(u8)] +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum WhichLdo { + Aldo1 = 0, + Aldo2 = 1, + Aldo3 = 2, + Aldo4 = 3, + Bldo1 = 4, + Bldo2 = 5, + Cpusldo1 = 6, + Dldo1 = 7, + Dldo2 = 8, +} +impl Into for WhichLdo { + fn into(self) -> f32 { + match self { + Self::Cpusldo1 | Self::Dldo1 => 0.050, + _ => 0.100, + } + } +} +impl From for WhichLdo { + fn from(value: usize) -> Self { + match value { + 0 => Self::Aldo1, + 1 => Self::Aldo2, + 2 => Self::Aldo3, + 3 => Self::Aldo4, + 4 => Self::Bldo1, + 5 => Self::Bldo2, + 6 => Self::Cpusldo1, + 7 => Self::Dldo1, + 8 => Self::Dldo2, + _ => panic!("bad WhichLdo"), + } + } +} + +#[repr(usize)] +#[derive(PartialEq, Eq, Copy, Clone)] +pub enum WhichDcDc { + Dcdc1 = 0, + Dcdc2 = 1, + Dcdc3 = 2, + Dcdc4 = 3, + Dcdc5 = 4, +} +impl From for WhichDcDc { + fn from(value: usize) -> Self { + match value { + 0 => Self::Dcdc1, + 1 => Self::Dcdc2, + 2 => Self::Dcdc3, + 3 => Self::Dcdc4, + 4 => Self::Dcdc5, + _ => panic!("bad WhichDcDc"), + } + } +} + +#[derive(Debug)] +pub struct Axp2101 { + pub dcdc_ena: [bool; 4], + pub fast_ramp: bool, + pub force_ccm: bool, + pub dcdc_v_dvm: [(f32, bool); 4], + pub ldo_ena: [bool; 9], + pub ldo_v: [f32; 9], +} + +impl Axp2101 { + pub fn new(i2c: &mut dyn i2c::I2cApi) -> Axp2101 { + let mut s = Axp2101 { + dcdc_ena: [false; 4], + fast_ramp: false, + force_ccm: false, + dcdc_v_dvm: [(0.0, false); 4], + ldo_ena: [false; 9], + ldo_v: [0.0; 9], + }; + s.update(i2c); + s + } + + pub fn update(&mut self, i2c: &mut dyn i2c::I2cApi) { + let mut buf = [0u8; 0xb0]; + i2c.i2c_read(AXP2101_DEV, 0x0, &mut buf, false).expect("couldn't access AXP2101"); + + (self.dcdc_ena, self.fast_ramp, self.force_ccm) = parse_dcdc_ena(buf[REG_DCDC_ENA]); + self.dcdc_v_dvm = [ + parse_dcdc(buf[REG_DCDC1_V], WhichDcDc::Dcdc1), + parse_dcdc(buf[REG_DCDC2_V], WhichDcDc::Dcdc2), + parse_dcdc(buf[REG_DCDC3_V], WhichDcDc::Dcdc3), + parse_dcdc(buf[REG_DCDC4_V], WhichDcDc::Dcdc4), + ]; + for (i, ena) in self.ldo_ena.iter_mut().enumerate() { + if i < 8 { + *ena = (buf[REG_LDO1_ENA] >> i) & 1 != 0; + } else { + *ena = (buf[REG_LDO2_ENA] & 1) != 0; + } + } + for (i, v) in self.ldo_v.iter_mut().enumerate() { + *v = parse_ldo(buf[REG_ALDO1_V + i], WhichLdo::from(i)); + } + } + + pub fn set_dcdc( + &mut self, + i2c: &mut dyn i2c::I2cApi, + setting: Option<(f32, bool)>, + which: WhichDcDc, + ) -> Result<(), xous::Error> { + if let Some((voltage, dvm)) = setting { + match encode_dcdc(voltage, dvm, which) { + Some(code) => { + self.dcdc_v_dvm[which as usize] = (voltage, dvm); + self.dcdc_ena[which as usize] = true; + i2c.i2c_write(AXP2101_DEV, (REG_DCDC1_V + which as usize) as u8, &[code]).map(|_| ())?; + i2c.i2c_write(AXP2101_DEV, REG_DCDC_ENA as u8, &[self.encode_dcdc_ena()]).map(|_| ()) + } + None => Err(xous::Error::InvalidLimit), + } + } else { + self.dcdc_ena[which as usize] = false; + i2c.i2c_write(AXP2101_DEV, REG_DCDC_ENA as u8, &[self.encode_dcdc_ena()]).map(|_| ()) + } + } + + pub fn set_ldo( + &mut self, + i2c: &mut dyn i2c::I2cApi, + setting: Option, + which: WhichLdo, + ) -> Result<(), xous::Error> { + if let Some(voltage) = setting { + match encode_ldo(voltage, which) { + Some(code) => { + self.ldo_v[which as usize] = voltage; + self.ldo_ena[which as usize] = true; + let ctl = self.encode_ldo_ena(); + i2c.i2c_write(AXP2101_DEV, (REG_ALDO1_V + which as usize) as u8, &[code]).map(|_| ())?; + i2c.i2c_write(AXP2101_DEV, REG_LDO1_ENA as u8, &ctl).map(|_| ()) + } + None => Err(xous::Error::InvalidLimit), + } + } else { + self.ldo_ena[which as usize] = false; + let ctl = self.encode_ldo_ena(); + i2c.i2c_write(AXP2101_DEV, REG_LDO1_ENA as u8, &ctl).map(|_| ()) + } + } + + fn encode_dcdc_ena(&self) -> u8 { + let mut code = 0; + for (i, &ena) in self.dcdc_ena.iter().enumerate() { + if ena { + code |= 1 << i; + } + } + if self.fast_ramp { + code |= 1 << 5; + } + if self.force_ccm { + code |= 1 << 6; + } + code + } + + fn encode_ldo_ena(&self) -> [u8; 2] { + let mut ctl = [0u8; 2]; + for (i, &ena) in self.ldo_ena.iter().enumerate() { + if i < 8 { + if ena { + ctl[0] |= 1 << i; + } + } else { + if ena { + ctl[1] = 1; + } + } + } + ctl + } +} + +pub fn parse_dcdc_ena(d: u8) -> ([bool; 4], bool, bool) { + let mut enable = [false; 4]; + let mut fast_ramp = false; + let mut force_ccm = false; + + for (i, ena) in enable.iter_mut().enumerate() { + if ((d >> i) & 1) == 1 { + *ena = true; + } + } + if ((d >> 5) & 1) == 1 { + fast_ramp = true; + } + if ((d >> 6) & 1) == 1 { + force_ccm = true; + } + (enable, fast_ramp, force_ccm) +} + +pub fn encode_dcdc(v: f32, dvm: bool, which: WhichDcDc) -> Option { + match which { + WhichDcDc::Dcdc1 => { + if v < 1.5 || v > 3.4 { + None + } else { + let code = (v - 1.5) / 0.100; + return Some(code as u8 | if dvm { 0x80 } else { 0x0 }); + } + } + _ => { + if which == WhichDcDc::Dcdc2 || which == WhichDcDc::Dcdc3 { + if v < 0.5 || v > 1.54 { + return None; + } + } else if which == WhichDcDc::Dcdc4 { + if v < 0.5 || v > 1.84 { + return None; + } + } else { + // we haven't coded the DCDC5 path yet + return None; + } + if v < 1.22 { + let code = (v - 0.5) / 0.010; + Some(code as u8) + } else { + // high side already bounds checked above + let code = (v - 1.22) / 0.020; + Some(code as u8 + 70) + } + } + } +} + +pub fn parse_dcdc(d: u8, which: WhichDcDc) -> (f32, bool) { + match which { + WhichDcDc::Dcdc1 => { + let step = 0.10f32; + let dvm = (d & 0x80) != 0; + let voltage = ((d & 0x3F) as f32) * step + 1.5; + (voltage, dvm) + } + _ => { + let step = if (d & 0x7F) <= 71 { 0.010f32 } else { 0.020f32 }; + let dvm = (d & 0x80) != 0; + let voltage = ((d & 0x7F) as f32) * step + 0.5; + (voltage, dvm) + } + } +} + +pub fn parse_ldo(code: u8, ldo: WhichLdo) -> f32 { + let step: f32 = ldo.into(); + 0.5f32 + (code as f32) * step +} + +// returns a tuple of (code, register) +pub fn encode_ldo(v: f32, ldo: WhichLdo) -> Option { + let step: f32 = ldo.into(); + if step == 0.10 { + if v < 0.5 || v > 3.5 { + return None; + } + } else if step == 0.050 { + if v < 0.5 || v > 1.4 { + return None; + } + } + Some(((v - 0.5) / step) as u8) +}