1
0
forked from me/IronOS

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:
Ben V. Brown
2021-10-02 14:48:58 +10:00
committed by GitHub
parent 04ad5a3bfc
commit 3594604efc
114 changed files with 3099 additions and 6434 deletions

View File

@@ -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); }

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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_ */

View File

@@ -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);
}
}

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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);

View 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

View 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