Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ where I2C::Error: core::fmt::Debug
### Protocol Configuration

```rust
use sw2303::{SW2303, ProtocolConfiguration, PdConfiguration, TypeCConfiguration, ProtocolType, registers::constants::DEFAULT_ADDRESS};
use sw2303::{SW2303, ProtocolConfiguration, PdConfiguration, PpsConfigMode, TypeCConfiguration, ProtocolType, registers::constants::DEFAULT_ADDRESS};
use embedded_hal::i2c::I2c;

#[cfg(not(feature = "async"))]
Expand Down Expand Up @@ -106,6 +106,7 @@ where I2C::Error: core::fmt::Debug
dr_swap: false,
emarker_enabled: true,
pps_enabled: true,
pps_config_mode: PpsConfigMode::Auto,
fixed_voltages: [true, true, true, false], // Enable 9V, 12V, 15V
emark_5a_bypass: false,
emarker_60_70w: true,
Expand Down
78 changes: 78 additions & 0 deletions src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,21 @@ pub struct ProtocolConfiguration {

// Default derived above

/// PPS advertisement source-selection mode.
///
/// `Auto` keeps the SW2303's built-in PPS profile selection logic.
/// `Register` tells the chip to source PPS advertisement from register-backed
/// configuration instead.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PpsConfigMode {
/// Let SW2303 choose PPS advertisement automatically.
#[default]
Auto,
/// Use register-backed PPS profile configuration.
Register,
}

/// PD-specific configuration for SW2303.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand All @@ -236,6 +251,11 @@ pub struct PdConfiguration {
pub emarker_enabled: bool,
/// Whether PPS (Programmable Power Supply) is enabled
pub pps_enabled: bool,
/// How PPS advertisement is sourced.
///
/// This controls REG `0xB4` bit 7 (`0 = auto`, `1 = register config`) and
/// is intentionally separate from `pps_enabled`.
pub pps_config_mode: PpsConfigMode,
/// Fixed voltage levels to enable (9V, 12V, 15V, 20V)
/// Each bit represents: [9V, 12V, 15V, 20V]
pub fixed_voltages: [bool; 4],
Expand All @@ -253,13 +273,71 @@ impl Default for PdConfiguration {
dr_swap: false,
emarker_enabled: false,
pps_enabled: false,
pps_config_mode: PpsConfigMode::Auto,
fixed_voltages: [false; 4], // All voltages disabled by default
emark_5a_bypass: false,
emarker_60_70w: false,
}
}
}

/// Structured PD capability snapshot decoded from SW2303 PD configuration
/// registers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PdCapabilityStatus {
/// Whether PD protocol is enabled.
pub enabled: bool,
/// Whether VCONN swap is supported.
pub vconn_swap: bool,
/// Whether data-role swap is supported.
pub dr_swap: bool,
/// Whether emarker detection is enabled.
pub emarker_enabled: bool,
/// Whether 60-70W operation bypasses emarker requirement.
pub emarker_60_70w: bool,
/// Whether any PPS range is enabled.
pub pps_enabled: bool,
/// Whether PPS advertisement is auto-generated or register-backed.
pub pps_config_mode: PpsConfigMode,
/// Advertised fixed-voltage PDOs [9V, 12V, 15V, 20V].
pub fixed_voltages: [bool; 4],
/// Advertised PPS ranges [3.3-5.9V, 3.3-11V, 3.3-16V, 3.3-21V].
pub pps_ranges: [bool; 4],
/// PPS3 current limit in milliamps.
pub pps3_current_limit_ma: u16,
/// Whether Discovery Identity command support is enabled.
pub discovery_identity_enabled: bool,
/// Whether Discovery SVID command support is enabled.
pub discovery_svid_enabled: bool,
/// Raw peak-current setting from REG 0xB4 bits 1-0.
pub peak_current_setting: u8,
/// Whether PD 5A emarker check is bypassed.
pub emark_5a_bypass: bool,
}

impl PdCapabilityStatus {
/// Returns the highest enabled PPS voltage in millivolts.
pub const fn max_pps_voltage_mv(&self) -> Option<u16> {
if self.pps_ranges[3] {
Some(21_000)
} else if self.pps_ranges[2] {
Some(16_000)
} else if self.pps_ranges[1] {
Some(11_000)
} else if self.pps_ranges[0] {
Some(5_900)
} else {
None
}
}

/// Returns true when PPS advertisement should include ranges above 11V.
pub const fn supports_pps_above_11v(&self) -> bool {
self.pps_ranges[2] || self.pps_ranges[3]
}
}

/// Fast charging configuration for SW2303.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand Down
172 changes: 135 additions & 37 deletions src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! This driver provides methods to interact with the SW2303 for UFP detection and charging management.

use crate::data_types::{
FastChargeConfiguration, PdConfiguration, PowerRequest, ProtocolConfiguration, ProtocolType,
TypeCConfiguration,
FastChargeConfiguration, PdCapabilityStatus, PdConfiguration, PowerRequest, PpsConfigMode,
ProtocolConfiguration, ProtocolType, TypeCConfiguration,
};
use crate::error::Error;
use crate::registers::{
Expand Down Expand Up @@ -85,7 +85,11 @@ where
pub async fn read_register(&mut self, register: Register) -> Result<u8, Error<I2C::Error>> {
let mut buffer = [0u8; 1];
self.i2c
.write_read(self.address, &[register.addr()], &mut buffer)
.write(self.address, &[register.addr()])
.await
.map_err(Error::I2c)?;
self.i2c
.read(self.address, &mut buffer)
.await
.map_err(Error::I2c)?;
Ok(buffer[0])
Expand Down Expand Up @@ -1145,11 +1149,24 @@ where
let mut config2 = self.get_fast_charge_config_2_raw().await?;
let mut config3 = self.get_fast_charge_config_3_raw().await?;

// QC protocols
// 有源低:清除禁用位=使能;置禁用位=禁用
if config.qc20_enabled || config.qc30_enabled {
// Fast charge global gate (active-low disable bit).
// If any fast-charge family is requested, clear the global disable bit.
if config.qc20_enabled
|| config.qc30_enabled
|| config.fcp_enabled
|| config.afc_enabled
|| config.scp_enabled
|| config.pe20_enabled
|| config.bc12_enabled
|| config.sfcp_enabled
{
config2.remove(FastChargeConfig2Flags::FAST_CHARGE_DISABLE);
} else {
config2.insert(FastChargeConfig2Flags::FAST_CHARGE_DISABLE);
}

// QC protocols
// 有源低:清除禁用位=使能;置禁用位=禁用
if config.qc20_enabled {
config2.remove(FastChargeConfig2Flags::QC2_DISABLE);
} else {
Expand Down Expand Up @@ -1264,7 +1281,8 @@ where
|| config.afc_enabled
|| config.scp_enabled
|| config.pe20_enabled
|| config.sfcp_enabled;
|| config.sfcp_enabled
|| config.bc12_enabled;
if any_fast {
config2.remove(FastChargeConfig2Flags::FAST_CHARGE_DISABLE);
} else {
Expand Down Expand Up @@ -1344,13 +1362,14 @@ where

Ok(FastChargeConfiguration {
qc_enabled: qc2_ok || qc3_ok,
fcp_enabled: !config3.contains(FastChargeConfig3Flags::FCP_DISABLE),
afc_enabled: !config3.contains(FastChargeConfig3Flags::AFC_DISABLE),
scp_enabled: !config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE),
pe20_enabled: !config3.contains(FastChargeConfig3Flags::PE_DISABLE),
sfcp_enabled: !config3.contains(FastChargeConfig3Flags::SFCP_DISABLE),
bc12_enabled: !config2.contains(FastChargeConfig2Flags::BC12_DISABLE),
fcp_enabled: fast_ok && !config3.contains(FastChargeConfig3Flags::FCP_DISABLE),
afc_enabled: fast_ok && !config3.contains(FastChargeConfig3Flags::AFC_DISABLE),
scp_enabled: fast_ok
&& (!config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE)),
pe20_enabled: fast_ok && !config3.contains(FastChargeConfig3Flags::PE_DISABLE),
sfcp_enabled: fast_ok && !config3.contains(FastChargeConfig3Flags::SFCP_DISABLE),
bc12_enabled: fast_ok && !config2.contains(FastChargeConfig2Flags::BC12_DISABLE),
scp_current_limit: (config4.bits() & FastChargeConfig4Flags::SCP_CURRENT_MASK.bits())
>> 4,
fcp_afc_sfcp_2_25a: config0.contains(FastChargeConfig0Flags::FCP_AFC_SFCP_2_25A),
Expand Down Expand Up @@ -1405,13 +1424,15 @@ where

// Configure PD Config 1 (REG 0xB4) 基于现值修改
let mut config1 = self.get_pd_config_1_raw().await?;
if config.pps_enabled {
if config.pps_config_mode == PpsConfigMode::Register {
config1.insert(PdConfig1Flags::PPS_REGISTER_CONFIG);
} else {
config1.remove(PdConfig1Flags::PPS_REGISTER_CONFIG);
}
if config.enabled {
config1.insert(PdConfig1Flags::DISCOVERY_IDENTITY | PdConfig1Flags::DISCOVERY_SVID);
} else {
config1.remove(PdConfig1Flags::DISCOVERY_IDENTITY | PdConfig1Flags::DISCOVERY_SVID);
}
self.set_pd_config_1_raw(config1).await?;

Expand Down Expand Up @@ -1470,6 +1491,55 @@ where
Ok(())
}

/// Get structured PD capability status decoded from PD configuration
/// registers.
pub async fn get_pd_capability_status(
&mut self,
) -> Result<PdCapabilityStatus, Error<I2C::Error>> {
let config0 = self.get_pd_config_0_raw().await?;
let config1 = self.get_pd_config_1_raw().await?;
let config2 = self.get_pd_config_2_raw().await?;
let config3 = self.get_pd_config_3_raw().await?;

let fixed_voltages = [
!config2.contains(PdConfig2Flags::FIXED_9V_DISABLE),
!config2.contains(PdConfig2Flags::FIXED_12V_DISABLE),
!config2.contains(PdConfig2Flags::FIXED_15V_DISABLE),
!config2.contains(PdConfig2Flags::FIXED_20V_DISABLE),
];
let pps_ranges = [
!config2.contains(PdConfig2Flags::PPS0_DISABLE),
!config2.contains(PdConfig2Flags::PPS1_DISABLE),
!config2.contains(PdConfig2Flags::PPS2_DISABLE),
!config2.contains(PdConfig2Flags::PPS3_DISABLE),
];

Ok(PdCapabilityStatus {
enabled: !config0.contains(PdConfig0Flags::PD_DISABLE),
vconn_swap: config0.contains(PdConfig0Flags::VCONN_SWAP),
dr_swap: config0.contains(PdConfig0Flags::DR_SWAP),
emarker_enabled: !config0.contains(PdConfig0Flags::EMARKER_DETECT_DISABLE),
emarker_60_70w: config0.contains(PdConfig0Flags::EMARKER_60_70W),
pps_enabled: pps_ranges[0] || pps_ranges[1] || pps_ranges[2] || pps_ranges[3],
pps_config_mode: if config1.contains(PdConfig1Flags::PPS_REGISTER_CONFIG) {
PpsConfigMode::Register
} else {
PpsConfigMode::Auto
},
fixed_voltages,
pps_ranges,
pps3_current_limit_ma: if config1.contains(PdConfig1Flags::PPS3_3A_LIMIT) {
3_000
} else {
5_000
},
discovery_identity_enabled: config1.contains(PdConfig1Flags::DISCOVERY_IDENTITY),
discovery_svid_enabled: config1.contains(PdConfig1Flags::DISCOVERY_SVID),
peak_current_setting: config1.bits() & PdConfig1Flags::PEAK_CURRENT_MASK.bits(),
emark_5a_bypass: config3.contains(PdConfig3Flags::EMARK_5A_BYPASS),
})
}

/// Get current protocol status.
///
/// This method reads the current protocol configuration from the device.
Expand All @@ -1486,22 +1556,22 @@ where
let fc_config3 = self.get_fast_charge_config_3_raw().await?;

let fast_ok = !fc_config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE);
let qc_ok = fast_ok
&& (!fc_config2.contains(FastChargeConfig2Flags::QC2_DISABLE)
|| !fc_config2.contains(FastChargeConfig2Flags::QC3_DISABLE));
let qc20_ok = fast_ok && !fc_config2.contains(FastChargeConfig2Flags::QC2_DISABLE);
let qc30_ok = fast_ok && !fc_config2.contains(FastChargeConfig2Flags::QC3_DISABLE);

Ok(ProtocolConfiguration {
pd_enabled: !pd_config0.contains(PdConfig0Flags::PD_DISABLE),
qc20_enabled: qc_ok,
qc30_enabled: qc_ok,
qc20_enabled: qc20_ok,
qc30_enabled: qc30_ok,
// 0=使能,1=不使能(取反)
fcp_enabled: !fc_config3.contains(FastChargeConfig3Flags::FCP_DISABLE),
afc_enabled: !fc_config3.contains(FastChargeConfig3Flags::AFC_DISABLE),
scp_enabled: !fc_config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !fc_config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE),
pe20_enabled: !fc_config3.contains(FastChargeConfig3Flags::PE_DISABLE),
bc12_enabled: !fc_config2.contains(FastChargeConfig2Flags::BC12_DISABLE),
sfcp_enabled: !fc_config3.contains(FastChargeConfig3Flags::SFCP_DISABLE),
fcp_enabled: fast_ok && !fc_config3.contains(FastChargeConfig3Flags::FCP_DISABLE),
afc_enabled: fast_ok && !fc_config3.contains(FastChargeConfig3Flags::AFC_DISABLE),
scp_enabled: fast_ok
&& (!fc_config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !fc_config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE)),
pe20_enabled: fast_ok && !fc_config3.contains(FastChargeConfig3Flags::PE_DISABLE),
bc12_enabled: fast_ok && !fc_config2.contains(FastChargeConfig2Flags::BC12_DISABLE),
sfcp_enabled: fast_ok && !fc_config3.contains(FastChargeConfig3Flags::SFCP_DISABLE),
})
}

Expand All @@ -1521,38 +1591,66 @@ where
) -> Result<bool, Error<I2C::Error>> {
match protocol {
ProtocolType::PD => self.is_pd_protocol_enabled().await,
ProtocolType::QC20 | ProtocolType::QC30 => {
ProtocolType::QC20 => {
let config2 = self.get_fast_charge_config_2_raw().await?;
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& (!config2.contains(FastChargeConfig2Flags::QC2_DISABLE)
|| !config2.contains(FastChargeConfig2Flags::QC3_DISABLE)),
&& !config2.contains(FastChargeConfig2Flags::QC2_DISABLE),
)
}
ProtocolType::QC30 => {
let config2 = self.get_fast_charge_config_2_raw().await?;
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config2.contains(FastChargeConfig2Flags::QC3_DISABLE),
)
}
ProtocolType::FCP => {
let config2 = self.get_fast_charge_config_2_raw().await?;
let config3 = self.get_fast_charge_config_3_raw().await?;
Ok(!config3.contains(FastChargeConfig3Flags::FCP_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config3.contains(FastChargeConfig3Flags::FCP_DISABLE),
)
}
ProtocolType::AFC => {
let config2 = self.get_fast_charge_config_2_raw().await?;
let config3 = self.get_fast_charge_config_3_raw().await?;
Ok(!config3.contains(FastChargeConfig3Flags::AFC_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config3.contains(FastChargeConfig3Flags::AFC_DISABLE),
)
}
ProtocolType::SCP => {
let config2 = self.get_fast_charge_config_2_raw().await?;
Ok(!config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& (!config2.contains(FastChargeConfig2Flags::SCP_HV_DISABLE)
|| !config2.contains(FastChargeConfig2Flags::SCP_LV_DISABLE)),
)
}
ProtocolType::PE20 => {
let config2 = self.get_fast_charge_config_2_raw().await?;
let config3 = self.get_fast_charge_config_3_raw().await?;
Ok(!config3.contains(FastChargeConfig3Flags::PE_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config3.contains(FastChargeConfig3Flags::PE_DISABLE),
)
}
ProtocolType::BC12 => {
let config2 = self.get_fast_charge_config_2_raw().await?;
Ok(!config2.contains(FastChargeConfig2Flags::BC12_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config2.contains(FastChargeConfig2Flags::BC12_DISABLE),
)
}
ProtocolType::SFCP => {
let config2 = self.get_fast_charge_config_2_raw().await?;
let config3 = self.get_fast_charge_config_3_raw().await?;
Ok(!config3.contains(FastChargeConfig3Flags::SFCP_DISABLE))
Ok(
!config2.contains(FastChargeConfig2Flags::FAST_CHARGE_DISABLE)
&& !config3.contains(FastChargeConfig3Flags::SFCP_DISABLE),
)
}
}
}
Expand Down
Loading
Loading