Fixes for I2C on Pinecil + USB-PD stack (#1099)
* Remove unused includes * Adding in submodule * Move fusb functions to the BSP * Remove old code * Creating IronOS PD integration wrapper * Redirect to wrapper * pd lib updates * fix Docker build * Finish linking across * Cleanup * Update Makefile * Update push.yml * Update push.yml * PD -> Compensate for different tick rates * Update codeql-analysis.yml * Fix PD #define for @Firebie * Check irq low at start * Update BSP.h * Update main.cpp * Closer delay * Update OLED.cpp * Bugfix trying to start QC too early * Missing fusb shouldnt hang qc * Update FreeRTOSConfig.h * Update the GD drivers * Update Pinecil IRQ setup * Redirect printf() to uart * Update Power.cpp * Adding extras to PD state * Update USBPD.cpp * Delay in printf * Iterate once before delay on start * Update usb-pd * master usb-pd now * Format gd libs * Update gd32vf103_bkp.c * Guard with PD timeout * Remove CodeQL * Slow for testing, fix runt pulses at start * Fix runt pulse in read size 1 * Cleaner probing setup * Testing delay during stop gen in read 1 * Update I2C driver * Update gd32vf103_i2c.c * Cleaning up i2c wrapper a little, given up on dma for rx * Update preRTOS.cpp * Update Setup.cpp * Update MOVThread.cpp * Slow down UART to work with new clock config * Better ack setup for 2 byte read * Cleanup POW_PD so cant be lost in #includes * tipResistance -> TIP_RESISTANCE * handle NOP race on len==2 * Update configuration.h * Dont use neg timeout to mask anymore * Not required for MHP * Fix up source display Miniware * Fix race on PD init * Update POWThread.cpp * Update formatting * MHP format * Update push.yml * Faster TS80P I2C * Bugfix for IRQ handlers * Correctly handle I2C race on PD access * Fix CI error (unused var) and MHP IRQ * Test Pinecil alt ADC mode
This commit is contained in:
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "fusb302b.h"
|
||||
#include "BSP.h"
|
||||
#include "I2C_Wrapper.hpp"
|
||||
#include "Setup.h"
|
||||
#include "fusb_user.h"
|
||||
#include "int_n.h"
|
||||
#include <pd.h>
|
||||
uint8_t fusb_read_byte(uint8_t addr);
|
||||
bool fusb_write_byte(uint8_t addr, uint8_t byte);
|
||||
void fusb_send_message(const union pd_msg *msg) {
|
||||
|
||||
/* Token sequences for the FUSB302B */
|
||||
static uint8_t sop_seq[5] = {FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP2, FUSB_FIFO_TX_PACKSYM};
|
||||
static const uint8_t eop_seq[4] = {FUSB_FIFO_TX_JAM_CRC, FUSB_FIFO_TX_EOP, FUSB_FIFO_TX_TXOFF, FUSB_FIFO_TX_TXON};
|
||||
|
||||
/* Take the I2C2 mutex now so there can't be a race condition on sop_seq */
|
||||
/* Get the length of the message: a two-octet header plus NUMOBJ four-octet
|
||||
* data objects */
|
||||
uint8_t msg_len = 2 + 4 * PD_NUMOBJ_GET(msg);
|
||||
|
||||
/* Set the number of bytes to be transmitted in the packet */
|
||||
sop_seq[4] = FUSB_FIFO_TX_PACKSYM | msg_len;
|
||||
|
||||
/* Write all three parts of the message to the TX FIFO */
|
||||
fusb_write_buf(FUSB_FIFOS, 5, sop_seq);
|
||||
fusb_write_buf(FUSB_FIFOS, msg_len, msg->bytes);
|
||||
fusb_write_buf(FUSB_FIFOS, 4, eop_seq);
|
||||
}
|
||||
|
||||
bool fusb_rx_pending() { return (fusb_read_byte(FUSB_STATUS1) & FUSB_STATUS1_RX_EMPTY) != FUSB_STATUS1_RX_EMPTY; }
|
||||
|
||||
uint8_t fusb_read_message(union pd_msg *msg) {
|
||||
|
||||
static uint8_t garbage[4];
|
||||
uint8_t numobj;
|
||||
|
||||
// Read the header. If its not a SOP we dont actually want it at all
|
||||
// But on some revisions of the fusb if you dont both pick them up and read them out of the fifo, it gets stuck
|
||||
if ((fusb_read_byte(FUSB_FIFOS) & FUSB_FIFO_RX_TOKEN_BITS) != FUSB_FIFO_RX_SOP) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// fusb_read_byte(FUSB_FIFOS);
|
||||
/* Read the message header into msg */
|
||||
fusb_read_buf(FUSB_FIFOS, 2, msg->bytes);
|
||||
/* Get the number of data objects */
|
||||
numobj = PD_NUMOBJ_GET(msg);
|
||||
/* If there is at least one data object, read the data objects */
|
||||
if (numobj > 0) {
|
||||
fusb_read_buf(FUSB_FIFOS, numobj * 4, msg->bytes + 2);
|
||||
}
|
||||
/* Throw the CRC32 in the garbage, since the PHY already checked it. */
|
||||
fusb_read_buf(FUSB_FIFOS, 4, garbage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fusb_send_hardrst() {
|
||||
|
||||
/* Send a hard reset */
|
||||
fusb_write_byte(FUSB_CONTROL3, 0x07 | FUSB_CONTROL3_SEND_HARD_RESET);
|
||||
}
|
||||
|
||||
bool fusb_setup() {
|
||||
/* Fully reset the FUSB302B */
|
||||
fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES);
|
||||
vTaskDelay(TICKS_10MS);
|
||||
uint8_t tries = 0;
|
||||
while (!fusb_read_id()) {
|
||||
vTaskDelay(TICKS_10MS);
|
||||
tries++;
|
||||
if (tries > 5) {
|
||||
return false; // Welp :(
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn on all power */
|
||||
fusb_write_byte(FUSB_POWER, 0x0F);
|
||||
|
||||
/* Set interrupt masks */
|
||||
// Setting to 0 so interrupts are allowed
|
||||
fusb_write_byte(FUSB_MASK1, 0x00);
|
||||
fusb_write_byte(FUSB_MASKA, 0x00);
|
||||
fusb_write_byte(FUSB_MASKB, 0x00);
|
||||
fusb_write_byte(FUSB_CONTROL0, 0b11 << 2);
|
||||
|
||||
/* Enable automatic retransmission */
|
||||
fusb_write_byte(FUSB_CONTROL3, 0x07);
|
||||
// set defaults
|
||||
fusb_write_byte(FUSB_CONTROL2, 0x00);
|
||||
/* Flush the RX buffer */
|
||||
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
|
||||
|
||||
/* Measure CC1 */
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x07);
|
||||
vTaskDelay(TICKS_10MS);
|
||||
uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
|
||||
|
||||
/* Measure CC2 */
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
|
||||
vTaskDelay(TICKS_10MS);
|
||||
uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
|
||||
|
||||
/* Select the correct CC line for BMC signaling; also enable AUTO_CRC */
|
||||
if (cc1 > cc2) {
|
||||
fusb_write_byte(FUSB_SWITCHES1, 0x25); // TX_CC1|AUTO_CRC|SPECREV0
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x07); // PWDN1|PWDN2|MEAS_CC1
|
||||
} else {
|
||||
fusb_write_byte(FUSB_SWITCHES1, 0x26); // TX_CC2|AUTO_CRC|SPECREV0
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x0B); // PWDN1|PWDN2|MEAS_CC2
|
||||
}
|
||||
|
||||
fusb_reset();
|
||||
setupFUSBIRQ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fusb_get_status(union fusb_status *status) {
|
||||
|
||||
/* Read the interrupt and status flags into status */
|
||||
return fusb_read_buf(FUSB_STATUS0A, 7, status->bytes);
|
||||
}
|
||||
|
||||
enum fusb_typec_current fusb_get_typec_current() {
|
||||
|
||||
/* Read the BC_LVL into a variable */
|
||||
enum fusb_typec_current bc_lvl = (enum fusb_typec_current)(fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL);
|
||||
|
||||
return bc_lvl;
|
||||
}
|
||||
|
||||
void fusb_reset() {
|
||||
|
||||
/* Flush the TX buffer */
|
||||
fusb_write_byte(FUSB_CONTROL0, 0x44);
|
||||
/* Flush the RX buffer */
|
||||
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
|
||||
/* Reset the PD logic */
|
||||
fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
|
||||
}
|
||||
|
||||
bool fusb_read_id() {
|
||||
// Return true if read of the revision ID is sane
|
||||
uint8_t version = 0;
|
||||
fusb_read_buf(FUSB_DEVICE_ID, 1, &version);
|
||||
if (version == 0 || version == 0xFF)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* Read a single byte from the FUSB302B
|
||||
*
|
||||
* cfg: The FUSB302B to communicate with
|
||||
* addr: The memory address from which to read
|
||||
*
|
||||
* Returns the value read from addr.
|
||||
*/
|
||||
uint8_t fusb_read_byte(uint8_t addr) {
|
||||
uint8_t data[1];
|
||||
if (!fusb_read_buf(addr, 1, (uint8_t *)data)) {
|
||||
return 0;
|
||||
}
|
||||
return data[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a single byte to the FUSB302B
|
||||
*
|
||||
* cfg: The FUSB302B to communicate with
|
||||
* addr: The memory address to which we will write
|
||||
* byte: The value to write
|
||||
*/
|
||||
bool fusb_write_byte(uint8_t addr, uint8_t byte) { return fusb_write_buf(addr, 1, (uint8_t *)&byte); }
|
||||
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_FUSB302B_H
|
||||
#define PDB_FUSB302B_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pd.h"
|
||||
#include <pdb_msg.h>
|
||||
|
||||
/* I2C addresses of the FUSB302B chips */
|
||||
#define FUSB302B_ADDR (0x22 << 1)
|
||||
#define FUSB302B01_ADDR (0x23 << 1)
|
||||
#define FUSB302B10_ADDR (0x24 << 1)
|
||||
#define FUSB302B11_ADDR (0x25 << 1)
|
||||
|
||||
/* Device ID register */
|
||||
#define FUSB_DEVICE_ID 0x01
|
||||
#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4
|
||||
#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0
|
||||
#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT)
|
||||
|
||||
/* Switches0 register */
|
||||
#define FUSB_SWITCHES0 0x02
|
||||
#define FUSB_SWITCHES0_PU_EN2 (1 << 7)
|
||||
#define FUSB_SWITCHES0_PU_EN1 (1 << 6)
|
||||
#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5)
|
||||
#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4)
|
||||
#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3)
|
||||
#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2)
|
||||
#define FUSB_SWITCHES0_PDWN_2 (1 << 1)
|
||||
#define FUSB_SWITCHES0_PDWN_1 1
|
||||
|
||||
/* Switches1 register */
|
||||
#define FUSB_SWITCHES1 0x03
|
||||
#define FUSB_SWITCHES1_POWERROLE (1 << 7)
|
||||
#define FUSB_SWITCHES1_SPECREV_SHIFT 5
|
||||
#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT)
|
||||
#define FUSB_SWITCHES1_DATAROLE (1 << 4)
|
||||
#define FUSB_SWITCHES1_AUTO_CRC (1 << 2)
|
||||
#define FUSB_SWITCHES1_TXCC2 (1 << 1)
|
||||
#define FUSB_SWITCHES1_TXCC1 1
|
||||
|
||||
/* Measure register */
|
||||
#define FUSB_MEASURE 0x04
|
||||
#define FUSB_MEASURE_MEAS_VBUS (1 << 6)
|
||||
#define FUSB_MEASURE_MDAC_SHIFT 0
|
||||
#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT)
|
||||
|
||||
/* Slice register */
|
||||
#define FUSB_SLICE 0x05
|
||||
#define FUSB_SLICE_SDAC_HYS_SHIFT 6
|
||||
#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT)
|
||||
#define FUSB_SLICE_SDAC_SHIFT 0
|
||||
#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT)
|
||||
|
||||
/* Control0 register */
|
||||
#define FUSB_CONTROL0 0x06
|
||||
#define FUSB_CONTROL0_TX_FLUSH (1 << 6)
|
||||
#define FUSB_CONTROL0_INT_MASK (1 << 5)
|
||||
#define FUSB_CONTROL0_HOST_CUR_SHIFT 2
|
||||
#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT)
|
||||
#define FUSB_CONTROL0_AUTO_PRE (1 << 1)
|
||||
#define FUSB_CONTROL0_TX_START 1
|
||||
|
||||
/* Control1 register */
|
||||
#define FUSB_CONTROL1 0x07
|
||||
#define FUSB_CONTROL1_ENSOP2DB (1 << 6)
|
||||
#define FUSB_CONTROL1_ENSOP1DB (1 << 5)
|
||||
#define FUSB_CONTROL1_BIST_MODE2 (1 << 4)
|
||||
#define FUSB_CONTROL1_RX_FLUSH (1 << 2)
|
||||
#define FUSB_CONTROL1_ENSOP2 (1 << 1)
|
||||
#define FUSB_CONTROL1_ENSOP1 1
|
||||
|
||||
/* Control2 register */
|
||||
#define FUSB_CONTROL2 0x08
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR)
|
||||
#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5)
|
||||
#define FUSB_CONTROL2_WAKE_EN (1 << 3)
|
||||
#define FUSB_CONTROL2_MODE_SHIFT 1
|
||||
#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT)
|
||||
#define FUSB_CONTROL2_TOGGLE 1
|
||||
|
||||
/* Control3 register */
|
||||
#define FUSB_CONTROL3 0x09
|
||||
#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6)
|
||||
#define FUSB_CONTROL3_BIST_TMODE (1 << 5)
|
||||
#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4)
|
||||
#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3)
|
||||
#define FUSB_CONTROL3_N_RETRIES_SHIFT 1
|
||||
#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT)
|
||||
#define FUSB_CONTROL3_AUTO_RETRY 1
|
||||
|
||||
/* Mask1 register */
|
||||
#define FUSB_MASK1 0x0A
|
||||
#define FUSB_MASK1_M_VBUSOK (1 << 7)
|
||||
#define FUSB_MASK1_M_ACTIVITY (1 << 6)
|
||||
#define FUSB_MASK1_M_COMP_CHNG (1 << 5)
|
||||
#define FUSB_MASK1_M_CRC_CHK (1 << 4)
|
||||
#define FUSB_MASK1_M_ALERT (1 << 3)
|
||||
#define FUSB_MASK1_M_WAKE (1 << 2)
|
||||
#define FUSB_MASK1_M_COLLISION (1 << 1)
|
||||
#define FUSB_MASK1_M_BC_LVL (1 << 0)
|
||||
|
||||
/* Power register */
|
||||
#define FUSB_POWER 0x0B
|
||||
#define FUSB_POWER_PWR3 (1 << 3)
|
||||
#define FUSB_POWER_PWR2 (1 << 2)
|
||||
#define FUSB_POWER_PWR1 (1 << 1)
|
||||
#define FUSB_POWER_PWR0 1
|
||||
|
||||
/* Reset register */
|
||||
#define FUSB_RESET 0x0C
|
||||
#define FUSB_RESET_PD_RESET (1 << 1)
|
||||
#define FUSB_RESET_SW_RES 1
|
||||
|
||||
/* OCPreg register */
|
||||
#define FUSB_OCPREG 0x0D
|
||||
#define FUSB_OCPREG_OCP_RANGE (1 << 3)
|
||||
#define FUSB_OCPREG_OCP_CUR_SHIFT 0
|
||||
#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT)
|
||||
|
||||
/* Maska register */
|
||||
#define FUSB_MASKA 0x0E
|
||||
#define FUSB_MASKA_M_OCP_TEMP (1 << 7)
|
||||
#define FUSB_MASKA_M_TOGDONE (1 << 6)
|
||||
#define FUSB_MASKA_M_SOFTFAIL (1 << 5)
|
||||
#define FUSB_MASKA_M_RETRYFAIL (1 << 4)
|
||||
#define FUSB_MASKA_M_HARDSENT (1 << 3)
|
||||
#define FUSB_MASKA_M_TXSENT (1 << 2)
|
||||
#define FUSB_MASKA_M_SOFTRST (1 << 1)
|
||||
#define FUSB_MASKA_M_HARDRST 1
|
||||
|
||||
/* Maskb register */
|
||||
#define FUSB_MASKB 0x0F
|
||||
#define FUSB_MASKB_M_GCRCSENT 1
|
||||
|
||||
/* Control4 register */
|
||||
#define FUSB_CONTROL4 0x10
|
||||
#define FUSB_CONTROL4_TOG_EXIT_AUD 1
|
||||
|
||||
/* Status0a register */
|
||||
#define FUSB_STATUS0A 0x3C
|
||||
#define FUSB_STATUS0A_SOFTFAIL (1 << 5)
|
||||
#define FUSB_STATUS0A_RETRYFAIL (1 << 4)
|
||||
#define FUSB_STATUS0A_POWER3 (1 << 3)
|
||||
#define FUSB_STATUS0A_POWER2 (1 << 2)
|
||||
#define FUSB_STATUS0A_SOFTRST (1 << 1)
|
||||
#define FUSB_STATUS0A_HARDRST 1
|
||||
|
||||
/* Status1a register */
|
||||
#define FUSB_STATUS1A 0x3D
|
||||
#define FUSB_STATUS1A_TOGSS_SHIFT 3
|
||||
#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT)
|
||||
#define FUSB_STATUS1A_RXSOP2DB (1 << 2)
|
||||
#define FUSB_STATUS1A_RXSOP1DB (1 << 1)
|
||||
#define FUSB_STATUS1A_RXSOP 1
|
||||
|
||||
/* Interrupta register */
|
||||
#define FUSB_INTERRUPTA 0x3E
|
||||
#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7)
|
||||
#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6)
|
||||
#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5)
|
||||
#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4)
|
||||
#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3)
|
||||
#define FUSB_INTERRUPTA_I_TXSENT (1 << 2)
|
||||
#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1)
|
||||
#define FUSB_INTERRUPTA_I_HARDRST 1
|
||||
|
||||
/* Interruptb register */
|
||||
#define FUSB_INTERRUPTB 0x3F
|
||||
#define FUSB_INTERRUPTB_I_GCRCSENT 1
|
||||
|
||||
/* Status0 register */
|
||||
#define FUSB_STATUS0 0x40
|
||||
#define FUSB_STATUS0_VBUSOK (1 << 7)
|
||||
#define FUSB_STATUS0_ACTIVITY (1 << 6)
|
||||
#define FUSB_STATUS0_COMP (1 << 5)
|
||||
#define FUSB_STATUS0_CRC_CHK (1 << 4)
|
||||
#define FUSB_STATUS0_ALERT (1 << 3)
|
||||
#define FUSB_STATUS0_WAKE (1 << 2)
|
||||
#define FUSB_STATUS0_BC_LVL_SHIFT 0
|
||||
#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT)
|
||||
|
||||
/* Status1 register */
|
||||
#define FUSB_STATUS1 0x41
|
||||
#define FUSB_STATUS1_RXSOP2 (1 << 7)
|
||||
#define FUSB_STATUS1_RXSOP1 (1 << 6)
|
||||
#define FUSB_STATUS1_RX_EMPTY (1 << 5)
|
||||
#define FUSB_STATUS1_RX_FULL (1 << 4)
|
||||
#define FUSB_STATUS1_TX_EMPTY (1 << 3)
|
||||
#define FUSB_STATUS1_TX_FULL (1 << 2)
|
||||
#define FUSB_STATUS1_OVRTEMP (1 << 1)
|
||||
#define FUSB_STATUS1_OCP 1
|
||||
|
||||
/* Interrupt register */
|
||||
#define FUSB_INTERRUPT 0x42
|
||||
#define FUSB_INTERRUPT_I_VBUSOK (1 << 7)
|
||||
#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6)
|
||||
#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5)
|
||||
#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4)
|
||||
#define FUSB_INTERRUPT_I_ALERT (1 << 3)
|
||||
#define FUSB_INTERRUPT_I_WAKE (1 << 2)
|
||||
#define FUSB_INTERRUPT_I_COLLISION (1 << 1)
|
||||
#define FUSB_INTERRUPT_I_BC_LVL 1
|
||||
|
||||
/* FIFOs register */
|
||||
#define FUSB_FIFOS 0x43
|
||||
|
||||
#define FUSB_FIFO_TX_TXON 0xA1
|
||||
#define FUSB_FIFO_TX_SOP1 0x12
|
||||
#define FUSB_FIFO_TX_SOP2 0x13
|
||||
#define FUSB_FIFO_TX_SOP3 0x1B
|
||||
#define FUSB_FIFO_TX_RESET1 0x15
|
||||
#define FUSB_FIFO_TX_RESET2 0x16
|
||||
#define FUSB_FIFO_TX_PACKSYM 0x80
|
||||
#define FUSB_FIFO_TX_JAM_CRC 0xFF
|
||||
#define FUSB_FIFO_TX_EOP 0x14
|
||||
#define FUSB_FIFO_TX_TXOFF 0xFE
|
||||
|
||||
#define FUSB_FIFO_RX_TOKEN_BITS 0xE0
|
||||
#define FUSB_FIFO_RX_SOP 0xE0
|
||||
#define FUSB_FIFO_RX_SOP1 0xC0
|
||||
#define FUSB_FIFO_RX_SOP2 0xA0
|
||||
#define FUSB_FIFO_RX_SOP1DB 0x80
|
||||
#define FUSB_FIFO_RX_SOP2DB 0x60
|
||||
|
||||
/*
|
||||
* FUSB status union
|
||||
*
|
||||
* Provides a nicer structure than just an array of uint8_t for working with
|
||||
* the FUSB302B status and interrupt flags.
|
||||
*/
|
||||
union fusb_status {
|
||||
uint8_t bytes[7];
|
||||
struct {
|
||||
uint8_t status0a;
|
||||
uint8_t status1a;
|
||||
uint8_t interrupta;
|
||||
uint8_t interruptb;
|
||||
uint8_t status0;
|
||||
uint8_t status1;
|
||||
uint8_t interrupt;
|
||||
};
|
||||
};
|
||||
|
||||
/* FUSB functions */
|
||||
|
||||
/*
|
||||
* Send a USB Power Delivery message to the FUSB302B
|
||||
*/
|
||||
void fusb_send_message(const union pd_msg *msg);
|
||||
bool fusb_rx_pending();
|
||||
/*
|
||||
* Read a USB Power Delivery message from the FUSB302B
|
||||
*/
|
||||
uint8_t fusb_read_message(union pd_msg *msg);
|
||||
|
||||
/*
|
||||
* Tell the FUSB302B to send a hard reset signal
|
||||
*/
|
||||
void fusb_send_hardrst();
|
||||
|
||||
/*
|
||||
* Read the FUSB302B status and interrupt flags into *status
|
||||
*/
|
||||
bool fusb_get_status(union fusb_status *status);
|
||||
|
||||
/*
|
||||
* Read the FUSB302B BC_LVL as an enum fusb_typec_current
|
||||
*/
|
||||
enum fusb_typec_current fusb_get_typec_current();
|
||||
|
||||
/*
|
||||
* Initialization routine for the FUSB302B
|
||||
*/
|
||||
bool fusb_setup();
|
||||
|
||||
/*
|
||||
* Reset the FUSB302B
|
||||
*/
|
||||
void fusb_reset();
|
||||
|
||||
bool fusb_read_id();
|
||||
|
||||
#endif /* PDB_FUSB302B_H */
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_FUSB_USER_H
|
||||
#define PDB_FUSB_USER_H
|
||||
|
||||
#include <stdint.h>
|
||||
/*
|
||||
* Read multiple bytes from the FUSB302B
|
||||
*
|
||||
* cfg: The FUSB302B to communicate with
|
||||
* addr: The memory address from which to read
|
||||
* size: The number of bytes to read
|
||||
* buf: The buffer into which data will be read
|
||||
*/
|
||||
bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf);
|
||||
/*
|
||||
* Write multiple bytes to the FUSB302B
|
||||
*
|
||||
* cfg: The FUSB302B to communicate with
|
||||
* addr: The memory address to which we will write
|
||||
* size: The number of bytes to write
|
||||
* buf: The buffer to write
|
||||
*/
|
||||
bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf);
|
||||
// Used to poll for the device existing on the I2C bus. This should return non-zero if the device is responding on the bus
|
||||
bool fusb302_detect();
|
||||
// Once this is called IRQ's should be enabled and routed to the IRQ handler thread
|
||||
void setupFUSBIRQ();
|
||||
// This should return true if the IRQ line for the FUSB302 is still held low
|
||||
bool getFUS302IRQLow();
|
||||
|
||||
#endif /* PDB_FUSB302B_H */
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* fusbpd.cpp
|
||||
*
|
||||
* Created on: 13 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "configuration.h"
|
||||
#ifdef POW_PD
|
||||
#include "BSP.h"
|
||||
#include "I2CBB.hpp"
|
||||
#include "fusb302b.h"
|
||||
#include "int_n.h"
|
||||
#include "policy_engine.h"
|
||||
|
||||
#include <fusbpd.h>
|
||||
#include <pd.h>
|
||||
|
||||
void fusb302_start_processing() {
|
||||
/* Initialize the FUSB302B */
|
||||
if (fusb_setup()) {
|
||||
PolicyEngine::init();
|
||||
InterruptHandler::init();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* fusbpd.h
|
||||
*
|
||||
* Created on: 13 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#ifndef DRIVERS_FUSB302_FUSBPD_H_
|
||||
#define DRIVERS_FUSB302_FUSBPD_H_
|
||||
// Wrapper for all of the FUSB302 PD work
|
||||
extern struct pdb_config pdb_config_data;
|
||||
#include <stdint.h>
|
||||
|
||||
void fusb302_start_processing();
|
||||
#endif /* DRIVERS_FUSB302_FUSBPD_H_ */
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "int_n.h"
|
||||
#include "Defines.h"
|
||||
#include "fusb302b.h"
|
||||
#include "fusb_user.h"
|
||||
#include "fusbpd.h"
|
||||
#include "policy_engine.h"
|
||||
#include "task.h"
|
||||
#include <pd.h>
|
||||
#include <string.h>
|
||||
|
||||
volatile osThreadId InterruptHandler::TaskHandle = NULL;
|
||||
uint32_t InterruptHandler::TaskBuffer[InterruptHandler::TaskStackSize];
|
||||
osStaticThreadDef_t InterruptHandler::TaskControlBlock;
|
||||
union pd_msg InterruptHandler::tempMessage;
|
||||
|
||||
void InterruptHandler::init() {
|
||||
TaskHandle = NULL;
|
||||
osThreadStaticDef(intTask, Thread, PDB_PRIO_PRL_INT_N, 0, TaskStackSize, TaskBuffer, &TaskControlBlock);
|
||||
TaskHandle = osThreadCreate(osThread(intTask), NULL);
|
||||
}
|
||||
volatile uint32_t msgCounter = 0;
|
||||
volatile uint32_t msgCounter1 = 0;
|
||||
void InterruptHandler::readPendingMessage() {
|
||||
memset(&tempMessage, 0, sizeof(tempMessage));
|
||||
while (fusb_rx_pending()) {
|
||||
msgCounter++;
|
||||
/* Read the message */
|
||||
if (fusb_read_message(&tempMessage) == 0) {
|
||||
/* If it's a Soft_Reset, go to the soft reset state */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* TX transitions to its reset state */
|
||||
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_RESET);
|
||||
} else {
|
||||
/* Tell PolicyEngine to discard the message being transmitted */
|
||||
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_TX_DISCARD);
|
||||
|
||||
/* Pass the message to the policy engine. */
|
||||
PolicyEngine::handleMessage(&tempMessage);
|
||||
}
|
||||
} else {
|
||||
msgCounter1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InterruptHandler::Thread(const void *arg) {
|
||||
(void)arg;
|
||||
union fusb_status status;
|
||||
for (;;) {
|
||||
// If the irq is low continue, otherwise wait for irq or timeout
|
||||
if (!getFUS302IRQLow()) {
|
||||
xTaskNotifyWait(0x00, 0x0F, NULL, TICKS_SECOND * 30);
|
||||
}
|
||||
/* Read the FUSB302B status and interrupt registers */
|
||||
if (fusb_get_status(&status)) {
|
||||
|
||||
/* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
|
||||
// This means a message was received with a good CRC
|
||||
if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
|
||||
readPendingMessage();
|
||||
}
|
||||
|
||||
/* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
|
||||
* thread */
|
||||
if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
|
||||
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_TX_I_TXSENT);
|
||||
}
|
||||
if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
|
||||
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_TX_I_RETRYFAIL);
|
||||
}
|
||||
|
||||
/* If the I_OCP_TEMP and OVRTEMP flags are set, tell the Policy
|
||||
* Engine thread */
|
||||
if ((status.interrupta & FUSB_INTERRUPTA_I_OCP_TEMP) && (status.status1 & FUSB_STATUS1_OVRTEMP)) {
|
||||
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_I_OVRTEMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void InterruptHandler::irqCallback() {
|
||||
if (TaskHandle != NULL) {
|
||||
BaseType_t taskWoke = pdFALSE;
|
||||
xTaskNotifyFromISR(TaskHandle, 0x01, eNotifyAction::eSetBits, &taskWoke);
|
||||
portYIELD_FROM_ISR(taskWoke);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_INT_N_OLD_H
|
||||
#define PDB_INT_N_OLD_H
|
||||
|
||||
#include <pd.h>
|
||||
|
||||
class InterruptHandler {
|
||||
public:
|
||||
// Creates the thread to handle the Interrupt pin
|
||||
static void init();
|
||||
|
||||
static void irqCallback();
|
||||
|
||||
private:
|
||||
static void Thread(const void *arg);
|
||||
static volatile osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 1536 / 3;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
/*
|
||||
* Hard Reset machine states
|
||||
*/
|
||||
enum hardrst_state { PRLHRResetLayer, PRLHRIndicateHardReset, PRLHRRequestHardReset, PRLHRWaitPHY, PRLHRHardResetRequested, PRLHRWaitPE, PRLHRComplete };
|
||||
static enum hardrst_state hardrst_reset_layer();
|
||||
static enum hardrst_state hardrst_indicate_hard_reset();
|
||||
static enum hardrst_state hardrst_request_hard_reset();
|
||||
static enum hardrst_state hardrst_wait_phy();
|
||||
static enum hardrst_state hardrst_hard_reset_requested();
|
||||
static enum hardrst_state hardrst_wait_pe();
|
||||
static enum hardrst_state hardrst_complete();
|
||||
// Mesage rx
|
||||
static void readPendingMessage();
|
||||
static union pd_msg tempMessage;
|
||||
};
|
||||
|
||||
#endif /* PDB_INT_N_OLD_H */
|
||||
@@ -1,388 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_PD_H
|
||||
#define PDB_PD_H
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "pdb_conf.h"
|
||||
#include "pdb_msg.h"
|
||||
#include <stdint.h>
|
||||
/*
|
||||
* Macros for working with USB Power Delivery messages.
|
||||
*
|
||||
* This file is mostly written from the PD Rev. 2.0 spec, but the header is
|
||||
* written from the Rev. 3.0 spec.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PD Header
|
||||
*/
|
||||
#define PD_HDR_MSGTYPE_SHIFT 0
|
||||
#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT)
|
||||
#define PD_HDR_DATAROLE_SHIFT 5
|
||||
#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_HDR_SPECREV_SHIFT 6
|
||||
#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_HDR_POWERROLE_SHIFT 8
|
||||
#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_HDR_MESSAGEID_SHIFT 9
|
||||
#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT)
|
||||
#define PD_HDR_NUMOBJ_SHIFT 12
|
||||
#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT)
|
||||
#define PD_HDR_EXT (1 << 15)
|
||||
|
||||
/* Message types */
|
||||
#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT)
|
||||
/* Control Message */
|
||||
#define PD_MSGTYPE_GOODCRC 0x01
|
||||
#define PD_MSGTYPE_GOTOMIN 0x02
|
||||
#define PD_MSGTYPE_ACCEPT 0x03
|
||||
#define PD_MSGTYPE_REJECT 0x04
|
||||
#define PD_MSGTYPE_PING 0x05
|
||||
#define PD_MSGTYPE_PS_RDY 0x06
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP 0x07
|
||||
#define PD_MSGTYPE_GET_SINK_CAP 0x08
|
||||
#define PD_MSGTYPE_DR_SWAP 0x09
|
||||
#define PD_MSGTYPE_PR_SWAP 0x0A
|
||||
#define PD_MSGTYPE_VCONN_SWAP 0x0B
|
||||
#define PD_MSGTYPE_WAIT 0x0C
|
||||
#define PD_MSGTYPE_SOFT_RESET 0x0D
|
||||
#define PD_MSGTYPE_NOT_SUPPORTED 0x10
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11
|
||||
#define PD_MSGTYPE_GET_STATUS 0x12
|
||||
#define PD_MSGTYPE_FR_SWAP 0x13
|
||||
#define PD_MSGTYPE_GET_PPS_STATUS 0x14
|
||||
#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15
|
||||
/* Data Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01
|
||||
#define PD_MSGTYPE_REQUEST 0x02
|
||||
#define PD_MSGTYPE_BIST 0x03
|
||||
#define PD_MSGTYPE_SINK_CAPABILITIES 0x04
|
||||
#define PD_MSGTYPE_BATTERY_STATUS 0x05
|
||||
#define PD_MSGTYPE_ALERT 0x06
|
||||
#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07
|
||||
#define PD_MSGTYPE_VENDOR_DEFINED 0x0F
|
||||
/* Extended Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01
|
||||
#define PD_MSGTYPE_STATUS 0x02
|
||||
#define PD_MSGTYPE_GET_BATTERY_CAP 0x03
|
||||
#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04
|
||||
#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05
|
||||
#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06
|
||||
#define PD_MSGTYPE_MANUFACTURER_INFO 0x07
|
||||
#define PD_MSGTYPE_SECURITY_REQUEST 0x08
|
||||
#define PD_MSGTYPE_SECURITY_RESPONSE 0x09
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B
|
||||
#define PD_MSGTYPE_PPS_STATUS 0x0C
|
||||
#define PD_MSGTYPE_COUNTRY_INFO 0x0D
|
||||
#define PD_MSGTYPE_COUNTRY_CODES 0x0E
|
||||
|
||||
/* Data roles */
|
||||
#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
|
||||
/* Specification revisions */
|
||||
#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT)
|
||||
|
||||
/* Port power roles */
|
||||
#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT)
|
||||
|
||||
/* Message ID */
|
||||
#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT)
|
||||
|
||||
/* Number of data objects */
|
||||
#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ)
|
||||
#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT)
|
||||
|
||||
/*
|
||||
* PD Extended Message Header
|
||||
*/
|
||||
#define PD_EXTHDR_DATA_SIZE_SHIFT 0
|
||||
#define PD_EXTHDR_DATA_SIZE (0x1FF << PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
#define PD_EXTHDR_REQUEST_CHUNK_SHIFT 10
|
||||
#define PD_EXTHDR_REQUEST_CHUNK (1 << PD_EXTHDR_REQUEST_CHUNK_SHIFT)
|
||||
#define PD_EXTHDR_CHUNK_NUMBER_SHIFT 11
|
||||
#define PD_EXTHDR_CHUNK_NUMBER (0xF << PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
#define PD_EXTHDR_CHUNKED_SHIFT 15
|
||||
#define PD_EXTHDR_CHUNKED (1 << PD_EXTHDR_CHUNKED_SHIFT)
|
||||
|
||||
/* Data size */
|
||||
#define PD_DATA_SIZE(n) (((n) << PD_EXTHDR_DATA_SIZE_SHIFT) & PD_EXTHDR_DATA_SIZE)
|
||||
#define PD_DATA_SIZE_GET(msg) (((msg)->exthdr & PD_EXTHDR_DATA_SIZE) >> PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
|
||||
/* Chunk number */
|
||||
#define PD_CHUNK_NUMBER(n) (((n) << PD_EXTHDR_CHUNK_NUMBER_SHIFT) & PD_EXTHDR_CHUNK_NUMBER)
|
||||
#define PD_CHUNK_NUMBER_GET(msg) (((msg)->exthdr & PD_EXTHDR_CHUNK_NUMBER) >> PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
|
||||
/*
|
||||
* PD Power Data Object
|
||||
*/
|
||||
#define PD_PDO_TYPE_SHIFT 30
|
||||
#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT)
|
||||
|
||||
/* PDO types */
|
||||
#define PD_PDO_TYPE_FIXED ((unsigned)(0x0 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_BATTERY ((unsigned)(0x1 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_VARIABLE ((unsigned)(0x2 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_AUGMENTED ((unsigned)(0x3 << PD_PDO_TYPE_SHIFT))
|
||||
|
||||
#define PD_APDO_TYPE_SHIFT 28
|
||||
#define PD_APDO_TYPE (0x3 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* APDO types */
|
||||
#define PD_APDO_TYPE_PPS (0x0 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO */
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT 24
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG (1 << PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO current */
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_GET(pdo) (((pdo)&PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO voltage */
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_GET(pdo) (((pdo)&PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SHIFT 17
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE (0xFF << PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SHIFT 8
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE (0xFF << PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_CURRENT_SHIFT 0
|
||||
#define PD_APDO_PPS_CURRENT (0x7F << PD_APDO_PPS_CURRENT_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO voltages */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_GET(pdo) (((pdo)&PD_APDO_PPS_MAX_VOLTAGE) >> PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_GET(pdo) (((pdo)&PD_APDO_PPS_MIN_VOLTAGE) >> PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) & PD_APDO_PPS_MAX_VOLTAGE)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) & PD_APDO_PPS_MIN_VOLTAGE)
|
||||
|
||||
/* PD Programmable Power Supply APDO current */
|
||||
#define PD_APDO_PPS_CURRENT_GET(pdo) ((uint8_t)(((pdo)&PD_APDO_PPS_CURRENT) >> PD_APDO_PPS_CURRENT_SHIFT))
|
||||
|
||||
#define PD_APDO_PPS_CURRENT_SET(i) (((i) << PD_APDO_PPS_CURRENT_SHIFT) & PD_APDO_PPS_CURRENT)
|
||||
|
||||
/* PD Sink Fixed PDO */
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Sink Fixed PDO current */
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT)
|
||||
|
||||
/* PD Sink Fixed PDO voltage */
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE)
|
||||
|
||||
/*
|
||||
* PD Request Data Object
|
||||
*/
|
||||
#define PD_RDO_OBJPOS_SHIFT 28
|
||||
#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT)
|
||||
#define PD_RDO_GIVEBACK_SHIFT 27
|
||||
#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT)
|
||||
#define PD_RDO_CAP_MISMATCH_SHIFT 26
|
||||
#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT)
|
||||
#define PD_RDO_USB_COMMS_SHIFT 25
|
||||
#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT)
|
||||
#define PD_RDO_NO_USB_SUSPEND_SHIFT 24
|
||||
#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT)
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG_SHIFT 23
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG (1 << PD_RDO_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
|
||||
#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS)
|
||||
#define PD_RDO_OBJPOS_GET(msg) (((msg)->obj[0] & PD_RDO_OBJPOS) >> PD_RDO_OBJPOS_SHIFT)
|
||||
|
||||
/* Fixed and Variable RDO, no GiveBack support */
|
||||
#define PD_RDO_FV_CURRENT_SHIFT 10
|
||||
#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT)
|
||||
|
||||
/* Fixed and Variable RDO with GiveBack support */
|
||||
#define PD_RDO_FV_MIN_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MIN_CURRENT (0x3FF << PD_RDO_FV_MIN_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_MIN_CURRENT_SET(i) (((i) << PD_RDO_FV_MIN_CURRENT_SHIFT) & PD_RDO_FV_MIN_CURRENT)
|
||||
|
||||
/* TODO: Battery RDOs */
|
||||
|
||||
/* Programmable RDO */
|
||||
#define PD_RDO_PROG_VOLTAGE_SHIFT 9
|
||||
#define PD_RDO_PROG_VOLTAGE (0x7FF << PD_RDO_PROG_VOLTAGE_SHIFT)
|
||||
#define PD_RDO_PROG_CURRENT_SHIFT 0
|
||||
#define PD_RDO_PROG_CURRENT (0x7F << PD_RDO_PROG_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_PROG_VOLTAGE_SET(i) (((i) << PD_RDO_PROG_VOLTAGE_SHIFT) & PD_RDO_PROG_VOLTAGE)
|
||||
#define PD_RDO_PROG_CURRENT_SET(i) (((i) << PD_RDO_PROG_CURRENT_SHIFT) & PD_RDO_PROG_CURRENT)
|
||||
|
||||
/*
|
||||
* Time values
|
||||
*
|
||||
* Where a range is specified, the middle of the range (rounded down to the
|
||||
* nearest millisecond) is used.
|
||||
*/
|
||||
#define PD_T_CHUNKING_NOT_SUPPORTED (TICKS_SECOND / 2)
|
||||
#define PD_T_HARD_RESET_COMPLETE (1 * TICKS_SECOND)
|
||||
#define PD_T_PS_TRANSITION (5 * TICKS_SECOND)
|
||||
#define PD_T_SENDER_RESPONSE (27 * TICKS_100MS)
|
||||
#define PD_T_SINK_REQUEST (1 * TICKS_SECOND)
|
||||
#define PD_T_TYPEC_SINK_WAIT_CAP (1 * TICKS_SECOND)
|
||||
#define PD_T_PD_DEBOUNCE (2 * TICKS_SECOND)
|
||||
|
||||
/*
|
||||
* Counter maximums
|
||||
*/
|
||||
#define PD_N_HARD_RESET_COUNT 2
|
||||
|
||||
/*
|
||||
* Value parameters
|
||||
*/
|
||||
#define PD_MAX_EXT_MSG_LEN 260
|
||||
#define PD_MAX_EXT_MSG_CHUNK_LEN 26
|
||||
#define PD_MAX_EXT_MSG_LEGACY_LEN 26
|
||||
|
||||
/*
|
||||
* Unit conversions
|
||||
*
|
||||
* V: volt
|
||||
* CV: centivolt
|
||||
* MV: millivolt
|
||||
* PRV: Programmable RDO voltage unit (20 mV)
|
||||
* PDV: Power Delivery voltage unit (50 mV)
|
||||
* PAV: PPS APDO voltage unit (100 mV)
|
||||
*
|
||||
* A: ampere
|
||||
* CA: centiampere
|
||||
* MA: milliampere
|
||||
* PDI: Power Delivery current unit (10 mA)
|
||||
* PAI: PPS APDO current unit (50 mA)
|
||||
*
|
||||
* W: watt
|
||||
* CW: centiwatt
|
||||
* MW: milliwatt
|
||||
*
|
||||
* O: ohm
|
||||
* CO: centiohm
|
||||
* MO: milliohm
|
||||
*/
|
||||
#define PD_MV2PRV(mv) ((mv) / 20)
|
||||
#define PD_MV2PDV(mv) ((mv) / 50)
|
||||
#define PD_MV2PAV(mv) ((mv) / 100)
|
||||
#define PD_PRV2MV(prv) ((prv)*20)
|
||||
#define PD_PDV2MV(pdv) ((pdv)*50)
|
||||
#define PD_PAV2MV(pav) ((pav)*100)
|
||||
|
||||
#define PD_MA2CA(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PDI(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PAI(ma) (((ma) + 50 - 1) / 50)
|
||||
#define PD_CA2PAI(ca) (((ca) + 5 - 1) / 5)
|
||||
#define PD_PDI2MA(pdi) ((pdi)*10)
|
||||
#define PD_PAI2MA(pai) ((pai)*50)
|
||||
#define PD_PAI2CA(pai) ((pai)*5)
|
||||
|
||||
#define PD_MW2CW(mw) ((mw) / 10)
|
||||
|
||||
#define PD_MO2CO(mo) ((mo) / 10)
|
||||
|
||||
/* Get portions of a voltage in more normal units */
|
||||
#define PD_MV_V(mv) ((mv) / 1000)
|
||||
#define PD_MV_MV(mv) ((mv) % 1000)
|
||||
|
||||
#define PD_PDV_V(pdv) ((pdv) / 20)
|
||||
#define PD_PDV_CV(pdv) (5 * ((pdv) % 20))
|
||||
|
||||
#define PD_PAV_V(pav) ((pav) / 10)
|
||||
#define PD_PAV_CV(pav) (10 * ((pav) % 10))
|
||||
|
||||
/* Get portions of a PD current in more normal units */
|
||||
#define PD_PDI_A(pdi) ((pdi) / 100)
|
||||
#define PD_PDI_CA(pdi) ((pdi) % 100)
|
||||
|
||||
#define PD_PAI_A(pai) ((pai) / 20)
|
||||
#define PD_PAI_CA(pai) (5 * ((pai) % 20))
|
||||
|
||||
/* Get portions of a power in more normal units */
|
||||
#define PD_CW_W(cw) ((cw) / 100)
|
||||
#define PD_CW_CW(cw) ((cw) % 100)
|
||||
|
||||
/* Get portions of a resistance in more normal units */
|
||||
#define PD_CO_O(co) ((co) / 100)
|
||||
#define PD_CO_CO(co) ((co) % 100)
|
||||
|
||||
/*
|
||||
* Unit constants
|
||||
*/
|
||||
#define PD_MV_MIN 0
|
||||
#define PD_MV_MAX 21000
|
||||
#define PD_PDV_MIN PD_MV2PDV(PD_MV_MIN)
|
||||
#define PD_PDV_MAX PD_MV2PDV(PD_MV_MAX)
|
||||
|
||||
#define PD_MA_MIN 0
|
||||
#define PD_MA_MAX 5000
|
||||
#define PD_CA_MIN PD_MA2CA(PD_MA_MIN)
|
||||
#define PD_CA_MAX PD_MA2CA(PD_MA_MAX)
|
||||
#define PD_PDI_MIN PD_MA2PDI(PD_MA_MIN)
|
||||
#define PD_PDI_MAX PD_MA2PDI(PD_MA_MAX)
|
||||
|
||||
#define PD_MW_MIN 0
|
||||
#define PD_MW_MAX 100000
|
||||
|
||||
#define PD_MO_MIN 500
|
||||
#define PD_MO_MAX 655350
|
||||
|
||||
/*
|
||||
* FUSB Type-C Current level enum
|
||||
*/
|
||||
enum fusb_typec_current { fusb_tcc_none = 0, fusb_tcc_default = 1, fusb_tcc_1_5 = 2, fusb_sink_tx_ng = 2, fusb_tcc_3_0 = 3, fusb_sink_tx_ok = 3 };
|
||||
|
||||
#endif /* PDB_PD_H */
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_CONF_H
|
||||
#define PDB_CONF_H
|
||||
|
||||
/* Number of messages in the message pool */
|
||||
#define PDB_MSG_POOL_SIZE 8
|
||||
|
||||
#define EVENT_MASK(x) (1 << x)
|
||||
|
||||
/* PD Buddy thread priorities */
|
||||
#define PDB_PRIO_PE (osPriorityAboveNormal)
|
||||
#define PDB_PRIO_PRL (osPriorityAboveNormal)
|
||||
#define PDB_PRIO_PRL_INT_N (osPriorityAboveNormal)
|
||||
|
||||
#endif /* PDB_CONF_H */
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_MSG_H
|
||||
#define PDB_MSG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* PD message union
|
||||
*
|
||||
* This can be safely read from or written to in any form without any
|
||||
* transformations because everything in the system is little-endian.
|
||||
*
|
||||
* Two bytes of padding are required at the start to prevent problems due to
|
||||
* alignment. Specifically, without the padding, &obj[0] != &bytes[2], making
|
||||
* the statement in the previous paragraph invalid.
|
||||
*/
|
||||
union pd_msg {
|
||||
struct {
|
||||
uint8_t _pad1[2];
|
||||
uint8_t bytes[30];
|
||||
} __attribute__((packed));
|
||||
struct {
|
||||
uint8_t _pad2[2];
|
||||
uint16_t hdr;
|
||||
union {
|
||||
uint32_t obj[7];
|
||||
struct {
|
||||
uint16_t exthdr;
|
||||
uint8_t data[26];
|
||||
};
|
||||
};
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
#endif /* PDB_MSG_H */
|
||||
@@ -1,686 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "policy_engine.h"
|
||||
#include "Defines.h"
|
||||
#include "fusb302b.h"
|
||||
#include "int_n.h"
|
||||
#include <pd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool PolicyEngine::pdNegotiationComplete;
|
||||
int PolicyEngine::current_voltage_mv;
|
||||
int PolicyEngine::_requested_voltage;
|
||||
bool PolicyEngine::_unconstrained_power;
|
||||
uint16_t PolicyEngine::hdr_template;
|
||||
bool PolicyEngine::_explicit_contract;
|
||||
int8_t PolicyEngine::_hard_reset_counter;
|
||||
uint8_t PolicyEngine::_pps_index;
|
||||
osThreadId PolicyEngine::TaskHandle = NULL;
|
||||
uint32_t PolicyEngine::TaskBuffer[PolicyEngine::TaskStackSize];
|
||||
osStaticThreadDef_t PolicyEngine::TaskControlBlock;
|
||||
union pd_msg PolicyEngine::tempMessage;
|
||||
union pd_msg PolicyEngine::_last_dpm_request;
|
||||
PolicyEngine::policy_engine_state PolicyEngine::state = PESinkStartup;
|
||||
StaticQueue_t PolicyEngine::xStaticQueue;
|
||||
uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
|
||||
QueueHandle_t PolicyEngine::messagesWaiting = NULL;
|
||||
EventGroupHandle_t PolicyEngine::xEventGroupHandle = NULL;
|
||||
StaticEventGroup_t PolicyEngine::xCreatedEventGroup;
|
||||
bool PolicyEngine::PPSTimerEnabled = false;
|
||||
TickType_t PolicyEngine::PPSTimeLastEvent = 0;
|
||||
uint8_t PolicyEngine::_tx_messageidcounter = 0;
|
||||
|
||||
void PolicyEngine::init() {
|
||||
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE, sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
|
||||
// Create static thread at PDB_PRIO_PE priority
|
||||
osThreadStaticDef(PolEng, pe_task, PDB_PRIO_PE, 0, TaskStackSize, TaskBuffer, &TaskControlBlock);
|
||||
TaskHandle = osThreadCreate(osThread(PolEng), NULL);
|
||||
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
|
||||
}
|
||||
|
||||
void PolicyEngine::notify(PolicyEngine::Notifications notification) {
|
||||
EventBits_t val = (EventBits_t)notification;
|
||||
if (xEventGroupHandle != NULL) {
|
||||
xEventGroupSetBits(xEventGroupHandle, val);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::pe_task(const void *arg) {
|
||||
(void)arg;
|
||||
// Internal thread loop
|
||||
hdr_template = PD_DATAROLE_UFP | PD_POWERROLE_SINK;
|
||||
/* Initialize the old_tcc_match */
|
||||
/* Initialize the pps_index */
|
||||
_pps_index = 0xFF;
|
||||
|
||||
for (;;) {
|
||||
// Loop based on state
|
||||
switch (state) {
|
||||
|
||||
case PESinkStartup:
|
||||
state = pe_sink_startup();
|
||||
break;
|
||||
case PESinkDiscovery:
|
||||
state = pe_sink_discovery();
|
||||
break;
|
||||
case PESinkWaitCap:
|
||||
state = pe_sink_wait_cap();
|
||||
break;
|
||||
case PESinkEvalCap:
|
||||
state = pe_sink_eval_cap();
|
||||
break;
|
||||
case PESinkSelectCap:
|
||||
state = pe_sink_select_cap();
|
||||
break;
|
||||
case PESinkTransitionSink:
|
||||
state = pe_sink_transition_sink();
|
||||
break;
|
||||
case PESinkReady:
|
||||
state = pe_sink_ready();
|
||||
break;
|
||||
case PESinkGetSourceCap:
|
||||
state = pe_sink_get_source_cap();
|
||||
break;
|
||||
case PESinkGiveSinkCap:
|
||||
state = pe_sink_give_sink_cap();
|
||||
break;
|
||||
case PESinkHardReset:
|
||||
state = pe_sink_hard_reset();
|
||||
break;
|
||||
case PESinkTransitionDefault:
|
||||
state = pe_sink_transition_default();
|
||||
break;
|
||||
case PESinkSoftReset:
|
||||
state = pe_sink_soft_reset();
|
||||
break;
|
||||
case PESinkSendSoftReset:
|
||||
state = pe_sink_send_soft_reset();
|
||||
break;
|
||||
case PESinkSendNotSupported:
|
||||
state = pe_sink_send_not_supported();
|
||||
break;
|
||||
case PESinkChunkReceived:
|
||||
state = pe_sink_chunk_received();
|
||||
break;
|
||||
case PESinkSourceUnresponsive:
|
||||
state = pe_sink_source_unresponsive();
|
||||
break;
|
||||
case PESinkNotSupportedReceived:
|
||||
state = pe_sink_not_supported_received();
|
||||
break;
|
||||
default:
|
||||
state = PESinkStartup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* state: startup and exiting hard reset. On startup, the protocol layer
|
||||
* is reset by the startup procedure. When exiting hard reset, the
|
||||
* protocol layer is reset by the hard reset state machine. Since it's
|
||||
* already done somewhere else, there's no need to do it again here. */
|
||||
|
||||
return PESinkDiscovery;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_discovery() {
|
||||
/* Wait for VBUS. Since it's our only power source, we already know that
|
||||
* we have it, so just move on. */
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_wait_cap() {
|
||||
/* Fetch a message from the protocol layer */
|
||||
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP | (uint32_t)Notifications::PDB_EVT_PE_RESET,
|
||||
// Wait for cap timeout
|
||||
PD_T_TYPEC_SINK_WAIT_CAP);
|
||||
|
||||
/* If we timed out waiting for Source_Capabilities, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
/* If we're too hot, we shouldn't negotiate power yet */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP) {
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
/* If we got a message */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_MSG_RX) {
|
||||
/* Get the message */
|
||||
while (readMessage()) {
|
||||
/* If we got a Source_Capabilities message, read it. */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOURCE_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
/* First, determine what PD revision we're using */
|
||||
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_1_0) {
|
||||
/* If the other end is using at least version 3.0, we'll
|
||||
* use version 3.0. */
|
||||
if ((tempMessage.hdr & PD_HDR_SPECREV) >= PD_SPECREV_3_0) {
|
||||
hdr_template |= PD_SPECREV_3_0;
|
||||
/* Otherwise, use 2.0. Don't worry about the 1.0 case
|
||||
* because we don't have hardware for PD 1.0 signaling. */
|
||||
} else {
|
||||
hdr_template |= PD_SPECREV_2_0;
|
||||
}
|
||||
}
|
||||
return PESinkEvalCap;
|
||||
}
|
||||
}
|
||||
return PESinkWaitCap; // wait for more messages?
|
||||
}
|
||||
|
||||
/* If we failed to get a message, wait longer */
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
|
||||
/* If we have a Source_Capabilities message, remember the index of the
|
||||
* first PPS APDO so we can check if the request is for a PPS APDO in
|
||||
* PE_SNK_Select_Cap. */
|
||||
/* Start by assuming we won't find a PPS APDO (set the index greater
|
||||
* than the maximum possible) */
|
||||
_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) {
|
||||
_pps_index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask the DPM what to request */
|
||||
if (pdbs_dpm_evaluate_capability(&tempMessage, &_last_dpm_request)) {
|
||||
/* 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) {
|
||||
PPSTimerEnabled = true;
|
||||
} else {
|
||||
PPSTimerEnabled = false;
|
||||
}
|
||||
}
|
||||
return PESinkSelectCap;
|
||||
}
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
|
||||
|
||||
/* Transmit the request */
|
||||
waitForEvent((uint32_t)Notifications::PDB_EVT_PE_ALL, 0); // clear pending
|
||||
EventBits_t evt = pushMessage(&_last_dpm_request);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET || evt == 0) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_ERR) == (uint32_t)Notifications::PDB_EVT_PE_TX_ERR) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Wait for a response */
|
||||
evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If we didn't get a response before the timeout, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkSoftReset;
|
||||
}
|
||||
|
||||
/* Get the response message */
|
||||
if (messageWaiting()) {
|
||||
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) {
|
||||
return PESinkSoftReset;
|
||||
/* If the message was Wait or Reject */
|
||||
} else if ((PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REJECT || PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_WAIT) && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* If we don't have an explicit contract, wait for capabilities */
|
||||
if (!_explicit_contract) {
|
||||
return PESinkWaitCap;
|
||||
/* If we do have an explicit contract, go to the ready state */
|
||||
} else {
|
||||
return PESinkReady;
|
||||
}
|
||||
} else {
|
||||
return PESinkSoftReset;
|
||||
}
|
||||
}
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_sink() {
|
||||
/* Wait for the PS_RDY message */
|
||||
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_PS_TRANSITION);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
/* If we received a message, read it */
|
||||
while (messageWaiting()) {
|
||||
readMessage();
|
||||
/* If we got a PS_RDY, handle it */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PS_RDY && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* We just finished negotiating an explicit contract */
|
||||
_explicit_contract = true;
|
||||
|
||||
/* Negotiation finished */
|
||||
pdbs_dpm_transition_requested();
|
||||
|
||||
return PESinkReady;
|
||||
/* If there was a protocol error, send a hard reset */
|
||||
}
|
||||
}
|
||||
return PESinkSoftReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() {
|
||||
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_ALL);
|
||||
/* If SinkPPSPeriodicTimer ran out, send a new request */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_PPS_REQUEST) {
|
||||
return PESinkSelectCap;
|
||||
}
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
/* If we overheated, send a hard reset */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
/* If the DPM wants us to, send a Get_Source_Cap message */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_GET_SOURCE_CAP) {
|
||||
return PESinkGetSourceCap;
|
||||
}
|
||||
/* If the DPM wants new power, let it figure out what power it wants
|
||||
* exactly. This isn't exactly the transition from the spec (that would be
|
||||
* SelectCap, not EvalCap), but this works better with the particular
|
||||
* design of this firmware. */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_NEW_POWER) {
|
||||
/* Tell the protocol layer we're starting an AMS */
|
||||
return PESinkEvalCap;
|
||||
}
|
||||
|
||||
/* If we received a message */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_MSG_RX) {
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* Ignore vendor-defined messages */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VENDOR_DEFINED && PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
return PESinkReady;
|
||||
/* Ignore Ping messages */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PING && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkReady;
|
||||
/* DR_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_DR_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* Get_Source_Cap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SOURCE_CAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* PR_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PR_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* VCONN_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VCONN_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* Request messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REQUEST && PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* Sink_Capabilities messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SINK_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* Handle GotoMin messages */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GOTOMIN && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSendNotSupported;
|
||||
/* Evaluate new Source_Capabilities */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOURCE_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
return PESinkEvalCap;
|
||||
/* Give sink capabilities when asked */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SINK_CAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkGiveSinkCap;
|
||||
/* 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) {
|
||||
return PESinkSoftReset;
|
||||
/* PD 3.0 messges */
|
||||
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
|
||||
/* If the message is a multi-chunk extended message, let it
|
||||
* time out. */
|
||||
if ((tempMessage.hdr & PD_HDR_EXT) && (PD_DATA_SIZE_GET(&tempMessage) > PD_MAX_EXT_MSG_LEGACY_LEN)) {
|
||||
|
||||
return PESinkChunkReceived;
|
||||
/* Tell the DPM a message we sent got a response of
|
||||
* Not_Supported. */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_NOT_SUPPORTED && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkNotSupportedReceived;
|
||||
/* If we got an unknown message, send a soft reset */
|
||||
} else {
|
||||
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_get_source_cap() {
|
||||
/* Get a message object */
|
||||
union pd_msg *get_source_cap = &tempMessage;
|
||||
/* Make a Get_Source_Cap message */
|
||||
get_source_cap->hdr = hdr_template | PD_MSGTYPE_GET_SOURCE_CAP | PD_NUMOBJ(0);
|
||||
/* Transmit the Get_Source_Cap */
|
||||
EventBits_t evt = pushMessage(get_source_cap);
|
||||
/* Free the sent message */
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_give_sink_cap() {
|
||||
/* Get a message object */
|
||||
union pd_msg *snk_cap = &tempMessage;
|
||||
/* Get our capabilities from the DPM */
|
||||
pdbs_dpm_get_sink_capability(snk_cap);
|
||||
|
||||
/* Transmit our capabilities */
|
||||
EventBits_t evt = pushMessage(snk_cap);
|
||||
|
||||
/* Free the Sink_Capabilities message */
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_hard_reset() {
|
||||
/* If we've already sent the maximum number of hard resets, assume the
|
||||
* source is unresponsive. */
|
||||
if (_hard_reset_counter > PD_N_HARD_RESET_COUNT) {
|
||||
return PESinkSourceUnresponsive;
|
||||
}
|
||||
// So, we could send a hardreset here; however that will cause a power cycle on the PSU end.. Which will then reset this MCU
|
||||
// So therefore we went get anywhere :)
|
||||
/* Increment HardResetCounter */
|
||||
_hard_reset_counter++;
|
||||
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_default() {
|
||||
_explicit_contract = false;
|
||||
|
||||
/* Tell the DPM to transition to default power */
|
||||
pdbs_dpm_transition_default();
|
||||
|
||||
/* There is no local hardware to reset. */
|
||||
/* Since we never change our data role from UFP, there is no reason to set
|
||||
* it here. */
|
||||
|
||||
return PESinkStartup;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_soft_reset() {
|
||||
/* No need to explicitly reset the protocol layer here. It resets itself
|
||||
* when a Soft_Reset message is received. */
|
||||
|
||||
/* Get a message object */
|
||||
union pd_msg accept;
|
||||
/* Make an Accept message */
|
||||
accept.hdr = hdr_template | PD_MSGTYPE_ACCEPT | PD_NUMOBJ(0);
|
||||
/* Transmit the Accept */
|
||||
EventBits_t evt = pushMessage(&accept);
|
||||
/* Free the sent message */
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_soft_reset() {
|
||||
/* No need to explicitly reset the protocol layer here. It resets itself
|
||||
* just before a Soft_Reset message is transmitted. */
|
||||
|
||||
/* Get a message object */
|
||||
union pd_msg *softrst = &tempMessage;
|
||||
/* Make a Soft_Reset message */
|
||||
softrst->hdr = hdr_template | PD_MSGTYPE_SOFT_RESET | PD_NUMOBJ(0);
|
||||
/* Transmit the soft reset */
|
||||
EventBits_t evt = pushMessage(softrst);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Wait for a response */
|
||||
evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If we didn't get a response before the timeout, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Get the response message */
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* If the source accepted our soft reset, wait for capabilities. */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT && PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkWaitCap;
|
||||
/* 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) {
|
||||
|
||||
return PESinkSoftReset;
|
||||
/* Otherwise, send a hard reset */
|
||||
} else {
|
||||
|
||||
return PESinkHardReset;
|
||||
}
|
||||
}
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_not_supported() {
|
||||
/* Get a message object */
|
||||
|
||||
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_2_0) {
|
||||
/* Make a Reject message */
|
||||
tempMessage.hdr = hdr_template | PD_MSGTYPE_REJECT | PD_NUMOBJ(0);
|
||||
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
|
||||
/* Make a Not_Supported message */
|
||||
tempMessage.hdr = hdr_template | PD_MSGTYPE_NOT_SUPPORTED | PD_NUMOBJ(0);
|
||||
}
|
||||
|
||||
/* Transmit the message */
|
||||
EventBits_t evt = pushMessage(&tempMessage);
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a soft reset */
|
||||
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_chunk_received() {
|
||||
|
||||
/* Wait for tChunkingNotSupported */
|
||||
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_CHUNKING_NOT_SUPPORTED);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_not_supported_received() {
|
||||
/* Inform the Device Policy Manager that we received a Not_Supported
|
||||
* message. */
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_source_unresponsive() {
|
||||
// Sit and chill, as PD is not working
|
||||
osDelay(PD_T_PD_DEBOUNCE);
|
||||
|
||||
return PESinkSourceUnresponsive;
|
||||
}
|
||||
|
||||
EventBits_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::handleMessage(union pd_msg *msg) {
|
||||
xQueueSend(messagesWaiting, msg, 100);
|
||||
notify(PolicyEngine::Notifications::PDB_EVT_PE_MSG_RX);
|
||||
}
|
||||
|
||||
void PolicyEngine::PPSTimerCallback() {
|
||||
if (PPSTimerEnabled && state == policy_engine_state::PESinkReady) {
|
||||
// I believe even once per second is totally fine, but leaning on faster since everything seems cool with faster
|
||||
// Have seen everything from 10ms to 1 second :D
|
||||
if ((xTaskGetTickCount() - PPSTimeLastEvent) > (TICKS_SECOND)) {
|
||||
// Send a new PPS message
|
||||
PolicyEngine::notify(Notifications::PDB_EVT_PE_PPS_REQUEST);
|
||||
PPSTimeLastEvent = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PolicyEngine::NegotiationTimeoutReached(uint8_t timeout) {
|
||||
if (timeout == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xTaskGetTickCount() > (TICKS_100MS * timeout)) {
|
||||
state = PESinkSourceUnresponsive;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EventBits_t PolicyEngine::pushMessage(union pd_msg *msg) {
|
||||
if (PD_MSGTYPE_GET(msg) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(msg) == 0) {
|
||||
/* Clear MessageIDCounter */
|
||||
_tx_messageidcounter = 0;
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_DONE;
|
||||
}
|
||||
msg->hdr &= ~PD_HDR_MESSAGEID;
|
||||
msg->hdr |= (_tx_messageidcounter % 8) << PD_HDR_MESSAGEID_SHIFT;
|
||||
|
||||
/* PD 3.0 collision avoidance */
|
||||
if (PolicyEngine::isPD3_0()) {
|
||||
/* If we're starting an AMS, wait for permission to transmit */
|
||||
// while (fusb_get_typec_current() != fusb_sink_tx_ok) {
|
||||
// vTaskDelay(TICKS_10MS);
|
||||
// }
|
||||
}
|
||||
/* Send the message to the PHY */
|
||||
fusb_send_message(msg);
|
||||
/* Waiting for response*/
|
||||
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_RESET | (uint32_t)Notifications::PDB_EVT_TX_DISCARD | (uint32_t)Notifications::PDB_EVT_TX_I_TXSENT
|
||||
| (uint32_t)Notifications::PDB_EVT_TX_I_RETRYFAIL);
|
||||
|
||||
if ((uint32_t)evt & (uint32_t)Notifications::PDB_EVT_TX_DISCARD) {
|
||||
// increment the counter
|
||||
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_ERR; //
|
||||
}
|
||||
|
||||
/* If the message was sent successfully */
|
||||
if ((uint32_t)evt & (uint32_t)Notifications::PDB_EVT_TX_I_TXSENT) {
|
||||
union pd_msg goodcrc;
|
||||
|
||||
/* Read the GoodCRC */
|
||||
fusb_read_message(&goodcrc);
|
||||
|
||||
/* Check that the message is correct */
|
||||
if (PD_MSGTYPE_GET(&goodcrc) == PD_MSGTYPE_GOODCRC && PD_NUMOBJ_GET(&goodcrc) == 0 && PD_MESSAGEID_GET(&goodcrc) == _tx_messageidcounter) {
|
||||
/* Increment MessageIDCounter */
|
||||
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
|
||||
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_DONE;
|
||||
} else {
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_ERR;
|
||||
}
|
||||
}
|
||||
/* If the message failed to be sent */
|
||||
if ((uint32_t)evt & (uint32_t)Notifications::PDB_EVT_TX_I_RETRYFAIL) {
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_ERR;
|
||||
}
|
||||
|
||||
/* Silence the compiler warning */
|
||||
return (EventBits_t)Notifications::PDB_EVT_PE_TX_ERR;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_POLICY_ENGINE_H
|
||||
#define PDB_POLICY_ENGINE_H
|
||||
|
||||
#include <pd.h>
|
||||
|
||||
/*
|
||||
* Events for the Policy Engine thread, used internally + sent by user code
|
||||
*
|
||||
*/
|
||||
|
||||
class PolicyEngine {
|
||||
public:
|
||||
// Sets up internal state and registers the thread
|
||||
static void init();
|
||||
// Push an incoming message to the Policy Engine
|
||||
static void handleMessage(union pd_msg *msg);
|
||||
// Returns true if headers indicate PD3.0 compliant
|
||||
static bool isPD3_0();
|
||||
static bool setupCompleteOrTimedOut(uint8_t timeout) {
|
||||
if (pdNegotiationComplete)
|
||||
return true;
|
||||
if (PolicyEngine::NegotiationTimeoutReached(timeout))
|
||||
return true;
|
||||
if (state == policy_engine_state::PESinkSourceUnresponsive)
|
||||
return true;
|
||||
if (state == policy_engine_state::PESinkReady)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
// Has pd negotiation completed
|
||||
static bool pdHasNegotiated() {
|
||||
if (state == policy_engine_state::PESinkSourceUnresponsive)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// Call this periodically, at least once every second
|
||||
static void PPSTimerCallback();
|
||||
|
||||
static bool NegotiationTimeoutReached(uint8_t timeout);
|
||||
|
||||
enum class Notifications {
|
||||
PDB_EVT_PE_RESET = EVENT_MASK(0),
|
||||
PDB_EVT_PE_MSG_RX = EVENT_MASK(1),
|
||||
PDB_EVT_PE_TX_DONE = EVENT_MASK(2),
|
||||
PDB_EVT_PE_TX_ERR = EVENT_MASK(3),
|
||||
PDB_EVT_PE_HARD_SENT = EVENT_MASK(4),
|
||||
PDB_EVT_PE_I_OVRTEMP = EVENT_MASK(5),
|
||||
PDB_EVT_PE_PPS_REQUEST = EVENT_MASK(6),
|
||||
PDB_EVT_PE_GET_SOURCE_CAP = EVENT_MASK(7),
|
||||
PDB_EVT_PE_NEW_POWER = EVENT_MASK(8),
|
||||
PDB_EVT_TX_I_TXSENT = EVENT_MASK(9),
|
||||
PDB_EVT_TX_I_RETRYFAIL = EVENT_MASK(10),
|
||||
PDB_EVT_TX_DISCARD = EVENT_MASK(11),
|
||||
PDB_EVT_PE_ALL = (EVENT_MASK(12) - 1),
|
||||
};
|
||||
// Send a notification
|
||||
static void notify(Notifications notification);
|
||||
// Debugging allows looking at state
|
||||
static uint32_t getState() { return (uint32_t)state; }
|
||||
|
||||
private:
|
||||
static bool pdNegotiationComplete;
|
||||
static int current_voltage_mv; // The current voltage PD is expecting
|
||||
static int _requested_voltage; // The voltage the unit wanted to requests
|
||||
static bool _unconstrained_power; // If the source is unconstrained
|
||||
/* PD message header template */
|
||||
static uint16_t hdr_template;
|
||||
/* Whether or not we have an explicit contract */
|
||||
static bool _explicit_contract;
|
||||
/* The number of hard resets we've sent */
|
||||
static int8_t _hard_reset_counter;
|
||||
/* The index of the first PPS APDO */
|
||||
static uint8_t _pps_index;
|
||||
|
||||
static void pe_task(const void *arg);
|
||||
static EventBits_t pushMessage(union pd_msg *msg);
|
||||
static uint8_t _tx_messageidcounter;
|
||||
enum policy_engine_state {
|
||||
PESinkStartup, // 0
|
||||
PESinkDiscovery, // 1
|
||||
PESinkWaitCap, // 2
|
||||
PESinkEvalCap, // 3
|
||||
PESinkSelectCap, // 4
|
||||
PESinkTransitionSink, // 5
|
||||
PESinkReady, // 6
|
||||
PESinkGetSourceCap, // 7
|
||||
PESinkGiveSinkCap, // 8
|
||||
PESinkHardReset, // 9
|
||||
PESinkTransitionDefault, // 10
|
||||
PESinkSoftReset, // 11
|
||||
PESinkSendSoftReset, // 12
|
||||
PESinkSendNotSupported, // 13
|
||||
PESinkChunkReceived, // 14
|
||||
PESinkNotSupportedReceived, // 15
|
||||
PESinkSourceUnresponsive // 16
|
||||
|
||||
};
|
||||
static enum policy_engine_state pe_sink_startup();
|
||||
static enum policy_engine_state pe_sink_discovery();
|
||||
static enum policy_engine_state pe_sink_wait_cap();
|
||||
static enum policy_engine_state pe_sink_eval_cap();
|
||||
static enum policy_engine_state pe_sink_select_cap();
|
||||
static enum policy_engine_state pe_sink_transition_sink();
|
||||
static enum policy_engine_state pe_sink_ready();
|
||||
static enum policy_engine_state pe_sink_get_source_cap();
|
||||
static enum policy_engine_state pe_sink_give_sink_cap();
|
||||
static enum policy_engine_state pe_sink_hard_reset();
|
||||
static enum policy_engine_state pe_sink_transition_default();
|
||||
static enum policy_engine_state pe_sink_soft_reset();
|
||||
static enum policy_engine_state pe_sink_send_soft_reset();
|
||||
static enum policy_engine_state pe_sink_send_not_supported();
|
||||
static enum policy_engine_state pe_sink_chunk_received();
|
||||
static enum policy_engine_state pe_sink_not_supported_received();
|
||||
static enum policy_engine_state pe_sink_source_unresponsive();
|
||||
static EventGroupHandle_t xEventGroupHandle;
|
||||
static StaticEventGroup_t xCreatedEventGroup;
|
||||
static EventBits_t waitForEvent(uint32_t mask, TickType_t ticksToWait = portMAX_DELAY);
|
||||
// Task resources
|
||||
static osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 2048 / 4;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
static union pd_msg tempMessage;
|
||||
static union pd_msg _last_dpm_request;
|
||||
static policy_engine_state state;
|
||||
// queue of up to PDB_MSG_POOL_SIZE messages to send
|
||||
static StaticQueue_t xStaticQueue;
|
||||
/* The array to use as the queue's storage area. This must be at least
|
||||
uxQueueLength * uxItemSize bytes. */
|
||||
static uint8_t ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
|
||||
static QueueHandle_t messagesWaiting;
|
||||
static bool messageWaiting();
|
||||
// Read a pending message into the temp message
|
||||
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
|
||||
|
||||
/*
|
||||
* Create a Request message based on the given Source_Capabilities message. If
|
||||
* capabilities is NULL, the last non-null Source_Capabilities message passes
|
||||
* is used. If none has been provided, the behavior is undefined.
|
||||
*
|
||||
* Returns true if sufficient power is available, false otherwise.
|
||||
*/
|
||||
static bool pdbs_dpm_evaluate_capability(const union pd_msg *capabilities, union pd_msg *request);
|
||||
|
||||
/*
|
||||
* Create a Sink_Capabilities message for our current capabilities.
|
||||
*/
|
||||
static void pdbs_dpm_get_sink_capability(union pd_msg *cap);
|
||||
|
||||
/*
|
||||
* Return whether or not GiveBack support is enabled.
|
||||
*/
|
||||
static bool pdbs_dpm_giveback_enabled();
|
||||
|
||||
/*
|
||||
* Evaluate whether or not the currently offered Type-C Current can fulfill our
|
||||
* power needs.
|
||||
*
|
||||
* Returns true if sufficient power is available, false otherwise.
|
||||
*/
|
||||
static bool pdbs_dpm_evaluate_typec_current(enum fusb_typec_current tcc);
|
||||
|
||||
/*
|
||||
* Indicate that power negotiations are starting.
|
||||
*/
|
||||
static void pdbs_dpm_pd_start();
|
||||
|
||||
/*
|
||||
* Transition the sink to default power.
|
||||
*/
|
||||
static void pdbs_dpm_transition_default();
|
||||
|
||||
/*
|
||||
* Transition to the requested minimum current.
|
||||
*/
|
||||
static void pdbs_dpm_transition_min();
|
||||
|
||||
/*
|
||||
* Transition to Sink Standby if necessary.
|
||||
*/
|
||||
static void pdbs_dpm_transition_standby();
|
||||
|
||||
/*
|
||||
* Transition to the requested power level
|
||||
*/
|
||||
static void pdbs_dpm_transition_requested();
|
||||
|
||||
/*
|
||||
* Transition to the Type-C Current power level
|
||||
*/
|
||||
static void pdbs_dpm_transition_typec();
|
||||
};
|
||||
|
||||
#endif /* PDB_POLICY_ENGINE_H */
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* policy_engine_user.cpp
|
||||
*
|
||||
* Created on: 14 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "BSP_PD.h"
|
||||
#include "configuration.h"
|
||||
#include "main.hpp"
|
||||
#include "pd.h"
|
||||
#include "policy_engine.h"
|
||||
|
||||
/* The current draw when the output is disabled */
|
||||
#define DPM_MIN_CURRENT PD_MA2PDI(100)
|
||||
/*
|
||||
* Find the index of the first PDO from capabilities in the voltage range,
|
||||
* using the desired order.
|
||||
*
|
||||
* If there is no such PDO, returns -1 instead.
|
||||
*/
|
||||
static int8_t dpm_get_range_fixed_pdo_index(const union pd_msg *caps) {
|
||||
/* Get the number of PDOs */
|
||||
uint8_t numobj = PD_NUMOBJ_GET(caps);
|
||||
|
||||
/* Get ready to iterate over the PDOs */
|
||||
int8_t i;
|
||||
int8_t step;
|
||||
i = numobj - 1;
|
||||
step = -1;
|
||||
uint16_t current = 100; // in centiamps
|
||||
uint16_t voltagemin = 8000;
|
||||
uint16_t voltagemax = 10000;
|
||||
/* Look at the PDOs to see if one falls in our voltage range. */
|
||||
while (0 <= i && i < numobj) {
|
||||
/* If we have a fixed PDO, its V is within our range, and its I is at
|
||||
* least our desired I */
|
||||
uint16_t v = PD_PDO_SRC_FIXED_VOLTAGE_GET(caps->obj[i]);
|
||||
if ((caps->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
|
||||
if (PD_PDO_SRC_FIXED_CURRENT_GET(caps->obj[i]) >= current) {
|
||||
if (v >= PD_MV2PDV(voltagemin) && v <= PD_MV2PDV(voltagemax)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += step;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
bool PolicyEngine::pdbs_dpm_evaluate_capability(const union pd_msg *capabilities, union pd_msg *request) {
|
||||
|
||||
/* Get the number of PDOs */
|
||||
uint8_t numobj = PD_NUMOBJ_GET(capabilities);
|
||||
|
||||
/* Get whether or not the power supply is constrained */
|
||||
_unconstrained_power = capabilities->obj[0] & PD_PDO_SRC_FIXED_UNCONSTRAINED;
|
||||
|
||||
/* Make sure we have configuration */
|
||||
/* Look at the PDOs to see if one matches our desires */
|
||||
// Look against USB_PD_Desired_Levels to select in order of preference
|
||||
uint8_t bestIndex = 0xFF;
|
||||
int bestIndexVoltage = 0;
|
||||
int bestIndexCurrent = 0;
|
||||
bool bestIsPPS = false;
|
||||
powerSupplyWattageLimit = 0;
|
||||
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 */
|
||||
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
|
||||
// This is a fixed PDO entry
|
||||
// Evaluate if it can produve sufficient current based on the tipResistance (ohms*10)
|
||||
// V=I*R -> V/I => minimum resistance, if our tip resistance is >= this then we can use this supply
|
||||
|
||||
int voltage_mv = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i])); // voltage in mV units
|
||||
int current_a_x100 = PD_PDO_SRC_FIXED_CURRENT_GET(capabilities->obj[i]); // current in 10mA units
|
||||
int min_resistance_ohmsx10 = voltage_mv / current_a_x100;
|
||||
if (voltage_mv <= (USB_PD_VMAX * 1000)) {
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// If this device has step down DC/DC inductor to smooth out current spikes
|
||||
// We can instead ignore resistance and go for max voltage we can accept
|
||||
if (voltage_mv <= (USB_PD_VMAX * 1000)) {
|
||||
min_resistance_ohmsx10 = tipResistance;
|
||||
}
|
||||
#endif
|
||||
// Fudge of 0.5 ohms to round up a little to account for other losses
|
||||
if (min_resistance_ohmsx10 <= (tipResistance + 5)) {
|
||||
// This is a valid power source we can select as
|
||||
if (voltage_mv > bestIndexVoltage || bestIndex == 0xFF) {
|
||||
// Higher voltage and valid, select this instead
|
||||
bestIndex = i;
|
||||
bestIndexVoltage = voltage_mv;
|
||||
bestIndexCurrent = current_a_x100;
|
||||
bestIsPPS = false;
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// set limiter for wattage
|
||||
powerSupplyWattageLimit = ((voltage_mv * current_a_x100) / 100 / 1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} else 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 == 0xFF) {
|
||||
bestIndex = i;
|
||||
bestIndexVoltage = ideal_voltage_mv;
|
||||
bestIndexCurrent = max_current;
|
||||
bestIsPPS = true;
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// set limiter for wattage
|
||||
powerSupplyWattageLimit = ((ideal_voltage_mv * max_current) / 100 / 1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestIndex != 0xFF) {
|
||||
/* We got what we wanted, so build a request for that */
|
||||
request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(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 dont do usb
|
||||
// request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
_requested_voltage = bestIndexVoltage;
|
||||
|
||||
} else {
|
||||
/* 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 (pdNegotiationComplete) {
|
||||
request->obj[0] |= PD_RDO_CAP_MISMATCH;
|
||||
}
|
||||
// We dont do usb
|
||||
// request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
_requested_voltage = 5000;
|
||||
}
|
||||
// Even if we didnt match, we return true as we would still like to handshake on 5V at the minimum
|
||||
return true;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
|
||||
/* Keep track of how many PDOs we've added */
|
||||
int numobj = 0;
|
||||
|
||||
/* If we have no configuration or want something other than 5 V, add a PDO
|
||||
* for vSafe5V */
|
||||
/* Minimum current, 5 V, and higher capability. */
|
||||
cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(5000)) | PD_PDO_SNK_FIXED_CURRENT_SET(DPM_MIN_CURRENT);
|
||||
|
||||
/* Get the current we want */
|
||||
uint16_t voltage = USB_PD_VMAX * 1000; // in mv
|
||||
if (_requested_voltage != 5000) {
|
||||
voltage = _requested_voltage;
|
||||
}
|
||||
uint16_t current = (voltage) / tipResistance; // In centi-amps
|
||||
|
||||
/* Add a PDO for the desired power. */
|
||||
cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(voltage)) | PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
|
||||
/* Get the PDO from the voltage range */
|
||||
int8_t i = dpm_get_range_fixed_pdo_index(cap);
|
||||
|
||||
/* If it's vSafe5V, set our vSafe5V's current to what we want */
|
||||
if (i == 0) {
|
||||
cap->obj[0] &= ~PD_PDO_SNK_FIXED_CURRENT;
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
} else {
|
||||
/* If we want more than 5 V, set the Higher Capability flag */
|
||||
if (PD_MV2PDV(voltage) != PD_MV2PDV(5000)) {
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_HIGHER_CAP;
|
||||
}
|
||||
|
||||
/* If the range PDO is a different voltage than the preferred
|
||||
* voltage, add it to the array. */
|
||||
if (i > 0 && PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i]) != PD_MV2PDV(voltage)) {
|
||||
cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i])) | PD_PDO_SNK_FIXED_CURRENT_SET(PD_PDO_SRC_FIXED_CURRENT_GET(cap->obj[i]));
|
||||
}
|
||||
|
||||
/* If we have three PDOs at this point, make sure the last two are
|
||||
* sorted by voltage. */
|
||||
if (numobj == 3 && (cap->obj[1] & PD_PDO_SNK_FIXED_VOLTAGE) > (cap->obj[2] & PD_PDO_SNK_FIXED_VOLTAGE)) {
|
||||
cap->obj[1] ^= cap->obj[2];
|
||||
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. */
|
||||
if (_unconstrained_power) {
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_UNCONSTRAINED;
|
||||
}
|
||||
/* Set the USB communications capable flag. */
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_USB_COMMS;
|
||||
|
||||
/* Set the Sink_Capabilities message header */
|
||||
cap->hdr = hdr_template | PD_MSGTYPE_SINK_CAPABILITIES | PD_NUMOBJ(numobj);
|
||||
}
|
||||
|
||||
bool PolicyEngine::pdbs_dpm_evaluate_typec_current(enum fusb_typec_current tcc) {
|
||||
(void)tcc;
|
||||
// This is for evaluating 5V static current advertised by resistors
|
||||
/* We don't control the voltage anymore; it will always be 5 V. */
|
||||
current_voltage_mv = _requested_voltage = 5000;
|
||||
// For the soldering iron we accept this as a fallback, but it sucks
|
||||
pdNegotiationComplete = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_default() {
|
||||
|
||||
/* Pretend we requested 5 V */
|
||||
current_voltage_mv = 5000;
|
||||
/* Turn the output off */
|
||||
pdNegotiationComplete = false;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_requested() { pdNegotiationComplete = true; }
|
||||
|
||||
bool PolicyEngine::messageWaiting() { return uxQueueMessagesWaiting(messagesWaiting) > 0; }
|
||||
|
||||
bool PolicyEngine::readMessage() { return xQueueReceive(messagesWaiting, &tempMessage, 0) == pdTRUE; }
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_typec() {
|
||||
// This means PD failed, so we either have a dump 5V only type C or a QC charger
|
||||
// For now; treat this as failed neg
|
||||
pdNegotiationComplete = false;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void OLED::initialize() {
|
||||
|
||||
for (int tries = 0; tries < 10; tries++) {
|
||||
if (I2C_CLASS::writeRegistersBulk(DEVICEADDR_OLED, OLED_Setup_Array, sizeof(OLED_Setup_Array) / sizeof(OLED_Setup_Array[0]))) {
|
||||
return;
|
||||
tries = 11;
|
||||
}
|
||||
}
|
||||
setDisplayState(DisplayState::ON);
|
||||
|
||||
236
source/Core/Drivers/USBPD.cpp
Normal file
236
source/Core/Drivers/USBPD.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "USBPD.h"
|
||||
#include "configuration.h"
|
||||
#if POW_PD
|
||||
|
||||
#include "BSP_PD.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "fusb302b.h"
|
||||
#include "main.hpp"
|
||||
#include "pd.h"
|
||||
#include "policy_engine.h"
|
||||
|
||||
#ifndef USB_PD_VMAX
|
||||
#error Max PD Voltage must be defined
|
||||
#endif
|
||||
#ifndef TIP_RESISTANCE
|
||||
#error Tip resistance must be defined
|
||||
#endif
|
||||
|
||||
void ms_delay(uint32_t delayms) {
|
||||
// Convert ms -> ticks
|
||||
TickType_t ticks = delayms / portTICK_PERIOD_MS;
|
||||
|
||||
vTaskDelay(ticks ? ticks : 1); /* Minimum delay = 1 tick */
|
||||
}
|
||||
uint32_t get_ms_timestamp() {
|
||||
// Convert ticks -> ms
|
||||
return xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
}
|
||||
bool pdbs_dpm_evaluate_capability(const pd_msg *capabilities, pd_msg *request);
|
||||
void pdbs_dpm_get_sink_capability(pd_msg *cap, const bool isPD3);
|
||||
FUSB302 fusb((0x22 << 1), fusb_read_buf, fusb_write_buf, ms_delay); // Create FUSB driver
|
||||
PolicyEngine pe(fusb, get_ms_timestamp, ms_delay, pdbs_dpm_get_sink_capability, pdbs_dpm_evaluate_capability);
|
||||
int USBPowerDelivery::detectionState = 0;
|
||||
uint16_t requested_voltage_mv = 0;
|
||||
|
||||
/* The current draw when the output is disabled */
|
||||
#define DPM_MIN_CURRENT PD_MA2PDI(100)
|
||||
|
||||
// Start processing
|
||||
bool USBPowerDelivery::start() {
|
||||
if (fusbPresent() && fusb.fusb_setup()) {
|
||||
setupFUSBIRQ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void USBPowerDelivery::IRQOccured() { pe.IRQOccured(); }
|
||||
bool USBPowerDelivery::negotiationHasWorked() { return pe.pdHasNegotiated(); }
|
||||
uint8_t USBPowerDelivery::getStateNumber() { return pe.currentStateCode(true); }
|
||||
void USBPowerDelivery::step() {
|
||||
while (pe.thread()) {}
|
||||
}
|
||||
|
||||
void USBPowerDelivery::PPSTimerCallback() { pe.PPSTimerCallback(); }
|
||||
bool USBPowerDelivery::negotiationComplete() {
|
||||
if (!fusbPresent()) {
|
||||
return true;
|
||||
}
|
||||
return pe.setupCompleteOrTimedOut(getSettingValue(SettingsOptions::PDNegTimeout));
|
||||
}
|
||||
bool USBPowerDelivery::fusbPresent() {
|
||||
if (detectionState == 0) {
|
||||
if (fusb.fusb_read_id()) {
|
||||
detectionState = 1;
|
||||
}
|
||||
}
|
||||
return detectionState == 1;
|
||||
}
|
||||
|
||||
bool pdbs_dpm_evaluate_capability(const pd_msg *capabilities, pd_msg *request) {
|
||||
|
||||
/* Get the number of PDOs */
|
||||
uint8_t numobj = PD_NUMOBJ_GET(capabilities);
|
||||
|
||||
/* Make sure we have configuration */
|
||||
/* Look at the PDOs to see if one matches our desires */
|
||||
// Look against USB_PD_Desired_Levels to select in order of preference
|
||||
uint8_t bestIndex = 0xFF;
|
||||
int bestIndexVoltage = 0;
|
||||
int bestIndexCurrent = 0;
|
||||
bool bestIsPPS = false;
|
||||
powerSupplyWattageLimit = 0;
|
||||
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 */
|
||||
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
|
||||
// This is a fixed PDO entry
|
||||
// Evaluate if it can produve sufficient current based on the TIP_RESISTANCE (ohms*10)
|
||||
// V=I*R -> V/I => minimum resistance, if our tip resistance is >= this then we can use this supply
|
||||
|
||||
int voltage_mv = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i])); // voltage in mV units
|
||||
int current_a_x100 = PD_PDO_SRC_FIXED_CURRENT_GET(capabilities->obj[i]); // current in 10mA units
|
||||
int min_resistance_ohmsx10 = voltage_mv / current_a_x100;
|
||||
if (voltage_mv <= (USB_PD_VMAX * 1000)) {
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// If this device has step down DC/DC inductor to smooth out current spikes
|
||||
// We can instead ignore resistance and go for max voltage we can accept
|
||||
min_resistance_ohmsx10 = TIP_RESISTANCE;
|
||||
#endif
|
||||
// Fudge of 0.5 ohms to round up a little to account for other losses
|
||||
if (min_resistance_ohmsx10 <= (TIP_RESISTANCE + 5)) {
|
||||
// This is a valid power source we can select as
|
||||
if ((voltage_mv > bestIndexVoltage) || bestIndex == 0xFF) {
|
||||
// Higher voltage and valid, select this instead
|
||||
bestIndex = i;
|
||||
bestIndexVoltage = voltage_mv;
|
||||
bestIndexCurrent = current_a_x100;
|
||||
bestIsPPS = false;
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// set limiter for wattage
|
||||
powerSupplyWattageLimit = ((voltage_mv * current_a_x100) / 100 / 1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} else 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 = (TIP_RESISTANCE * 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 == 0xFF) {
|
||||
bestIndex = i;
|
||||
bestIndexVoltage = ideal_voltage_mv;
|
||||
bestIndexCurrent = max_current;
|
||||
bestIsPPS = true;
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// set limiter for wattage
|
||||
powerSupplyWattageLimit = ((ideal_voltage_mv * max_current) / 100 / 1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestIndex != 0xFF) {
|
||||
/* We got what we wanted, so build a request for that */
|
||||
request->hdr = PD_MSGTYPE_REQUEST | PD_NUMOBJ(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 dont do usb
|
||||
// request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
requested_voltage_mv = bestIndexVoltage;
|
||||
|
||||
} else {
|
||||
/* Nothing matched (or no configuration), so get 5 V at low current */
|
||||
request->hdr = 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);
|
||||
// We dont do usb
|
||||
// request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
requested_voltage_mv = 5000;
|
||||
}
|
||||
// Even if we didnt match, we return true as we would still like to handshake on 5V at the minimum
|
||||
return true;
|
||||
}
|
||||
|
||||
void pdbs_dpm_get_sink_capability(pd_msg *cap, const bool isPD3) {
|
||||
/* Keep track of how many PDOs we've added */
|
||||
// int numobj = 0;
|
||||
|
||||
// /* If we have no configuration or want something other than 5 V, add a PDO
|
||||
// * for vSafe5V */
|
||||
// /* Minimum current, 5 V, and higher capability. */
|
||||
// cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(5000)) | PD_PDO_SNK_FIXED_CURRENT_SET(DPM_MIN_CURRENT);
|
||||
|
||||
// /* Get the current we want */
|
||||
// uint16_t voltage = USB_PD_VMAX * 1000; // in mv
|
||||
// if (requested_voltage_mv != 5000) {
|
||||
// voltage = requested_voltage_mv;
|
||||
// }
|
||||
// uint16_t current = (voltage) / TIP_RESISTANCE; // In centi-amps
|
||||
|
||||
// /* Add a PDO for the desired power. */
|
||||
// cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(voltage)) | PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
|
||||
// /* Get the PDO from the voltage range */
|
||||
// int8_t i = dpm_get_range_fixed_pdo_index(cap);
|
||||
|
||||
// /* If it's vSafe5V, set our vSafe5V's current to what we want */
|
||||
// if (i == 0) {
|
||||
// cap->obj[0] &= ~PD_PDO_SNK_FIXED_CURRENT;
|
||||
// cap->obj[0] |= PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
// } else {
|
||||
// /* If we want more than 5 V, set the Higher Capability flag */
|
||||
// if (PD_MV2PDV(voltage) != PD_MV2PDV(5000)) {
|
||||
// cap->obj[0] |= PD_PDO_SNK_FIXED_HIGHER_CAP;
|
||||
// }
|
||||
|
||||
// /* If the range PDO is a different voltage than the preferred
|
||||
// * voltage, add it to the array. */
|
||||
// if (i > 0 && PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i]) != PD_MV2PDV(voltage)) {
|
||||
// cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i])) | PD_PDO_SNK_FIXED_CURRENT_SET(PD_PDO_SRC_FIXED_CURRENT_GET(cap->obj[i]));
|
||||
// }
|
||||
|
||||
// /* If we have three PDOs at this point, make sure the last two are
|
||||
// * sorted by voltage. */
|
||||
// if (numobj == 3 && (cap->obj[1] & PD_PDO_SNK_FIXED_VOLTAGE) > (cap->obj[2] & PD_PDO_SNK_FIXED_VOLTAGE)) {
|
||||
// cap->obj[1] ^= cap->obj[2];
|
||||
// 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. */
|
||||
// if (_unconstrained_power) {
|
||||
// cap->obj[0] |= PD_PDO_SNK_FIXED_UNCONSTRAINED;
|
||||
// }
|
||||
// /* Set the USB communications capable flag. */
|
||||
// cap->obj[0] |= PD_PDO_SNK_FIXED_USB_COMMS;
|
||||
|
||||
// /* Set the Sink_Capabilities message header */
|
||||
// cap->hdr = hdr_template | PD_MSGTYPE_SINK_CAPABILITIES | PD_NUMOBJ(numobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
29
source/Core/Drivers/USBPD.h
Normal file
29
source/Core/Drivers/USBPD.h
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
#ifndef DRIVERS_USBPD_H_
|
||||
#define DRIVERS_USBPD_H_
|
||||
#include "configuration.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if POW_PD
|
||||
class USBPowerDelivery {
|
||||
public:
|
||||
static bool start(); // Start the PD stack
|
||||
static bool negotiationComplete(); // Has negotiation completed to a voltage > 5v
|
||||
static bool negotiationInProgress(); // Is negotiation ongoing
|
||||
static bool fusbPresent(); // Is the FUSB302 present on the bus
|
||||
static void PPSTimerCallback(); // PPS Timer
|
||||
static void IRQOccured(); // Thread callback that an irq occured
|
||||
static void step(); // Iterate the step machine
|
||||
static bool negotiationHasWorked(); //
|
||||
static uint8_t getStateNumber(); //
|
||||
|
||||
private:
|
||||
//
|
||||
static int detectionState;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
1
source/Core/Drivers/usb-pd
Submodule
1
source/Core/Drivers/usb-pd
Submodule
Submodule source/Core/Drivers/usb-pd added at a9ea9e9917
Reference in New Issue
Block a user