SensorLib 0.5.0
Multi-platform sensor driver library for Arduino, PlatformIO, and ESP-IDF
Loading...
Searching...
No Matches
AXP517PdNegotiator.cpp
Go to the documentation of this file.
1
30#include "../../../SensorBuildOpt.h"
31#if !SENSORLIB_EXCLUDE_PMIC_AXP517
32
34#include "UsbPdDefs.hpp"
35#include "../../../platform/SensorLibLog.hpp"
36
37using namespace usbpd;
38
39namespace
40{
41static constexpr uint8_t MSG_SOURCE_CAP = 0x01;
42static constexpr uint8_t MSG_REQUEST = 0x02;
43static constexpr uint8_t MSG_ACCEPT = 0x03;
44static constexpr uint8_t MSG_REJECT = 0x04;
45static constexpr uint8_t MSG_PS_RDY = 0x06;
46
47#if defined(INPUT_PULLUP)
48static constexpr uint8_t IRQ_INPUT_MODE = INPUT_PULLUP;
49#elif defined(INPUT)
50static constexpr uint8_t IRQ_INPUT_MODE = INPUT;
51#else
52static constexpr uint8_t IRQ_INPUT_MODE = 0;
53#endif
54static constexpr uint8_t IRQ_ACTIVE_LOW = 0;
55static constexpr uint8_t MAX_TX_RETRY = 100;
56static constexpr uint16_t ACTIONABLE_ALERT_MASK =
65
66static bool configureSinkRx(AXP517Pd &pd)
67{
69 return pd.configureHeaderInfo(hi) && pd.setReceiveDetectSop(true);
70}
71} // namespace
72
74 : _tcpc(tcpc), _pd(pd)
75{
76}
77
79{
80 if (irqPin < 0) {
81 _irqPin = -1;
82 return false;
83 }
84
85 _irqPin = irqPin;
86 _tcpc.pinMode(static_cast<uint8_t>(_irqPin), IRQ_INPUT_MODE);
87 return true;
88}
89
91{
92 if (!setIrqPin(irqPin)) {
93 SENSORLIB_LOG_E("AXP517 PD requires an IRQ pin");
94 return false;
95 }
96
97 if (!_tcpc.checkVendorId()) {
98 SENSORLIB_LOG_E("AXP517 TCPC vendor id mismatch");
99 return false;
100 }
101
102 if (!_tcpc.init()) {
103 SENSORLIB_LOG_E("AXP517 TCPC init failed");
104 return false;
105 }
106
107 if (!configureSinkRx(_pd)) {
108 SENSORLIB_LOG_E("AXP517 PD sink config failed");
109 return false;
110 }
111
112 _pd.resetMsgId();
113 _tcpc.clearPmicIrqStatus();
114 _state = State::WaitSourceCap;
115 _sinkReady = true;
116 _txPending = false;
117 _txRetry = 0;
118 return true;
119}
120
122{
123 _pd.resetMsgId();
124 _state = _sinkReady ? State::WaitSourceCap : State::Idle;
125 _txPending = false;
126 _txRetry = 0;
127}
128
130 uint32_t timeoutMs)
131{
132 if (!hasIrqPin()) {
133 SENSORLIB_LOG_E("AXP517 PD request skipped: IRQ pin not configured");
134 return false;
135 }
136
137 if (!_sinkReady) {
138 if (!initSink(_irqPin)) return false;
139 } else {
140 if (!_tcpc.init()) {
141 SENSORLIB_LOG_E("AXP517 TCPC init failed");
142 return false;
143 }
144 if (!configureSinkRx(_pd)) {
145 SENSORLIB_LOG_E("AXP517 PD sink config failed");
146 return false;
147 }
148 _tcpc.clearPmicIrqStatus();
149 }
150
151 reset();
152
153 uint16_t pending = 0;
154 if (_tcpc.readAlert(pending) && pending) {
155 Event event = Event::None;
156 if (!handleIrq(req, event, outCaps)) return false;
157 if (isDone()) return isSuccess();
158 }
159
160 uint32_t deadline = _tcpc.millis() + timeoutMs;
161 while (_tcpc.millis() < deadline) {
162 if (_tcpc.digitalRead(static_cast<uint8_t>(_irqPin)) == IRQ_ACTIVE_LOW) {
163 Event event = Event::None;
164 if (!service(req, event, outCaps)) return false;
165 if (isDone()) return isSuccess();
166 }
167 _tcpc.delayMs(1);
168 }
169
170 SENSORLIB_LOG_W("AXP517 PD negotiation timeout");
171 return false;
172}
173
175 SourceCaps *outCaps)
176{
177 outEvent = Event::None;
178 if (!hasIrqPin()) {
179 SENSORLIB_LOG_E("AXP517 PD IRQ pin not configured");
180 return false;
181 }
182
183 if (_tcpc.digitalRead(static_cast<uint8_t>(_irqPin)) != IRQ_ACTIVE_LOW) {
184 return true;
185 }
186
187 while (true) {
188 if (!handleIrq(req, outEvent, outCaps)) return false;
189 if (isDone()) return true;
190
191 uint16_t alert = 0;
192 if (!_tcpc.readAlert(alert)) return false;
193 if (alert == 0) break;
194 if ((alert & ACTIONABLE_ALERT_MASK) == 0) break;
195 if (_tcpc.digitalRead(static_cast<uint8_t>(_irqPin)) != IRQ_ACTIVE_LOW) break;
196 }
197
198 return true;
199}
200
202 SourceCaps *outCaps)
203{
204 outEvent = Event::None;
205 if (!hasIrqPin()) {
206 SENSORLIB_LOG_E("AXP517 PD IRQ pin not configured");
207 return false;
208 }
209
210 if (!_tcpc.clearPmicIrqStatus()) return false;
211
212 uint16_t alert = 0;
213 if (!_tcpc.readAlert(alert)) return false;
214 if (alert == 0) return true;
215
216 uint16_t clearMask = alert & ~AXP517Tcpc::AlertRxStatus;
217 if (clearMask && !_tcpc.clearAlert(clearMask)) return false;
218
219 if (alert & AXP517Tcpc::AlertFault) {
220 uint8_t fault = 0;
221 if (!_tcpc.readFaultStatus(fault)) return false;
222 if (fault && !_tcpc.clearFaultStatus(fault)) return false;
223 }
224
225 if (alert & AXP517Tcpc::AlertRxHardReset) {
226 _tcpc.clearTx();
228 _tcpc.clearAllAlerts();
229 reset();
230 outEvent = Event::HardReset;
231 return true;
232 }
233
234 if (alert & (AXP517Tcpc::AlertCcStatus |
237 if (!_tcpc.isVbusPresent() || !_tcpc.isAttached()) {
238 _tcpc.clearTx();
239 reset();
240 outEvent = Event::CcDetached;
241 return true;
242 }
243 }
244
245 if (alert & AXP517Tcpc::AlertCcStatus) {
246 _tcpc.writeReceiveDetect(0x01);
247 if (!_tcpc.setPolarityFromCc()) return false;
248 outEvent = Event::CcChanged;
249 }
250
251 if (alert & AXP517Tcpc::AlertTxSuccess) {
252 _txPending = false;
253 _txRetry = 0;
254 _tcpc.writeReceiveDetect(0x01);
255 outEvent = Event::TxSuccess;
256 }
257
258 if (alert & AXP517Tcpc::AlertTxDiscarded) {
259 _tcpc.clearTx();
260 _txPending = false;
261 if (++_txRetry >= MAX_TX_RETRY) {
262 reset();
263 } else {
264 _tcpc.writeReceiveDetect(0x01);
265 }
266 outEvent = Event::TxDiscarded;
267 }
268
269 if (alert & AXP517Tcpc::AlertTxFailed) {
270 _tcpc.clearTx();
271 _txPending = false;
272 if (++_txRetry >= MAX_TX_RETRY) {
273 reset();
274 } else {
275 _tcpc.writeReceiveDetect(0x01);
276 }
277 outEvent = Event::TxFailed;
278 }
279
280 if (alert & AXP517Tcpc::AlertRxStatus) {
282 bool rxOk = _tcpc.rxReadMessage(rx);
284 if (!rxOk) return true;
285
286 Header hdr(rx.headerLE);
287 uint8_t msgType = hdr.msgType();
288 uint8_t objCount = hdr.objCount();
289
290 if (msgType == MSG_SOURCE_CAP && objCount > 0) {
291 outEvent = Event::SourceCapabilities;
292 if (_state == State::WaitSourceCap || (_state == State::WaitAccept && !_txPending)) {
293 SourceCaps caps{};
294 if (!parseSourceCaps(rx, caps)) {
295 _state = State::Failed;
296 return true;
297 }
298 if (outCaps) *outCaps = caps;
299
300 if (!requestFromCaps(caps, req)) {
301 _state = State::Failed;
302 return true;
303 }
304
305 _txPending = true;
306 _state = State::WaitAccept;
307 outEvent = Event::RequestSent;
308 }
309 } else if (_state == State::WaitAccept && msgType == MSG_ACCEPT && objCount == 0) {
310 _txPending = false;
311 _state = State::WaitPsRdy;
312 _tcpc.writeReceiveDetect(0x01);
313 outEvent = Event::Accept;
314 } else if (_state == State::WaitAccept && msgType == MSG_REJECT && objCount == 0) {
315 _state = State::Failed;
316 outEvent = Event::Reject;
317 } else if (_state == State::WaitPsRdy && msgType == MSG_PS_RDY && objCount == 0) {
318 _state = State::Success;
319 outEvent = Event::PsRdy;
320 }
321 }
322
323 return true;
324}
325
327{
328 outCaps = SourceCaps{};
329
330 Header hdr(rx.headerLE);
331 if (hdr.msgType() != MSG_SOURCE_CAP || hdr.objCount() == 0) return false;
332
333 outCaps.rawHeaderLE = rx.headerLE;
334 outCaps.totalPdoCount = hdr.objCount();
335
336 uint8_t parseCount = static_cast<uint8_t>(rx.payloadLen / 4);
337 if (parseCount > outCaps.totalPdoCount) parseCount = outCaps.totalPdoCount;
338 if (parseCount > 7) parseCount = 7;
339
340 for (uint8_t i = 0; i < parseCount; ++i) {
341 uint32_t pdo = le32(&rx.payload[i * 4]);
342 FixedSourcePdo fixed{};
343 if (!parseFixedSourcePdo(pdo, fixed)) continue;
344
345 FixedOffer &offer = outCaps.fixed[outCaps.fixedCount++];
346 offer.mv = fixed.mv;
347 offer.maMax = fixed.ma;
348 offer.pdoIndex1Based = static_cast<uint8_t>(i + 1);
349 offer.rawPdo = pdo;
350 if (outCaps.fixedCount >= 7) break;
351 }
352
353 return outCaps.fixedCount > 0;
354}
355
357 FixedOffer &outOffer)
358{
359 if (caps.fixedCount == 0) return false;
360
361 if (req.pdoIndex1Based > 0) {
362 for (uint8_t i = 0; i < caps.fixedCount; ++i) {
363 if (caps.fixed[i].pdoIndex1Based == req.pdoIndex1Based) {
364 outOffer = caps.fixed[i];
365 return true;
366 }
367 }
368 return false;
369 }
370
371 if (req.targetMv > 0) {
372 for (uint8_t i = 0; i < caps.fixedCount; ++i) {
373 if (caps.fixed[i].mv == req.targetMv) {
374 outOffer = caps.fixed[i];
375 return true;
376 }
377 }
378 return false;
379 }
380
381 outOffer = caps.fixed[0];
382 return true;
383}
384
386{
387 FixedOffer offer{};
388 if (!selectFixedOffer(caps, req, offer)) return false;
389
390 if (!_tcpc.clearTx()) return false;
391 if (!_tcpc.clearAllAlerts()) return false;
392 return sendRequest(offer, req);
393}
394
395bool AXP517PdNegotiator::sendRequest(const FixedOffer &offer, const RequestParams &req)
396{
397 uint16_t opMa = req.opMa ? req.opMa : offer.maMax;
398 if (offer.maMax && opMa > offer.maMax) opMa = offer.maMax;
399
400 uint16_t maxMa = req.maxMa ? req.maxMa : opMa;
401 if (offer.maMax && maxMa > offer.maMax) maxMa = offer.maMax;
402
403 uint32_t rdo = buildRdoFixedCurrent(offer.pdoIndex1Based, opMa, maxMa,
404 req.capMismatch, req.usbCommCapable,
405 req.noUsbSuspend);
406 uint8_t payload[4] = {0};
407 wr_le32(payload, rdo);
408
409 return _pd.sendRaw(MSG_REQUEST, payload, sizeof(payload));
410}
411
412#endif
@license MIT License
#define SENSORLIB_LOG_W(...)
#define SENSORLIB_LOG_E(...)
@license MIT License
bool handleIrq(const RequestParams &req, Event &outEvent, SourceCaps *outCaps=nullptr)
Process one TCPC IRQ status sample.
@ WaitAccept
Request sent; waiting for Accept.
@ Success
Requested contract is ready.
@ WaitPsRdy
Accept received; waiting for PS_RDY.
@ WaitSourceCap
Waiting for Source_Capabilities.
@ Failed
Negotiation failed.
@ Idle
Not initialized or inactive.
AXP517PdNegotiator(AXP517Tcpc &tcpc, AXP517Pd &pd)
Construct a negotiator from the low-level TCPC and PD helpers.
bool selectFixedOffer(const SourceCaps &caps, const RequestParams &req, FixedOffer &outOffer)
Select one fixed PDO offer matching the request.
bool hasIrqPin() const
Check whether a valid IRQ pin is configured.
void reset()
Reset message IDs and return to the initial wait state.
bool negotiate(const RequestParams &req, SourceCaps *outCaps=nullptr, uint32_t timeoutMs=6000)
Run a blocking fixed PDO negotiation.
bool setIrqPin(int irqPin)
Configure the PMIC/TCPC IRQ pin used for PD negotiation.
bool requestFromCaps(const SourceCaps &caps, const RequestParams &req)
Send a Request message selected from parsed source capabilities.
bool service(const RequestParams &req, Event &outEvent, SourceCaps *outCaps=nullptr)
Service pending IRQ-driven PD events.
bool parseSourceCaps(const AXP517Tcpc::RxFifoMsg &rx, SourceCaps &outCaps)
Parse fixed PDOs from a Source_Capabilities RX message.
bool initSink(int irqPin)
Initialize the TCPC sink and configure the PD receive path.
bool isDone() const
Check whether negotiation reached a terminal state.
bool isSuccess() const
Check whether negotiation completed successfully.
Event
Event reported by service() and handleIrq().
@ PsRdy
PS_RDY received; contract is active.
@ TxDiscarded
TCPC discarded TX due to RX activity.
@ TxFailed
TCPC reported TX failure.
@ None
No meaningful state change.
@ HardReset
Hard Reset received.
@ RequestSent
Request message transmitted.
@ Accept
Accept control message received.
@ SourceCapabilities
Source_Capabilities received.
@ Reject
Reject control message received.
@ CcDetached
CC or VBUS detach detected.
@ TxSuccess
TCPC reported TX success.
@ CcChanged
CC state changed while attached.
USB-PD message builder and FIFO helper for AXP517.
Definition AXP517Pd.hpp:45
void resetMsgId()
Reset the local USB-PD message ID counter to zero.
Definition AXP517Pd.hpp:164
bool sendRaw(uint8_t msgType, const uint8_t *payload, uint8_t payloadLen)
Send a raw USB-PD data message.
Definition AXP517Pd.cpp:93
bool setReceiveDetectSop(bool enable)
Enable or disable SOP receive detection.
Definition AXP517Pd.cpp:48
bool configureHeaderInfo(const HeaderInfoCfg &cfg)
Configure local message-header defaults.
Definition AXP517Pd.cpp:36
AXP517 Type-C Port Controller helper.
uint8_t digitalRead(uint8_t pin)
Read a platform GPIO pin through the active HAL.
bool clearAlert(uint16_t maskRw1c)
Clear PD_ALERTL/H bits using write-one-to-clear semantics.
bool setSinkMessageHeaderAndRx()
Configure sink message-header defaults and enable SOP RX.
bool clearPmicIrqStatus()
Clear the non-PD PMIC IRQ status registers.
bool init()
Initialize the TCPC as a USB-PD sink.
bool clearTx()
Reset the TX byte count and transmit command register.
void pinMode(uint8_t pin, uint8_t mode)
Configure a platform GPIO pin through the active HAL.
bool clearFaultStatus(uint8_t maskRw1c)
Clear TCPC FAULT_STATUS bits using write-one-to-clear semantics.
bool isAttached()
Check whether either CC pin indicates attachment.
bool readFaultStatus(uint8_t &out)
Read the TCPC FAULT_STATUS register.
void delayMs(uint32_t ms)
Delay using the active platform HAL.
@ AlertTxFailed
Message transmission failed.
@ AlertPowerStatus
POWER_STATUS register changed.
@ AlertTxSuccess
Message transmission completed.
@ AlertRxHardReset
Hard Reset received.
@ AlertRxStatus
RX buffer contains a SOP message.
@ AlertTxDiscarded
Transmission discarded due to RX activity.
@ AlertFault
TCPC fault status changed.
@ AlertVbusDisconnect
VBUS sink disconnect threshold crossed.
@ AlertCcStatus
CC_STATUS register changed.
bool writeReceiveDetect(uint8_t v)
Write the TCPCI RECEIVE_DETECT register.
bool readAlert(uint16_t &out)
Read PD_ALERTL/H.
bool rxReadMessage(RxFifoMsg &out)
Read and decode one TCPC RX FIFO message.
uint32_t millis()
Return the platform millisecond counter.
bool checkVendorId()
Verify the TCPC vendor ID.
bool clearAllAlerts()
Clear all PD alert bits.
bool isVbusPresent()
Check whether TCPC VBUS present status is set.
bool setPolarityFromCc()
Set TCPC polarity from the current CC status.
USB Power Delivery protocol helper definitions.
Definition UsbPdDefs.hpp:39
bool parseFixedSourcePdo(uint32_t pdo, FixedSourcePdo &out)
Parse a fixed source PDO.
void wr_le32(uint8_t *p, uint32_t v)
Write a 32-bit value to a byte buffer in little-endian order.
Definition UsbPdDefs.hpp:80
uint32_t le32(const uint8_t *p)
Read a 32-bit little-endian integer from a byte buffer.
Definition UsbPdDefs.hpp:56
uint32_t buildRdoFixedCurrent(uint8_t objectPos1based, uint16_t opMa, uint16_t maxMa, bool capMismatch=false, bool usbCommCapable=true, bool noUsbSuspend=true)
Build a fixed-current Request Data Object.
uint8_t i
One fixed supply PDO offered by the source.
uint16_t mv
Fixed supply voltage in millivolts.
uint32_t rawPdo
Raw 32-bit PDO value.
uint8_t pdoIndex1Based
USB-PD object position, 1 through 7.
uint16_t maMax
Maximum current in milliamps.
Requested sink operating point.
uint8_t pdoIndex1Based
Optional explicit source PDO index, 1 through 7.
uint16_t targetMv
Optional fixed PDO voltage to select.
Parsed Source_Capabilities message.
uint16_t rawHeaderLE
Raw Source_Capabilities header.
uint8_t totalPdoCount
Total PDO count advertised in the message.
FixedOffer fixed[7]
Parsed fixed PDO offers.
uint8_t fixedCount
Number of valid entries in fixed[].
Local USB-PD header defaults used for transmitted messages.
Definition AXP517Pd.hpp:91
Decoded RX FIFO message.
uint16_t headerLE
USB-PD message header in little-endian order.
uint8_t payload[28]
USB-PD payload bytes.
uint8_t payloadLen
Number of valid payload bytes.
Parsed fixed source PDO fields.
USB-PD 16-bit message header wrapper.
Definition UsbPdDefs.hpp:91
uint8_t objCount() const
Return the number of 32-bit data objects in this message.
uint8_t msgType() const
Return the USB-PD message type field.