Skip to content

Commit

Permalink
Merge pull request #67 from qmsk/usb-pd-st4500-sink
Browse files Browse the repository at this point in the history
ST4500 USB-PD Sink
  • Loading branch information
SpComb committed Jan 26, 2024
2 parents 743c69d + a40fbea commit 9fd841f
Show file tree
Hide file tree
Showing 23 changed files with 1,944 additions and 1 deletion.
5 changes: 5 additions & 0 deletions components/usb_pd_sink/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS .
INCLUDE_DIRS "include"
PRIV_REQUIRES logging
)
100 changes: 100 additions & 0 deletions components/usb_pd_sink/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
menu "STUSB4500"
config STUSB4500_SNK_PDO_NUMB
int "Number of sink PDOs"
range 1 3
default 3

config STUSB4500_V_SNK_PDO2
int "Voltage value for SNK_PDO2 (mV)"
range 5000 20000
default 15000

config STUSB4500_V_SNK_PDO3
int "Voltage value for SNK_PDO3 (mV)"
range 5000 20000
default 20000

config STUSB4500_I_SNK_PDO1
int "Current value for SNK_PDO1 (mA)"
range 500 5000
default 1500

config STUSB4500_I_SNK_PDO2
int "Current value for SNK_PDO2 (mA)"
range 500 5000
default 1500

config STUSB4500_I_SNK_PDO3
int "Current value for SNK_PDO3 (mA)"
range 500 5000
default 1000

config STUSB4500_VBUS_DISCH_DISABLE
bool "VBUS discharge deactivation on VBUS_VS_DISCH and DISCH pins"
default 0

config STUSB4500_USB_COMM_CAPABLE
bool "USB 2.0 or 3.x data communication capability by sink system"
default 0

config STUSB4500_SNK_UNCONS_POWER
bool "Unconstrained Power bit setting in capabilities message sent by the sink"
default 0

config STUSB4500_REQ_SRC_CURRENT
bool "Request maximum available source current from source"
default 0

choice STUSB4500_POWER_OK_CFG_CHOICE
prompt "Selects POWER_OK pins configuration"
default STUSB4500_POWER_OK_CFG_2

config STUSB4500_POWER_OK_CFG_1
bool "Configuration 1"

config STUSB4500_POWER_OK_CFG_2
bool "Configuration 2"

config STUSB4500_POWER_OK_CFG_3
bool "Configuration 2"

endchoice

config STUSB4500_POWER_OK_CFG
int "Selects POWER_OK pins configuration"
range 0 3
default 0 if STUSB4500_POWER_OK_CFG_1
default 2 if STUSB4500_POWER_OK_CFG_2
default 3 if STUSB4500_POWER_OK_CFG_3

config STUSB4500_POWER_ONLY_ABOVE_5V
bool "Only enable VBUS_EN_SNK pin when source attached and voltage is negotiated to PDO2/3"
default 0

choice STUSB4500_GPIO_CFG_CHOICE
prompt "Selects POWER_OK pins configuration"
default STUSB4500_GPIO_CFG_ERROR_RECOVERY

config STUSB4500_GPIO_CFG_SW_CTRL_GPIO
bool "SW_CTRL_GPIO"

config STUSB4500_GPIO_CFG_ERROR_RECOVERY
bool "ERROR_RECOVERY"

config STUSB4500_GPIO_CFG_DEBUG
bool "DEBUG"

config STUSB4500_GPIO_CFG_SINK_POWER
bool "SINK_POWER"

endchoice

config STUSB4500_GPIO_CFG
int "Selects GPIO pin configuration"
range 0 3
default 0 if STUSB4500_GPIO_CFG_SW_CTRL_GPIO
default 1 if STUSB4500_GPIO_CFG_ERROR_RECOVERY
default 2 if STUSB4500_GPIO_CFG_DEBUG
default 3 if STUSB4500_GPIO_CFG_SINK_POWER

endmenu
Empty file.
62 changes: 62 additions & 0 deletions components/usb_pd_sink/include/usb_pd_sink.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once

#include <freertos/FreeRTOS.h>
#include <hal/i2c_types.h>
#include <stdio.h>

#define USB_PD_SINK_STUSB4500_I2C_ADDR_BASE 0x28
#define USB_PD_SINK_STUSB4500_I2C_ADDR_MASK 0x03
#define USB_PD_SINK_STUSB4500_I2C_ADDR(addr) (USB_PD_SINK_STUSB4500_I2C_ADDR_BASE | ((addr) & USB_PD_SINK_STUSB4500_I2C_ADDR_MASK))
#define USB_PD_SINK_STUSB4500_I2C_TIMEOUT (1000 / portTICK_RATE_MS)

enum usb_pd_sink_type {
USB_PD_SINK_STUSB4500,
};

struct usb_pd_sink_options {
enum usb_pd_sink_type type;

i2c_port_t i2c_port;
uint8_t i2c_addr;
// gpio_pin_t int_pin;
};

struct usb_pd_sink_status {
uint8_t usb_pd_version_major;
uint8_t usb_pd_version_minor;

bool port_attached;
bool port_power; // USB-PD Sink active
bool vbus_ready; // VBUS at 5V or higher negotiated power level

uint8_t pdo_number; // negotiated source PDO, 0 if no USB-PD
bool pd_capability_mismatch; // failed to match source/sink PDO

unsigned voltage_mV; // negotiated voltage in mV
unsigned operating_current_mA; // negotiated operating current in mA
unsigned maximum_current_mA; // negotiated maximum current in mA
};

struct usb_pd_sink;

int usb_pd_sink_new(struct usb_pd_sink **sinkp, const struct usb_pd_sink_options *options);

/*
* Setup USB-PD Sink using static Kconfig defaults.
*/
int usb_pd_sink_setup(struct usb_pd_sink *sink);

/*
* Start USB-PD Sink.
*/
int usb_pd_sink_start(struct usb_pd_sink *sink);

/*
* Get generic USB-PD Sink info.
*/
int usb_pd_sink_status(struct usb_pd_sink *sink, struct usb_pd_sink_status *status);

/*
* Print detailed USB-PD Sink info.
*/
int usb_pd_sink_print(struct usb_pd_sink *sink, FILE *file);
133 changes: 133 additions & 0 deletions components/usb_pd_sink/stusb4500.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#include "stusb4500.h"
#include "stusb4500_i2c.h"

#include <esp_err.h>

#include <logging.h>

int stusb4500_init(struct stusb4500 *stusb4500, const struct usb_pd_sink_options *options)
{
stusb4500->i2c_port = options->i2c_port;
stusb4500->i2c_addr = USB_PD_SINK_STUSB4500_I2C_ADDR(options->i2c_addr);
stusb4500->i2c_timeout = USB_PD_SINK_STUSB4500_I2C_TIMEOUT;

return 0;
}

int stusb4500_reset(struct stusb4500 *stusb4500)
{
struct stusb4500_reset_ctrl reset_ctrl = { .reset_sw_en = 1 };
struct stusb4500_reset_ctrl reset_ctrl_clear = { .reset_sw_en = 0 };
int err;

if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_RESET_CTRL, &reset_ctrl, sizeof(reset_ctrl)))) {
return err;
}

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_RESET_CTRL, &reset_ctrl, sizeof(reset_ctrl)))) {
return err;
}

// TODO: delay 25ms?
LOG_DEBUG("reset_ctrl: reset_sw_en=%u",
reset_ctrl.reset_sw_en
);

if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_RESET_CTRL, &reset_ctrl_clear, sizeof(reset_ctrl_clear)))) {
return err;
}

return 0;
}

int stusb4500_setup(struct stusb4500 *stusb4500)
{
union stusb4500_nvm nvm = {};
int err;

if ((err = stusb4500_nvm_read(stusb4500, &nvm))) {
LOG_ERROR("stusb4500_nvm_read");
return err;
}

if ((err = stusb4500_nvm_validate(&nvm)) < 0) {
LOG_ERROR("stusb4500_nvm_validate");
return err;
} else if (!err) {
LOG_DEBUG("stusb4500_nvm_validate OK");
} else {
LOG_WARN("stusb4500_nvm_validate");
return err;
}

if ((err = stusb4500_nvm_config(&nvm)) < 0) {
LOG_ERROR("stusb4500_nvm_config");
return err;
} else if (!err) {
LOG_INFO("NVM config OK");
} else {
LOG_WARN("NVM config changed:");

if ((err = stusb4500_nvm_print(&nvm, stderr))) {
LOG_WARN("stusb4500_nvm_print");
}

if ((err = stusb4500_nvm_write(stusb4500, &nvm))) {
LOG_ERROR("stusb4500_nvm_write");
return err;
}
}

return 0;
}

int stusb4500_start(struct stusb4500 *stusb4500)
{
return 0;
}

int stusb4500_status(struct stusb4500 *stusb4500, struct usb_pd_sink_status *status)
{
struct stusb4500_bcd_usbpd_rev_high bcd_usbpd_rev_high;
struct stusb4500_port_status_1 port_status_1;
struct stusb4500_typec_monitoring_status_1 typec_monitoring_status_1;
struct stusb4500_monitoring_ctrl_1 monitoring_ctrl_1;
union stusb4500_rdo_reg_status rdo;
int err;

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_BCD_USBPD_REV_HIGH, &bcd_usbpd_rev_high, sizeof(bcd_usbpd_rev_high)))) {
return err;
}

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_PORT_STATUS_1, &port_status_1, sizeof(port_status_1)))) {
return err;
}

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_TYPEC_MONITORING_STATUS_1, &typec_monitoring_status_1, sizeof(typec_monitoring_status_1)))) {
return err;
}

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_MONITORING_CTRL_1, &monitoring_ctrl_1, sizeof(monitoring_ctrl_1)))) {
return err;
}

if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_RDO_REG_STATUS_0, &rdo, sizeof(rdo)))) {
return err;
}

status->usb_pd_version_major = bcd_usbpd_rev_high.major;
status->usb_pd_version_minor = bcd_usbpd_rev_high.minor;

status->port_attached = (port_status_1.attach == 1);
status->port_power = (port_status_1.power_mode == 0);
status->vbus_ready = (typec_monitoring_status_1.vbus_ready == 1);

status->pdo_number = rdo.fixed_supply.object_position;
status->pd_capability_mismatch = rdo.fixed_supply.capability_mismatch;

status->voltage_mV = monitoring_ctrl_1.voltage * 100; // 100mV -> mV
status->operating_current_mA = rdo.fixed_supply.operating_current * 10; // 10mA -> mA
status->maximum_current_mA = rdo.fixed_supply.max_current * 10; // 10mA -> mA

return 0;
}
34 changes: 34 additions & 0 deletions components/usb_pd_sink/stusb4500.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "stusb4500_i2c.h"
#include "stusb4500_nvm.h"

#include <usb_pd_sink.h>

struct stusb4500 {
i2c_port_t i2c_port;
uint8_t i2c_addr;
TickType_t i2c_timeout;
};

int stusb4500_init(struct stusb4500 *stusb4500, const struct usb_pd_sink_options *options);
int stusb4500_reset(struct stusb4500 *stusb4500);
int stusb4500_setup(struct stusb4500 *stusb4500);
int stusb4500_start(struct stusb4500 *stusb4500);
int stusb4500_status(struct stusb4500 *stusb4500, struct usb_pd_sink_status *status);

int stusb4500_print(struct stusb4500 *stusb4500, FILE *file);

int stusb4500_i2c_read(struct stusb4500 *stusb4500, enum stusb4500_i2c_register reg, void *buf, size_t size);
int stusb4500_i2c_write(struct stusb4500 *stusb4500, enum stusb4500_i2c_register reg, const void *buf, size_t size);

int stusb4500_nvm_read(struct stusb4500 *stusb4500, union stusb4500_nvm *nvm);
int stusb4500_nvm_write(struct stusb4500 *stusb4500, const union stusb4500_nvm *nvm);

/* Return >0 if conents invalid */
int stusb4500_nvm_validate(union stusb4500_nvm *nvm);

/* Return 0 if unchanged, >0 if modified, <0 on errors */
int stusb4500_nvm_config(union stusb4500_nvm *nvm);

int stusb4500_nvm_print(const union stusb4500_nvm *nvm, FILE *file);
Loading

0 comments on commit 9fd841f

Please sign in to comment.