1
0
forked from me/IronOS

Moving PD framework + big thread cleanup

This commit is contained in:
Ben V. Brown
2020-06-14 16:32:11 +10:00
parent 2c0b14edd4
commit f196c5f1c9
36 changed files with 3540 additions and 9106 deletions

View File

@@ -51,6 +51,8 @@ uint8_t showBootLogoIfavailable();
void delay_ms(uint16_t count);
//Used to allow knowledge of if usb_pd is being used
uint8_t usb_pd_detect();
//Returns 0 when the irq line is pulled down
uint8_t pd_irq_read();
#ifdef __cplusplus
}
#endif

View File

@@ -81,6 +81,8 @@
#define SCL2_GPIO_Port GPIOA
#define SDA2_Pin GPIO_PIN_1
#define SDA2_GPIO_Port GPIOA
#define INT_PD_Pin GPIO_PIN_9
#define INT_PD_GPIO_Port GPIOA
#endif

View File

@@ -2,7 +2,8 @@
#include "BSP_Power.h"
#include "QC3.h"
#include "Settings.h"
#include "FUSB302.h"
#include "Pins.h"
#include "fusbpd.h"
bool FUSB302_present = false;
void power_probe() {
// If TS80 probe for QC
@@ -20,13 +21,6 @@ void power_probe() {
void power_check() {
#ifdef MODEL_TS80
QC_resync();
if (FUSB302_present) {
pd_run_state_machine();
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9) == GPIO_PIN_RESET) {
tcpc_alert();
}
}
#endif
}
uint8_t usb_pd_detect() {
@@ -44,3 +38,10 @@ uint8_t usb_pd_detect() {
#endif
return false;
}
uint8_t pd_irq_read() {
#ifdef MODEL_TS80
return HAL_GPIO_ReadPin(INT_PD_GPIO_Port, INT_PD_Pin) == GPIO_PIN_SET ?
1 : 0;
#endif
return 0;
}

View File

@@ -8,14 +8,8 @@
#include "stdlib.h"
#include "task.h"
#include "I2C_Wrapper.hpp"
#include "USBC_TCPM/tcpm.h"
void postRToSInit() {
// Any after RTos setup
FRToSI2C::FRToSInit();
#ifdef MODEL_TS80
tcpm_init();
osDelay(50);
pd_init();
osDelay(50);
#endif
}

View File

@@ -10,6 +10,7 @@
#include "Setup.h"
#include "Pins.h"
#include "I2CBB.hpp"
#include "fusbpd.h"
void preRToSInit() {
/* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/
@@ -19,5 +20,9 @@ void preRToSInit() {
HAL_Delay(50);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
HAL_Delay(50);
#ifdef MODEL_TS80
I2CBB::init();
//Spawn all of the USB-C processors
fusb302_start_processing();
#endif
}

View File

@@ -1,935 +0,0 @@
/*
FUSB302.c - Library for interacting with the FUSB302B chip.
Copyright 2015 The Chromium OS Authors
Copyright 2017 Jason Cerundolo
Released under an MIT license. See LICENSE file.
*/
#include "FUSB302.h"
#include "USBC_TCPM/usb_pd_tcpm.h"
#include "USBC_TCPM/tcpm.h"
#include "USBC_PD/usb_pd.h"
#include <string.h>
#include "cmsis_os.h"
#include "I2C_Wrapper.hpp"
#define PACKET_IS_GOOD_CRC(head) (PD_HEADER_TYPE(head) == PD_CTRL_GOOD_CRC && \
PD_HEADER_CNT(head) == 0)
const struct tcpc_config_t tcpc_config = {
fusb302_I2C_SLAVE_ADDR, &fusb302_tcpm_drv };
static struct fusb302_chip_state {
int cc_polarity;
int vconn_enabled;
/* 1 = pulling up (DFP) 0 = pulling down (UFP) */
int pulling_up;
int rx_enable;
uint8_t mdac_vnc;
uint8_t mdac_rd;
} state;
/*
* Bring the FUSB302 out of reset after Hard Reset signaling. This will
* automatically flush both the Rx and Tx FIFOs.
*/
static void fusb302_pd_reset() {
tcpc_write( TCPC_REG_RESET, TCPC_REG_RESET_PD_RESET);
}
/*
* Flush our Rx FIFO. To prevent packet framing issues, this function should
* only be called when Rx is disabled.
*/
static void fusb302_flush_rx_fifo() {
/*
* other bits in the register _should_ be 0
* until the day we support other SOP* types...
* then we'll have to keep a shadow of what this register
* value should be so we don't clobber it here!
*/
tcpc_write( TCPC_REG_CONTROL1, TCPC_REG_CONTROL1_RX_FLUSH);
}
static void fusb302_flush_tx_fifo() {
int reg;
tcpc_read( TCPC_REG_CONTROL0, &reg);
reg |= TCPC_REG_CONTROL0_TX_FLUSH;
tcpc_write( TCPC_REG_CONTROL0, reg);
}
static void fusb302_auto_goodcrc_enable(int enable) {
int reg;
tcpc_read( TCPC_REG_SWITCHES1, &reg);
if (enable)
reg |= TCPC_REG_SWITCHES1_AUTO_GCRC;
else
reg &= ~TCPC_REG_SWITCHES1_AUTO_GCRC;
tcpc_write( TCPC_REG_SWITCHES1, reg);
}
/* Convert BC LVL values (in FUSB302) to Type-C CC Voltage Status */
static int convert_bc_lvl(int bc_lvl) {
/* assume OPEN unless one of the following conditions is true... */
int ret = TYPEC_CC_VOLT_OPEN;
if (state.pulling_up) {
if (bc_lvl == 0x00)
ret = TYPEC_CC_VOLT_RA;
else if (bc_lvl < 0x3)
ret = TYPEC_CC_VOLT_RD;
} else {
if (bc_lvl == 0x1)
ret = TYPEC_CC_VOLT_SNK_DEF;
else if (bc_lvl == 0x2)
ret = TYPEC_CC_VOLT_SNK_1_5;
else if (bc_lvl == 0x3)
ret = TYPEC_CC_VOLT_SNK_3_0;
}
return ret;
}
static int measure_cc_pin_source(int cc_measure) {
int switches0_reg;
int reg;
int cc_lvl;
/* Read status register */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* Save current value */
switches0_reg = reg;
/* Clear pull-up register settings and measure bits */
reg &= ~(TCPC_REG_SWITCHES0_MEAS_CC1 | TCPC_REG_SWITCHES0_MEAS_CC2);
/* Set desired pullup register bit */
if (cc_measure == TCPC_REG_SWITCHES0_MEAS_CC1)
reg |= TCPC_REG_SWITCHES0_CC1_PU_EN;
else
reg |= TCPC_REG_SWITCHES0_CC2_PU_EN;
/* Set CC measure bit */
reg |= cc_measure;
/* Set measurement switch */
tcpc_write( TCPC_REG_SWITCHES0, reg);
/* Set MDAC for Open vs Rd/Ra comparison */
tcpc_write( TCPC_REG_MEASURE, state.mdac_vnc);
/* Wait on measurement */
usleep(250);
/* Read status register */
tcpc_read( TCPC_REG_STATUS0, &reg);
/* Assume open */
cc_lvl = TYPEC_CC_VOLT_OPEN;
/* CC level is below the 'no connect' threshold (vOpen) */
if ((reg & TCPC_REG_STATUS0_COMP) == 0) {
/* Set MDAC for Rd vs Ra comparison */
tcpc_write( TCPC_REG_MEASURE, state.mdac_rd);
/* Wait on measurement */
usleep(250);
/* Read status register */
tcpc_read( TCPC_REG_STATUS0, &reg);
cc_lvl =
(reg & TCPC_REG_STATUS0_COMP) ?
TYPEC_CC_VOLT_RD : TYPEC_CC_VOLT_RA;
}
/* Restore SWITCHES0 register to its value prior */
tcpc_write( TCPC_REG_SWITCHES0, switches0_reg);
return cc_lvl;
}
/* Determine cc pin state for source when in manual detect mode */
static void detect_cc_pin_source_manual(int *cc1_lvl, int *cc2_lvl) {
int cc1_measure = TCPC_REG_SWITCHES0_MEAS_CC1;
int cc2_measure = TCPC_REG_SWITCHES0_MEAS_CC2;
if (state.vconn_enabled) {
/* If VCONN enabled, measure cc_pin that matches polarity */
if (state.cc_polarity)
*cc2_lvl = measure_cc_pin_source(cc2_measure);
else
*cc1_lvl = measure_cc_pin_source(cc1_measure);
} else {
/* If VCONN not enabled, measure both cc1 and cc2 */
*cc1_lvl = measure_cc_pin_source(cc1_measure);
*cc2_lvl = measure_cc_pin_source(cc2_measure);
}
}
/* Determine cc pin state for sink */
static void detect_cc_pin_sink(int *cc1, int *cc2) {
int reg;
int orig_meas_cc1;
int orig_meas_cc2;
int bc_lvl_cc1;
int bc_lvl_cc2;
/*
* Measure CC1 first.
*/
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* save original state to be returned to later... */
if (reg & TCPC_REG_SWITCHES0_MEAS_CC1)
orig_meas_cc1 = 1;
else
orig_meas_cc1 = 0;
if (reg & TCPC_REG_SWITCHES0_MEAS_CC2)
orig_meas_cc2 = 1;
else
orig_meas_cc2 = 0;
/* Disable CC2 measurement switch, enable CC1 measurement switch */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
tcpc_write( TCPC_REG_SWITCHES0, reg);
/* CC1 is now being measured by FUSB302. */
/* Wait on measurement */
usleep(250);
tcpc_read( TCPC_REG_STATUS0, &bc_lvl_cc1);
/* mask away unwanted bits */
bc_lvl_cc1 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
/*
* Measure CC2 next.
*/
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* Disable CC1 measurement switch, enable CC2 measurement switch */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
tcpc_write( TCPC_REG_SWITCHES0, reg);
/* CC2 is now being measured by FUSB302. */
/* Wait on measurement */
usleep(250);
tcpc_read( TCPC_REG_STATUS0, &bc_lvl_cc2);
/* mask away unwanted bits */
bc_lvl_cc2 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
*cc1 = convert_bc_lvl(bc_lvl_cc1);
*cc2 = convert_bc_lvl(bc_lvl_cc2);
/* return MEAS_CC1/2 switches to original state */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
if (orig_meas_cc1)
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
else
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
if (orig_meas_cc2)
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
else
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
tcpc_write( TCPC_REG_SWITCHES0, reg);
}
/* Parse header bytes for the size of packet */
static int get_num_bytes(uint16_t header) {
int rv;
/* Grab the Number of Data Objects field.*/
rv = PD_HEADER_CNT(header);
/* Multiply by four to go from 32-bit words -> bytes */
rv *= 4;
/* Plus 2 for header */
rv += 2;
return rv;
}
static int fusb302_send_message(uint16_t header, const uint32_t *data,
uint8_t *buf, int buf_pos) {
int rv;
int reg;
int len;
len = get_num_bytes(header);
/*
* packsym tells the TXFIFO that the next X bytes are payload,
* and should not be interpreted as special tokens.
* The 5 LSBs represent X, the number of bytes.
*/
reg = fusb302_TKN_PACKSYM;
reg |= (len & 0x1F);
buf[buf_pos++] = reg;
/* write in the header */
reg = header;
buf[buf_pos++] = reg & 0xFF;
reg >>= 8;
buf[buf_pos++] = reg & 0xFF;
/* header is done, subtract from length to make this for-loop simpler */
len -= 2;
/* write data objects, if present */
memcpy(&buf[buf_pos], data, len);
buf_pos += len;
/* put in the CRC */
buf[buf_pos++] = fusb302_TKN_JAMCRC;
/* put in EOP */
buf[buf_pos++] = fusb302_TKN_EOP;
/* Turn transmitter off after sending message */
buf[buf_pos++] = fusb302_TKN_TXOFF;
/* Start transmission */
reg = fusb302_TKN_TXON;
buf[buf_pos++] = fusb302_TKN_TXON;
/* burst write for speed! */
rv = tcpc_xfer(buf, buf_pos, 0, 0, I2C_XFER_SINGLE);
return rv;
}
static int fusb302_tcpm_select_rp_value(int rp) {
int reg;
int rv;
uint8_t vnc, rd;
rv = tcpc_read( TCPC_REG_CONTROL0, &reg);
if (rv)
return rv;
/* Set the current source for Rp value */
reg &= ~TCPC_REG_CONTROL0_HOST_CUR_MASK;
switch (rp) {
case TYPEC_RP_1A5:
reg |= TCPC_REG_CONTROL0_HOST_CUR_1A5;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_RD_THRESH_MV);
break;
case TYPEC_RP_3A0:
reg |= TCPC_REG_CONTROL0_HOST_CUR_3A0;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_RD_THRESH_MV);
break;
case TYPEC_RP_USB:
default:
reg |= TCPC_REG_CONTROL0_HOST_CUR_USB;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV);
}
state.mdac_vnc = vnc;
state.mdac_rd = rd;
rv = tcpc_write( TCPC_REG_CONTROL0, reg);
return rv;
}
static int fusb302_tcpm_init() {
int reg;
/* set default */
state.cc_polarity = -1;
/* set the voltage threshold for no connect detection (vOpen) */
state.mdac_vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV);
/* set the voltage threshold for Rd vs Ra detection */
state.mdac_rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV);
/* all other variables assumed to default to 0 */
/* Restore default settings */
tcpc_write( TCPC_REG_RESET, TCPC_REG_RESET_SW_RESET);
/* Turn on retries and set number of retries */
tcpc_read( TCPC_REG_CONTROL3, &reg);
reg |= TCPC_REG_CONTROL3_AUTO_RETRY;
reg |= (PD_RETRY_COUNT & 0x3) <<
TCPC_REG_CONTROL3_N_RETRIES_POS;
tcpc_write( TCPC_REG_CONTROL3, reg);
/* Create interrupt masks */
reg = 0xFF;
/* CC level changes */
reg &= ~TCPC_REG_MASK_BC_LVL;
/* collisions */
reg &= ~TCPC_REG_MASK_COLLISION;
/* misc alert */
reg &= ~TCPC_REG_MASK_ALERT;
/* packet received with correct CRC */
reg &= ~TCPC_REG_MASK_CRC_CHK;
tcpc_write( TCPC_REG_MASK, reg);
reg = 0xFF;
/* when all pd message retries fail... */
reg &= ~TCPC_REG_MASKA_RETRYFAIL;
/* when fusb302 send a hard reset. */
reg &= ~TCPC_REG_MASKA_HARDSENT;
/* when fusb302 receives GoodCRC ack for a pd message */
reg &= ~TCPC_REG_MASKA_TX_SUCCESS;
/* when fusb302 receives a hard reset */
reg &= ~TCPC_REG_MASKA_HARDRESET;
tcpc_write( TCPC_REG_MASKA, reg);
reg = 0xFF;
/* when fusb302 sends GoodCRC to ack a pd message */
reg &= ~TCPC_REG_MASKB_GCRCSENT;
tcpc_write( TCPC_REG_MASKB, reg);
/* Interrupt Enable */
tcpc_read( TCPC_REG_CONTROL0, &reg);
reg &= ~TCPC_REG_CONTROL0_INT_MASK;
tcpc_write( TCPC_REG_CONTROL0, reg);
/* Set VCONN switch defaults */
tcpm_set_polarity(0);
tcpm_set_vconn(0);
/* Turn on the power! */
/* TODO: Reduce power consumption */
tcpc_write( TCPC_REG_POWER, TCPC_REG_POWER_PWR_ALL);
return 0;
}
static int fusb302_tcpm_release() {
return EC_ERROR_UNIMPLEMENTED;
}
static int fusb302_tcpm_get_cc(int *cc1, int *cc2) {
if (state.pulling_up) {
/* Source mode? */
detect_cc_pin_source_manual(cc1, cc2);
} else {
/* Sink mode? */
detect_cc_pin_sink(cc1, cc2);
}
return 0;
}
static int fusb302_tcpm_set_cc(int pull) {
int reg;
/* NOTE: FUSB302 toggles a single pull-up between CC1 and CC2 */
/* NOTE: FUSB302 Does not support Ra. */
switch (pull) {
case TYPEC_CC_RP:
/* enable the pull-up we know to be necessary */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN |
TCPC_REG_SWITCHES0_CC1_PU_EN |
TCPC_REG_SWITCHES0_CC1_PD_EN |
TCPC_REG_SWITCHES0_CC2_PD_EN |
TCPC_REG_SWITCHES0_VCONN_CC1 |
TCPC_REG_SWITCHES0_VCONN_CC2);
reg |= TCPC_REG_SWITCHES0_CC1_PU_EN |
TCPC_REG_SWITCHES0_CC2_PU_EN;
if (state.vconn_enabled)
reg |= state.cc_polarity ?
TCPC_REG_SWITCHES0_VCONN_CC1 :
TCPC_REG_SWITCHES0_VCONN_CC2;
tcpc_write( TCPC_REG_SWITCHES0, reg);
state.pulling_up = 1;
break;
case TYPEC_CC_RD:
/* Enable UFP Mode */
/* turn off toggle */
tcpc_read( TCPC_REG_CONTROL2, &reg);
reg &= ~TCPC_REG_CONTROL2_TOGGLE;
tcpc_write( TCPC_REG_CONTROL2, reg);
/* enable pull-downs, disable pullups */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN);
reg &= ~(TCPC_REG_SWITCHES0_CC1_PU_EN);
reg |= (TCPC_REG_SWITCHES0_CC1_PD_EN);
reg |= (TCPC_REG_SWITCHES0_CC2_PD_EN);
tcpc_write( TCPC_REG_SWITCHES0, reg);
state.pulling_up = 0;
break;
case TYPEC_CC_OPEN:
/* Disable toggling */
tcpc_read( TCPC_REG_CONTROL2, &reg);
reg &= ~TCPC_REG_CONTROL2_TOGGLE;
tcpc_write( TCPC_REG_CONTROL2, reg);
/* Ensure manual switches are opened */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
reg &= ~TCPC_REG_SWITCHES0_CC1_PU_EN;
reg &= ~TCPC_REG_SWITCHES0_CC2_PU_EN;
reg &= ~TCPC_REG_SWITCHES0_CC1_PD_EN;
reg &= ~TCPC_REG_SWITCHES0_CC2_PD_EN;
tcpc_write( TCPC_REG_SWITCHES0, reg);
state.pulling_up = 0;
break;
default:
/* Unsupported... */
return EC_ERROR_UNIMPLEMENTED;
}
return 0;
}
static int fusb302_tcpm_set_polarity(int polarity) {
/* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */
int reg;
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* clear VCONN switch bits */
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1;
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2;
if (state.vconn_enabled) {
/* set VCONN switch to be non-CC line */
if (polarity)
reg |= TCPC_REG_SWITCHES0_VCONN_CC1;
else
reg |= TCPC_REG_SWITCHES0_VCONN_CC2;
}
/* clear meas_cc bits (RX line select) */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
/* set rx polarity */
if (polarity)
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
else
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
tcpc_write( TCPC_REG_SWITCHES0, reg);
tcpc_read( TCPC_REG_SWITCHES1, &reg);
/* clear tx_cc bits */
reg &= ~TCPC_REG_SWITCHES1_TXCC1_EN;
reg &= ~TCPC_REG_SWITCHES1_TXCC2_EN;
/* set tx polarity */
if (polarity)
reg |= TCPC_REG_SWITCHES1_TXCC2_EN;
else
reg |= TCPC_REG_SWITCHES1_TXCC1_EN;
tcpc_write( TCPC_REG_SWITCHES1, reg);
/* Save the polarity for later */
state.cc_polarity = polarity;
return 0;
}
static int fusb302_tcpm_set_vconn(int enable) {
/*
* FUSB302 does not have dedicated VCONN Enable switch.
* We'll get through this by disabling both of the
* VCONN - CC* switches to disable, and enabling the
* saved polarity when enabling.
* Therefore at startup, tcpm_set_polarity should be called first,
* or else live with the default put into tcpm_init.
*/
int reg;
/* save enable state for later use */
state.vconn_enabled = enable;
if (enable) {
/* set to saved polarity */
tcpm_set_polarity(state.cc_polarity);
} else {
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* clear VCONN switch bits */
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1;
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2;
tcpc_write( TCPC_REG_SWITCHES0, reg);
}
return 0;
}
static int fusb302_tcpm_set_msg_header(int power_role, int data_role) {
int reg;
tcpc_read( TCPC_REG_SWITCHES1, &reg);
reg &= ~TCPC_REG_SWITCHES1_POWERROLE;
reg &= ~TCPC_REG_SWITCHES1_DATAROLE;
if (power_role)
reg |= TCPC_REG_SWITCHES1_POWERROLE;
if (data_role)
reg |= TCPC_REG_SWITCHES1_DATAROLE;
tcpc_write( TCPC_REG_SWITCHES1, reg);
return 0;
}
static int fusb302_tcpm_set_rx_enable(int enable) {
int reg;
state.rx_enable = enable;
/* Get current switch state */
tcpc_read( TCPC_REG_SWITCHES0, &reg);
/* Clear CC1/CC2 measure bits */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
if (enable) {
switch (state.cc_polarity) {
/* if CC polarity hasnt been determined, can't enable */
case -1:
return EC_ERROR_UNKNOWN;
case 0:
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
break;
case 1:
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
break;
default:
/* "shouldn't get here" */
return EC_ERROR_UNKNOWN;
}
tcpc_write( TCPC_REG_SWITCHES0, reg);
/* Disable BC_LVL interrupt when enabling PD comm */
if (!tcpc_read( TCPC_REG_MASK, &reg))
tcpc_write( TCPC_REG_MASK, reg | TCPC_REG_MASK_BC_LVL);
/* flush rx fifo in case messages have been coming our way */
fusb302_flush_rx_fifo();
} else {
tcpc_write( TCPC_REG_SWITCHES0, reg);
/* Enable BC_LVL interrupt when disabling PD comm */
if (!tcpc_read( TCPC_REG_MASK, &reg))
tcpc_write( TCPC_REG_MASK, reg & ~TCPC_REG_MASK_BC_LVL);
}
fusb302_auto_goodcrc_enable(enable);
return 0;
}
/* Return true if our Rx FIFO is empty */
static int fusb302_rx_fifo_is_empty() {
int reg, ret;
ret = (!tcpc_read( TCPC_REG_STATUS1, &reg))
&& (reg & TCPC_REG_STATUS1_RX_EMPTY);
return ret;
}
static int fusb302_tcpm_get_message(uint32_t *payload, int *head) {
/*
* This is the buffer that will get the burst-read data
* from the fusb302.
*
* It's re-used in a couple different spots, the worst of which
* is the PD packet (not header) and CRC.
* maximum size necessary = 28 + 4 = 32
*/
uint8_t buf[32];
int rv, len;
/* If our FIFO is empty then we have no packet */
if (fusb302_rx_fifo_is_empty())
return EC_ERROR_UNKNOWN;
/* Read until we have a non-GoodCRC packet or an empty FIFO */
do {
buf[0] = TCPC_REG_FIFOS;
/*
* PART 1 OF BURST READ: Write in register address.
* Issue a START, no STOP.
*/
rv = tcpc_xfer(buf, 1, 0, 0, I2C_XFER_START);
/*
* PART 2 OF BURST READ: Read up to the header.
* Issue a repeated START, no STOP.
* only grab three bytes so we can get the header
* and determine how many more bytes we need to read.
* TODO: Check token to ensure valid packet.
*/
rv |= tcpc_xfer(0, 0, buf, 3, I2C_XFER_START);
/* Grab the header */
*head = (buf[1] & 0xFF);
*head |= ((buf[2] << 8) & 0xFF00);
/* figure out packet length, subtract header bytes */
len = get_num_bytes(*head) - 2;
/*
* PART 3 OF BURST READ: Read everything else.
* No START, but do issue a STOP at the end.
* add 4 to len to read CRC out
*/
rv |= tcpc_xfer(0, 0, buf, len + 4, I2C_XFER_STOP);
} while (!rv && PACKET_IS_GOOD_CRC(*head) && !fusb302_rx_fifo_is_empty());
if (!rv) {
/* Discard GoodCRC packets */
if (PACKET_IS_GOOD_CRC(*head))
rv = EC_ERROR_UNKNOWN;
else
memcpy(payload, buf, len);
}
/*
* If our FIFO is non-empty then we may have a packet, we may get
* fewer interrupts than packets due to interrupt latency.
*/
//if (!fusb302_rx_fifo_is_empty(port))
// task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0);
return rv;
}
static int fusb302_tcpm_transmit(enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data) {
/*
* this is the buffer that will be burst-written into the fusb302
* maximum size necessary =
* 1: FIFO register address
* 4: SOP* tokens
* 1: Token that signifies "next X bytes are not tokens"
* 30: 2 for header and up to 7*4 = 28 for rest of message
* 1: "Insert CRC" Token
* 1: EOP Token
* 1: "Turn transmitter off" token
* 1: "Star Transmission" Command
* -
* 40: 40 bytes worst-case
*/
uint8_t buf[40];
int buf_pos = 0;
int reg;
/* Flush the TXFIFO */
fusb302_flush_tx_fifo ();
switch (type) {
case TCPC_TX_SOP:
/* put register address first for of burst tcpc write */
buf[buf_pos++] = TCPC_REG_FIFOS;
/* Write the SOP Ordered Set into TX FIFO */
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC2;
fusb302_send_message(header, data, buf, buf_pos);
// wait for the GoodCRC to come back before we let the rest
// of the code do stuff like change polarity and miss it
// delay_us(600);
osDelay(1);
break;
case TCPC_TX_HARD_RESET:
/* Simply hit the SEND_HARD_RESET bit */
tcpc_read( TCPC_REG_CONTROL3, &reg);
reg |= TCPC_REG_CONTROL3_SEND_HARDRESET;
tcpc_write( TCPC_REG_CONTROL3, reg);
break;
case TCPC_TX_BIST_MODE_2:
/* Hit the BIST_MODE2 bit and start TX */
tcpc_read( TCPC_REG_CONTROL1, &reg);
reg |= TCPC_REG_CONTROL1_BIST_MODE2;
tcpc_write( TCPC_REG_CONTROL1, reg);
tcpc_read( TCPC_REG_CONTROL0, &reg);
reg |= TCPC_REG_CONTROL0_TX_START;
tcpc_write( TCPC_REG_CONTROL0, reg);
//task_wait_event(PD_T_BIST_TRANSMIT);
/* Clear BIST mode bit, TX_START is self-clearing */
tcpc_read( TCPC_REG_CONTROL1, &reg);
reg &= ~TCPC_REG_CONTROL1_BIST_MODE2;
tcpc_write( TCPC_REG_CONTROL1, reg);
break;
default:
return EC_ERROR_UNIMPLEMENTED;
}
return 0;
}
#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
static int fusb302_tcpm_get_vbus_level()
{
int reg;
/* Read status register */
tcpc_read( TCPC_REG_STATUS0, &reg);
return (reg & TCPC_REG_STATUS0_VBUSOK) ? 1 : 0;
}
#endif
void fusb302_tcpc_alert() {
/* interrupt has been received */
int interrupt;
int interrupta;
int interruptb;
/* reading interrupt registers clears them */
tcpc_read( TCPC_REG_INTERRUPT, &interrupt);
tcpc_read( TCPC_REG_INTERRUPTA, &interrupta);
tcpc_read( TCPC_REG_INTERRUPTB, &interruptb);
/*
* Ignore BC_LVL changes when transmitting / receiving PD,
* since CC level will constantly change.
*/
if (state.rx_enable)
interrupt &= ~TCPC_REG_INTERRUPT_BC_LVL;
if (interrupt & TCPC_REG_INTERRUPT_BC_LVL) {
/* CC Status change */
//task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
}
if (interrupt & TCPC_REG_INTERRUPT_COLLISION) {
/* packet sending collided */
pd_transmit_complete(TCPC_TX_COMPLETE_FAILED);
}
/* GoodCRC was received, our FIFO is now non-empty */
if (interrupta & TCPC_REG_INTERRUPTA_TX_SUCCESS) {
//task_set_event(PD_PORT_TO_TASK_ID(port),
// PD_EVENT_RX, 0);
pd_transmit_complete(TCPC_TX_COMPLETE_SUCCESS);
}
if (interrupta & TCPC_REG_INTERRUPTA_RETRYFAIL) {
/* all retries have failed to get a GoodCRC */
pd_transmit_complete(TCPC_TX_COMPLETE_FAILED);
}
if (interrupta & TCPC_REG_INTERRUPTA_HARDSENT) {
/* hard reset has been sent */
/* bring FUSB302 out of reset */
fusb302_pd_reset();
pd_transmit_complete(TCPC_TX_COMPLETE_SUCCESS);
}
if (interrupta & TCPC_REG_INTERRUPTA_HARDRESET) {
/* hard reset has been received */
/* bring FUSB302 out of reset */
fusb302_pd_reset();
pd_execute_hard_reset ();
//task_wake(PD_PORT_TO_TASK_ID(port));
}
if (interruptb & TCPC_REG_INTERRUPTB_GCRCSENT) {
/* Packet received and GoodCRC sent */
/* (this interrupt fires after the GoodCRC finishes) */
if (state.rx_enable) {
//task_set_event(PD_PORT_TO_TASK_ID(port),
// PD_EVENT_RX, 0);
} else {
/* flush rx fifo if rx isn't enabled */
fusb302_flush_rx_fifo();
}
}
}
/* For BIST receiving */
void tcpm_set_bist_test_data() {
int reg;
/* Read control3 register */
tcpc_read( TCPC_REG_CONTROL3, &reg);
/* Set the BIST_TMODE bit (Clears on Hard Reset) */
reg |= TCPC_REG_CONTROL3_BIST_TMODE;
/* Write the updated value */
tcpc_write( TCPC_REG_CONTROL3, reg);
}
const struct tcpm_drv fusb302_tcpm_drv = { &fusb302_tcpm_init, //init
&fusb302_tcpm_release, //.release
&fusb302_tcpm_get_cc, //get_cc
NULL, //get_vbus_level
&fusb302_tcpm_select_rp_value, //select_rp_value
&fusb302_tcpm_set_cc, //set_cc
&fusb302_tcpm_set_polarity, //set_polarity
&fusb302_tcpm_set_vconn, //set_vconn
&fusb302_tcpm_set_msg_header, //set_msg_header
&fusb302_tcpm_set_rx_enable, //set_rx_enable
&fusb302_tcpm_get_message, //get_message
&fusb302_tcpm_transmit, //transmit
&fusb302_tcpc_alert, //tcpc_alert
NULL, //tcpc_discharge_vbus
NULL, //get_chip_info
};

View File

@@ -1,217 +0,0 @@
/*
FUSB302.h - Library for interacting with the FUSB302B chip.
Copyright 2010 The Chromium OS Authors
Copyright 2017 Jason Cerundolo
Released under an MIT license. See LICENSE file.
*/
#ifndef fusb302_H
#define fusb302_H
#include <stdint.h>
#include "USBC_TCPM/usb_pd_tcpm.h"
#include "USBC_PD/usb_pd.h"
/* Chip Device ID - 302A or 302B */
#define fusb302_DEVID_302A 0x08
#define fusb302_DEVID_302B 0x09
/* I2C slave address varies by part number */
/* FUSB302BUCX / FUSB302BMPX */
#define fusb302_I2C_SLAVE_ADDR 0x22<<1 // 7-bit address
/* FUSB302B01MPX */
#define fusb302_I2C_SLAVE_ADDR_B01 0x23<<1
/* FUSB302B10MPX */
#define fusb302_I2C_SLAVE_ADDR_B10 0x24<<1
/* FUSB302B11MPX */
#define fusb302_I2C_SLAVE_ADDR_B11 0x25<<1
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
#define TCPC_REG_DEVICE_ID 0x01
#define TCPC_REG_SWITCHES0 0x02
#define TCPC_REG_SWITCHES0_CC2_PU_EN (1<<7)
#define TCPC_REG_SWITCHES0_CC1_PU_EN (1<<6)
#define TCPC_REG_SWITCHES0_VCONN_CC2 (1<<5)
#define TCPC_REG_SWITCHES0_VCONN_CC1 (1<<4)
#define TCPC_REG_SWITCHES0_MEAS_CC2 (1<<3)
#define TCPC_REG_SWITCHES0_MEAS_CC1 (1<<2)
#define TCPC_REG_SWITCHES0_CC2_PD_EN (1<<1)
#define TCPC_REG_SWITCHES0_CC1_PD_EN (1<<0)
#define TCPC_REG_SWITCHES1 0x03
#define TCPC_REG_SWITCHES1_POWERROLE (1<<7)
#define TCPC_REG_SWITCHES1_SPECREV1 (1<<6)
#define TCPC_REG_SWITCHES1_SPECREV0 (1<<5)
#define TCPC_REG_SWITCHES1_DATAROLE (1<<4)
#define TCPC_REG_SWITCHES1_AUTO_GCRC (1<<2)
#define TCPC_REG_SWITCHES1_TXCC2_EN (1<<1)
#define TCPC_REG_SWITCHES1_TXCC1_EN (1<<0)
#define TCPC_REG_MEASURE 0x04
#define TCPC_REG_MEASURE_VBUS (1<<6)
#define TCPC_REG_MEASURE_MDAC_MV(mv) (((mv)/42) & 0x3f)
#define TCPC_REG_CONTROL0 0x06
#define TCPC_REG_CONTROL0_TX_FLUSH (1<<6)
#define TCPC_REG_CONTROL0_INT_MASK (1<<5)
#define TCPC_REG_CONTROL0_HOST_CUR_MASK (3<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_3A0 (3<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_1A5 (2<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_USB (1<<2)
#define TCPC_REG_CONTROL0_TX_START (1<<0)
#define TCPC_REG_CONTROL1 0x07
#define TCPC_REG_CONTROL1_ENSOP2DB (1<<6)
#define TCPC_REG_CONTROL1_ENSOP1DB (1<<5)
#define TCPC_REG_CONTROL1_BIST_MODE2 (1<<4)
#define TCPC_REG_CONTROL1_RX_FLUSH (1<<2)
#define TCPC_REG_CONTROL1_ENSOP2 (1<<1)
#define TCPC_REG_CONTROL1_ENSOP1 (1<<0)
#define TCPC_REG_CONTROL2 0x08
/* two-bit field, valid values below */
#define TCPC_REG_CONTROL2_MODE (1<<1)
#define TCPC_REG_CONTROL2_MODE_DFP (0x3)
#define TCPC_REG_CONTROL2_MODE_UFP (0x2)
#define TCPC_REG_CONTROL2_MODE_DRP (0x1)
#define TCPC_REG_CONTROL2_MODE_POS (1)
#define TCPC_REG_CONTROL2_TOGGLE (1<<0)
#define TCPC_REG_CONTROL3 0x09
#define TCPC_REG_CONTROL3_SEND_HARDRESET (1<<6)
#define TCPC_REG_CONTROL3_BIST_TMODE (1<<5) /* 302B Only */
#define TCPC_REG_CONTROL3_AUTO_HARDRESET (1<<4)
#define TCPC_REG_CONTROL3_AUTO_SOFTRESET (1<<3)
/* two-bit field */
#define TCPC_REG_CONTROL3_N_RETRIES (1<<1)
#define TCPC_REG_CONTROL3_N_RETRIES_POS (1)
#define TCPC_REG_CONTROL3_N_RETRIES_SIZE (2)
#define TCPC_REG_CONTROL3_AUTO_RETRY (1<<0)
#define TCPC_REG_MASK 0x0A
#define TCPC_REG_MASK_VBUSOK (1<<7)
#define TCPC_REG_MASK_ACTIVITY (1<<6)
#define TCPC_REG_MASK_COMP_CHNG (1<<5)
#define TCPC_REG_MASK_CRC_CHK (1<<4)
#define TCPC_REG_MASK_ALERT (1<<3)
#define TCPC_REG_MASK_WAKE (1<<2)
#define TCPC_REG_MASK_COLLISION (1<<1)
#define TCPC_REG_MASK_BC_LVL (1<<0)
#define TCPC_REG_POWER 0x0B
#define TCPC_REG_POWER_PWR (1<<0) /* four-bit field */
#define TCPC_REG_POWER_PWR_LOW 0x1 /* Bandgap + Wake circuitry */
#define TCPC_REG_POWER_PWR_MEDIUM 0x3 /* LOW + Receiver + Current refs */
#define TCPC_REG_POWER_PWR_HIGH 0x7 /* MEDIUM + Measure block */
#define TCPC_REG_POWER_PWR_ALL 0xF /* HIGH + Internal Oscillator */
#define TCPC_REG_RESET 0x0C
#define TCPC_REG_RESET_PD_RESET (1<<1)
#define TCPC_REG_RESET_SW_RESET (1<<0)
#define TCPC_REG_MASKA 0x0E
#define TCPC_REG_MASKA_OCP_TEMP (1<<7)
#define TCPC_REG_MASKA_TOGDONE (1<<6)
#define TCPC_REG_MASKA_SOFTFAIL (1<<5)
#define TCPC_REG_MASKA_RETRYFAIL (1<<4)
#define TCPC_REG_MASKA_HARDSENT (1<<3)
#define TCPC_REG_MASKA_TX_SUCCESS (1<<2)
#define TCPC_REG_MASKA_SOFTRESET (1<<1)
#define TCPC_REG_MASKA_HARDRESET (1<<0)
#define TCPC_REG_MASKB 0x0F
#define TCPC_REG_MASKB_GCRCSENT (1<<0)
#define TCPC_REG_STATUS0A 0x3C
#define TCPC_REG_STATUS0A_SOFTFAIL (1<<5)
#define TCPC_REG_STATUS0A_RETRYFAIL (1<<4)
#define TCPC_REG_STATUS0A_POWER (1<<2) /* two-bit field */
#define TCPC_REG_STATUS0A_RX_SOFT_RESET (1<<1)
#define TCPC_REG_STATUS0A_RX_HARD_RESET (1<<0)
#define TCPC_REG_STATUS1A 0x3D
/* three-bit field, valid values below */
#define TCPC_REG_STATUS1A_TOGSS (1<<3)
#define TCPC_REG_STATUS1A_TOGSS_RUNNING 0x0
#define TCPC_REG_STATUS1A_TOGSS_SRC1 0x1
#define TCPC_REG_STATUS1A_TOGSS_SRC2 0x2
#define TCPC_REG_STATUS1A_TOGSS_SNK1 0x5
#define TCPC_REG_STATUS1A_TOGSS_SNK2 0x6
#define TCPC_REG_STATUS1A_TOGSS_AA 0x7
#define TCPC_REG_STATUS1A_TOGSS_POS (3)
#define TCPC_REG_STATUS1A_TOGSS_MASK (0x7)
#define TCPC_REG_STATUS1A_RXSOP2DB (1<<2)
#define TCPC_REG_STATUS1A_RXSOP1DB (1<<1)
#define TCPC_REG_STATUS1A_RXSOP (1<<0)
#define TCPC_REG_INTERRUPTA 0x3E
#define TCPC_REG_INTERRUPTA_OCP_TEMP (1<<7)
#define TCPC_REG_INTERRUPTA_TOGDONE (1<<6)
#define TCPC_REG_INTERRUPTA_SOFTFAIL (1<<5)
#define TCPC_REG_INTERRUPTA_RETRYFAIL (1<<4)
#define TCPC_REG_INTERRUPTA_HARDSENT (1<<3)
#define TCPC_REG_INTERRUPTA_TX_SUCCESS (1<<2)
#define TCPC_REG_INTERRUPTA_SOFTRESET (1<<1)
#define TCPC_REG_INTERRUPTA_HARDRESET (1<<0)
#define TCPC_REG_INTERRUPTB 0x3F
#define TCPC_REG_INTERRUPTB_GCRCSENT (1<<0)
#define TCPC_REG_STATUS0 0x40
#define TCPC_REG_STATUS0_VBUSOK (1<<7)
#define TCPC_REG_STATUS0_ACTIVITY (1<<6)
#define TCPC_REG_STATUS0_COMP (1<<5)
#define TCPC_REG_STATUS0_CRC_CHK (1<<4)
#define TCPC_REG_STATUS0_ALERT (1<<3)
#define TCPC_REG_STATUS0_WAKE (1<<2)
#define TCPC_REG_STATUS0_BC_LVL1 (1<<1) /* two-bit field */
#define TCPC_REG_STATUS0_BC_LVL0 (1<<0) /* two-bit field */
#define TCPC_REG_STATUS1 0x41
#define TCPC_REG_STATUS1_RXSOP2 (1<<7)
#define TCPC_REG_STATUS1_RXSOP1 (1<<6)
#define TCPC_REG_STATUS1_RX_EMPTY (1<<5)
#define TCPC_REG_STATUS1_RX_FULL (1<<4)
#define TCPC_REG_STATUS1_TX_EMPTY (1<<3)
#define TCPC_REG_STATUS1_TX_FULL (1<<2)
#define TCPC_REG_INTERRUPT 0x42
#define TCPC_REG_INTERRUPT_VBUSOK (1<<7)
#define TCPC_REG_INTERRUPT_ACTIVITY (1<<6)
#define TCPC_REG_INTERRUPT_COMP_CHNG (1<<5)
#define TCPC_REG_INTERRUPT_CRC_CHK (1<<4)
#define TCPC_REG_INTERRUPT_ALERT (1<<3)
#define TCPC_REG_INTERRUPT_WAKE (1<<2)
#define TCPC_REG_INTERRUPT_COLLISION (1<<1)
#define TCPC_REG_INTERRUPT_BC_LVL (1<<0)
#define TCPC_REG_FIFOS 0x43
/* Tokens defined for the FUSB302 TX FIFO */
enum fusb302_txfifo_tokens {
fusb302_TKN_TXON = 0xA1,
fusb302_TKN_SYNC1 = 0x12,
fusb302_TKN_SYNC2 = 0x13,
fusb302_TKN_SYNC3 = 0x1B,
fusb302_TKN_RST1 = 0x15,
fusb302_TKN_RST2 = 0x16,
fusb302_TKN_PACKSYM = 0x80,
fusb302_TKN_JAMCRC = 0xFF,
fusb302_TKN_EOP = 0x14,
fusb302_TKN_TXOFF = 0xFE,
};
extern const struct tcpm_drv fusb302_tcpm_drv;
//returns 1 if the FUSB302 is on the I2C bus
uint8_t fusb302_detect();
#endif /* fusb302_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,882 +0,0 @@
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "USBC_TCPM/tcpm.h"
#include "usb_pd.h"
#include <stddef.h>
#ifdef CONFIG_COMMON_RUNTIME
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#else
#define CPRINTS(format, args...)
#define CPRINTF(format, args...)
#endif
static int rw_flash_changed = 1;
int pd_check_requested_voltage(uint32_t rdo, const int port) {
//No source
return EC_ERROR_INVAL;
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
/* Last received source cap */
static uint32_t pd_src_caps[PDO_MAX_OBJECTS];
static uint8_t pd_src_cap_cnt;
/* Cap on the max voltage requested as a sink (in millivolts) */
static unsigned max_request_mv = PD_MAX_VOLTAGE_MV; /* no cap */
int pd_find_pdo_index(int max_mv, uint32_t *selected_pdo) {
int i, uw, mv, ma;
int ret = 0;
int __attribute__((unused)) cur_mv = 0;
int cur_uw = 0;
int prefer_cur;
const uint32_t *src_caps = pd_src_caps;
/* max voltage is always limited by this boards max request */
max_mv = MIN(max_mv, PD_MAX_VOLTAGE_MV);
/* Get max power that is under our max voltage input */
for (i = 0; i < pd_src_cap_cnt; i++) {
/* its an unsupported Augmented PDO (PD3.0) */
if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED)
continue;
mv = ((src_caps[i] >> 10) & 0x3FF) * 50;
/* Skip invalid voltage */
if (!mv)
continue;
/* Skip any voltage not supported by this board */
if (!pd_is_valid_input_voltage(mv))
continue;
if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
uw = 250000 * (src_caps[i] & 0x3FF);
} else {
ma = (src_caps[i] & 0x3FF) * 10;
ma = MIN(ma, PD_MAX_CURRENT_MA);
uw = ma * mv;
}
if (mv > max_mv)
continue;
uw = MIN(uw, PD_MAX_POWER_MW * 1000);
prefer_cur = 0;
/* Apply special rules in case of 'tie' */
#ifdef PD_PREFER_LOW_VOLTAGE
if (uw == cur_uw && mv < cur_mv)
prefer_cur = 1;
#elif defined(PD_PREFER_HIGH_VOLTAGE)
if (uw == cur_uw && mv > cur_mv)
prefer_cur = 1;
#endif
/* Prefer higher power, except for tiebreaker */
if (uw > cur_uw || prefer_cur) {
ret = i;
cur_uw = uw;
cur_mv = mv;
}
}
if (selected_pdo)
*selected_pdo = src_caps[ret];
return ret;
}
void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *mv) {
int max_ma, uw;
*mv = ((pdo >> 10) & 0x3FF) * 50;
if (*mv == 0) {
CPRINTF("ERR:PDO mv=0\n");
*ma = 0;
return;
}
if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
uw = 250000 * (pdo & 0x3FF);
max_ma = 1000 * MIN(1000 * uw, PD_MAX_POWER_MW) / *mv;
} else {
max_ma = 10 * (pdo & 0x3FF);
max_ma = MIN(max_ma, PD_MAX_POWER_MW * 1000 / *mv);
}
*ma = MIN(max_ma, PD_MAX_CURRENT_MA);
}
int pd_build_request(uint32_t *rdo, uint32_t *ma, uint32_t *mv,
enum pd_request_type req_type) {
uint32_t pdo;
int pdo_index, flags = 0;
int uw;
int max_or_min_ma;
int max_or_min_mw;
if (req_type == PD_REQUEST_VSAFE5V) {
/* src cap 0 should be vSafe5V */
pdo_index = 0;
pdo = pd_src_caps[0];
} else {
/* find pdo index for max voltage we can request */
pdo_index = pd_find_pdo_index(max_request_mv, &pdo);
}
pd_extract_pdo_power(pdo, ma, mv);
uw = *ma * *mv;
/* Mismatch bit set if less power offered than the operating power */
if (uw < (1000 * PD_OPERATING_POWER_MW))
flags |= RDO_CAP_MISMATCH;
#ifdef CONFIG_USB_PD_GIVE_BACK
/* Tell source we are give back capable. */
flags |= RDO_GIVE_BACK;
/*
* BATTERY PDO: Inform the source that the sink will reduce
* power to this minimum level on receipt of a GotoMin Request.
*/
max_or_min_mw = PD_MIN_POWER_MW;
/*
* FIXED or VARIABLE PDO: Inform the source that the sink will reduce
* current to this minimum level on receipt of a GotoMin Request.
*/
max_or_min_ma = PD_MIN_CURRENT_MA;
#else
/*
* Can't give back, so set maximum current and power to operating
* level.
*/
max_or_min_ma = *ma;
max_or_min_mw = uw / 1000;
#endif
if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
int mw = uw / 1000;
*rdo = RDO_BATT(pdo_index + 1, mw, max_or_min_mw, flags);
} else {
*rdo = RDO_FIXED(pdo_index + 1, *ma, max_or_min_ma, flags);
}
return EC_SUCCESS;
}
void pd_process_source_cap(int cnt, uint32_t *src_caps) {
#ifdef CONFIG_CHARGE_MANAGER
uint32_t ma, mv, pdo;
#endif
int i;
pd_src_cap_cnt = cnt;
for (i = 0; i < cnt; i++)
pd_src_caps[i] = *src_caps++;
#ifdef CONFIG_CHARGE_MANAGER
/* Get max power info that we could request */
pd_find_pdo_index( PD_MAX_VOLTAGE_MV, &pdo);
pd_extract_pdo_power(pdo, &ma, &mv);
/* Set max. limit, but apply 500mA ceiling */
//charge_manager_set_ceil( CEIL_REQUESTOR_PD, PD_MIN_MA);
pd_set_input_current_limit(ma, mv);
#endif
}
#pragma weak pd_process_source_cap_callback
void pd_process_source_cap_callback(int cnt, uint32_t *src_caps) {
}
void pd_set_max_voltage(unsigned mv) {
max_request_mv = mv;
}
unsigned pd_get_max_voltage(void) {
return max_request_mv;
}
int pd_charge_from_device(uint16_t vid, uint16_t pid) {
/* TODO: rewrite into table if we get more of these */
/*
* White-list Apple charge-through accessory since it doesn't set
* externally powered bit, but we still need to charge from it when
* we are a sink.
*/
return (vid == USB_VID_APPLE && (pid == 0x1012 || pid == 0x1013));
}
#endif /* CONFIG_USB_PD_DUAL_ROLE */
#ifdef CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
static struct pd_policy pe[CONFIG_USB_PD_PORT_COUNT];
void pd_dfp_pe_init(int port)
{
memset(&pe, 0, sizeof(struct pd_policy));
}
static void dfp_consume_identity( int cnt, uint32_t *payload)
{
int ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]);
size_t identity_size = MIN(sizeof(pe.identity),
(cnt - 1) * sizeof(uint32_t));
pd_dfp_pe_init(port);
memcpy(&pe.identity, payload + 1, identity_size);
switch (ptype) {
case IDH_PTYPE_AMA:
/* TODO(tbroch) do I disable VBUS here if power contract
* requested it
*/
if (!PD_VDO_AMA_VBUS_REQ(payload[VDO_I(AMA)]))
pd_power_supply_reset(port);
#if defined(CONFIG_USB_PD_DUAL_ROLE) && defined(CONFIG_USBC_VCONN_SWAP)
/* Adapter is requesting vconn, try to supply it */
if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)]))
pd_try_vconn_src(port);
#endif
break;
default:
break;
}
}
static int dfp_discover_svids( uint32_t *payload)
{
payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID);
return 1;
}
static void dfp_consume_svids( uint32_t *payload)
{
int i;
uint32_t *ptr = payload + 1;
uint16_t svid0, svid1;
for (i = pe.svid_cnt; i < pe.svid_cnt + 12; i += 2) {
if (i == SVID_DISCOVERY_MAX) {
CPRINTF("ERR:SVIDCNT\n");
break;
}
svid0 = PD_VDO_SVID_SVID0(*ptr);
if (!svid0)
break;
pe.svids[i].svid = svid0;
pe.svid_cnt++;
svid1 = PD_VDO_SVID_SVID1(*ptr);
if (!svid1)
break;
pe.svids[i + 1].svid = svid1;
pe.svid_cnt++;
ptr++;
}
/* TODO(tbroch) need to re-issue discover svids if > 12 */
if (i && ((i % 12) == 0))
CPRINTF("ERR:SVID+12\n");
}
static int dfp_discover_modes( uint32_t *payload)
{
uint16_t svid = pe.svids[pe.svid_idx].svid;
if (pe.svid_idx >= pe.svid_cnt)
return 0;
payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
return 1;
}
static void dfp_consume_modes( int cnt, uint32_t *payload)
{
int idx = pe.svid_idx;
pe.svids[idx].mode_cnt = cnt - 1;
if (pe.svids[idx].mode_cnt < 0) {
CPRINTF("ERR:NOMODE\n");
} else {
memcpy(pe.svids[pe.svid_idx].mode_vdo, &payload[1],
sizeof(uint32_t) * pe.svids[idx].mode_cnt);
}
pe.svid_idx++;
}
static int get_mode_idx( uint16_t svid)
{
int i;
for (i = 0; i < PD_AMODE_COUNT; i++) {
if (pe.amodes[i].fx->svid == svid)
return i;
}
return -1;
}
static struct svdm_amode_data *get_modep( uint16_t svid)
{
int idx = get_mode_idx( svid);
return (idx == -1) ? NULL : &pe.amodes[idx];
}
int pd_alt_mode( uint16_t svid)
{
struct svdm_amode_data *modep = get_modep( svid);
return (modep) ? modep->opos : -1;
}
int allocate_mode( uint16_t svid)
{
int i, j;
struct svdm_amode_data *modep;
int mode_idx = get_mode_idx( svid);
if (mode_idx != -1)
return mode_idx;
/* There's no space to enter another mode */
if (pe.amode_idx == PD_AMODE_COUNT) {
CPRINTF("ERR:NO AMODE SPACE\n");
return -1;
}
/* Allocate ... if SVID == 0 enter default supported policy */
for (i = 0; i < supported_modes_cnt; i++) {
if (!&supported_modes[i])
continue;
for (j = 0; j < pe.svid_cnt; j++) {
struct svdm_svid_data *svidp = &pe.svids[j];
if ((svidp->svid != supported_modes[i].svid) ||
(svid && (svidp->svid != svid)))
continue;
modep = &pe.amodes[pe.amode_idx];
modep->fx = &supported_modes[i];
modep->data = &pe.svids[j];
pe.amode_idx++;
return pe.amode_idx - 1;
}
}
return -1;
}
/*
* Enter default mode ( payload[0] == 0 ) or attempt to enter mode via svid &
* opos
*/
uint32_t pd_dfp_enter_mode( uint16_t svid, int opos)
{
int mode_idx = allocate_mode( svid);
struct svdm_amode_data *modep;
uint32_t mode_caps;
if (mode_idx == -1)
return 0;
modep = &pe.amodes[mode_idx];
if (!opos) {
/* choose the lowest as default */
modep->opos = 1;
} else if (opos <= modep->data->mode_cnt) {
modep->opos = opos;
} else {
CPRINTF("opos error\n");
return 0;
}
mode_caps = modep->data->mode_vdo[modep->opos - 1];
if (modep->fx->enter( mode_caps) == -1)
return 0;
/* SVDM to send to UFP for mode entry */
return VDO(modep->fx->svid, 1, CMD_ENTER_MODE | VDO_OPOS(modep->opos));
}
static int validate_mode_request(struct svdm_amode_data *modep,
uint16_t svid, int opos)
{
if (!modep->fx)
return 0;
if (svid != modep->fx->svid) {
CPRINTF("ERR:svid r:0x%04x != c:0x%04x\n",
svid, modep->fx->svid);
return 0;
}
if (opos != modep->opos) {
CPRINTF("ERR:opos r:%d != c:%d\n",
opos, modep->opos);
return 0;
}
return 1;
}
static void dfp_consume_attention( uint32_t *payload)
{
uint16_t svid = PD_VDO_VID(payload[0]);
int opos = PD_VDO_OPOS(payload[0]);
struct svdm_amode_data *modep = get_modep( svid);
if (!modep || !validate_mode_request(modep, svid, opos))
return;
if (modep->fx->attention)
modep->fx->attention( payload);
}
/*
* This algorithm defaults to choosing higher pin config over lower ones in
* order to prefer multi-function if desired.
*
* NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG
* -------------------------------------------------------------
* A | USB G2 | ? | no | 00_0001
* B | USB G2 | ? | yes | 00_0010
* C | DP | CONVERTED | no | 00_0100
* D | PD | CONVERTED | yes | 00_1000
* E | DP | DP | no | 01_0000
* F | PD | DP | yes | 10_0000
*
* if UFP has NOT asserted multi-function preferred code masks away B/D/F
* leaving only A/C/E. For single-output dongles that should leave only one
* possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP
* output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C
* receptacle must always choose C/D in those cases.
*/
int pd_dfp_dp_get_pin_mode( uint32_t status)
{
struct svdm_amode_data *modep = get_modep( USB_SID_DISPLAYPORT);
uint32_t mode_caps;
uint32_t pin_caps;
if (!modep)
return 0;
mode_caps = modep->data->mode_vdo[modep->opos - 1];
/* TODO(crosbug.com/p/39656) revisit with DFP that can be a sink */
pin_caps = PD_DP_PIN_CAPS(mode_caps);
/* if don't want multi-function then ignore those pin configs */
if (!PD_VDO_DPSTS_MF_PREF(status))
pin_caps &= ~MODE_DP_PIN_MF_MASK;
/* TODO(crosbug.com/p/39656) revisit if DFP drives USB Gen 2 signals */
pin_caps &= ~MODE_DP_PIN_BR2_MASK;
/* if C/D present they have precedence over E/F for USB-C->USB-C */
if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D))
pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F);
/* get_next_bit returns undefined for zero */
if (!pin_caps)
return 0;
return 1 << get_next_bit(&pin_caps);
}
int pd_dfp_exit_mode( uint16_t svid, int opos)
{
struct svdm_amode_data *modep;
int idx;
/*
* Empty svid signals we should reset DFP VDM state by exiting all
* entered modes then clearing state. This occurs when we've
* disconnected or for hard reset.
*/
if (!svid) {
for (idx = 0; idx < PD_AMODE_COUNT; idx++)
if (pe.amodes[idx].fx)
pe.amodes[idx].fx->exit(port);
pd_dfp_pe_init(port);
return 0;
}
/*
* TODO(crosbug.com/p/33946) : below needs revisited to allow multiple
* mode exit. Additionally it should honor OPOS == 7 as DFP's request
* to exit all modes. We currently don't have any UFPs that support
* multiple modes on one SVID.
*/
modep = get_modep( svid);
if (!modep || !validate_mode_request(modep, svid, opos))
return 0;
/* call DFPs exit function */
modep->fx->exit(port);
/* exit the mode */
modep->opos = 0;
return 1;
}
uint16_t pd_get_identity_vid(int port)
{
return PD_IDH_VID(pe.identity[0]);
}
uint16_t pd_get_identity_pid(int port)
{
return PD_PRODUCT_PID(pe.identity[2]);
}
#ifdef CONFIG_CMD_USB_PD_PE
static void dump_pe(int port)
{
const char * const idh_ptype_names[] = {
"UNDEF", "Hub", "Periph", "PCable", "ACable", "AMA",
"RSV6", "RSV7"};
int i, j, idh_ptype;
struct svdm_amode_data *modep;
uint32_t mode_caps;
if (pe.identity[0] == 0) {
ccprintf("No identity discovered yet.\n");
return;
}
idh_ptype = PD_IDH_PTYPE(pe.identity[0]);
ccprintf("IDENT:\n");
ccprintf("\t[ID Header] %08x :: %s, VID:%04x\n", pe.identity[0],
idh_ptype_names[idh_ptype], pd_get_identity_vid(port));
ccprintf("\t[Cert Stat] %08x\n", pe.identity[1]);
for (i = 2; i < ARRAY_SIZE(pe.identity); i++) {
ccprintf("\t");
if (pe.identity[i])
ccprintf("[%d] %08x ", i, pe.identity[i]);
}
ccprintf("\n");
if (pe.svid_cnt < 1) {
ccprintf("No SVIDS discovered yet.\n");
return;
}
for (i = 0; i < pe.svid_cnt; i++) {
ccprintf("SVID[%d]: %04x MODES:", i, pe.svids[i].svid);
for (j = 0; j < pe.svids[j].mode_cnt; j++)
ccprintf(" [%d] %08x", j + 1,
pe.svids[i].mode_vdo[j]);
ccprintf("\n");
modep = get_modep( pe.svids[i].svid);
if (modep) {
mode_caps = modep->data->mode_vdo[modep->opos - 1];
ccprintf("MODE[%d]: svid:%04x caps:%08x\n", modep->opos,
modep->fx->svid, mode_caps);
}
}
}
static int command_pe(int argc, char **argv)
{
int port;
char *e;
if (argc < 3)
return EC_ERROR_PARAM_COUNT;
/* command: pe <port> <subcmd> <args> */
port = strtoi(argv[1], &e, 10);
if (*e || port >= CONFIG_USB_PD_PORT_COUNT)
return EC_ERROR_PARAM2;
if (!strncasecmp(argv[2], "dump", 4))
dump_pe(port);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(pe, command_pe,
"<port> dump",
"USB PE");
#endif /* CONFIG_CMD_USB_PD_PE */
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
int pd_svdm(int cnt, uint32_t *payload, uint32_t **rpayload) {
int cmd = PD_VDO_CMD(payload[0]);
int cmd_type = PD_VDO_CMDT(payload[0]);
int (*func)(uint32_t *payload) = NULL;
int rsize = 1; /* VDM header at a minimum */
payload[0] &= ~VDO_CMDT_MASK;
*rpayload = payload;
if (cmd_type == CMDT_INIT) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
func = svdm_rsp.identity;
break;
case CMD_DISCOVER_SVID:
func = svdm_rsp.svids;
break;
case CMD_DISCOVER_MODES:
func = svdm_rsp.modes;
break;
case CMD_ENTER_MODE:
func = svdm_rsp.enter_mode;
break;
case CMD_DP_STATUS:
func = svdm_rsp.amode->status;
break;
case CMD_DP_CONFIG:
func = svdm_rsp.amode->config;
break;
case CMD_EXIT_MODE:
func = svdm_rsp.exit_mode;
break;
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
case CMD_ATTENTION:
/*
* attention is only SVDM with no response
* (just goodCRC) return zero here.
*/
dfp_consume_attention( payload);
return 0;
#endif
default:
CPRINTF("ERR:CMD:%d\n", cmd);
rsize = 0;
}
if (func)
rsize = func(payload);
else
/* not supported : NACK it */
rsize = 0;
if (rsize >= 1)
payload[0] |= VDO_CMDT(CMDT_RSP_ACK);
else if (!rsize) {
payload[0] |= VDO_CMDT(CMDT_RSP_NAK);
rsize = 1;
} else {
payload[0] |= VDO_CMDT(CMDT_RSP_BUSY);
rsize = 1;
}
payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port));
} else if (cmd_type == CMDT_RSP_ACK) {
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
struct svdm_amode_data *modep;
modep = get_modep( PD_VDO_VID(payload[0]));
#endif
switch (cmd) {
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
case CMD_DISCOVER_IDENT:
dfp_consume_identity( cnt, payload);
rsize = dfp_discover_svids( payload);
#ifdef CONFIG_CHARGE_MANAGER
if (pd_charge_from_device(pd_get_identity_vid(port),
pd_get_identity_pid(port)))
charge_manager_update_dualrole(
CAP_DEDICATED);
#endif
break;
case CMD_DISCOVER_SVID:
dfp_consume_svids( payload);
rsize = dfp_discover_modes( payload);
break;
case CMD_DISCOVER_MODES:
dfp_consume_modes( cnt, payload);
rsize = dfp_discover_modes( payload);
/* enter the default mode for DFP */
if (!rsize) {
payload[0] = pd_dfp_enter_mode( 0, 0);
if (payload[0])
rsize = 1;
}
break;
case CMD_ENTER_MODE:
if (!modep) {
rsize = 0;
} else {
if (!modep->opos)
pd_dfp_enter_mode( 0, 0);
if (modep->opos) {
rsize = modep->fx->status(
payload);
payload[0] |= PD_VDO_OPOS(modep->opos);
}
}
break;
case CMD_DP_STATUS:
/* DP status response & UFP's DP attention have same
payload */
dfp_consume_attention( payload);
if (modep && modep->opos)
rsize = modep->fx->config( payload);
else
rsize = 0;
break;
case CMD_DP_CONFIG:
if (modep && modep->opos && modep->fx->post_config)
modep->fx->post_config(port);
/* no response after DFPs ack */
rsize = 0;
break;
case CMD_EXIT_MODE:
/* no response after DFPs ack */
rsize = 0;
break;
#endif
case CMD_ATTENTION:
/* no response after DFPs ack */
rsize = 0;
break;
default:
CPRINTF("ERR:CMD:%d\n", cmd);
rsize = 0;
}
payload[0] |= VDO_CMDT(CMDT_INIT);
payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port));
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
} else if (cmd_type == CMDT_RSP_BUSY) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
case CMD_DISCOVER_SVID:
case CMD_DISCOVER_MODES:
/* resend if its discovery */
rsize = 1;
break;
case CMD_ENTER_MODE:
/* Error */
CPRINTF("ERR:ENTBUSY\n");
rsize = 0;
break;
case CMD_EXIT_MODE:
rsize = 0;
break;
default:
rsize = 0;
}
} else if (cmd_type == CMDT_RSP_NAK) {
/* nothing to do */
rsize = 0;
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
} else {
CPRINTF("ERR:CMDT:%d\n", cmd);
/* do not answer */
rsize = 0;
}
return rsize;
}
#else
int pd_svdm( int cnt, uint32_t *payload, uint32_t **rpayload)
{
return 0;
}
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifndef CONFIG_USB_PD_CUSTOM_VDM
int pd_vdm(int cnt, uint32_t *payload, uint32_t **rpayload) {
return 0;
}
#endif /* !CONFIG_USB_PD_CUSTOM_VDM */
static void pd_usb_billboard_deferred(void) {
#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) \
&& !defined(CONFIG_USB_PD_SIMPLE_DFP) && defined(CONFIG_USB_BOS)
/*
* TODO(tbroch)
* 1. Will we have multiple type-C port UFPs
* 2. Will there be other modes applicable to DFPs besides DP
*/
if (!pd_alt_mode(0, USB_SID_DISPLAYPORT))
usb_connect();
#endif
}
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
static int hc_remote_pd_discovery(struct host_cmd_handler_args *args)
{
const uint8_t *port = args->params;
struct ec_params_usb_pd_discovery_entry *r = args->response;
if (*port >= CONFIG_USB_PD_PORT_COUNT)
return EC_RES_INVALID_PARAM;
r->vid = pd_get_identity_vid(*port);
r->ptype = PD_IDH_PTYPE(pe[*port].identity[0]);
/* pid only included if vid is assigned */
if (r->vid)
r->pid = PD_PRODUCT_PID(pe[*port].identity[2]);
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DISCOVERY,
hc_remote_pd_discovery,
EC_VER_MASK(0));
static int hc_remote_pd_get_amode(struct host_cmd_handler_args *args)
{
struct svdm_amode_data *modep;
const struct ec_params_usb_pd_get_mode_request *p = args->params;
struct ec_params_usb_pd_get_mode_response *r = args->response;
if (p->port >= CONFIG_USB_PD_PORT_COUNT)
return EC_RES_INVALID_PARAM;
/* no more to send */
if (p->svid_idx >= pe[p->port].svid_cnt) {
r->svid = 0;
args->response_size = sizeof(r->svid);
return EC_RES_SUCCESS;
}
r->svid = pe[p->port].svids[p->svid_idx].svid;
r->opos = 0;
memcpy(r->vdo, pe[p->port].svids[p->svid_idx].mode_vdo, 24);
modep = get_modep(p->port, r->svid);
if (modep)
r->opos = pd_alt_mode(p->port, r->svid);
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_GET_AMODE,
hc_remote_pd_get_amode,
EC_VER_MASK(0));
#endif
#define FW_RW_END (CONFIG_EC_WRITABLE_STORAGE_OFF + \
CONFIG_RW_STORAGE_OFF + CONFIG_RW_SIZE)
#ifdef CONFIG_USB_PD_DISCHARGE
void pd_set_vbus_discharge( int enable)
{
static struct mutex discharge_lock[CONFIG_USB_PD_PORT_COUNT];
mutex_lock(&discharge_lock);
enable &= !board_vbus_source_enabled(port);
#ifdef CONFIG_USB_PD_DISCHARGE_GPIO
if (!port)
gpio_set_level(GPIO_USB_C0_DISCHARGE, enable);
#if CONFIG_USB_PD_PORT_COUNT > 1
else
gpio_set_level(GPIO_USB_C1_DISCHARGE, enable);
#endif /* CONFIG_USB_PD_PORT_COUNT */
#elif defined(CONFIG_USB_PD_DISCHARGE_TCPC)
tcpc_discharge_vbus( enable);
#else
#error "PD discharge implementation not defined"
#endif
mutex_unlock(&discharge_lock);
}
#endif /* CONFIG_USB_PD_DISCHARGE */

View File

@@ -1,258 +0,0 @@
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* USB Power delivery port management - common header for TCPM drivers */
#ifndef __CROS_EC_USB_PD_TCPM_TCPM_H
#define __CROS_EC_USB_PD_TCPM_TCPM_H
#include "tcpm_driver.h"
#include "usb_pd_tcpm.h"
#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
!defined(CONFIG_USB_PD_DUAL_ROLE)
#error "DRP auto toggle requires board to have DRP support"
#error "Please upgrade your board configuration"
#endif
#ifndef CONFIG_USB_PD_TCPC
extern const struct tcpc_config_t tcpc_config;
/* I2C wrapper functions - get I2C port / slave addr from config struct. */
int tcpc_write(int reg, int val);
int tcpc_write16(int reg, int val);
int tcpc_read(int reg, int *val);
int tcpc_read16(int reg, int *val);
int tcpc_xfer(const uint8_t *out, int out_size, uint8_t *in, int in_size,
int flags);
/* TCPM driver wrapper function */
static inline int tcpm_init() {
int rv;
rv = tcpc_config.drv->init();
if (rv)
return rv;
/* Board specific post TCPC init */
if (board_tcpc_post_init)
rv = board_tcpc_post_init();
return rv;
}
static inline int tcpm_release() {
return tcpc_config.drv->release();
}
static inline int tcpm_get_cc(int *cc1, int *cc2) {
return tcpc_config.drv->get_cc(cc1, cc2);
}
static inline int tcpm_get_vbus_level() {
return tcpc_config.drv->get_vbus_level();
}
static inline int tcpm_select_rp_value(int rp) {
return tcpc_config.drv->select_rp_value(rp);
}
static inline int tcpm_set_cc(int pull) {
return tcpc_config.drv->set_cc(pull);
}
static inline int tcpm_set_polarity(int polarity) {
return tcpc_config.drv->set_polarity(polarity);
}
static inline int tcpm_set_vconn(int enable) {
return tcpc_config.drv->set_vconn(enable);
}
static inline int tcpm_set_msg_header(int power_role, int data_role) {
return tcpc_config.drv->set_msg_header(power_role, data_role);
}
static inline int tcpm_set_rx_enable(int enable) {
return tcpc_config.drv->set_rx_enable(enable);
}
static inline int tcpm_get_message(uint32_t *payload, int *head) {
return tcpc_config.drv->get_message(payload, head);
}
static inline int tcpm_transmit(enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data) {
return tcpc_config.drv->transmit(type, header, data);
}
static inline void tcpc_alert() {
tcpc_config.drv->tcpc_alert();
}
static inline void tcpc_discharge_vbus(int enable) {
tcpc_config.drv->tcpc_discharge_vbus(enable);
}
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
static inline int tcpm_auto_toggle_supported()
{
return !!tcpc_config.drv->drp_toggle;
}
static inline int tcpm_set_drp_toggle( int enable)
{
return tcpc_config.drv->drp_toggle( enable);
}
#endif
#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC
static inline int tcpc_i2c_read(const int port, const int addr,
const int reg, int *data)
{
return tcpc_read( reg, data);
}
static inline int tcpc_i2c_write(const int port, const int addr,
const int reg, int data)
{
return tcpc_write( reg, data);
}
#endif
static inline int tcpm_get_chip_info(int renew,
struct ec_response_pd_chip_info **info) {
if (tcpc_config.drv->get_chip_info)
return tcpc_config.drv->get_chip_info(renew, info);
return EC_ERROR_UNIMPLEMENTED;
}
#else
/**
* Initialize TCPM driver and wait for TCPC readiness.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int tcpm_init();
/**
* Read the CC line status.
*
* @param port Type-C port number
* @param cc1 pointer to CC status for CC1
* @param cc2 pointer to CC status for CC2
*
* @return EC_SUCCESS or error
*/
int tcpm_get_cc( int *cc1, int *cc2);
/**
* Read VBUS
*
* @param port Type-C port number
*
* @return 0 => VBUS not detected, 1 => VBUS detected
*/
int tcpm_get_vbus_level();
/**
* Set the value of the CC pull-up used when we are a source.
*
* @param port Type-C port number
* @param rp One of enum tcpc_rp_value
*
* @return EC_SUCCESS or error
*/
int tcpm_select_rp_value( int rp);
/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
* @param pull One of enum tcpc_cc_pull
*
* @return EC_SUCCESS or error
*/
int tcpm_set_cc( int pull);
/**
* Set polarity
*
* @param port Type-C port number
* @param polarity 0=> transmit on CC1, 1=> transmit on CC2
*
* @return EC_SUCCESS or error
*/
int tcpm_set_polarity( int polarity);
/**
* Set Vconn.
*
* @param port Type-C port number
* @param polarity Polarity of the CC line to read
*
* @return EC_SUCCESS or error
*/
int tcpm_set_vconn( int enable);
/**
* Set PD message header to use for goodCRC
*
* @param port Type-C port number
* @param power_role Power role to use in header
* @param data_role Data role to use in header
*
* @return EC_SUCCESS or error
*/
int tcpm_set_msg_header( int power_role, int data_role);
/**
* Set RX enable flag
*
* @param port Type-C port number
* @enable true for enable, false for disable
*
* @return EC_SUCCESS or error
*/
int tcpm_set_rx_enable( int enable);
/**
* Read last received PD message.
*
* @param port Type-C port number
* @param payload Pointer to location to copy payload of message
* @param header of message
*
* @return EC_SUCCESS or error
*/
int tcpm_get_message( uint32_t *payload, int *head);
/**
* Transmit PD message
*
* @param port Type-C port number
* @param type Transmit type
* @param header Packet header
* @param cnt Number of bytes in payload
* @param data Payload
*
* @return EC_SUCCESS or error
*/
int tcpm_transmit( enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void tcpc_alert();
#endif
#endif

View File

@@ -1,344 +0,0 @@
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* USB Power delivery port management */
#ifndef __CROS_EC_USB_PD_TCPM_H
#define __CROS_EC_USB_PD_TCPM_H
/* List of common error codes that can be returned */
enum ec_error_list {
/* Success - no error */
EC_SUCCESS = 0,
/* Unknown error */
EC_ERROR_UNKNOWN = 1,
/* Function not implemented yet */
EC_ERROR_UNIMPLEMENTED = 2,
/* Overflow error; too much input provided. */
EC_ERROR_OVERFLOW = 3,
/* Timeout */
EC_ERROR_TIMEOUT = 4,
/* Invalid argument */
EC_ERROR_INVAL = 5,
/* Already in use, or not ready yet */
EC_ERROR_BUSY = 6,
/* Access denied */
EC_ERROR_ACCESS_DENIED = 7,
/* Failed because component does not have power */
EC_ERROR_NOT_POWERED = 8,
/* Failed because component is not calibrated */
EC_ERROR_NOT_CALIBRATED = 9,
/* Failed because CRC error */
EC_ERROR_CRC = 10,
/* Invalid console command param (PARAMn means parameter n is bad) */
EC_ERROR_PARAM1 = 11,
EC_ERROR_PARAM2 = 12,
EC_ERROR_PARAM3 = 13,
EC_ERROR_PARAM4 = 14,
EC_ERROR_PARAM5 = 15,
EC_ERROR_PARAM6 = 16,
EC_ERROR_PARAM7 = 17,
EC_ERROR_PARAM8 = 18,
EC_ERROR_PARAM9 = 19,
/* Wrong number of params */
EC_ERROR_PARAM_COUNT = 20,
/* Interrupt event not handled */
EC_ERROR_NOT_HANDLED = 21,
/* Data has not changed */
EC_ERROR_UNCHANGED = 22,
/* Memory allocation */
EC_ERROR_MEMORY_ALLOCATION = 23,
/* Verified boot errors */
EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
EC_ERROR_VBOOT_SIG_MAGIC = 0x1001,
EC_ERROR_VBOOT_SIG_SIZE = 0x1002,
EC_ERROR_VBOOT_SIG_ALGORITHM = 0x1003,
EC_ERROR_VBOOT_HASH_ALGORITHM = 0x1004,
EC_ERROR_VBOOT_SIG_OFFSET = 0x1005,
EC_ERROR_VBOOT_DATA_SIZE = 0x1006,
/* Verified boot key errors */
EC_ERROR_VBOOT_KEY = 0x1100,
EC_ERROR_VBOOT_KEY_MAGIC = 0x1101,
EC_ERROR_VBOOT_KEY_SIZE = 0x1102,
/* Verified boot data errors */
EC_ERROR_VBOOT_DATA = 0x1200,
EC_ERROR_VBOOT_DATA_VERIFY = 0x1201,
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
EC_ERROR_INTERNAL_LAST = 0x1FFFF
};
/* Flags for i2c_xfer() */
#define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */
#define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */
#define I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) /* One transaction */
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
TYPEC_CC_VOLT_RA = 1,
TYPEC_CC_VOLT_RD = 2,
TYPEC_CC_VOLT_SNK_DEF = 5,
TYPEC_CC_VOLT_SNK_1_5 = 6,
TYPEC_CC_VOLT_SNK_3_0 = 7,
};
enum tcpc_cc_pull {
TYPEC_CC_RA = 0, TYPEC_CC_RP = 1, TYPEC_CC_RD = 2, TYPEC_CC_OPEN = 3,
};
enum tcpc_rp_value {
TYPEC_RP_USB = 0, TYPEC_RP_1A5 = 1, TYPEC_RP_3A0 = 2, TYPEC_RP_RESERVED = 3,
};
enum tcpm_transmit_type {
TCPC_TX_SOP = 0,
TCPC_TX_SOP_PRIME = 1,
TCPC_TX_SOP_PRIME_PRIME = 2,
TCPC_TX_SOP_DEBUG_PRIME = 3,
TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4,
TCPC_TX_HARD_RESET = 5,
TCPC_TX_CABLE_RESET = 6,
TCPC_TX_BIST_MODE_2 = 7
};
enum tcpc_transmit_complete {
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
};
struct tcpm_drv {
/**
* Initialize TCPM driver and wait for TCPC readiness.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*init)();
/**
* Release the TCPM hardware and disconnect the driver.
* Only .init() can be called after .release().
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*release)();
/**
* Read the CC line status.
*
* @param port Type-C port number
* @param cc1 pointer to CC status for CC1
* @param cc2 pointer to CC status for CC2
*
* @return EC_SUCCESS or error
*/
int (*get_cc)(int *cc1, int *cc2);
/**
* Read VBUS
*
* @param port Type-C port number
*
* @return 0 => VBUS not detected, 1 => VBUS detected
*/
int (*get_vbus_level)();
/**
* Set the value of the CC pull-up used when we are a source.
*
* @param port Type-C port number
* @param rp One of enum tcpc_rp_value
*
* @return EC_SUCCESS or error
*/
int (*select_rp_value)(int rp);
/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
* @param pull One of enum tcpc_cc_pull
*
* @return EC_SUCCESS or error
*/
int (*set_cc)(int pull);
/**
* Set polarity
*
* @param port Type-C port number
* @param polarity 0=> transmit on CC1, 1=> transmit on CC2
*
* @return EC_SUCCESS or error
*/
int (*set_polarity)(int polarity);
/**
* Set Vconn.
*
* @param port Type-C port number
* @param polarity Polarity of the CC line to read
*
* @return EC_SUCCESS or error
*/
int (*set_vconn)(int enable);
/**
* Set PD message header to use for goodCRC
*
* @param port Type-C port number
* @param power_role Power role to use in header
* @param data_role Data role to use in header
*
* @return EC_SUCCESS or error
*/
int (*set_msg_header)(int power_role, int data_role);
/**
* Set RX enable flag
*
* @param port Type-C port number
* @enable true for enable, false for disable
*
* @return EC_SUCCESS or error
*/
int (*set_rx_enable)(int enable);
/**
* Read last received PD message.
*
* @param port Type-C port number
* @param payload Pointer to location to copy payload of message
* @param header of message
*
* @return EC_SUCCESS or error
*/
int (*get_message)(uint32_t *payload, int *head);
/**
* Transmit PD message
*
* @param port Type-C port number
* @param type Transmit type
* @param header Packet header
* @param cnt Number of bytes in payload
* @param data Payload
*
* @return EC_SUCCESS or error
*/
int (*transmit)(enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void (*tcpc_alert)();
/**
* Discharge PD VBUS on src/sink disconnect & power role swap
*
* @param port Type-C port number
* @param enable Discharge enable or disable
*/
void (*tcpc_discharge_vbus)(int enable);
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
/**
* Enable TCPC auto DRP toggling.
*
* @param port Type-C port number
* @param enable 1: Enable 0: Disable
*
* @return EC_SUCCESS or error
*/
int (*drp_toggle)( int enable);
#endif
/**
* Get firmware version.
*
* @param port Type-C port number
* @param renew Force renewal
* @param info Pointer to pointer to PD chip info
*
* @return EC_SUCCESS or error
*/
int (*get_chip_info)(int renew, struct ec_response_pd_chip_info **info);
};
enum tcpc_alert_polarity {
TCPC_ALERT_ACTIVE_LOW, TCPC_ALERT_ACTIVE_HIGH,
};
struct tcpc_config_t {
int i2c_slave_addr;
const struct tcpm_drv *drv;
};
/**
* Returns the PD_STATUS_TCPC_ALERT_* mask corresponding to the TCPC ports
* that are currently asserting ALERT.
*
* @return PD_STATUS_TCPC_ALERT_* mask.
*/
uint16_t tcpc_get_alert_status(void);
/**
* Optional, set the TCPC power mode.
*
* @param port Type-C port number
* @param mode 0: off/sleep, 1: on/awake
*/
void board_set_tcpc_power_mode(int mode) __attribute__((weak));
/**
* Initialize TCPC.
*
* @param port Type-C port number
*/
void tcpc_init();
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void tcpc_alert_clear();
/**
* Run TCPC task once. This checks for incoming messages, processes
* any outgoing messages, and reads CC lines.
*
* @param port Type-C port number
* @param evt Event type that woke up this task
*/
int tcpc_run(int evt);
/**
* Initialize board specific TCPC functions post TCPC initialization.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int board_tcpc_post_init() __attribute__((weak));
#endif /* __CROS_EC_USB_PD_TCPM_H */

View File

@@ -0,0 +1,204 @@
/*
* 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 "I2CBB.hpp"
#include <pd.h>
/*
* 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.
*/
static uint8_t fusb_read_byte(uint8_t addr) {
uint8_t data[1];
I2CBB::Mem_Read(FUSB302B_ADDR, addr, (uint8_t*) data, 1);
return data[0];
}
/*
* 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
*/
static void fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) {
I2CBB::Mem_Read(FUSB302B_ADDR, addr, (uint8_t*) buf, size);
}
/*
* 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
*/
static void fusb_write_byte(uint8_t addr, uint8_t byte) {
I2CBB::Mem_Write(FUSB302B_ADDR, addr, (uint8_t*) &byte, 1);
}
/*
* 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
*/
static void fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) {
I2CBB::Mem_Write(FUSB302B_ADDR, addr, (uint8_t*) &buf, size);
}
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);
}
uint8_t fusb_read_message(union pd_msg *msg) {
uint8_t garbage[4];
uint8_t numobj;
/* If this isn't an SOP message, return error.
* Because of our configuration, we should be able to assume this means the
* buffer is empty, and not try to read past a non-SOP message. */
if ((fusb_read_byte( FUSB_FIFOS) & FUSB_FIFO_RX_TOKEN_BITS)
!= FUSB_FIFO_RX_SOP) {
return 1;
}
/* 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);
}
void fusb_setup() {
/* Fully reset the FUSB302B */
fusb_write_byte( FUSB_RESET, FUSB_RESET_SW_RES);
/* Turn on all power */
fusb_write_byte( FUSB_POWER, 0x0F);
/* Set interrupt masks */
fusb_write_byte( FUSB_MASK1, 0x00);
fusb_write_byte( FUSB_MASKA, 0x00);
fusb_write_byte( FUSB_MASKB, 0x00);
fusb_write_byte( FUSB_CONTROL0, 0x04);
/* Enable automatic retransmission */
fusb_write_byte( FUSB_CONTROL3, 0x07);
/* Flush the RX buffer */
fusb_write_byte( FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
/* Measure CC1 */
fusb_write_byte( FUSB_SWITCHES0, 0x07);
osDelay(1);
uint8_t cc1 = fusb_read_byte( FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
/* Measure CC2 */
fusb_write_byte( FUSB_SWITCHES0, 0x0B);
osDelay(1);
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);
fusb_write_byte( FUSB_SWITCHES0, 0x07);
} else {
fusb_write_byte( FUSB_SWITCHES1, 0x26);
fusb_write_byte( FUSB_SWITCHES0, 0x0B);
}
/* Reset the PD logic */
fusb_write_byte( FUSB_RESET, FUSB_RESET_PD_RESET);
}
void fusb_get_status(union fusb_status *status) {
/* Read the interrupt and status flags into status */
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);
}

View File

@@ -0,0 +1,303 @@
/*
* 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
#define FUSB302B01_ADDR 0x23
#define FUSB302B10_ADDR 0x24
#define FUSB302B11_ADDR 0x25
/* 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);
/*
* 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
*/
void 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
*/
void fusb_setup();
/*
* Reset the FUSB302B
*/
void fusb_reset();
#endif /* PDB_FUSB302B_H */

View File

@@ -0,0 +1,38 @@
/*
* fusbpd.cpp
*
* Created on: 13 Jun 2020
* Author: Ralim
*/
#include <fusbpd.h>
#include <pd.h>
#include "I2CBB.hpp"
#include "fusb302b.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "int_n.h"
#include "hard_reset.h"
uint8_t fusb302_detect() {
//Probe the I2C bus for its address
return I2CBB::probe(FUSB302B_ADDR);
}
void fusb302_start_processing() {
/* Initialize the FUSB302B */
fusb_setup();
/* Create the policy engine thread. */
PolicyEngine::init();
/* Create the protocol layer threads. */
ProtocolReceive::init();
ProtocolTransmit::init();
ResetHandler::init();
/* Create the INT_N thread. */
InterruptHandler::init();
}

View File

@@ -0,0 +1,18 @@
/*
* 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>
//returns 1 if the FUSB302 is on the I2C bus
uint8_t fusb302_detect();
void fusb302_start_processing();
#endif /* DRIVERS_FUSB302_FUSBPD_H_ */

View File

@@ -0,0 +1,148 @@
/*
* 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 "hard_reset.h"
#include "fusbpd.h"
#include <pd.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "fusb302b.h"
osThreadId ResetHandler::TaskHandle;
uint32_t ResetHandler::TaskBuffer[ResetHandler::TaskStackSize];
osStaticThreadDef_t ResetHandler::TaskControlBlock;
/*
* PRL_HR_Reset_Layer state
*/
ResetHandler::hardrst_state ResetHandler::hardrst_reset_layer() {
/* First, wait for the signal to run a hard reset. */
eventmask_t evt = waitForEvent(
PDB_EVT_HARDRST_RESET | PDB_EVT_HARDRST_I_HARDRST);
/* Reset the Protocol RX machine */
ProtocolReceive::notify( PDB_EVT_PRLRX_RESET);
taskYIELD();
/* Reset the Protocol TX machine */
ProtocolTransmit::notify(PDB_EVT_PRLTX_RESET);
taskYIELD();
/* Continue the process based on what event started the reset. */
if (evt & PDB_EVT_HARDRST_RESET) {
/* Policy Engine started the reset. */
return PRLHRRequestHardReset;
} else {
/* PHY started the reset */
return PRLHRIndicateHardReset;
}
}
ResetHandler::hardrst_state ResetHandler::hardrst_indicate_hard_reset() {
/* Tell the PE that we're doing a hard reset */
PolicyEngine::notify( PDB_EVT_PE_RESET);
return PRLHRWaitPE;
}
ResetHandler::hardrst_state ResetHandler::hardrst_request_hard_reset() {
/* Tell the PHY to send a hard reset */
fusb_send_hardrst();
return PRLHRWaitPHY;
}
ResetHandler::hardrst_state ResetHandler::hardrst_wait_phy() {
/* Wait for the PHY to tell us that it's done sending the hard reset */
waitForEvent(PDB_EVT_HARDRST_I_HARDSENT, PD_T_HARD_RESET_COMPLETE);
/* Move on no matter what made us stop waiting. */
return PRLHRHardResetRequested;
}
ResetHandler::hardrst_state ResetHandler::hardrst_hard_reset_requested() {
/* Tell the PE that the hard reset was sent */
PolicyEngine::notify( PDB_EVT_PE_HARD_SENT);
return PRLHRWaitPE;
}
ResetHandler::hardrst_state ResetHandler::hardrst_wait_pe() {
/* Wait for the PE to tell us that it's done */
waitForEvent(PDB_EVT_HARDRST_DONE);
return PRLHRComplete;
}
ResetHandler::hardrst_state ResetHandler::hardrst_complete() {
/* I'm not aware of anything we have to tell the FUSB302B, so just finish
* the reset routine. */
return PRLHRResetLayer;
}
void ResetHandler::init() {
osThreadStaticDef(Task, Thread, PDB_PRIO_PE, 0, TaskStackSize, TaskBuffer,
&TaskControlBlock);
TaskHandle = osThreadCreate(osThread(Task), NULL);
}
void ResetHandler::notify(uint32_t notification) {
xTaskNotify(TaskHandle, notification,
eNotifyAction::eSetValueWithOverwrite);
}
void ResetHandler::Thread(const void *arg) {
(void) arg;
ResetHandler::hardrst_state state = PRLHRResetLayer;
while (true) {
switch (state) {
case PRLHRResetLayer:
state = hardrst_reset_layer();
break;
case PRLHRIndicateHardReset:
state = hardrst_indicate_hard_reset();
break;
case PRLHRRequestHardReset:
state = hardrst_request_hard_reset();
break;
case PRLHRWaitPHY:
state = hardrst_wait_phy();
break;
case PRLHRHardResetRequested:
state = hardrst_hard_reset_requested();
break;
case PRLHRWaitPE:
state = hardrst_wait_pe();
break;
case PRLHRComplete:
state = hardrst_complete();
break;
default:
/* This is an error. It really shouldn't happen. We might
* want to handle it anyway, though. */
break;
}
}
}
uint32_t ResetHandler::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
uint32_t pulNotificationValue;
xTaskNotifyWait(0x00, mask, &pulNotificationValue, ticksToWait);
return pulNotificationValue;
}

View File

@@ -0,0 +1,63 @@
/*
* 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_HARD_RESET_H
#define PDB_HARD_RESET_H
#include <pd.h>
/* Events for the Hard Reset thread */
#define PDB_EVT_HARDRST_RESET EVENT_MASK(0)
#define PDB_EVT_HARDRST_I_HARDRST EVENT_MASK(1)
#define PDB_EVT_HARDRST_I_HARDSENT EVENT_MASK(2)
#define PDB_EVT_HARDRST_DONE EVENT_MASK(3)
class ResetHandler {
public:
static void init();
static void notify(uint32_t notification);
private:
static void Thread(const void *arg);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1024 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
static uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
/*
* Hard Reset machine states
*/
enum hardrst_state {
PRLHRResetLayer,
PRLHRIndicateHardReset,
PRLHRRequestHardReset,
PRLHRWaitPHY,
PRLHRHardResetRequested,
PRLHRWaitPE,
PRLHRComplete
};
static hardrst_state hardrst_reset_layer();
static hardrst_state hardrst_indicate_hard_reset();
static hardrst_state hardrst_request_hard_reset();
static hardrst_state hardrst_wait_phy();
static hardrst_state hardrst_hard_reset_requested();
static hardrst_state hardrst_wait_pe();
static hardrst_state hardrst_complete();
};
#endif /* PDB_HARD_RESET_H */

View File

@@ -0,0 +1,97 @@
/*
* 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 "fusbpd.h"
#include <pd.h>
#include "fusb302b.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "hard_reset.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "BSP.h"
osThreadId InterruptHandler::TaskHandle;
uint32_t InterruptHandler::TaskBuffer[InterruptHandler::TaskStackSize];
osStaticThreadDef_t InterruptHandler::TaskControlBlock;
void InterruptHandler::init() {
osThreadStaticDef(Task, Thread, PDB_PRIO_PE, 0, TaskStackSize, TaskBuffer,
&TaskControlBlock);
TaskHandle = osThreadCreate(osThread(Task), NULL);
}
void InterruptHandler::Thread(const void *arg) {
(void) arg;
union fusb_status status;
eventmask_t events;
//TODO use IRQ task notification to unblock this thread to stop it spinning
while (true) {
/* If the INT_N line is low */
if (pd_irq_read() == 0) {
/* Read the FUSB302B status and interrupt registers */
fusb_get_status(&status);
//Check for rx alerts
{
/* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
ProtocolReceive::notify(PDB_EVT_PRLRX_I_GCRCSENT);
}
}
/* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
* thread */
{
events = 0;
if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
events |= PDB_EVT_PRLTX_I_RETRYFAIL;
}
if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
events |= PDB_EVT_PRLTX_I_TXSENT;
}
if (events) {
ProtocolTransmit::notify(events);
}
}
/* If the I_HARDRST or I_HARDSENT flag is set, tell the Hard Reset
* thread */
{
events = 0;
if (status.interrupta & FUSB_INTERRUPTA_I_HARDRST) {
events |= PDB_EVT_HARDRST_I_HARDRST;
}
if (status.interrupta & FUSB_INTERRUPTA_I_HARDSENT) {
events |= PDB_EVT_HARDRST_I_HARDSENT;
}
if (events) {
ResetHandler::notify(events);
}
}
{
/* 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(PDB_EVT_PE_I_OVRTEMP);
}
}
}
osDelay(1);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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();
//TODO handle irq callbacks instead of polling the pin
private:
static void Thread(const void *arg);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1024 / 4;
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();
};
#endif /* PDB_INT_N_OLD_H */

View File

@@ -0,0 +1,402 @@
/*
* 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 <stdint.h>
#include "FreeRTOS.h"
#include "pdb_msg.h"
#include "cmsis_os.h"
#include "pdb_conf.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 (45)
#define PD_T_HARD_RESET_COMPLETE (4)
#define PD_T_PS_TRANSITION (500)
#define PD_T_SENDER_RESPONSE (27)
#define PD_T_SINK_REQUEST (100)
#define PD_T_TYPEC_SINK_WAIT_CAP (465)
#define PD_T_PPS_REQUEST TIME_S2I(10)
/* This is actually from Type-C, not Power Delivery, but who cares? */
#define PD_T_PD_DEBOUNCE (15)
/*
* 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

@@ -0,0 +1,47 @@
/*
* 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 4
/* Size of the Policy Engine thread's working area */
#define PDB_PE_WA_SIZE 256
/* Size of the protocol layer RX thread's working area */
#define PDB_PRLRX_WA_SIZE 256
/* Size of the protocol layer TX thread's working area */
#define PDB_PRLTX_WA_SIZE 256
/* Size of the protocol layer hard reset thread's working area */
#define PDB_HARDRST_WA_SIZE 256
/* Size of the INT_N thread's working area */
#define PDB_INT_N_WA_SIZE 128
#define EVENT_MASK(x) (1<<x)
#define eventmask_t uint32_t
/* PD Buddy thread priorities */
#define PDB_PRIO_PE (osPriorityNormal)
#define PDB_PRIO_PRL (osPriorityBelowNormal)
#define PDB_PRIO_PRL_INT_N (osPriorityLow)
#endif /* PDB_CONF_H */

View File

@@ -0,0 +1,55 @@
/*
* 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

@@ -0,0 +1,799 @@
/*
* 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 <stdbool.h>
#include <pd.h>
#include "protocol_tx.h"
#include "hard_reset.h"
#include "fusb302b.h"
bool PolicyEngine::pdNegotiationComplete;
bool PolicyEngine::pdHasEnteredLowPower;
int PolicyEngine::current_voltage_mv;
int PolicyEngine::_requested_voltage;
bool PolicyEngine::_unconstrained_power;
union pd_msg PolicyEngine::currentMessage;
uint16_t PolicyEngine::hdr_template;
bool PolicyEngine::_explicit_contract;
bool PolicyEngine::_min_power;
int8_t PolicyEngine::_hard_reset_counter;
int8_t PolicyEngine::_old_tcc_match;
uint8_t PolicyEngine::_pps_index;
uint8_t PolicyEngine::_last_pps;
osThreadId PolicyEngine::TaskHandle;
uint32_t PolicyEngine::TaskBuffer[PolicyEngine::TaskStackSize];
osStaticThreadDef_t PolicyEngine::TaskControlBlock;
union pd_msg PolicyEngine::tempMessage;
union pd_msg PolicyEngine::_last_dpm_request;
StaticQueue_t PolicyEngine::xStaticQueue;
uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_SIZE
* sizeof(union pd_msg)];
QueueHandle_t PolicyEngine::messagesWaiting;
void PolicyEngine::init() {
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
//Create static thread at PDB_PRIO_PE priority
osThreadStaticDef(Task, pe_task, PDB_PRIO_PE, 0, TaskStackSize, TaskBuffer,
&TaskControlBlock);
TaskHandle = osThreadCreate(osThread(Task), NULL);
}
void PolicyEngine::notify(uint32_t notification) {
xTaskNotify(TaskHandle, notification,
eNotifyAction::eSetValueWithOverwrite);
}
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 */
_old_tcc_match = -1;
/* Initialize the pps_index */
_pps_index = 8;
/* Initialize the last_pps */
_last_pps = 8;
PolicyEngine::policy_engine_state state = PESinkStartup;
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;
//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 */
eventmask_t evt = waitForEvent(
PDB_EVT_PE_MSG_RX | PDB_EVT_PE_I_OVRTEMP | PDB_EVT_PE_RESET,
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 & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we're too hot, we shouldn't negotiate power yet */
if (evt & PDB_EVT_PE_I_OVRTEMP) {
return PESinkWaitCap;
}
/* If we got a message */
if (evt & PDB_EVT_PE_MSG_RX) {
/* Get the message */
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;
/* 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 we got an unexpected message, reset */
} else {
/* Free the received message */
return PESinkHardReset;
}
}
/* If we failed to get a message, send a hard reset */
return PESinkHardReset;
}
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 = 8;
/* Search for the first PPS APDO */
for (int8_t 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;
}
}
/* New capabilities also means we can't be making a request from the
* same PPS APDO */
_last_pps = 8;
/* Get a message object for the request if we don't have one already */
/* Remember the last PDO we requested if it was a PPS APDO */
if (PD_RDO_OBJPOS_GET(&_last_dpm_request) >= _pps_index) {
_last_pps = PD_RDO_OBJPOS_GET(&_last_dpm_request);
/* Otherwise, forget any PPS APDO we had requested */
} else {
_last_pps = 8;
}
/* Ask the DPM what to request */
pdbs_dpm_evaluate_capability(&tempMessage, &_last_dpm_request);
return PESinkSelectCap;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
/* Transmit the request */
ProtocolTransmit::pushMessage(&_last_dpm_request);
//Send indication that there is a message pending
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* Don't free the request; we might need it again */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
/* If we're using PD 3.0 */
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
/* If the request was for a PPS APDO, start SinkPPSPeriodicTimer */
if (PD_RDO_OBJPOS_GET(&_last_dpm_request) >= _pps_index) {
start_pps_timer();
/* Otherwise, stop SinkPPSPeriodicTimer */
} else {
stop_pps_timer();
}
}
/* This will use a virtual timer to send an event flag to this thread after
* PD_T_PPS_REQUEST */
/* Wait for a response */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
PD_T_SENDER_RESPONSE);
/* If we got reset signaling, transition to default */
if (evt & 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 request, wait for the new power */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
/* Transition to Sink Standby if necessary */
if (PD_RDO_OBJPOS_GET(&_last_dpm_request) != _last_pps) {
pdbs_dpm_transition_standby();
}
_min_power = false;
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 {
/* If we got here from a Wait message, we Should run
* SinkRequestTimer in the Ready state. */
_min_power = (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_WAIT);
return PESinkReady;
}
} else {
return PESinkSendSoftReset;
}
}
return PESinkHardReset;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_sink() {
/* Wait for the PS_RDY message */
eventmask_t evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
PD_T_PS_TRANSITION);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If no message was received, send a hard reset */
if (evt == 0) {
return PESinkHardReset;
}
/* If we received a message, read it */
if (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;
/* Set the output appropriately */
if (!_min_power) {
pdbs_dpm_transition_requested();
}
return PESinkReady;
/* If there was a protocol error, send a hard reset */
} else {
/* Turn off the power output before this hard reset to make sure we
* don't supply an incorrect voltage to the device we're powering.
*/
pdbs_dpm_transition_default();
return PESinkHardReset;
}
}
return PESinkHardReset;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() {
eventmask_t evt;
/* Wait for an event */
if (_min_power) {
evt = waitForEvent(
PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP
| PDB_EVT_PE_GET_SOURCE_CAP | PDB_EVT_PE_NEW_POWER
| PDB_EVT_PE_PPS_REQUEST,
PD_T_SINK_REQUEST);
} else {
evt = waitForEvent(
PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP
| PDB_EVT_PE_GET_SOURCE_CAP | PDB_EVT_PE_NEW_POWER
| PDB_EVT_PE_PPS_REQUEST);
}
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we overheated, send a hard reset */
if (evt & PDB_EVT_PE_I_OVRTEMP) {
return PESinkHardReset;
}
/* If the DPM wants us to, send a Get_Source_Cap message */
if (evt & PDB_EVT_PE_GET_SOURCE_CAP) {
/* Tell the protocol layer we're starting an AMS */
ProtocolTransmit::notify( PDB_EVT_PRLTX_START_AMS);
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 & PDB_EVT_PE_NEW_POWER) {
/* Tell the protocol layer we're starting an AMS */
ProtocolTransmit::notify( PDB_EVT_PRLTX_START_AMS);
return PESinkEvalCap;
}
/* If SinkPPSPeriodicTimer ran out, send a new request */
if (evt & PDB_EVT_PE_PPS_REQUEST) {
/* Tell the protocol layer we're starting an AMS */
ProtocolTransmit::notify( PDB_EVT_PRLTX_START_AMS);
return PESinkSelectCap;
}
/* If no event was received, the timer ran out. */
if (evt == 0) {
/* Repeat our Request message */
return PESinkSelectCap;
}
/* If we received a message */
if (evt & 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) {
if (pdbs_dpm_giveback_enabled()) {
/* Transition to the minimum current level */
pdbs_dpm_transition_min();
_min_power = true;
return PESinkTransitionSink;
} else {
/* GiveBack is not supported */
return PESinkSendNotSupported;
}
/* Evaluate new Source_Capabilities */
} else if (PD_MSGTYPE_GET(&tempMessage)
== PD_MSGTYPE_SOURCE_CAPABILITIES
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
/* Don't free the message: we need to keep the
* Source_Capabilities message so we can evaluate it. */
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;
}
/* 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 */
ProtocolTransmit::pushMessage(get_source_cap);
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & 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 */
ProtocolTransmit::pushMessage(snk_cap);
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* Free the Sink_Capabilities message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & 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;
}
/* Generate a hard reset signal */
ResetHandler::notify(PDB_EVT_HARDRST_RESET);
waitForEvent(PDB_EVT_PE_HARD_SENT);
/* 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. */
/* Tell the protocol layer we're done with the reset */
ResetHandler::notify( PDB_EVT_HARDRST_DONE);
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 */
ProtocolTransmit::pushMessage(&accept);
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & 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 */
ProtocolTransmit::pushMessage(softrst);
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
/* Wait for a response */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
PD_T_SENDER_RESPONSE);
/* If we got reset signaling, transition to default */
if (evt & 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 */
union pd_msg *not_supported = &tempMessage;
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_2_0) {
/* Make a Reject message */
not_supported->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 */
not_supported->hdr = hdr_template | PD_MSGTYPE_NOT_SUPPORTED
| PD_NUMOBJ(0);
}
/* Transmit the message */
ProtocolTransmit::pushMessage(not_supported);
ProtocolTransmit::notify( PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a soft reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkSendSoftReset;
}
return PESinkReady;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_chunk_received() {
/* Wait for tChunkingNotSupported */
eventmask_t evt = waitForEvent(PDB_EVT_PE_RESET,
PD_T_CHUNKING_NOT_SUPPORTED);
/* If we got reset signaling, transition to default */
if (evt & 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() {
/* If the DPM can evaluate the Type-C Current advertisement */
//TS80P doesnt
// if (cfg->dpm.evaluate_typec_current != NULL) {
// /* Make the DPM evaluate the Type-C Current advertisement */
// int tcc_match = cfg->dpm.evaluate_typec_current(cfg,
// fusb_get_typec_current(&cfg->fusb));
//
// /* If the last two readings are the same, set the output */
// if (_old_tcc_match == tcc_match) {
// cfg->dpm.transition_typec(cfg);
// }
//
// /* Remember whether or not the last measurement succeeded */
// _old_tcc_match = tcc_match;
// }
/* Wait tPDDebounce between measurements */
osDelay(PD_T_PD_DEBOUNCE);
return PESinkSourceUnresponsive;
}
void PolicyEngine::PPSTimerCallBack() {
notify(PDB_EVT_PE_PPS_REQUEST);
}
bool PolicyEngine::pdHasNegotiated() {
return pdNegotiationComplete;
}
bool PolicyEngine::heatingAllowed() {
if (pdHasNegotiated())
return !pdHasEnteredLowPower;
//Not pd -- pass through
return true;
}
uint32_t PolicyEngine::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
uint32_t pulNotificationValue;
xTaskNotifyWait(0x00, mask, &pulNotificationValue, ticksToWait);
return pulNotificationValue;
}
bool PolicyEngine::isPD3_0() {
return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0;
}
void PolicyEngine::start_pps_timer() {
}
void PolicyEngine::stop_pps_timer() {
}

View File

@@ -0,0 +1,197 @@
/*
* 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
*
*/
/* Tell the PE to send a Get_Source_Cap message */
#define PDB_EVT_PE_GET_SOURCE_CAP EVENT_MASK(7)
/* Tell the PE that new power is required */
#define PDB_EVT_PE_NEW_POWER EVENT_MASK(8)
#define PDB_EVT_PE_RESET EVENT_MASK(0)
#define PDB_EVT_PE_MSG_RX EVENT_MASK(1)
#define PDB_EVT_PE_TX_DONE EVENT_MASK(2)
#define PDB_EVT_PE_TX_ERR EVENT_MASK(3)
#define PDB_EVT_PE_HARD_SENT EVENT_MASK(4)
#define PDB_EVT_PE_I_OVRTEMP EVENT_MASK(5)
#define PDB_EVT_PE_PPS_REQUEST EVENT_MASK(6)
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);
//Send a notification
static void notify(uint32_t notification);
//Returns true if headers indicate PD3.0 compliant
static bool isPD3_0();
static void PPSTimerCallBack();
//Has pd negotiation completed
static bool pdHasNegotiated();
//Used in case the USB-C PD source requests minimum power
static bool heatingAllowed();
private:
static bool pdNegotiationComplete;
static bool pdHasEnteredLowPower;
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
//Current message being handled
static union pd_msg currentMessage;
/* PD message header template */
static uint16_t hdr_template;
/* Whether or not we have an explicit contract */
static bool _explicit_contract;
/* Whether or not we're receiving minimum power */
static bool _min_power;
/* The number of hard resets we've sent */
static int8_t _hard_reset_counter;
/* The result of the last Type-C Current match comparison */
static int8_t _old_tcc_match;
/* The index of the first PPS APDO */
static uint8_t _pps_index;
/* The index of the just-requested PPS APDO */
static uint8_t _last_pps;
static void pe_task(const void *arg);
enum policy_engine_state {
PESinkStartup,
PESinkDiscovery,
PESinkWaitCap,
PESinkEvalCap,
PESinkSelectCap,
PESinkTransitionSink,
PESinkReady,
PESinkGetSourceCap,
PESinkGiveSinkCap,
PESinkHardReset,
PESinkTransitionDefault,
PESinkSoftReset,
PESinkSendSoftReset,
PESinkSendNotSupported,
PESinkChunkReceived,
PESinkNotSupportedReceived,
PESinkSourceUnresponsive
};
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 uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
//Task resources
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1024 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
static union pd_msg tempMessage;
static union pd_msg _last_dpm_request;
//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 void readMessage();
static void start_pps_timer();
static void stop_pps_timer();
// 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

@@ -0,0 +1,305 @@
/*
* policy_engine_user.cpp
*
* Created on: 14 Jun 2020
* Author: Ralim
*/
#include "pd.h"
#include "policy_engine.h"
/* The current draw when the output is disabled */
#define DPM_MIN_CURRENT PD_MA2PDI(50)
/*
* 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 */
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
int voltage = PD_PDV2MV(
PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i]));
int current = PD_PDO_SRC_FIXED_CURRENT_GET(capabilities->obj[i]);
if (voltage == 9000) {
/* We got what we wanted, so build a request for that */
request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
/* GiveBack disabled */
request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(
current) | PD_RDO_FV_CURRENT_SET(current)
| PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(i + 1);
request->obj[0] |= PD_RDO_USB_COMMS;
/* Update requested voltage */
_requested_voltage = voltage;
return true;
}
}
/* If we have a PPS APDO, our desired V lies within its range, and
* its I is at least our desired I */
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED
&& (capabilities->obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) {
int min_mv = PD_PAV2MV(
PD_APDO_PPS_MIN_VOLTAGE_GET(capabilities->obj[i]));
int max_mv = PD_PAV2MV(
PD_APDO_PPS_MAX_VOLTAGE_GET(capabilities->obj[i]));
int current = PD_CA2PAI(
PD_APDO_PPS_CURRENT_GET(capabilities->obj[i]));
/* We got what we wanted, so build a request for that */
//
// request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
//
// /* Build a request */
// request->obj[0] =
// PD_RDO_PROG_CURRENT_SET(
// PD_CA2PAI(current)) | PD_RDO_PROG_VOLTAGE_SET(PD_MV2PRV(voltage))
// | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(i + 1);
//
// request->obj[0] |= PD_RDO_USB_COMMS;
//
// /* Update requested voltage */
// _requested_voltage = PD_PRV2MV(PD_MV2PRV(voltage));
//
// return true;
}
}
/* If there's a PDO in the voltage range, use it */
// int8_t i = dpm_get_range_fixed_pdo_index(caps, scfg);
// if (i >= 0) {
// /* We got what we wanted, so build a request for that */
// request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
// /* Get the current we need at this voltage */
// current = dpm_get_current(scfg,
// PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(caps->obj[i])));
// if (scfg->flags & PDBS_CONFIG_FLAGS_GIVEBACK) {
// /* GiveBack enabled */
// request->obj[0] =
// PD_RDO_FV_MIN_CURRENT_SET(
// DPM_MIN_CURRENT) | PD_RDO_FV_CURRENT_SET(current) | PD_RDO_NO_USB_SUSPEND
// | PD_RDO_GIVEBACK | PD_RDO_OBJPOS_SET(i + 1);
// } else {
// /* GiveBack disabled */
// request->obj[0] =
// PD_RDO_FV_MAX_CURRENT_SET(
// current) | PD_RDO_FV_CURRENT_SET(current) | PD_RDO_NO_USB_SUSPEND
// | PD_RDO_OBJPOS_SET(i + 1);
// }
// if (usb_comms) {
// request->obj[0] |= PD_RDO_USB_COMMS;
// }
//
// /* Update requested voltage */
// _requested_voltage = PD_PDV2MV(
// PD_PDO_SRC_FIXED_VOLTAGE_GET(caps->obj[i]));
//
// _capability_match = true;
// return true;
// }
/* 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;
}
/* If we can do USB communications, tell the power supply */
request->obj[0] |= PD_RDO_USB_COMMS;
/* Update requested voltage */
_requested_voltage = 5000;
/* At this point, we have a capability match iff the output is disabled */
return false;
}
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 current = 100; // In centi-amps
uint16_t voltage = 9000; // in mv
/* 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 (isPD3_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_giveback_enabled() {
return false;
//We do not support giveback
}
bool PolicyEngine::pdbs_dpm_evaluate_typec_current(
enum fusb_typec_current 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
return true;
}
void PolicyEngine::pdbs_dpm_pd_start() {
//PD neg is starting
}
void PolicyEngine::pdbs_dpm_transition_default() {
/* Cast the dpm_data to the right type */
/* Pretend we requested 5 V */
current_voltage_mv = 5000;
/* Turn the output off */
pdNegotiationComplete = false;
pdHasEnteredLowPower = true;
}
void PolicyEngine::pdbs_dpm_transition_min() {
pdHasEnteredLowPower = true;
}
void PolicyEngine::pdbs_dpm_transition_standby() {
/* If the voltage is changing, enter Sink Standby */
if (_requested_voltage != current_voltage_mv) {
/* For the PD Buddy Sink, entering Sink Standby is equivalent to
* turning the output off. However, we don't want to change the LED
* state for standby mode. */
pdHasEnteredLowPower = true;
}
}
void PolicyEngine::pdbs_dpm_transition_requested() {
/* Cast the dpm_data to the right type */
pdNegotiationComplete = true;
pdHasEnteredLowPower = false;
}
void PolicyEngine::handleMessage(union pd_msg *msg) {
xQueueSend(messagesWaiting, msg, 100);
}
bool PolicyEngine::messageWaiting() {
return uxQueueMessagesWaiting(messagesWaiting) > 0;
}
void PolicyEngine::readMessage() {
xQueueReceive(messagesWaiting, &tempMessage, 1);
}
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;
pdHasEnteredLowPower = false;
}

View File

@@ -0,0 +1,171 @@
/*
* 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 "protocol_rx.h"
#include <stdlib.h>
#include <pd.h>
#include "policy_engine.h"
#include "protocol_tx.h"
#include "fusb302b.h"
osThreadId ProtocolReceive::TaskHandle;
uint32_t ProtocolReceive::TaskBuffer[ProtocolReceive::TaskStackSize];
osStaticThreadDef_t ProtocolReceive::TaskControlBlock;
union pd_msg ProtocolReceive::tempMessage;
uint8_t ProtocolReceive::_rx_messageid;
uint8_t ProtocolReceive::_tx_messageidcounter;
/*
* PRL_Rx_Wait_for_PHY_Message state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_wait_phy() {
/* Wait for an event */
eventmask_t evt = waitForEvent(0xFFFFFFFF);
/* If we got a reset event, reset */
if (evt & PDB_EVT_PRLRX_RESET) {
return PRLRxWaitPHY;
}
/* If we got an I_GCRCSENT event, read the message and decide what to do */
if (evt & PDB_EVT_PRLRX_I_GCRCSENT) {
/* Get a buffer to read the message into. Guaranteed to not fail
* because we have a big enough pool and are careful. */
union pd_msg *_rx_message = &tempMessage;
/* Read the message */
fusb_read_message(_rx_message);
/* If it's a Soft_Reset, go to the soft reset state */
if (PD_MSGTYPE_GET(_rx_message) == PD_MSGTYPE_SOFT_RESET
&& PD_NUMOBJ_GET(_rx_message) == 0) {
return PRLRxReset;
} else {
/* Otherwise, check the message ID */
return PRLRxCheckMessageID;
}
}
return PRLRxWaitPHY;
}
/*
* PRL_Rx_Layer_Reset_for_Receive state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_reset() {
/* Reset MessageIDCounter */
_tx_messageidcounter = 0;
/* Clear stored MessageID */
_rx_messageid = -1;
/* TX transitions to its reset state */
ProtocolTransmit::notify( PDB_EVT_PRLTX_RESET);
taskYIELD();
/* If we got a RESET signal, reset the machine */
if (waitForEvent(PDB_EVT_PRLRX_RESET) != 0) {
return PRLRxWaitPHY;
}
/* Go to the Check_MessageID state */
return PRLRxCheckMessageID;
}
/*
* PRL_Rx_Check_MessageID state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_check_messageid() {
/* If we got a RESET signal, reset the machine */
if (waitForEvent(PDB_EVT_PRLRX_RESET) != 0) {
return PRLRxWaitPHY;
}
/* If the message has the stored ID, we've seen this message before. Free
* it and don't pass it to the policy engine. */
if (PD_MESSAGEID_GET(&tempMessage) == _rx_messageid) {
return PRLRxWaitPHY;
/* Otherwise, there's either no stored ID or this message has an ID we
* haven't just seen. Transition to the Store_MessageID state. */
} else {
return PRLRxStoreMessageID;
}
}
/*
* PRL_Rx_Store_MessageID state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_store_messageid() {
/* Tell ProtocolTX to discard the message being transmitted */
ProtocolTransmit::notify( PDB_EVT_PRLTX_DISCARD);
taskYIELD();
/* Update the stored MessageID */
_rx_messageid = PD_MESSAGEID_GET(&tempMessage);
/* Pass the message to the policy engine. */
PolicyEngine::handleMessage(&tempMessage);
PolicyEngine::notify( PDB_EVT_PE_MSG_RX);
/* Don't check if we got a RESET because we'd do nothing different. */
return PRLRxWaitPHY;
}
void ProtocolReceive::init() {
osThreadStaticDef(Task, thread, PDB_PRIO_PRL, 0, TaskStackSize, TaskBuffer,
&TaskControlBlock);
TaskHandle = osThreadCreate(osThread(Task), NULL);
}
void ProtocolReceive::thread(const void *args) {
(void) args;
ProtocolReceive::protocol_rx_state state = PRLRxWaitPHY;
while (true) {
switch (state) {
case PRLRxWaitPHY:
state = protocol_rx_wait_phy();
break;
case PRLRxReset:
state = protocol_rx_reset();
break;
case PRLRxCheckMessageID:
state = protocol_rx_check_messageid();
break;
case PRLRxStoreMessageID:
state = protocol_rx_store_messageid();
break;
default:
/* This is an error. It really shouldn't happen. We might
* want to handle it anyway, though. */
state = PRLRxWaitPHY;
break;
}
}
}
void ProtocolReceive::notify(uint32_t notification) {
xTaskNotify(TaskHandle, notification,
eNotifyAction::eSetValueWithOverwrite);
}
uint32_t ProtocolReceive::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
uint32_t pulNotificationValue;
xTaskNotifyWait(0x00, mask, &pulNotificationValue, ticksToWait);
return pulNotificationValue;
}

View File

@@ -0,0 +1,61 @@
/*
* 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_PROTOCOL_RX_H
#define PDB_PROTOCOL_RX_H
#include <stdint.h>
#include <pd.h>
/* Events for the Protocol RX thread */
#define PDB_EVT_PRLRX_RESET EVENT_MASK(0)
#define PDB_EVT_PRLRX_I_GCRCSENT EVENT_MASK(1)
class ProtocolReceive {
public:
static void init();
static void notify(uint32_t notification);
private:
static void thread(const void *args);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 512 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
/*
* Protocol RX machine states
*
* There is no Send_GoodCRC state because the PHY sends the GoodCRC for us.
* All transitions that would go to that state instead go to Check_MessageID.
*/
enum protocol_rx_state {
PRLRxWaitPHY, PRLRxReset, PRLRxCheckMessageID, PRLRxStoreMessageID
};
static protocol_rx_state protocol_rx_store_messageid();
static protocol_rx_state protocol_rx_check_messageid();
static protocol_rx_state protocol_rx_reset();
static protocol_rx_state protocol_rx_wait_phy();
static union pd_msg tempMessage;
static uint8_t _rx_messageid;
static uint8_t _tx_messageidcounter;
static uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
};
#endif /* PDB_PROTOCOL_RX_H */

View File

@@ -0,0 +1,285 @@
/*
* 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 "protocol_tx.h"
#include <pd.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include "fusb302b.h"
#include "fusbpd.h"
osThreadId ProtocolTransmit::TaskHandle;
uint32_t ProtocolTransmit::TaskBuffer[ProtocolTransmit::TaskStackSize];
osStaticThreadDef_t ProtocolTransmit::TaskControlBlock;
StaticQueue_t ProtocolTransmit::xStaticQueue;
uint8_t ProtocolTransmit::ucQueueStorageArea[PDB_MSG_POOL_SIZE
* sizeof(union pd_msg)];
QueueHandle_t ProtocolTransmit::messagesWaiting;
uint8_t ProtocolTransmit::_tx_messageidcounter;
union pd_msg ProtocolTransmit::temp_msg;
/*
* PRL_Tx_PHY_Layer_Reset state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_phy_reset() {
/* Reset the PHY */
fusb_reset();
/* If a message was pending when we got here, tell the policy engine that
* we failed to send it */
if (messagePending()) {
/* Tell the policy engine that we failed */
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
/* Finish failing to send the message */
getMessage(); //Discard
}
/* Wait for a message request */
return PRLTxWaitMessage;
}
/*
* PRL_Tx_Wait_for_Message_Request state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_message() {
/* Wait for an event */
eventmask_t evt = waitForEvent(
PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD | PDB_EVT_PRLTX_MSG_TX);
if (evt & PDB_EVT_PRLTX_RESET) {
return PRLTxPHYReset;
}
if (evt & PDB_EVT_PRLTX_DISCARD) {
return PRLTxDiscardMessage;
}
/* If the policy engine is trying to send a message */
if (evt & PDB_EVT_PRLTX_MSG_TX) {
/* Get the message */
getMessage();
/* If it's a Soft_Reset, reset the TX layer first */
if (PD_MSGTYPE_GET(&temp_msg) == PD_MSGTYPE_SOFT_RESET
&& PD_NUMOBJ_GET(&(temp_msg)) == 0) {
return PRLTxReset;
/* Otherwise, just send the message */
} else {
return PRLTxConstructMessage;
}
}
/* Silence the compiler warning */
return PRLTxDiscardMessage;
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_reset() {
/* Clear MessageIDCounter */
_tx_messageidcounter = 0;
/* Tell the Protocol RX thread to reset */
ProtocolReceive::notify( PDB_EVT_PRLRX_RESET);
taskYIELD();
return PRLTxConstructMessage;
}
/*
* PRL_Tx_Construct_Message state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_construct_message() {
/* Make sure nobody wants us to reset */
eventmask_t evt = waitForEvent(
PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD);
if (evt & PDB_EVT_PRLTX_RESET) {
return PRLTxPHYReset;
}
if (evt & PDB_EVT_PRLTX_DISCARD) {
return PRLTxDiscardMessage;
}
/* Set the correct MessageID in the message */
temp_msg.hdr &= ~PD_HDR_MESSAGEID;
temp_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 */
evt = waitForEvent(PDB_EVT_PRLTX_START_AMS);
if (evt & PDB_EVT_PRLTX_START_AMS) {
while (fusb_get_typec_current() != fusb_sink_tx_ok) {
osDelay(1);
}
}
}
/* Send the message to the PHY */
fusb_send_message(&temp_msg);
return PRLTxWaitResponse;
}
/*
* PRL_Tx_Wait_for_PHY_Response state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_response() {
/* Wait for an event. There is no need to run CRCReceiveTimer, since the
* FUSB302B handles that as part of its retry mechanism. */
eventmask_t evt = waitForEvent(
PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD | PDB_EVT_PRLTX_I_TXSENT
| PDB_EVT_PRLTX_I_RETRYFAIL);
if (evt & PDB_EVT_PRLTX_RESET) {
return PRLTxPHYReset;
}
if (evt & PDB_EVT_PRLTX_DISCARD) {
return PRLTxDiscardMessage;
}
/* If the message was sent successfully */
if (evt & PDB_EVT_PRLTX_I_TXSENT) {
return PRLTxMatchMessageID;
}
/* If the message failed to be sent */
if (evt & PDB_EVT_PRLTX_I_RETRYFAIL) {
return PRLTxTransmissionError;
}
/* Silence the compiler warning */
return PRLTxDiscardMessage;
}
/*
* PRL_Tx_Match_MessageID state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_match_messageid() {
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) {
return PRLTxMessageSent;
} else {
return PRLTxTransmissionError;
}
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_transmission_error() {
/* Increment MessageIDCounter */
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we failed */
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
return PRLTxWaitMessage;
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_message_sent() {
/* Increment MessageIDCounter */
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we succeeded */
PolicyEngine::notify( PDB_EVT_PE_TX_DONE);
return PRLTxWaitMessage;
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_discard_message() {
/* If we were working on sending a message, increment MessageIDCounter */
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
return PRLTxPHYReset;
}
void ProtocolTransmit::thread(const void *args) {
(void) args;
ProtocolTransmit::protocol_tx_state state = PRLTxPHYReset;
//Init the incoming message queue
while (true) {
switch (state) {
case PRLTxPHYReset:
state = protocol_tx_phy_reset();
break;
case PRLTxWaitMessage:
state = protocol_tx_wait_message();
break;
case PRLTxReset:
state = protocol_tx_reset();
break;
case PRLTxConstructMessage:
state = protocol_tx_construct_message();
break;
case PRLTxWaitResponse:
state = protocol_tx_wait_response();
break;
case PRLTxMatchMessageID:
state = protocol_tx_match_messageid();
break;
case PRLTxTransmissionError:
state = protocol_tx_transmission_error();
break;
case PRLTxMessageSent:
state = protocol_tx_message_sent();
break;
case PRLTxDiscardMessage:
state = protocol_tx_discard_message();
break;
default:
state = PRLTxPHYReset;
break;
}
}
}
void ProtocolTransmit::notify(uint32_t notification) {
xTaskNotify(TaskHandle, notification,
eNotifyAction::eSetValueWithOverwrite);
}
void ProtocolTransmit::init() {
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
osThreadStaticDef(pd_txTask, thread, PDB_PRIO_PRL, 0, TaskStackSize,
TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(pd_txTask), NULL);
}
void ProtocolTransmit::pushMessage(union pd_msg *msg) {
xQueueSend(messagesWaiting, msg, 100);
}
bool ProtocolTransmit::messagePending() {
return uxQueueMessagesWaiting(messagesWaiting) > 0;
}
void ProtocolTransmit::getMessage() {
//Loads the pending message into the buffer
xQueueReceive(messagesWaiting, &temp_msg, 1);
}
uint32_t ProtocolTransmit::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
uint32_t pulNotificationValue;
xTaskNotifyWait(0x00, mask, &pulNotificationValue, ticksToWait);
return pulNotificationValue;
}

View File

@@ -0,0 +1,91 @@
/*
* 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_PROTOCOL_TX_H
#define PDB_PROTOCOL_TX_H
#include <stdint.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include <pd.h>
/* Events for the Protocol TX thread */
#define PDB_EVT_PRLTX_RESET EVENT_MASK(0)
#define PDB_EVT_PRLTX_I_TXSENT EVENT_MASK(1)
#define PDB_EVT_PRLTX_I_RETRYFAIL EVENT_MASK(2)
#define PDB_EVT_PRLTX_DISCARD EVENT_MASK(3)
#define PDB_EVT_PRLTX_MSG_TX EVENT_MASK(4)
#define PDB_EVT_PRLTX_START_AMS EVENT_MASK(5)
class ProtocolTransmit {
public:
static void init();
//Push a message to the queue to be sent out the pd comms bus
static void pushMessage(union pd_msg *msg);
static void notify(uint32_t notification);
private:
static void thread(const void *args);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 512 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
/*
* Protocol TX machine states
*
* Because the PHY can automatically send retries, the Check_RetryCounter state
* has been removed, transitions relating to it are modified appropriately, and
* we don't even keep a RetryCounter.
*/
enum protocol_tx_state {
PRLTxPHYReset,
PRLTxWaitMessage,
PRLTxReset,
PRLTxConstructMessage,
PRLTxWaitResponse,
PRLTxMatchMessageID,
PRLTxTransmissionError,
PRLTxMessageSent,
PRLTxDiscardMessage
};
//Internal states
static protocol_tx_state protocol_tx_discard_message();
static protocol_tx_state protocol_tx_message_sent();
static protocol_tx_state protocol_tx_transmission_error();
static protocol_tx_state protocol_tx_match_messageid();
static protocol_tx_state protocol_tx_wait_response();
static protocol_tx_state protocol_tx_construct_message();
static protocol_tx_state protocol_tx_reset();
static protocol_tx_state protocol_tx_wait_message();
static protocol_tx_state protocol_tx_phy_reset();
//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 uint8_t _tx_messageidcounter;
static bool messagePending();
//Reads a message off the queue into the temp message
static void getMessage();
static union pd_msg temp_msg;
static uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
};
#endif /* PDB_PROTOCOL_TX_H */

View File

@@ -1,72 +0,0 @@
/*
* tcpm_driver.c
*
* Created: 11/11/2017 18:42:26
* Author: jason
*/
#include "tcpm_driver.h"
#include "I2C_Wrapper.hpp"
#include "I2CBB.hpp"
extern const struct tcpc_config_t tcpc_config;
#define STATUS_OK 0
/* I2C wrapper functions - get I2C port / slave addr from config struct. */
int tcpc_write(int reg, int val) {
I2CBB::Mem_Write(tcpc_config.i2c_slave_addr, reg, (uint8_t*) &val, 1);
return STATUS_OK;
}
int tcpc_write16(int reg, int val) {
uint8_t data[2];
data[0] = (0xFF) & val;
data[1] = (0xFF) & (val >> 8);
I2CBB::Mem_Write(tcpc_config.i2c_slave_addr, reg, (uint8_t*) data, 2);
return STATUS_OK;
}
int tcpc_read(int reg, int *val) {
uint8_t data[1];
I2CBB::Mem_Read(tcpc_config.i2c_slave_addr, reg, (uint8_t*) data, 1);
*val = data[0];
return STATUS_OK;
}
int tcpc_read16(int reg, int *val) {
uint8_t data[2];
I2CBB::Mem_Write(tcpc_config.i2c_slave_addr, reg, (uint8_t*) data, 2);
*val = data[0];
*val |= (data[1] << 8);
return STATUS_OK;
}
int tcpc_xfer(const uint8_t *out, int out_size, uint8_t *in, int in_size,
int flags) {
// Write out the I2C port to the given slave address
// Write out the out byte array to the device (sending a stop if the flag is set)
// Then issue a read from the device
if (flags & I2C_XFER_STOP) {
//Issuing a stop between the requests
//Send as a Tx followed by a Rx
I2CBB::Transmit(tcpc_config.i2c_slave_addr, (uint8_t*) out,
out_size);
I2CBB::Receive(tcpc_config.i2c_slave_addr, in, in_size);
} else {
//issue as a continious transmit & recieve
I2CBB::TransmitReceive(tcpc_config.i2c_slave_addr, (uint8_t*) out,
out_size, in, in_size);
}
return STATUS_OK;
}
uint8_t fusb302_detect() {
//Probe the I2C bus for its address
return I2CBB::probe(fusb302_I2C_SLAVE_ADDR);
}

View File

@@ -1,22 +0,0 @@
/*
* tcpm_driver.h
*
* Created: 11/11/2017 18:42:39
* Author: jason
*/
#ifndef TCPM_DRIVER_H_
#define TCPM_DRIVER_H_
#include <stdint.h>
// USB-C Stuff
#include "USBC_TCPM/tcpm.h"
#include "FUSB302.h"
#define CONFIG_USB_PD_PORT_COUNT 1
extern struct i2c_master_module i2c_master_instance;
#endif /* TCPM_DRIVER_H_ */

View File

@@ -1,258 +0,0 @@
/*
* usb_pd_driver.c
*
* Created: 11/11/2017 23:55:12
* Author: jason
*/
#include "usb_pd_driver.h"
#include "USBC_PD/usb_pd.h"
#include <string.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
#endif
extern struct tc_module tc_instance;
extern uint32_t g_us_timestamp_upper_32bit;
uint32_t pd_task_set_event(uint32_t event, int wait_for_reply) {
switch (event) {
case PD_EVENT_TX:
break;
default:
break;
}
return 0;
}
const uint32_t pd_src_pdo[] = { PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS), };
const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
const uint32_t pd_snk_pdo[] = { PDO_FIXED(5000, 1000, PDO_FIXED_FLAGS),
PDO_FIXED(9000, 1500, PDO_FIXED_FLAGS), PDO_FIXED(12000, 3500,
PDO_FIXED_FLAGS), };
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
void pd_set_input_current_limit(uint32_t max_ma, uint32_t supply_voltage) {
}
int pd_is_valid_input_voltage(int mv) {
return 1;
}
int pd_snk_is_vbus_provided() {
return 1;
}
timestamp_t get_time(void) {
timestamp_t t;
//TODO
//
// system_interrupt_enter_critical_section();
// t.le.lo = tc_get_count_value(&tc_instance);
// t.le.hi = g_us_timestamp_upper_32bit;
// system_interrupt_leave_critical_section();
return t;
}
void pd_power_supply_reset() {
return;
}
void pd_execute_data_swap(int data_role) {
/* Do nothing */
}
int pd_check_data_swap(int data_role) {
// Never allow data swap
return 0;
}
int pd_check_power_swap() {
/* Always refuse power swap */
return 0;
}
int pd_board_checks(void) {
return EC_SUCCESS;
}
int pd_set_power_supply_ready() {
//Tells other device if we can supply power
return EC_SUCCESS; /* we are ready */
}
void pd_transition_voltage(int idx) {
/* No-operation: we are always 5V */
#if 0
timestamp_t deadline;
uint32_t mv = src_pdo_charge[idx - 1].mv;
/* Is this a transition to a new voltage? */
if (charge_port_is_active() && vbus[CHG].mv != mv) {
/*
* Alter voltage limit on charge port, this should cause
* the port to select the desired PDO.
*/
pd_set_external_voltage_limit(CHG, mv);
/* Wait for CHG transition */
deadline.val = get_time().val + PD_T_PS_TRANSITION;
CPRINTS("Waiting for CHG port transition");
while (charge_port_is_active() &&
vbus[CHG].mv != mv &&
get_time().val < deadline.val)
msleep(10);
if (vbus[CHG].mv != mv) {
CPRINTS("Missed CHG transition, resetting DUT");
pd_power_supply_reset(DUT);
return;
}
CPRINTS("CHG transitioned");
}
vbus[DUT].mv = vbus[CHG].mv;
vbus[DUT].ma = vbus[CHG].ma;
#endif // if 0
}
void pd_check_dr_role(int dr_role, int flags) {
#if 0
/* If UFP, try to switch to DFP */
if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_UFP)
pd_request_data_swap(port);
#endif
}
void pd_check_pr_role(int pr_role, int flags) {
#if 0
/*
* If partner is dual-role power and dualrole toggling is on, consider
* if a power swap is necessary.
*/
if ((flags & PD_FLAGS_PARTNER_DR_POWER) &&
pd_get_dual_role() == PD_DRP_TOGGLE_ON) {
/*
* If we are a sink and partner is not externally powered, then
* swap to become a source. If we are source and partner is
* externally powered, swap to become a sink.
*/
int partner_extpower = flags & PD_FLAGS_PARTNER_EXTPOWER;
if ((!partner_extpower && pr_role == PD_ROLE_SINK) ||
(partner_extpower && pr_role == PD_ROLE_SOURCE))
pd_request_power_swap(port);
}
#endif // if 0
}
void pd_process_source_cap_callback(int cnt, uint32_t *src_caps) {
char str[256];
int i;
uint32_t ma, mv, pdo;
for (i = 0; i < cnt; i++) {
pd_extract_pdo_power(src_caps[i], &ma, &mv);
//Charger can supply power at mv & mA
//TODO we want to ask for the charger to select the closest to our ideal (12V)
//And fall back to 9V
}
//TODO Handle information on supported voltages?
}
/* ----------------- Vendor Defined Messages ------------------ */
/* Holds valid object position (opos) for entered mode */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
1, /* data caps as USB device */
IDH_PTYPE_PERIPH, /* Alternate mode */
0, /* Does not support alt modes */
USB_VID_GOOGLE);
const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV);
const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS,
CONFIG_USB_PD_IDENTITY_SW_VERS, 0, 0, 0, 0, /* SS[TR][12] */
0, /* Vconn power */
0, /* Vconn power required */
1, /* Vbus power required */
AMA_USBSS_U2_ONLY /* USB 2.0 support */);
static int svdm_response_identity(uint32_t *payload) {
payload[VDO_I(IDH)] = vdo_idh;
payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
payload[VDO_I(PRODUCT)] = vdo_product;
payload[VDO_I(AMA)] = vdo_ama;
return VDO_I(AMA) + 1;
}
//No custom svdm
static int svdm_response_svids(uint32_t *payload) {
payload[1] = 0;
return 2;
}
#define OPOS_DP 1
#define OPOS_GFU 1
const uint32_t vdo_dp_modes[1] = { VDO_MODE_DP(0, /* UFP pin cfg supported : none */
MODE_DP_PIN_C, /* DFP pin cfg supported */
1, /* no usb2.0 signalling in AMode */
CABLE_PLUG, /* its a plug */
MODE_DP_V13, /* DPv1.3 Support, no Gen2 */
MODE_DP_SNK) /* Its a sink only */
};
static int svdm_response_modes(uint32_t *payload) {
return 0; /* nak */
}
static int dp_status(uint32_t *payload) {
int opos = PD_VDO_OPOS(payload[0]);
int hpd = 0; // gpio_get_level(GPIO_DP_HPD);
if (opos != OPOS_DP)
return 0; /* nak */
payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */
(hpd == 1), /* HPD_HI|LOW */
0, /* request exit DP */
0, /* request exit USB */
0, /* MF pref */
0, //gpio_get_level(GPIO_PD_SBU_ENABLE),
0, /* power low */
0x2);
return 2;
}
static int dp_config(uint32_t *payload) {
return 1;
}
static int svdm_enter_mode(uint32_t *payload) {
int rv = 0; /* will generate a NAK */
return rv;
}
int pd_alt_mode(uint16_t svid) {
return 0;
}
static int svdm_exit_mode(uint32_t *payload) {
return 1; /* Must return ACK */
}
static struct amode_fx dp_fx = { .status = &dp_status, .config = &dp_config, };
const struct svdm_response svdm_rsp = { &svdm_response_identity,
&svdm_response_svids, &svdm_response_modes, &svdm_enter_mode,
&svdm_exit_mode, &dp_fx, };

View File

@@ -1,104 +0,0 @@
/*
* usb_pd_driver.h
*
* Created: 11/11/2017 23:55:25
* Author: jason
*/
#ifndef USB_PD_DRIVER_H_
#define USB_PD_DRIVER_H_
#include "USBC_PD/usb_pd.h"
#include "cmsis_os.h"
#include <stdint.h>
//#define CONFIG_BBRAM
#define CONFIG_CHARGE_MANAGER
//#define CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP
//#define CONFIG_USBC_VCONN_SWAP
//#define CONFIG_USB_PD_ALT_MODE
//#define CONFIG_USB_PD_CHROMEOS
//#define CONFIG_USB_PD_DUAL_ROLE
//#define CONFIG_USB_PD_GIVE_BACK
//#define CONFIG_USB_PD_SIMPLE_DFP
//#define CONFIG_USB_PD_TCPM_TCPCI
#define PD_PREFER_HIGH_VOLTAGE
/* Default pull-up value on the USB-C ports when they are used as source. */
#define CONFIG_USB_PD_PULLUP TYPEC_RP_USB
/* Override PD_ROLE_DEFAULT in usb_pd.h */
#define PD_ROLE_DEFAULT(port) (PD_ROLE_SINK)
/* Don't automatically change roles */
#undef CONFIG_USB_PD_INITIAL_DRP_STATE
#define CONFIG_USB_PD_INITIAL_DRP_STATE PD_DRP_FREEZE
/* Prefer higher voltage, and more importantly, lower current */
#define PD_PREFER_HIGH_VOLTAGE
//#define PD_PREFER_LOW_VOLTAGE
/* board specific type-C power constants */
/*
* delay to turn on the power supply max is ~16ms.
* delay to turn off the power supply max is about ~180ms.
*/
#define PD_POWER_SUPPLY_TURN_ON_DELAY 10000 /* us */
#define PD_POWER_SUPPLY_TURN_OFF_DELAY 20000 /* us */
/* Define typical operating power and max power */
#define PD_OPERATING_POWER_MW 15000
#define PD_MAX_POWER_MW 100000
#define PD_MAX_CURRENT_MA 5000
#define PD_MAX_VOLTAGE_MV 20000
#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\
PDO_FIXED_COMM_CAP)
/* USB configuration */
#define CONFIG_USB_PID 0x502f // Stolen, so should change for anything useful
#define CONFIG_USB_BCD_DEV 0x0001 /* v 0.01 */
/* Optional features */
#define CONFIG_USB_PD_IDENTITY_HW_VERS 1
#define CONFIG_USB_PD_IDENTITY_SW_VERS 1
#define usleep(us) (osDelay(1))
#define msleep(us) (osDelay(us))
typedef union {
uint64_t val;
struct {
uint32_t lo;
uint32_t hi;
} le /* little endian words */;
} timestamp_t;
uint32_t pd_task_set_event(uint32_t event, int wait_for_reply);
void pd_power_supply_reset(int port);
// Get the current timestamp from the system timer.
timestamp_t get_time(void);
/* Standard macros / definitions */
#ifndef MAX
#define MAX(a, b) \
({ \
__typeof__(a) temp_a = (a); \
__typeof__(b) temp_b = (b); \
\
temp_a > temp_b ? temp_a : temp_b; \
})
#endif
#ifndef MIN
#define MIN(a, b) \
({ \
__typeof__(a) temp_a = (a); \
__typeof__(b) temp_b = (b); \
\
temp_a < temp_b ? temp_a : temp_b; \
})
#endif
#endif /* USB_PD_DRIVER_H_ */

View File

@@ -1,173 +1,171 @@
/*
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
This file is part of the FreeRTOS distribution.
FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
***************************************************************************
>>! NOTE: The modification to the GPL is included to allow you to !<<
>>! distribute a combined work that includes FreeRTOS without being !<<
>>! obliged to provide the source code for proprietary components !<<
>>! outside of the FreeRTOS kernel. !<<
***************************************************************************
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. Full license text is available on the following
link: http://www.freertos.org/a00114.html
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
the FAQ page "My application does not run, what could be wrong?". Have you
defined configASSERT()?
http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.
http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible. Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html.
*----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
/* USER CODE END Includes */
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)100)
#define configMAX_PRIORITIES ( 4 )
#define configMINIMAL_STACK_SIZE ((uint16_t)256)
#define configTOTAL_HEAP_SIZE ((size_t)1024*14) /*Currently use about 9000*/
#define configMAX_TASK_NAME_LEN ( 24 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configCHECK_FOR_STACK_OVERFLOW 2 /*Bump this to 2 during development and bug hunting*/
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 0
#define INCLUDE_vTaskDelayUntil 0
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
/* #define xPortSysTickHandler SysTick_Handler */
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */
/*
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
This file is part of the FreeRTOS distribution.
FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
***************************************************************************
>>! NOTE: The modification to the GPL is included to allow you to !<<
>>! distribute a combined work that includes FreeRTOS without being !<<
>>! obliged to provide the source code for proprietary components !<<
>>! outside of the FreeRTOS kernel. !<<
***************************************************************************
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. Full license text is available on the following
link: http://www.freertos.org/a00114.html
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
the FAQ page "My application does not run, what could be wrong?". Have you
defined configASSERT()?
http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.
http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible. Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html.
*----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
/* USER CODE END Includes */
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)100)
#define configMAX_PRIORITIES ( 6 )
#define configMINIMAL_STACK_SIZE ((uint16_t)256)
#define configTOTAL_HEAP_SIZE ((size_t)1024*14) /*Currently use about 9000*/
#define configMAX_TASK_NAME_LEN ( 24 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_TIMERS 1 /* Required for PD 10ms callback for PPS mode*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configCHECK_FOR_STACK_OVERFLOW 2 /*Bump this to 2 during development and bug hunting*/
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 0
#define INCLUDE_vTaskDelayUntil 0
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#if configUSE_TIMERS
#define configTIMER_TASK_PRIORITY 2
#define configTIMER_QUEUE_LENGTH 8
#define configTIMER_TASK_STACK_DEPTH (512/4)
#endif
#endif /* FREERTOS_CONFIG_H */

View File

@@ -22,6 +22,16 @@ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
/* place for user code */
}
static StaticTask_t xTimerTaskTCBBuffer;
static StackType_t xTimerStack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) {
*ppxTimerTaskTCBBuffer = &xTimerTaskTCBBuffer;
*ppxTimerTaskStackBuffer = &xTimerStack[0];
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
/* place for user code */
}
void vApplicationStackOverflowHook(xTaskHandle *pxTask,
signed portCHAR *pcTaskName) {