import re
from enum import IntFlag
from ekfsm.core.components import SysTree
from ekfsm.devices.utils import retry
from ekfsm.exceptions import ConfigError, HWMonError
from ekfsm.log import ekfsm_logger
from ..core.probe import ProbeableDevice
from ..core.sysfs import list_sysfs_attributes, sysfs_root
from .generic import Device
__all__ = [
"PSUStatus",
"PMBus",
]
logger = ekfsm_logger(__name__)
[docs]
class PSUStatus(IntFlag):
"""
Represents the status of a PSU according to STATUS_BYTE register.
See also
--------
External Documentation:
`PMBus Power System Management Protocol Specification - Part II - Revision 1.4, Fig. 60 <https://pmbus.org/>`_
Example
-------
>>> from ekfsm.devices.pmbus import PsuStatus
>>> status = PsuStatus(0x1F)
>>> status
<PsuStatus.OUTPUT_OVERCURRENT|INPUT_UNDERVOLTAGE|TEMP_ANORMALY|COMMUNICATION_ERROR|ERROR: 31>
>>> PsuStatus.OUTPUT_OVERCURRENT in status
True
>>> # OK is always present
>>> PsuStatus.OK in status
True
>>> # Instead, check if status is OK
>>> status == PsuStatus(0x00)
False
>>> PsuStatus.OUTPUT_OVERCURRENT in status
False
"""
OUTPUT_OVERVOLTAGE = 0x20
OUTPUT_OVERCURRENT = 0x10
INPUT_UNDERVOLTAGE = 0x08
TEMP_ANORMALY = 0x04
COMMUNICATION_ERROR = 0x02
ERROR = 0x01
OK = 0x00
logger = ekfsm_logger(__name__)
[docs]
class PMBus(Device, ProbeableDevice):
def __init__(
self,
name: str,
parent: SysTree | None = None,
children: list[Device] | None = None,
abort: bool = False,
*args,
**kwargs,
):
super().__init__(name, parent, children, abort, *args, **kwargs)
self.addr = self.get_i2c_chip_addr()
self.sysfs_device = self.get_i2c_sysfs_device(self.addr, driver_required=True)
try:
for entry in self.sysfs_device.path.glob("hwmon/hwmon*"):
if entry.is_dir():
attrs = list_sysfs_attributes(entry)
self.sysfs_device.extend_attributes(attrs)
debug_attrs_path = sysfs_root().joinpath(
f"kernel/debug/pmbus/{entry.name}"
)
debug_attrs = list_sysfs_attributes(debug_attrs_path)
self.sysfs_device.extend_attributes(debug_attrs)
except FileNotFoundError:
logger.debug("Expected sysfs attribute not found")
except StopIteration:
raise HWMonError("Device is not managed by hwmon subsystem")
[docs]
def probe(self, *args, **kwargs) -> bool:
from ekfsm.core import HWModule
if not isinstance(self.hw_module, HWModule):
raise ConfigError(f"{self.name}: hw_module must be a HWModule instance")
# compare the regexp from the board yaml file with the model
return re.match(self.hw_module.id, self.model()) is not None
# Voltage and Current Interfaces
def __convert_and_scale(self, attr: str) -> float:
return self.sysfs.read_float(attr) / 1000.0
# Status Interface
# Temperature Interface
# Inventory Interface
[docs]
def vendor(self) -> str:
"""
Get the vendor of the PSU.
Returns
-------
PSU vendor
"""
return self.sysfs.read_utf8("vendor")
[docs]
def model(self) -> str:
"""
Get the model of the PSU.
Returns
-------
PSU model
"""
return self.sysfs.read_utf8("model")
[docs]
def serial(self) -> str:
"""
Get the serial number of the PSU.
Returns
-------
PSU serial number
"""
return self.sysfs.read_utf8("serial")
[docs]
def revision(self) -> str:
"""
Get the revision of the PSU.
Returns
-------
PSU revision
"""
return self.sysfs.read_utf8("revision")