blob: e262ede175063798bc520af6da93944fa9b95e29 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/devices/power/drivers/fusb302/fusb302-sensors.h"
#include <fidl/fuchsia.hardware.i2c/cpp/wire.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/types.h>
#include <cstdint>
#include <utility>
#include "src/devices/power/drivers/fusb302/registers.h"
#include "src/devices/power/drivers/fusb302/usb-pd-defs.h"
namespace fusb302 {
Fusb302Sensors::Fusb302Sensors(fidl::ClientEnd<fuchsia_hardware_i2c::Device>& i2c_channel,
inspect::Node root_node)
: i2c_(i2c_channel),
root_node_(std::move(root_node)),
vbus_power_good_(&root_node_, "VBusPowerGood", false),
cc_termination_(&root_node_, "CCTermination", usb_pd::ConfigChannelTermination::kUnknown),
power_role_detection_state_(&root_node, "PowerRoleDetection",
PowerRoleDetectionState::kDetecting),
detected_power_role_(&root_node, "DetectedPowerRole", usb_pd::PowerRole::kSink),
detected_wired_cc_pin_(&root_node, "DetectedCCPin", usb_pd::ConfigChannelPinSwitch::kNone) {}
Fusb302Sensors::~Fusb302Sensors() = default;
bool Fusb302Sensors::UpdateComparatorsResult() {
auto status0 = Status0Reg::ReadFrom(i2c_);
const bool vbus_power_good = status0.vbusok();
usb_pd::ConfigChannelTermination cc_termination;
if (configured_wired_cc_pin_ == usb_pd::ConfigChannelPinSwitch::kNone) {
// Measure block disconnected from CC pin.
cc_termination = usb_pd::ConfigChannelTermination::kUnknown;
} else if (configured_power_role_ == usb_pd::PowerRole::kSink) {
cc_termination = ConfigChannelTerminationFromFixedComparatorResult(status0.bc_lvl());
if (cc_termination == usb_pd::ConfigChannelTermination::kRp3000mA && status0.comp()) {
FDF_LOG(WARNING, "%s pin voltage exceeds 2.05V (c/o variable comparator)",
ConfigChannelPinSwitchToString(configured_wired_cc_pin_));
cc_termination = usb_pd::ConfigChannelTermination::kUnknown;
}
} else {
ZX_DEBUG_ASSERT_MSG(false, "Source sensing not implemented");
cc_termination = usb_pd::ConfigChannelTermination::kUnknown;
}
const bool state_changed =
(vbus_power_good_.get() != vbus_power_good) || (cc_termination_.get() != cc_termination);
vbus_power_good_.set(vbus_power_good);
cc_termination_.set(cc_termination);
if (state_changed) {
FDF_LOG(INFO, "Voltage comparators result: VBUS %s, CC wire termination %s",
vbus_power_good ? ">= 4.0 V" : "< 4.0 V",
ConfigChannelTerminationToString(cc_termination));
}
return state_changed;
}
bool Fusb302Sensors::UpdatePowerRoleDetectionResult() {
const PowerRoleDetectionState power_role_detection_state = Status1AReg::ReadFrom(i2c_).togss();
const bool state_changed = power_role_detection_state_.get() != power_role_detection_state;
power_role_detection_state_.set(power_role_detection_state);
if (state_changed) {
FDF_LOG(INFO, "Power role detection state: %s",
PowerRoleDetectionStateToString(power_role_detection_state));
SyncDerivedPowerRoleDetectionState();
}
return state_changed;
}
void Fusb302Sensors::SyncDerivedPowerRoleDetectionState() {
const PowerRoleDetectionState power_role_detection_state = power_role_detection_state_.get();
const usb_pd::ConfigChannelPinSwitch wired_cc_pin =
WiredCcPinFromPowerRoleDetectionState(power_role_detection_state);
detected_wired_cc_pin_.set(wired_cc_pin);
if (wired_cc_pin == usb_pd::ConfigChannelPinSwitch::kNone) {
FDF_LOG(TRACE, "Power role detection result: running");
detected_power_role_.set(usb_pd::PowerRole::kSink);
return;
}
// Only safe to do after we know that `togss` corresponds
// to an attached state.
const usb_pd::PowerRole power_role = PowerRoleFromDetectionState(power_role_detection_state);
detected_power_role_.set(power_role);
FDF_LOG(INFO, "Power role detection result: state %s, power %s, Config Channel on %s",
PowerRoleDetectionStateToString(power_role_detection_state),
usb_pd::PowerRoleToString(power_role),
usb_pd::ConfigChannelPinSwitchToString(wired_cc_pin));
}
} // namespace fusb302