From 9f0054f7b048746af6d6efb61b31e4b69ad29b70 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Mon, 5 Apr 2021 00:02:10 +1000 Subject: [PATCH] WiP PPS --- source/Core/BSP/Defines.h | 1 + source/Core/BSP/Miniware/Power.cpp | 1 + source/Core/BSP/Pine64/Power.cpp | 1 + source/Core/Drivers/FUSB302/policy_engine.cpp | 57 +++++++++++++------ source/Core/Drivers/FUSB302/policy_engine.h | 13 +++-- .../Drivers/FUSB302/policy_engine_user.cpp | 37 ++++++++++-- 6 files changed, 83 insertions(+), 27 deletions(-) diff --git a/source/Core/BSP/Defines.h b/source/Core/BSP/Defines.h index c7b5d9ff..f5a8d841 100644 --- a/source/Core/BSP/Defines.h +++ b/source/Core/BSP/Defines.h @@ -15,4 +15,5 @@ enum Orientation { ORIENTATION_LEFT_HAND = 0, ORIENTATION_RIGHT_HAND = 1, ORIENT #define TICKS_SECOND configTICK_RATE_HZ #define TICKS_MIN (60 * TICKS_SECOND) #define TICKS_100MS (TICKS_SECOND / 10) +#define TICKS_10MS (TICKS_100MS / 10) #endif /* BSP_DEFINES_H_ */ diff --git a/source/Core/BSP/Miniware/Power.cpp b/source/Core/BSP/Miniware/Power.cpp index f2f23534..10494ea9 100644 --- a/source/Core/BSP/Miniware/Power.cpp +++ b/source/Core/BSP/Miniware/Power.cpp @@ -12,6 +12,7 @@ bool FUSB302_present = false; void power_check() { #ifdef POW_PD if (FUSB302_present) { + PolicyEngine::PPSTimerCallback(); // Cant start QC until either PD works or fails if (PolicyEngine::setupCompleteOrTimedOut() == false) { return; diff --git a/source/Core/BSP/Pine64/Power.cpp b/source/Core/BSP/Pine64/Power.cpp index c7d18a6e..6de2fe82 100644 --- a/source/Core/BSP/Pine64/Power.cpp +++ b/source/Core/BSP/Pine64/Power.cpp @@ -12,6 +12,7 @@ bool FUSB302_present = false; void power_check() { #ifdef POW_PD if (FUSB302_present) { + PolicyEngine::PPSTimerCallback(); // Cant start QC until either PD works or fails if (PolicyEngine::setupCompleteOrTimedOut() == false) { return; diff --git a/source/Core/Drivers/FUSB302/policy_engine.cpp b/source/Core/Drivers/FUSB302/policy_engine.cpp index 2ab7f99a..30f591c1 100644 --- a/source/Core/Drivers/FUSB302/policy_engine.cpp +++ b/source/Core/Drivers/FUSB302/policy_engine.cpp @@ -16,6 +16,7 @@ */ #include "policy_engine.h" +#include "Defines.h" #include "fusb302b.h" #include "int_n.h" #include "protocol_tx.h" @@ -31,7 +32,6 @@ bool PolicyEngine::_explicit_contract; int8_t PolicyEngine::_hard_reset_counter; int8_t PolicyEngine::_old_tcc_match; uint8_t PolicyEngine::_pps_index; -uint8_t PolicyEngine::_last_pps; osThreadId PolicyEngine::TaskHandle = NULL; uint32_t PolicyEngine::TaskBuffer[PolicyEngine::TaskStackSize]; osStaticThreadDef_t PolicyEngine::TaskControlBlock; @@ -43,6 +43,8 @@ uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_ QueueHandle_t PolicyEngine::messagesWaiting = NULL; EventGroupHandle_t PolicyEngine::xEventGroupHandle = NULL; StaticEventGroup_t PolicyEngine::xCreatedEventGroup; +bool PolicyEngine::PPSTimerEnabled = false; +TickType_t PolicyEngine::PPSTimeLastEvent = 0; void PolicyEngine::init() { messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE, sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue); // Create static thread at PDB_PRIO_PE priority @@ -64,9 +66,7 @@ void PolicyEngine::pe_task(const void *arg) { /* Initialize the old_tcc_match */ _old_tcc_match = -1; /* Initialize the pps_index */ - _pps_index = 8; - /* Initialize the last_pps */ - _last_pps = 8; + _pps_index = 0xFF; for (;;) { // Loop based on state @@ -133,7 +133,7 @@ void PolicyEngine::pe_task(const void *arg) { PolicyEngine::policy_engine_state PolicyEngine::pe_sink_startup() { /* We don't have an explicit contract currently */ _explicit_contract = false; - + PPSTimerEnabled = false; // If desired could send an alert that PD is starting /* No need to reset the protocol layer here. There are two ways into this @@ -211,7 +211,9 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() { * PE_SNK_Select_Cap. */ /* Start by assuming we won't find a PPS APDO (set the index greater * than the maximum possible) */ - _pps_index = 8; + _pps_index = 0xFF; + /* New capabilities also means we can't be making a request from the + * same PPS APDO */ /* Search for the first PPS APDO */ for (int i = 0; i < PD_NUMOBJ_GET(&tempMessage); i++) { if ((tempMessage.obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (tempMessage.obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) { @@ -219,13 +221,9 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() { break; } } - /* New capabilities also means we can't be making a request from the - * same PPS APDO */ - _last_pps = 8; /* Ask the DPM what to request */ if (pdbs_dpm_evaluate_capability(&tempMessage, &_last_dpm_request)) { - return PESinkSelectCap; } @@ -248,6 +246,18 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() { if ((evt & PDB_EVT_PE_TX_ERR) == PDB_EVT_PE_TX_ERR) { return PESinkHardReset; } + /* If we're using PD 3.0 */ + if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) { + /* If the request was for a PPS APDO, start time callbacks if not started */ + if (PD_RDO_OBJPOS_GET(&_last_dpm_request) >= _pps_index) { + if (PPSTimerEnabled == false) { + PPSTimerEnabled = true; + PPSTimeLastEvent = xTaskGetTickCount(); + } + } else { + PPSTimerEnabled = false; + } + } /* Wait for a response */ evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE); @@ -265,7 +275,6 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() { readMessage(); /* If the source accepted our request, wait for the new power */ if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT && PD_NUMOBJ_GET(&tempMessage) == 0) { - return PESinkTransitionSink; /* If the message was a Soft_Reset, do the soft reset procedure */ } else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(&tempMessage) == 0) { @@ -280,7 +289,7 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() { return PESinkReady; } } else { - return PESinkSendSoftReset; + return PESinkSelectCap; } } return PESinkHardReset; @@ -306,15 +315,12 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_sink() { /* We just finished negotiating an explicit contract */ _explicit_contract = true; - /* Set the output appropriately */ + /* Negotiation finished */ pdbs_dpm_transition_requested(); return PESinkReady; /* If there was a protocol error, send a hard reset */ } else { - /* Turn off the power output before this hard reset to make sure we - * don't supply an incorrect voltage to the device we're powering. - */ pdbs_dpm_transition_default(); return PESinkHardReset; @@ -328,7 +334,7 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() { eventmask_t evt; /* Wait for an event */ - evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP); + evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP | PDB_EVT_PE_PPS_REQUEST); /* If we got reset signaling, transition to default */ if (evt & PDB_EVT_PE_RESET) { @@ -340,6 +346,12 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() { return PESinkHardReset; } + /* If SinkPPSPeriodicTimer ran out, send a new request */ + if (evt & PDB_EVT_PE_PPS_REQUEST) { + /* Tell the protocol layer we're starting an AMS */ + ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_START_AMS); + return PESinkSelectCap; + } /* If we received a message */ if (evt & PDB_EVT_PE_MSG_RX) { if (messageWaiting()) { @@ -631,3 +643,14 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_source_unresponsive() { uint32_t PolicyEngine::waitForEvent(uint32_t mask, TickType_t ticksToWait) { return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE, ticksToWait); } bool PolicyEngine::isPD3_0() { return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0; } + +void PolicyEngine::PPSTimerCallback() { + + if (PPSTimerEnabled) { + if (xTaskGetTickCount() - PPSTimeLastEvent > TICKS_100MS) { + // Send a new PPS message + PPSTimeLastEvent = xTaskGetTickCount(); + notify(PDB_EVT_PE_PPS_REQUEST); + } + } +} diff --git a/source/Core/Drivers/FUSB302/policy_engine.h b/source/Core/Drivers/FUSB302/policy_engine.h index c97ce063..f81b90e7 100644 --- a/source/Core/Drivers/FUSB302/policy_engine.h +++ b/source/Core/Drivers/FUSB302/policy_engine.h @@ -31,8 +31,8 @@ #define PDB_EVT_PE_TX_ERR EVENT_MASK(3) #define PDB_EVT_PE_HARD_SENT EVENT_MASK(4) #define PDB_EVT_PE_I_OVRTEMP EVENT_MASK(5) +#define PDB_EVT_PE_PPS_REQUEST EVENT_MASK(6) #define PDB_EVT_PE_MSG_RX_PEND EVENT_MASK(7) /* Never SEND THIS DIRECTLY*/ - class PolicyEngine { public: // Sets up internal state and registers the thread @@ -55,6 +55,8 @@ public: // Has pd negotiation completed static bool pdHasNegotiated() { return pdNegotiationComplete; } + static void PPSTimerCallback(); + private: static bool pdNegotiationComplete; static int current_voltage_mv; // The current voltage PD is expecting @@ -72,9 +74,8 @@ private: static int8_t _old_tcc_match; /* The index of the first PPS APDO */ static uint8_t _pps_index; - /* The index of the just-requested PPS APDO */ - static uint8_t _last_pps; - static void pe_task(const void *arg); + + static void pe_task(const void *arg); enum policy_engine_state { PESinkStartup, PESinkDiscovery, @@ -130,7 +131,9 @@ private: static QueueHandle_t messagesWaiting; static bool messageWaiting(); // Read a pending message into the temp message - static bool readMessage(); + static bool readMessage(); + static bool PPSTimerEnabled; + static TickType_t PPSTimeLastEvent; // These callbacks are called to implement the logic for the iron to select the desired voltage diff --git a/source/Core/Drivers/FUSB302/policy_engine_user.cpp b/source/Core/Drivers/FUSB302/policy_engine_user.cpp index 06a6c387..397d4791 100644 --- a/source/Core/Drivers/FUSB302/policy_engine_user.cpp +++ b/source/Core/Drivers/FUSB302/policy_engine_user.cpp @@ -59,6 +59,7 @@ bool PolicyEngine::pdbs_dpm_evaluate_capability(const union pd_msg *capabilities uint8_t bestIndex = 0xFF; int bestIndexVoltage = 0; int bestIndexCurrent = 0; + bool bestIsPPS = false; for (uint8_t i = 0; i < numobj; i++) { /* If we have a fixed PDO, its V equals our desired V, and its I is * at least our desired I */ @@ -83,15 +84,37 @@ bool PolicyEngine::pdbs_dpm_evaluate_capability(const union pd_msg *capabilities bestIndexVoltage = voltage_mv; bestIndexCurrent = current_a_x100; } + if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (capabilities->obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) { + // If this is a PPS slot, calculate the max voltage in the PPS range that can we be used and maintain + uint16_t max_voltage = PD_PAV2MV(PD_APDO_PPS_MAX_VOLTAGE_GET(capabilities->obj[i])); + // uint16_t min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(capabilities->obj[i])); + uint16_t max_current = PD_PAI2CA(PD_APDO_PPS_CURRENT_GET(capabilities->obj[i])); // max current in 10mA units + // Using the current and tip resistance, calculate the ideal max voltage + // if this is range, then we will work with this voltage + // if this is not in range; then max_voltage can be safely selected + int ideal_voltage_mv = (tipResistance * max_current); + if (ideal_voltage_mv > max_voltage) { + ideal_voltage_mv = max_voltage; // constrain + } + if (ideal_voltage_mv > (USB_PD_VMAX * 1000)) { + ideal_voltage_mv = (USB_PD_VMAX * 1000); // constrain to model max + } + if (ideal_voltage_mv > bestIndexVoltage) { + bestIndex = i; + bestIndexVoltage = ideal_voltage_mv; + bestIndexCurrent = max_current; + bestIsPPS = true; } } } if (bestIndex != 0xFF) { /* We got what we wanted, so build a request for that */ request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1); - - /* GiveBack disabled */ - request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(bestIndexCurrent) | PD_RDO_FV_CURRENT_SET(bestIndexCurrent) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(bestIndex + 1); + if (bestIsPPS) { + request->obj[0] = PD_RDO_PROG_CURRENT_SET(PD_CA2PAI(bestIndexCurrent)) | PD_RDO_PROG_VOLTAGE_SET(PD_MV2PRV(bestIndexVoltage)) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(bestIndex + 1); + } else { + request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(bestIndexCurrent) | PD_RDO_FV_CURRENT_SET(bestIndexCurrent) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(bestIndex + 1); + } // We support usb comms (ish) request->obj[0] |= PD_RDO_USB_COMMS; @@ -103,8 +126,7 @@ bool PolicyEngine::pdbs_dpm_evaluate_capability(const union pd_msg *capabilities /* Nothing matched (or no configuration), so get 5 V at low current */ request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1); request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(DPM_MIN_CURRENT) | PD_RDO_FV_CURRENT_SET(DPM_MIN_CURRENT) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(1); - /* If the output is enabled and we got here, it must be a capability - * mismatch. */ + /* If the output is enabled and we got here, it must be a capability mismatch. */ if (pdNegotiationComplete) { request->obj[0] |= PD_RDO_CAP_MISMATCH; } @@ -162,6 +184,11 @@ void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) { cap->obj[2] ^= cap->obj[1]; cap->obj[1] ^= cap->obj[2]; } + /* If we're using PD 3.0, add a PPS APDO for our desired voltage */ + if ((hdr_template & PD_HDR_SPECREV) >= PD_SPECREV_3_0) { + cap->obj[numobj++] + = PD_PDO_TYPE_AUGMENTED | PD_APDO_TYPE_PPS | PD_APDO_PPS_MAX_VOLTAGE_SET(PD_MV2PAV(voltage)) | PD_APDO_PPS_MIN_VOLTAGE_SET(PD_MV2PAV(voltage)) | PD_APDO_PPS_CURRENT_SET(PD_CA2PAI(current)); + } } /* Set the unconstrained power flag. */