Moving PD framework + big thread cleanup
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 |= 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, ®);
|
||||
|
||||
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, ®);
|
||||
/* 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, ®);
|
||||
|
||||
/* 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, ®);
|
||||
|
||||
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, ®);
|
||||
|
||||
/* 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, ®);
|
||||
|
||||
/* 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, ®);
|
||||
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, ®);
|
||||
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 |= 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 &= ~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 &= ~(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 &= ~TCPC_REG_CONTROL2_TOGGLE;
|
||||
tcpc_write( TCPC_REG_CONTROL2, reg);
|
||||
|
||||
/* enable pull-downs, disable pullups */
|
||||
tcpc_read( TCPC_REG_SWITCHES0, ®);
|
||||
|
||||
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 &= ~TCPC_REG_CONTROL2_TOGGLE;
|
||||
tcpc_write( TCPC_REG_CONTROL2, reg);
|
||||
|
||||
/* Ensure manual switches are opened */
|
||||
tcpc_read( TCPC_REG_SWITCHES0, ®);
|
||||
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, ®);
|
||||
|
||||
/* 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, ®);
|
||||
|
||||
/* 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, ®);
|
||||
|
||||
/* 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 &= ~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, ®);
|
||||
|
||||
/* 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, ®))
|
||||
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, ®))
|
||||
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 & 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 |= 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 |= TCPC_REG_CONTROL1_BIST_MODE2;
|
||||
tcpc_write( TCPC_REG_CONTROL1, reg);
|
||||
|
||||
tcpc_read( TCPC_REG_CONTROL0, ®);
|
||||
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 &= ~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, ®);
|
||||
|
||||
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, ®);
|
||||
|
||||
/* 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
|
||||
};
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 */
|
||||
204
workspace/TS100/Core/Drivers/FUSB302/fusb302b.cpp
Normal file
204
workspace/TS100/Core/Drivers/FUSB302/fusb302b.cpp
Normal 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);
|
||||
|
||||
}
|
||||
303
workspace/TS100/Core/Drivers/FUSB302/fusb302b.h
Normal file
303
workspace/TS100/Core/Drivers/FUSB302/fusb302b.h
Normal 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 */
|
||||
38
workspace/TS100/Core/Drivers/FUSB302/fusbpd.cpp
Normal file
38
workspace/TS100/Core/Drivers/FUSB302/fusbpd.cpp
Normal 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();
|
||||
}
|
||||
18
workspace/TS100/Core/Drivers/FUSB302/fusbpd.h
Normal file
18
workspace/TS100/Core/Drivers/FUSB302/fusbpd.h
Normal 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_ */
|
||||
148
workspace/TS100/Core/Drivers/FUSB302/hard_reset.cpp
Normal file
148
workspace/TS100/Core/Drivers/FUSB302/hard_reset.cpp
Normal 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;
|
||||
}
|
||||
63
workspace/TS100/Core/Drivers/FUSB302/hard_reset.h
Normal file
63
workspace/TS100/Core/Drivers/FUSB302/hard_reset.h
Normal 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 */
|
||||
97
workspace/TS100/Core/Drivers/FUSB302/int_n.cpp
Normal file
97
workspace/TS100/Core/Drivers/FUSB302/int_n.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
56
workspace/TS100/Core/Drivers/FUSB302/int_n.h
Normal file
56
workspace/TS100/Core/Drivers/FUSB302/int_n.h
Normal 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 */
|
||||
402
workspace/TS100/Core/Drivers/FUSB302/pd.h
Normal file
402
workspace/TS100/Core/Drivers/FUSB302/pd.h
Normal 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 */
|
||||
47
workspace/TS100/Core/Drivers/FUSB302/pdb_conf.h
Normal file
47
workspace/TS100/Core/Drivers/FUSB302/pdb_conf.h
Normal 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 */
|
||||
55
workspace/TS100/Core/Drivers/FUSB302/pdb_msg.h
Normal file
55
workspace/TS100/Core/Drivers/FUSB302/pdb_msg.h
Normal 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 */
|
||||
799
workspace/TS100/Core/Drivers/FUSB302/policy_engine.cpp
Normal file
799
workspace/TS100/Core/Drivers/FUSB302/policy_engine.cpp
Normal 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() {
|
||||
}
|
||||
197
workspace/TS100/Core/Drivers/FUSB302/policy_engine.h
Normal file
197
workspace/TS100/Core/Drivers/FUSB302/policy_engine.h
Normal 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 */
|
||||
305
workspace/TS100/Core/Drivers/FUSB302/policy_engine_user.cpp
Normal file
305
workspace/TS100/Core/Drivers/FUSB302/policy_engine_user.cpp
Normal 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;
|
||||
}
|
||||
171
workspace/TS100/Core/Drivers/FUSB302/protocol_rx.cpp
Normal file
171
workspace/TS100/Core/Drivers/FUSB302/protocol_rx.cpp
Normal 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;
|
||||
}
|
||||
61
workspace/TS100/Core/Drivers/FUSB302/protocol_rx.h
Normal file
61
workspace/TS100/Core/Drivers/FUSB302/protocol_rx.h
Normal 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 */
|
||||
285
workspace/TS100/Core/Drivers/FUSB302/protocol_tx.cpp
Normal file
285
workspace/TS100/Core/Drivers/FUSB302/protocol_tx.cpp
Normal 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;
|
||||
}
|
||||
91
workspace/TS100/Core/Drivers/FUSB302/protocol_tx.h
Normal file
91
workspace/TS100/Core/Drivers/FUSB302/protocol_tx.h
Normal 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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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, };
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user