diff --git a/components/usb_pd_sink/CMakeLists.txt b/components/usb_pd_sink/CMakeLists.txt new file mode 100644 index 00000000..9a27cb8e --- /dev/null +++ b/components/usb_pd_sink/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS . + INCLUDE_DIRS "include" + PRIV_REQUIRES logging +) diff --git a/components/usb_pd_sink/Kconfig b/components/usb_pd_sink/Kconfig new file mode 100644 index 00000000..a3f4a5db --- /dev/null +++ b/components/usb_pd_sink/Kconfig @@ -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 diff --git a/components/usb_pd_sink/component.mk b/components/usb_pd_sink/component.mk new file mode 100644 index 00000000..e69de29b diff --git a/components/usb_pd_sink/include/usb_pd_sink.h b/components/usb_pd_sink/include/usb_pd_sink.h new file mode 100644 index 00000000..86fbf688 --- /dev/null +++ b/components/usb_pd_sink/include/usb_pd_sink.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +#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); diff --git a/components/usb_pd_sink/stusb4500.c b/components/usb_pd_sink/stusb4500.c new file mode 100644 index 00000000..3609fe67 --- /dev/null +++ b/components/usb_pd_sink/stusb4500.c @@ -0,0 +1,133 @@ +#include "stusb4500.h" +#include "stusb4500_i2c.h" + +#include + +#include + +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; +} diff --git a/components/usb_pd_sink/stusb4500.h b/components/usb_pd_sink/stusb4500.h new file mode 100644 index 00000000..b668a0a5 --- /dev/null +++ b/components/usb_pd_sink/stusb4500.h @@ -0,0 +1,34 @@ +#pragma once + +#include "stusb4500_i2c.h" +#include "stusb4500_nvm.h" + +#include + +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); diff --git a/components/usb_pd_sink/stusb4500_i2c.c b/components/usb_pd_sink/stusb4500_i2c.c new file mode 100644 index 00000000..652eec96 --- /dev/null +++ b/components/usb_pd_sink/stusb4500_i2c.c @@ -0,0 +1,123 @@ +#include "stusb4500.h" +#include "stusb4500_i2c.h" + +#include +#include + +#include + +int stusb4500_i2c_read(struct stusb4500 *stusb4500, enum stusb4500_i2c_register cmd, void *buf, size_t size) +{ + uint8_t buffer[I2C_LINK_RECOMMENDED_SIZE(2)] = { 0 }; + i2c_cmd_handle_t handle; + esp_err_t err; + + if (!(handle = i2c_cmd_link_create_static(buffer, sizeof(buffer)))) { + LOG_ERROR("i2c_cmd_link_create_static"); + return -1; + } + + if ((err = i2c_master_start(handle))) { + LOG_ERROR("i2c_master_start: %s", esp_err_to_name(err)); + goto error; + } + + LOG_DEBUG("i2c_addr=%02x cmd=%02x", stusb4500->i2c_addr, cmd); + + if ((err = i2c_master_write_byte(handle, stusb4500->i2c_addr << 1 | I2C_MASTER_WRITE, true))) { + LOG_ERROR("i2c_master_write_byte: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_write_byte(handle, cmd, true))) { + LOG_ERROR("i2c_master_write_byte: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_start(handle))) { + LOG_ERROR("i2c_master_start: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_write_byte(handle, stusb4500->i2c_addr << 1 | I2C_MASTER_READ, true))) { + LOG_ERROR("i2c_master_write_byte: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_read(handle, buf, size, I2C_MASTER_LAST_NACK))) { + LOG_ERROR("i2c_master_write: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_stop(handle))) { + LOG_ERROR("i2c_master_stop: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_cmd_begin(stusb4500->i2c_port, handle, stusb4500->i2c_timeout))) { + LOG_ERROR("i2c_master_cmd_begin: %s", esp_err_to_name(err)); + goto error; + } + + for (int i = 0; i < size; i++) { + LOG_DEBUG("\t%02x", ((uint8_t *) buf)[i]); + } + +error: + i2c_cmd_link_delete_static(handle); + + return err; +} + +int stusb4500_i2c_write(struct stusb4500 *stusb4500, enum stusb4500_i2c_register cmd, const void *buf, size_t size) +{ + uint8_t buffer[I2C_LINK_RECOMMENDED_SIZE(1)] = { 0 }; + i2c_cmd_handle_t handle; + esp_err_t err; + + if (!(handle = i2c_cmd_link_create_static(buffer, sizeof(buffer)))) { + LOG_ERROR("i2c_cmd_link_create_static"); + return -1; + } + + if ((err = i2c_master_start(handle))) { + LOG_ERROR("i2c_master_start: %s", esp_err_to_name(err)); + goto error; + } + + LOG_DEBUG("i2c_addr=%02x cmd=%02x", stusb4500->i2c_addr, cmd); + + if ((err = i2c_master_write_byte(handle, stusb4500->i2c_addr << 1 | I2C_MASTER_WRITE, true))) { + LOG_ERROR("i2c_master_write_byte: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_write_byte(handle, cmd, true))) { + LOG_ERROR("i2c_master_write_byte: %s", esp_err_to_name(err)); + goto error; + } + + for (int i = 0; i < size; i++) { + LOG_DEBUG("\t%02x", ((uint8_t *) buf)[i]); + } + + if ((err = i2c_master_write(handle, buf, size, true))) { + LOG_ERROR("i2c_master_write: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_stop(handle))) { + LOG_ERROR("i2c_master_stop: %s", esp_err_to_name(err)); + goto error; + } + + if ((err = i2c_master_cmd_begin(stusb4500->i2c_port, handle, stusb4500->i2c_timeout))) { + LOG_ERROR("i2c_master_cmd_begin: %s", esp_err_to_name(err)); + goto error; + } + +error: + i2c_cmd_link_delete_static(handle); + + return err; +} diff --git a/components/usb_pd_sink/stusb4500_i2c.h b/components/usb_pd_sink/stusb4500_i2c.h new file mode 100644 index 00000000..af42c7e0 --- /dev/null +++ b/components/usb_pd_sink/stusb4500_i2c.h @@ -0,0 +1,361 @@ +#pragma once + +#include + +#define STUSB4500_DPM_SNK_PDO(i) (STUSB4500_DPM_SNK_PDO1_0 + 4 * (i)) +#define STUSB4500_DPM_SNK_PDO_COUNT 3 + +enum stusb4500_i2c_register { + STUSB4500_BCD_TYPEC_REV_LOW = 0x06, + STUSB4500_BCD_TYPEC_REV_HIGH = 0x07, + STUSB4500_BCD_USBPD_REV_LOW = 0x08, + STUSB4500_BCD_USBPD_REV_HIGH = 0x09, + STUSB4500_DEVICE_CAPAB_HIGH = 0x0A, + STUSB4500_ALERT_STATUS_1 = 0x0B, + STUSB4500_ALERT_STATUS_1_MASK = 0x0C, + STUSB4500_PORT_STATUS_0 = 0x0D, + STUSB4500_PORT_STATUS_1 = 0x0E, + STUSB4500_TYPEC_MONITORING_STATUS_0 = 0x0F, + STUSB4500_TYPEC_MONITORING_STATUS_1 = 0x10, + STUSB4500_CC_STATUS = 0x11, + STUSB4500_CC_HW_FAULT_STATUS_0 = 0x12, + STUSB4500_CC_HW_FAULT_STATUS_1 = 0x13, + STUSB4500_PD_TYPEC_STATUS = 0x14, + STUSB4500_TYPEC_STATUS = 0x15, + STUSB4500_PRT_STATUS = 0x16, + + STUSB4500_PD_COMMAND_CTRL = 0x1A, + + STUSB4500_MONITORING_CTRL_0 = 0x20, + STUSB4500_MONITORING_CTRL_1 = 0x21, // undocumented + STUSB4500_MONITORING_CTRL_2 = 0x22, + STUSB4500_RESET_CTRL = 0x23, + + STUSB4500_VBUS_DISCHARGE_TIME_CTRL = 0x25, + STUSB4500_VBUS_DISCHARGE_CTRL = 0x26, + STUSB4500_VBUS_CTRL = 0x27, + + STUSB4500_PE_FSM = 0x29, + + STUSB4500_DEVICE_ID = 0x2F, + + STUSB4500_RW_BUFFER_0 = 0x53, // NVM + STUSB4500_RW_BUFFER_1, + STUSB4500_RW_BUFFER_2, + STUSB4500_RW_BUFFER_3, + STUSB4500_RW_BUFFER_4, + STUSB4500_RW_BUFFER_5, + STUSB4500_RW_BUFFER_6, + STUSB4500_RW_BUFFER_7, + + STUSB4500_DPM_PDO_NUMB = 0x70, + STUSB4500_DPM_SNK_PDO1_0 = 0x85, + STUSB4500_DPM_SNK_PDO1_1 = 0x86, + STUSB4500_DPM_SNK_PDO1_2 = 0x87, + STUSB4500_DPM_SNK_PDO1_3 = 0x88, + STUSB4500_DPM_SNK_PDO2_0 = 0x89, + STUSB4500_DPM_SNK_PDO2_1 = 0x8A, + STUSB4500_DPM_SNK_PDO2_2 = 0x8B, + STUSB4500_DPM_SNK_PDO2_3 = 0x8C, + STUSB4500_DPM_SNK_PDO3_0 = 0x8D, + STUSB4500_DPM_SNK_PDO3_1 = 0x8E, + STUSB4500_DPM_SNK_PDO3_2 = 0x8F, + STUSB4500_DPM_SNK_PDO3_3 = 0x90, + STUSB4500_RDO_REG_STATUS_0 = 0x91, + STUSB4500_RDO_REG_STATUS_1 = 0x92, + STUSB4500_RDO_REG_STATUS_2 = 0x93, + STUSB4500_RDO_REG_STATUS_3 = 0x94, + STUSB4500_FTP_CUST_PASSWORD = 0x95, // NVM + STUSB4500_FTP_CTRL_0 = 0x96, // NVM + STUSB4500_FTP_CTRL_1 = 0x97, // NVM +}; + +enum stusb4500_port_status_attached_device { + STUSB4500_PORT_STATUS_ATTACHED_DEVICE_NONE = 0b000, + STUSB4500_PORT_STATUS_ATTACHED_DEVICE_SINK = 0b001, + STUSB4500_PORT_STATUS_ATTACHED_DEVICE_DEBUG = 0b011, +}; + +enum stusb4500_typec_fsm_state { + STUSB4500_TYPEC_FSM_STATE_UNATTACHED_SNK = 0b00000, + STUSB4500_TYPEC_FSM_STATE_ATTACHWAIT_SNK = 0b00001, + STUSB4500_TYPEC_FSM_STATE_ATTACHED_SNK = 0b00010, + STUSB4500_TYPEC_FSM_STATE_DEBUGACCESSORY_SNK = 0b00011, + STUSB4500_TYPEC_FSM_STATE_TRY_SRC = 0b01100, + STUSB4500_TYPEC_FSM_STATE_UNATTACHED_ACCESSORY = 0b01101, + STUSB4500_TYPEC_FSM_STATE_ATTACHWAIT_ACCESSORY = 0b01110, + STUSB4500_TYPEC_FSM_STATE_TYPEC_ERRORRECOVERY = 0b10011, +}; + +enum stusb4500_pdo_type { + STUSB4500_PDO_TYPE_FIXED_SUPPLY = 0b00, + STUSB4500_PDO_TYPE_BATTERY = 0b01, + STUSB4500_PDO_TYPE_VARIABLE_SUPPLY = 0b10, + // Augmented Power Data Object (APDO) = 0b11, // XXX: PD 3.0 +}; + +struct stusb4500_i2c_rev { + // STUSB4500_BCD_TYPEC_REV_LOW + struct stusb4500_bcd_typec_rev_low { + uint8_t minor : 4; + uint8_t major : 4; + } bcd_typec_rev_low; + + // STUSB4500_BCD_TYPEC_REV_HIGH + struct stusb4500_bcd_typec_rev_high { + uint8_t minor : 4; + uint8_t major : 4; + } bcd_typec_rev_high; + + // STUSB4500_BCD_USBPD_REV_LOW + struct stusb4500_bcd_usbpd_rev_low { + uint8_t minor : 4; + uint8_t major : 4; + } bcd_usbpd_rev_low; + + struct stusb4500_bcd_usbpd_rev_high { + uint8_t minor : 4; + uint8_t major : 4; + } bcd_usbpd_rev_high; +}; + +struct stusb4500_device_capab_high { + uint8_t device_capab_high : 8; // not used +}; + +struct stusb4500_i2c_alert_status { + struct stusb4500_alert_status_1 { + uint8_t reserved0 : 1; + uint8_t prt_status_al : 1; + uint8_t reserved2 : 1; + uint8_t pd_typec_status_al : 1; + uint8_t cc_hw_fault_status_al : 1; + uint8_t typec_monitoring_status_al : 1; + uint8_t port_status_al : 1; + uint8_t reserved7 : 1; + } alert_status_1 ; + + struct stusb4500_alert_status_1_mask { + uint8_t reserved0 : 1; + uint8_t prt_status_al_mask : 1; + uint8_t reserved2 : 1; + uint8_t reserved3 : 1; // XXX: pd_typec_status_al_mask? + uint8_t cc_fault_status_al_mask : 1; + uint8_t typec_monitoring_status_mask : 1; + uint8_t port_status_al_mask : 1; + uint8_t reserved7 : 1; + } alert_status_1_mask; +}; + +struct stusb4500_i2c_status { + struct stusb4500_port_status_0 { + uint8_t attach_trans : 1; // Transition detected in attached state + uint8_t reserved1 : 7; + } port_status_0; + + struct stusb4500_port_status_1 { + uint8_t attach : 1; // 1 = ATTACHED + uint8_t reserved2 : 1; + uint8_t data_mode : 1; // 0 = UFP + uint8_t power_mode : 1; // 0 = device is sinking power + uint8_t reserved3 : 1; + uint8_t attached_device : 3; // STUSB4500_PORT_STATUS_ATTACHED_DEVICE_* + } port_status_1; + + struct stusb4500_typec_monitoring_status_0 { + uint8_t reserved0 : 1; + uint8_t vbus_valid_snk_trans : 1; + uint8_t vbus_vsafe0v_trans : 1; + uint8_t vbus_ready_trans : 1; + uint8_t vbus_low_status : 1; + uint8_t vbus_high_status : 1; + uint8_t reserved6 : 2; + } typec_monitoring_status_0; + + struct stusb4500_typec_monitoring_status_1 { + uint8_t reserved0 : 1; + uint8_t vbus_valid_snk : 1; + uint8_t vbus_vsafe0v : 1; + uint8_t vbus_ready : 1; + uint8_t reserved4 : 4; + } typec_monitoring_status_1; + + struct stusb4500_cc_status { + uint8_t cc1_state : 1; + uint8_t cc2_state : 1; + uint8_t connect_result : 1; + uint8_t looking_4_connection : 1; + uint8_t reserved4 : 4; + } cc_status; + + struct stusb4500_cc_hw_fault_status_0 { + uint8_t reserved0 : 4; + uint8_t vpu_ovp_fault_trans : 1; + uint8_t vpu_valid_trans : 1; + uint8_t reserved6 : 2; + } cc_hw_fault_status_0; + + struct stusb4500_cc_hw_fault_status_1 { + uint8_t reserved0 : 4; + uint8_t vbus_disch_fault : 1; + uint8_t reserved5 : 1; + uint8_t vpu_valid : 1; + uint8_t vpu_ovp_fault : 1; + } cc_hw_fault_status_1; + + struct stusb4500_pd_typec_status { + uint8_t pd_typec_hand_check : 4; + uint8_t reserved4 : 4; + } pd_typec_status; + + struct stusb4500_typec_status { + uint8_t typec_fsm_state : 5; // enum stusb4500_typec_fsm_state + uint8_t reserved5 : 1; + uint8_t reserved6 : 1; + uint8_t reverse : 1; + } typec_status; + + struct stusb4500_prt_status { + uint8_t prl_hw_rst_received : 1; + uint8_t reserved1 : 1; + uint8_t prl_msg_received : 1; + uint8_t reserved3 : 1; + uint8_t prt_bist_received : 1; + uint8_t reserved5 : 3; + } prt_status; +}; + +struct stusb4500_pd_command_ctrl { + uint8_t send_message_command : 6; + uint8_t reserved6 : 2; +}; + +struct stusb4500_monitoring_ctrl_0 { + uint8_t reserved0 : 3; + uint8_t vbus_snk_disc_threshold : 1; + uint8_t reserved4 : 4; +}; + +struct stusb4500_monitoring_ctrl_1 { + uint8_t voltage : 8; // undocumented, 100mV +}; + +struct stusb4500_monitoring_ctrl_2 { + uint8_t vshift_low : 4; + uint8_t vshift_high : 4; +}; + +struct stusb4500_reset_ctrl { + uint8_t reset_sw_en : 1; + uint8_t reserved1 : 7; +}; + +struct stusb4500_vbus_discharge_time_ctrl { + uint8_t discharge_time_transition : 4; + uint8_t discharge_time_to_0v : 4; +}; + +struct stusb4500_vbus_discharge_ctrl { + uint8_t reserved0 : 6; + uint8_t reserved6 : 1; + uint8_t vbus_discharge_en : 1; +}; + +struct stusb4500_vbus_ctrl { + uint8_t reserved0 : 1; + uint8_t sink_vbus_en : 1; + uint8_t reserved2 : 6; +}; + +struct stusb4500_pe_fsm { + uint8_t pe_fsm_state : 8; +}; + +struct stusb4500_device_id { + uint8_t device_id : 8; +}; + +struct stusb4500_rw_buffer { + uint8_t data[8]; +}; + +struct stusb4500_dpm_pdo_numb { + uint8_t dpm_snk_pdo_numb : 3; + uint8_t reserved3 : 5; +}; + +union stusb4500_pdo { + struct stusb4500_pdo_header { + uint32_t body : 30; + uint32_t type : 2; + } header; + + struct stusb4500_sink_fixed_supply_pdo { + uint32_t max_current : 10; // 10mA + uint32_t voltage : 10; // 50mV + uint32_t reserved20 : 3; // peak_current for source + uint32_t fast_role_swap : 2; // XXX: PD 3.0 + uint32_t dual_role_data : 1; + uint32_t usb_comm_capable : 1; + uint32_t unconstrained_power : 1; + uint32_t higher_capability : 1; // usb_suspend_supported for source + uint32_t dual_role_power : 1; + uint32_t type : 2; + } fixed_supply; +}; + +union stusb4500_rdo_reg_status { + struct stusb4500_fuxed_supply_rdo { + uint32_t max_current : 10; // 10mA + uint32_t operating_current : 10; // 10mA + uint32_t reserved20 : 3; + uint32_t unchunked_messages_supported : 1; // XXX: PD3.0 + uint32_t no_usb_suspend : 1; + uint32_t usb_comm_capable : 1; + uint32_t capability_mismatch : 1; + uint32_t give_back : 1; + uint32_t object_position : 3; + uint32_t reserved31 : 1; + } fixed_supply; +}; + +static const uint8_t stusb4500_ftp_cust_password_password = 0x47; + +struct stusb4500_ftp_cust_password { + uint8_t password : 8; +}; + +enum stusb4500_ftp_ctrl_opcode { + STUSB4500_FTP_CTRL_OPCODE_READ = 0x00, + STUSB4500_FTP_CTRL_OPCODE_WRITE_PL = 0x01, + STUSB4500_FTP_CTRL_OPCODE_WRITE_SER = 0x02, + STUSB4500_FTP_CTRL_OPCODE_READ_PL = 0x03, + STUSB4500_FTP_CTRL_OPCODE_READ_SER = 0x04, + STUSB4500_FTP_CTRL_OPCODE_ERASE_SECTOR = 0x05, + STUSB4500_FTP_CTRL_OPCODE_PROG_SECTOR = 0x06, + STUSB4500_FTP_CTRL_OPCODE_SOFT_PROG_SECTOR = 0x07, +}; + +#define STUSB4500_FTP_SER_SECTORS (0x1F) + +enum stusb4500_ftp_ctrl_ser { + STUSB4500_FTP_SER_SECTOR_0 = 0x01, + STUSB4500_FTP_SER_SECTOR_1 = 0x02, + STUSB4500_FTP_SER_SECTOR_2 = 0x04, + STUSB4500_FTP_SER_SECTOR_3 = 0x08, + STUSB4500_FTP_SER_SECTOR_4 = 0x10, +}; + +struct stusb4500_ftp_ctrl_0 { + uint8_t sector : 3; + uint8_t _3 : 1; + uint8_t req : 1; + uint8_t _5 : 1; + uint8_t rst_n : 1; + uint8_t pwr : 1; +}; + +struct stusb4500_ftp_ctrl_1 { + uint8_t opcode : 3; + uint8_t ser : 5; +}; diff --git a/components/usb_pd_sink/stusb4500_nvm.c b/components/usb_pd_sink/stusb4500_nvm.c new file mode 100644 index 00000000..cfe6a3ff --- /dev/null +++ b/components/usb_pd_sink/stusb4500_nvm.c @@ -0,0 +1,222 @@ +#include "stusb4500.h" +#include "stusb4500_i2c.h" +#include "stusb4500_nvm.h" + +#include + +#define DEBUG + +#include + +static int stusb4500_nvm_unlock(struct stusb4500 *stusb4500) +{ + struct stusb4500_ftp_cust_password password = { .password = stusb4500_ftp_cust_password_password }; + int err; + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CUST_PASSWORD, &password, sizeof(password)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CUST_PASSWORD"); + return err; + } + + return 0; +} + +static int stusb4500_nvm_lock(struct stusb4500 *stusb4500, bool reset) +{ + struct stusb4500_ftp_ctrl_0 ctrl0_reset = {}; + struct stusb4500_ftp_ctrl_0 ctrl0_off = { .rst_n = 1 }; + struct stusb4500_ftp_ctrl_1 ctrl1_clear = { }; + struct stusb4500_ftp_cust_password password_clear = { }; + int err; + + if (reset) { + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_reset, sizeof(ctrl0_reset)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_0"); + return err; + } + } + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_off, sizeof(ctrl0_off)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_0"); + return err; + } + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_1, &ctrl1_clear, sizeof(ctrl1_clear)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_1"); + return err; + } + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CUST_PASSWORD, &password_clear, sizeof(password_clear)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CUST_PASSWORD"); + return err; + } + + return 0; +} + +static int stusb4500_nvm_reset(struct stusb4500 *stusb4500) +{ + struct stusb4500_ftp_ctrl_0 ctrl0_reset = {}; + struct stusb4500_ftp_ctrl_0 ctrl0_on = { .rst_n = 1, .pwr = 1 }; + int err; + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_reset, sizeof(ctrl0_reset)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_0"); + return err; + } + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_on, sizeof(ctrl0_on)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_0"); + return err; + } + + return 0; +} + +static int stusb4500_nvm_op(struct stusb4500 *stusb4500, uint8_t opcode, uint8_t ser, uint8_t sector) +{ + struct stusb4500_ftp_ctrl_1 ctrl1_op = { .opcode = opcode, .ser = ser }; + struct stusb4500_ftp_ctrl_0 ctrl0_op = { .sector = sector, .req = 1, .rst_n = 1, .pwr = 1 }; + int err; + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_1, &ctrl1_op, sizeof(ctrl1_op)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_1"); + return err; + } + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_op, sizeof(ctrl0_op)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_FTP_CTRL_1"); + return err; + } + + while (ctrl0_op.req) { + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_FTP_CTRL_0, &ctrl0_op, sizeof(ctrl0_op)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_FTP_CTRL_0"); + return err; + } + } + + return 0; +} + +static int stusb4500_nvm_read_sector(struct stusb4500 *stusb4500, uint8_t sector, stusb4500_nvm_sector_t *data) +{ + int err; + + if ((err = stusb4500_nvm_reset(stusb4500))) { + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_READ, 0, sector))) { + return err; + } + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_RW_BUFFER_0, data, sizeof(*data)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_RW_BUFFER_0"); + return err; + } + + return 0; +} + +static int stusb4500_nvm_erase_sectors(struct stusb4500 *stusb4500, uint8_t ser) +{ + stusb4500_nvm_sector_t sector_empty = {}; + int err; + + // RWBUFFER must be 0 for Partial Erase + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_RW_BUFFER_0, §or_empty, sizeof(sector_empty)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_RW_BUFFER_0"); + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_WRITE_SER, ser, 0))) { + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_SOFT_PROG_SECTOR, 0, 0))) { + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_ERASE_SECTOR, 0, 0))) { + return err; + } + + return 0; +} + +static int stusb4500_nvm_write_sector(struct stusb4500 *stusb4500, uint8_t sector, const stusb4500_nvm_sector_t *data) +{ + int err; + + if ((err = stusb4500_i2c_write(stusb4500, STUSB4500_RW_BUFFER_0, data, sizeof(*data)))) { + LOG_ERROR("stusb4500_i2c_write STUSB4500_RW_BUFFER_0"); + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_WRITE_PL, 0, 0))) { + return err; + } + + if ((err = stusb4500_nvm_op(stusb4500, STUSB4500_FTP_CTRL_OPCODE_PROG_SECTOR, 0, sector))) { + return err; + } + + return 0; +} + +int stusb4500_nvm_read(struct stusb4500 *stusb4500, union stusb4500_nvm *nvm) +{ + int err; + + if ((err = stusb4500_nvm_unlock(stusb4500))) { + LOG_ERROR("stusb4500_nvm_unlock"); + return err; + } + + for (int i = 0; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + if ((err = stusb4500_nvm_read_sector(stusb4500, i, &nvm->sectors[i]))) { + LOG_ERROR("stusb4500_nvm_read_sector %d", i); + return err; + } + } + + if ((err = stusb4500_nvm_lock(stusb4500, true))) { + LOG_ERROR("stusb4500_nvm_lock"); + return err; + } + + return 0; +} + +int stusb4500_nvm_write(struct stusb4500 *stusb4500, const union stusb4500_nvm *nvm) +{ + int err; + + if ((err = stusb4500_nvm_unlock(stusb4500))) { + LOG_ERROR("stusb4500_nvm_unlock"); + return err; + } + + if ((err = stusb4500_nvm_reset(stusb4500))) { + return err; + } + + if ((err = stusb4500_nvm_erase_sectors(stusb4500, STUSB4500_FTP_SER_SECTORS))) { + return err; + } + + for (int i = 0; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + if ((err = stusb4500_nvm_write_sector(stusb4500, i, &nvm->sectors[i]))) { + LOG_ERROR("stusb4500_nvm_write_sector %d", i); + return err; + } + } + + if ((err = stusb4500_nvm_lock(stusb4500, false))) { + LOG_ERROR("stusb4500_nvm_lock"); + return err; + } + + return 0; +} diff --git a/components/usb_pd_sink/stusb4500_nvm.h b/components/usb_pd_sink/stusb4500_nvm.h new file mode 100644 index 00000000..f3563e61 --- /dev/null +++ b/components/usb_pd_sink/stusb4500_nvm.h @@ -0,0 +1,103 @@ +#pragma once + +#include + +#define STUSB4500_NVM_START 0xC0 +#define STUSB4500_NVM_SECTOR_COUNT 5 +#define STUSB4500_NVM_SECTOR_SIZE 8 + +#define STUSB4500_NVM_VENDOR_ID 0x0000 +#define STUSB4500_NVM_PRODUCT_ID 0xABB0 +#define STUSB4500_NVM_DEVICE_ID 0x4500 + +typedef uint8_t stusb4500_nvm_sector_t[STUSB4500_NVM_SECTOR_SIZE]; + +union stusb4500_nvm { + stusb4500_nvm_sector_t sectors[STUSB4500_NVM_SECTOR_COUNT]; + + // from STUSB4500_NVM TNxxx + // https://community.st.com/t5/interface-and-connectivity-ics/about-the-nvm-programming-for-stusb4500/m-p/378217/highlight/true#M6664 + struct stusb4500_banks { + struct stusb4500_bank0 { + uint16_t vendor_id; // 0x0000 + + uint16_t product_id; // 0xAAB0 + + uint16_t bcd_device_id; // 0x4500 + + uint16_t port_role_ctrl : 8; // 0x00 + uint16_t device_power_role_ctrl : 8; // 0x00 + } bank0; + + struct stusb4500_bank1 { + uint8_t reserved0_0 : 4; // 0x0 + uint8_t gpio_cfg : 2; + uint8_t reserved0_6 : 2; // 0b00 + + uint8_t reserved1_0 : 5; // 0b00000 + uint8_t vbus_dchg_mask : 1; + uint8_t reserved1_6 : 1; // 1 + uint8_t reserved1_7 : 1; // 0 + + uint8_t vbus_disch_time_to_pdo : 4; + uint8_t discharge_time_to_0v : 4; + + uint8_t reserved3 : 8; // 0x1C + uint8_t reserved4 : 8; // 0xFF + uint8_t reserved5 : 8; // 0x01 + uint8_t reserved6 : 8; // 0x3C + uint8_t reserved7 : 8; // 0xDF + } bank1; + + struct stusb4500_bank2 { + uint8_t reserved0 : 8; // 0x02 + uint8_t reserved1 : 8; // 0x40 + uint8_t reserved2 : 8; // 0x0F + uint8_t reserved3 : 8; // 0x00 + uint8_t reserved4 : 8; // 0x32 + uint8_t reserved5 : 8; // 0x00 + uint8_t reserved6 : 8; // 0xFC + uint8_t reserved7 : 8; // 0xF1 + } bank2; + + struct stusb4500_bank3 { + uint8_t reserved0 : 8; // 0x00 + uint8_t reserved1 : 8; // 0x19 + + uint8_t usb_comm_capable : 1; + uint8_t dpm_snk_pdo_numb : 2; + uint8_t snk_uncons_power : 1; + uint8_t lut_snk_pdo1_i : 4; + + uint8_t snk_ll1 : 4; + uint8_t snk_hl1 : 4; + + uint8_t lut_snk_pdo2_i : 4; + uint8_t snk_ll2 : 4; + + uint8_t snk_hl2 : 4; + uint8_t lut_snk_pdo3_i : 4; + + uint8_t snk_ll3 : 4; + uint8_t snk_hl3 : 4; + + uint8_t reserved7 : 8; // SNK_PDO_FILL_0xDF = 0x00 + } bank3; + + struct stusb4500_bank4 { + uint64_t reserved0_0 : 6; // 0b000000 + uint64_t snk_pdo_flex1_v : 10; + uint64_t snk_pdo_flex2_v : 10; + uint64_t snk_pdo_flex_i : 10; + uint64_t reserved4_4 : 1; // 0 + uint64_t power_ok_cfg : 2; + uint64_t reserved4_7 : 1; // 0 + uint64_t reserved5 : 8; // 0x0 + uint64_t reserved6_0 : 3; // 0x0 + uint64_t power_only_above_5v : 1; + uint64_t req_src_current : 1; + uint64_t reserved6_5 : 3; // 0b010 + uint64_t reserved7 : 8; // ALERT_STATUS_1_MASK + } bank4; + } banks; +}; diff --git a/components/usb_pd_sink/stusb4500_nvm_config.c b/components/usb_pd_sink/stusb4500_nvm_config.c new file mode 100644 index 00000000..d9c1c856 --- /dev/null +++ b/components/usb_pd_sink/stusb4500_nvm_config.c @@ -0,0 +1,241 @@ +#include "stusb4500.h" +#include "stusb4500_nvm.h" + +#include + +#include + +#include + +#ifndef CONFIG_STUSB4500_VBUS_DISCH_DISABLE + #define CONFIG_STUSB4500_VBUS_DISCH_DISABLE 0 +#endif + +#ifndef CONFIG_STUSB4500_USB_COMM_CAPABLE + #define CONFIG_STUSB4500_USB_COMM_CAPABLE 0 +#endif + +#ifndef CONFIG_STUSB4500_SNK_UNCONS_POWER + #define CONFIG_STUSB4500_SNK_UNCONS_POWER 0 +#endif + +#ifndef CONFIG_STUSB4500_REQ_SRC_CURRENT + #define CONFIG_STUSB4500_REQ_SRC_CURRENT 0 +#endif + +#ifndef CONFIG_STUSB4500_POWER_ONLY_ABOVE_5V + #define CONFIG_STUSB4500_POWER_ONLY_ABOVE_5V 0 +#endif + +// convert from kconfig mV -> flex_v [50mV] +static inline unsigned config_snk_pdo_flex_v(unsigned config_mV) +{ + return config_mV / 50; +} + +// convert from kconfig mA -> lut_i +static inline unsigned config_snk_pdo_i(unsigned config_mA) +{ + if (config_mA < 500) { + return 0b001; // 0.5A + } else if (config_mA < 3000) { + return (config_mA - 500) / 250 + 1; + } else if (config_mA < 5000) { + return (config_mA - 3000) / 500 + 11; + } else { + return 0b1111; + } +} + +int stusb4500_nvm_validate(union stusb4500_nvm *nvm) +{ + stusb4500_nvm_sector_t zero_sector = {}; + + for (int i = 0; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + if (!memcmp(nvm->sectors[i], &zero_sector, sizeof(zero_sector))) { + LOG_ERROR("sector%d is all-zeroes, corrupted read?", i); + return 1; + } + } + + for (int i = 1; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + if (!memcmp(nvm->sectors[i], nvm->sectors[i - 1], sizeof(nvm->sectors[i]))) { + LOG_ERROR("sector%d is identical to sector%d, corrupted read?", i, i - 1); + return 1; + } + } + + if (nvm->banks.bank0.vendor_id != STUSB4500_NVM_VENDOR_ID) { + LOG_ERROR("bank0 vendor_id=%04x product_id=%04x bcd_device_id=%04x unsupported vendor_id", + nvm->banks.bank0.vendor_id, + nvm->banks.bank0.product_id, + nvm->banks.bank0.bcd_device_id + ); + return 1; + } + + if (nvm->banks.bank0.product_id != STUSB4500_NVM_PRODUCT_ID) { + LOG_ERROR("bank0 vendor_id=%04x product_id=%04x bcd_device_id=%04x unsupported product_id", + nvm->banks.bank0.vendor_id, + nvm->banks.bank0.product_id, + nvm->banks.bank0.bcd_device_id + ); + return 1; + } + + if (nvm->banks.bank0.bcd_device_id != STUSB4500_NVM_DEVICE_ID) { + LOG_ERROR("bank0 vendor_id=%04x product_id=%04x bcd_device_id=%04x unsupported bcd_device_id", + nvm->banks.bank0.vendor_id, + nvm->banks.bank0.product_id, + nvm->banks.bank0.bcd_device_id + ); + return 1; + } + + return 0; +} + +int stusb4500_nvm_config(union stusb4500_nvm *nvm) +{ + int ret = 0; + + if (nvm->banks.bank3.dpm_snk_pdo_numb != CONFIG_STUSB4500_SNK_PDO_NUMB) { + nvm->banks.bank3.dpm_snk_pdo_numb = CONFIG_STUSB4500_SNK_PDO_NUMB; + + LOG_WARN("set CONFIG_STUSB4500_SNK_PDO_NUMB = %u", CONFIG_STUSB4500_SNK_PDO_NUMB); + + ret++; + } + + if (nvm->banks.bank4.snk_pdo_flex1_v != config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO2)) { + nvm->banks.bank4.snk_pdo_flex1_v = config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO2); + + LOG_WARN("set CONFIG_STUSB4500_V_SNK_PDO2 = %u", config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO2)); + + ret++; + } + + if (nvm->banks.bank4.snk_pdo_flex2_v != config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO3)) { + nvm->banks.bank4.snk_pdo_flex2_v = config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO3); + + LOG_WARN("set CONFIG_STUSB4500_V_SNK_PDO3 = %u", config_snk_pdo_flex_v(CONFIG_STUSB4500_V_SNK_PDO3)); + + ret++; + } + + if (nvm->banks.bank3.lut_snk_pdo1_i != config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO1)) { + nvm->banks.bank3.lut_snk_pdo1_i = config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO1); + + LOG_WARN("set CONFIG_STUSB4500_I_SNK_PDO1 = %u", config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO1)); + + ret++; + } + + if (nvm->banks.bank3.lut_snk_pdo2_i != config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO2)) { + nvm->banks.bank3.lut_snk_pdo2_i = config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO2); + + LOG_WARN("set STUSB4500_I_SNK_PDO2 = %u", config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO2)); + + ret++; + } + + if (nvm->banks.bank3.lut_snk_pdo3_i != config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO3)) { + nvm->banks.bank3.lut_snk_pdo3_i = config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO3); + + LOG_WARN("set STUSB4500_I_SNK_PDO3 = %u", config_snk_pdo_i(CONFIG_STUSB4500_I_SNK_PDO3)); + + ret++; + } + + if (nvm->banks.bank3.dpm_snk_pdo_numb != CONFIG_STUSB4500_SNK_PDO_NUMB) { + nvm->banks.bank3.dpm_snk_pdo_numb = CONFIG_STUSB4500_SNK_PDO_NUMB; + + LOG_WARN("set CONFIG_STUSB4500_SNK_PDO_NUMB = %u", CONFIG_STUSB4500_SNK_PDO_NUMB); + + ret++; + } + + if (nvm->banks.bank1.vbus_dchg_mask != CONFIG_STUSB4500_VBUS_DISCH_DISABLE) { + nvm->banks.bank1.vbus_dchg_mask = CONFIG_STUSB4500_VBUS_DISCH_DISABLE; + + LOG_WARN("set CONFIG_STUSB4500_VBUS_DISCH_DISABLE = %u", CONFIG_STUSB4500_VBUS_DISCH_DISABLE); + + ret++; + } + + if (nvm->banks.bank3.usb_comm_capable != CONFIG_STUSB4500_USB_COMM_CAPABLE) { + nvm->banks.bank3.usb_comm_capable = CONFIG_STUSB4500_USB_COMM_CAPABLE; + + LOG_WARN("set CONFIG_STUSB4500_USB_COMM_CAPABLE = %u", CONFIG_STUSB4500_USB_COMM_CAPABLE); + + ret++; + } + + if (nvm->banks.bank3.snk_uncons_power != CONFIG_STUSB4500_SNK_UNCONS_POWER) { + nvm->banks.bank3.snk_uncons_power = CONFIG_STUSB4500_SNK_UNCONS_POWER; + + LOG_WARN("set CONFIG_STUSB4500_SNK_UNCONS_POWER = %u", CONFIG_STUSB4500_SNK_UNCONS_POWER); + + ret++; + } + + if (nvm->banks.bank4.req_src_current != CONFIG_STUSB4500_REQ_SRC_CURRENT) { + nvm->banks.bank4.req_src_current = CONFIG_STUSB4500_REQ_SRC_CURRENT; + + LOG_WARN("set CONFIG_STUSB4500_REQ_SRC_CURRENT = %u", CONFIG_STUSB4500_REQ_SRC_CURRENT); + + ret++; + } + + if (nvm->banks.bank4.power_ok_cfg != CONFIG_STUSB4500_POWER_OK_CFG) { + nvm->banks.bank4.power_ok_cfg = CONFIG_STUSB4500_POWER_OK_CFG; + + LOG_WARN("set CONFIG_STUSB4500_POWER_OK_CFG = %u", CONFIG_STUSB4500_POWER_OK_CFG); + + ret++; + } + + if (nvm->banks.bank4.power_only_above_5v != CONFIG_STUSB4500_POWER_ONLY_ABOVE_5V) { + nvm->banks.bank4.power_only_above_5v = CONFIG_STUSB4500_POWER_ONLY_ABOVE_5V; + + LOG_WARN("set POWER_ONLY_ABOVE_5V = %u", CONFIG_STUSB4500_POWER_ONLY_ABOVE_5V); + + ret++; + } + + if (nvm->banks.bank1.gpio_cfg != CONFIG_STUSB4500_GPIO_CFG) { + nvm->banks.bank1.gpio_cfg = CONFIG_STUSB4500_GPIO_CFG; + + LOG_WARN("set STUSB4500_GPIO_CFG = %u", CONFIG_STUSB4500_GPIO_CFG); + + ret++; + } + + return ret; +} + +int stusb4500_nvm_print(const union stusb4500_nvm *nvm, FILE *file) +{ + int err; + + for (int i = 0; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + // using GUI file format + err = fprintf(file, "0x%02X:\t0x%02X\t0x%02X\t0x%02X\t0x%02X\t0x%02X\t0x%02X\t0x%02X\t0x%02X\r\n", + STUSB4500_NVM_START + i * STUSB4500_NVM_SECTOR_SIZE, + nvm->sectors[i][0], + nvm->sectors[i][1], + nvm->sectors[i][2], + nvm->sectors[i][3], + nvm->sectors[i][4], + nvm->sectors[i][5], + nvm->sectors[i][6], + nvm->sectors[i][7] + ); + + if (err < 0) { + return err; + } + } + + return 0; +} diff --git a/components/usb_pd_sink/stusb4500_print.c b/components/usb_pd_sink/stusb4500_print.c new file mode 100644 index 00000000..2912808d --- /dev/null +++ b/components/usb_pd_sink/stusb4500_print.c @@ -0,0 +1,275 @@ +#include "stusb4500.h" +#include "stusb4500_i2c.h" + +#include + +#include + +static void stusb4500_print_status(struct stusb4500 *stusb4500, FILE *file) +{ + struct stusb4500_i2c_rev rev; + struct stusb4500_i2c_status status; + struct stusb4500_pe_fsm pe_fsm; + struct stusb4500_device_id device_id; + int err; + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_BCD_TYPEC_REV_LOW, &rev, sizeof(rev)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_BCD_TYPEC_REV_LOW"); + } else { + fprintf(file, "\tbcd_rev: typec_low=%u.%u typec_high=%u.%u usbpd_low=%u.%u usbpd_high=%u.%u\n", + rev.bcd_typec_rev_low.major, rev.bcd_typec_rev_low.minor, + rev.bcd_typec_rev_high.major, rev.bcd_typec_rev_high.minor, + rev.bcd_usbpd_rev_low.major, rev.bcd_usbpd_rev_low.minor, + rev.bcd_usbpd_rev_high.major, rev.bcd_usbpd_rev_high.minor + ); + } + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_PORT_STATUS_0, &status, sizeof(status)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_PORT_STATUS_0"); + } else { + fprintf(file, "\tport_status: attach_trans=%u attached_device=%u power_mode=%u data_mode=%u attach=%u\n", + status.port_status_0.attach_trans, + status.port_status_1.attached_device, + status.port_status_1.power_mode, + status.port_status_1.data_mode, + status.port_status_1.attach + ); + + fprintf(file, "\ttypec_monitoring_status: vbus_low_status=%u vbus_high_status=%u vbus_valid_snk=%u vbus_vsafe0v=%u vbus_ready=%u\n", + status.typec_monitoring_status_0.vbus_low_status, + status.typec_monitoring_status_0.vbus_high_status, + status.typec_monitoring_status_1.vbus_valid_snk, + status.typec_monitoring_status_1.vbus_vsafe0v, + status.typec_monitoring_status_1.vbus_ready + ); + + fprintf(file, "\tcc_status: cc1_state=%u cc2_state=%u connect_result=%u looking_4_connection=%u\n", + status.cc_status.cc1_state, + status.cc_status.cc2_state, + status.cc_status.connect_result, + status.cc_status.looking_4_connection + ); + + fprintf(file, "\tcc_hw_fault_status: vbus_disch_fault=%u vpu_valid=%u vpu_ovp_fault=%u\n", + status.cc_hw_fault_status_1.vbus_disch_fault, + status.cc_hw_fault_status_1.vpu_valid, + status.cc_hw_fault_status_1.vpu_ovp_fault + ); + + fprintf(file, "\tpd_typec_status: pd_typec_hand_check=%u\n", + status.pd_typec_status.pd_typec_hand_check + ); + + fprintf(file, "\ttypec_status: typec_fsm_state=%u reverse=%u\n", + status.typec_status.typec_fsm_state, + status.typec_status.reverse + ); + + fprintf(file, "\tprt_status: prl_hw_rst_received=%u prl_msg_received=%u prt_bist_received=%u\n", + status.prt_status.prl_hw_rst_received, + status.prt_status.prl_msg_received, + status.prt_status.prt_bist_received + ); + + } + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_PE_FSM, &pe_fsm, sizeof(pe_fsm)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_PE_FSM"); + } else { + fprintf(file, "\tpe_fsm: pe_fsm_state=%u\n", + pe_fsm.pe_fsm_state + ); + } + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_DEVICE_ID, &device_id, sizeof(device_id)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_DEVICE_ID"); + } else { + fprintf(file, "\tdevice_id: device_id=%u\n", + device_id.device_id + ); + } +} + +static void stusb4500_print_ctrl(struct stusb4500 *stusb4500, FILE *file) +{ + struct stusb4500_monitoring_ctrl_0 monitoring_ctrl_0; + struct stusb4500_monitoring_ctrl_1 monitoring_ctrl_1; + struct stusb4500_monitoring_ctrl_2 monitoring_ctrl_2; + struct stusb4500_vbus_discharge_time_ctrl vbus_discharge_time_ctrl; + int err; + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_MONITORING_CTRL_0, &monitoring_ctrl_0, sizeof(monitoring_ctrl_0)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_MONITORING_CTRL_0"); + } else if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_MONITORING_CTRL_1, &monitoring_ctrl_1, sizeof(monitoring_ctrl_1)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_MONITORING_CTRL_1"); + } else if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_MONITORING_CTRL_2, &monitoring_ctrl_2, sizeof(monitoring_ctrl_2)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_MONITORING_CTRL_2"); + } else { + fprintf(file, "\tmonitoring_ctrl: vbus_snk_disc_threshold=%u voltage=%u vshift_low=%u vshift_high=%u\n", + monitoring_ctrl_0.vbus_snk_disc_threshold, + monitoring_ctrl_1.voltage, + monitoring_ctrl_2.vshift_low, monitoring_ctrl_2.vshift_high + ); + } + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_VBUS_DISCHARGE_TIME_CTRL, &vbus_discharge_time_ctrl, sizeof(vbus_discharge_time_ctrl)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_VBUS_DISCHARGE_TIME_CTRL"); + } else { + fprintf(file, "\tvbus_discharge_time_ctrl: discharge_time_transition=%u discharge_time_to_0v=%u\n", + vbus_discharge_time_ctrl.discharge_time_transition, + vbus_discharge_time_ctrl.discharge_time_to_0v + ); + } +} + +static void stusb4500_print_pdo(struct stusb4500 *stusb4500, FILE *file) +{ + struct stusb4500_dpm_pdo_numb pdo_numb; + union stusb4500_pdo pdo; + int err; + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_DPM_PDO_NUMB, &pdo_numb, sizeof(pdo_numb)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_DPM_PDO_NUMB"); + return; + } + + for (int i = 0; i < pdo_numb.dpm_snk_pdo_numb && i < STUSB4500_DPM_SNK_PDO_COUNT; i++) { + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_DPM_SNK_PDO(i), &pdo, sizeof(pdo)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_DPM_SNK_PDO(%d)", i); + continue; + } + + switch(pdo.header.type) { + case STUSB4500_PDO_TYPE_FIXED_SUPPLY: + fprintf(file, "\tdpm_snk_pdo%d: fixed_supply max_current=%u voltage=%u fast_role_swap=%u dual_role_data=%u usb_comm_capable=%u unconstrained_power=%u higher_capability=%u dual_role_power=%u\n", i, + pdo.fixed_supply.max_current, + pdo.fixed_supply.voltage, + pdo.fixed_supply.fast_role_swap, + pdo.fixed_supply.dual_role_data, + pdo.fixed_supply.usb_comm_capable, + pdo.fixed_supply.unconstrained_power, + pdo.fixed_supply.higher_capability, + pdo.fixed_supply.dual_role_power + ); + break; + + default: + LOG_WARN("dpm_snk_pdo%d: unsupported type=%u", i, pdo.header.type); + break; + } + } +} + +static void stusb4500_print_rdo(struct stusb4500 *stusb4500, FILE *file) +{ + union stusb4500_rdo_reg_status rdo; + int err; + + if ((err = stusb4500_i2c_read(stusb4500, STUSB4500_RDO_REG_STATUS_0, &rdo, sizeof(rdo)))) { + LOG_ERROR("stusb4500_i2c_read STUSB4500_RDO_REG_STATUS_0"); + } else { + fprintf(file, "\trdo: fixed_supply max_current=%u operating_current=%u unchunked_messages_supported=%u no_usb_suspend=%u usb_comm_capable=%u capability_mismatch=%u give_back=%u object_position=%u\n", + rdo.fixed_supply.max_current, + rdo.fixed_supply.operating_current, + rdo.fixed_supply.unchunked_messages_supported, + rdo.fixed_supply.no_usb_suspend, + rdo.fixed_supply.usb_comm_capable, + rdo.fixed_supply.capability_mismatch, + rdo.fixed_supply.give_back, + rdo.fixed_supply.object_position + ); + } +} + +static void stusb4500_print_nvm(struct stusb4500 *stusb4500, FILE *file) +{ + union stusb4500_nvm nvm = {}; + int err; + + if ((err = stusb4500_nvm_read(stusb4500, &nvm))) { + LOG_ERROR("stusb4500_nvm_read"); + return; + } + + for (int i = 0; i < STUSB4500_NVM_SECTOR_COUNT; i++) { + fprintf(file, "\tnvm bank%d: %02x %02x %02x %02x %02x %02x %02x %02x", i, + nvm.sectors[i][0], + nvm.sectors[i][1], + nvm.sectors[i][2], + nvm.sectors[i][3], + nvm.sectors[i][4], + nvm.sectors[i][5], + nvm.sectors[i][6], + nvm.sectors[i][7] + ); + + switch(i) { + case 0: + fprintf(file, "\tvendor_id=%04x product_id=%04x bcd_device_id=%04x port_role_ctrl=%u device_power_role_ctrl=%u", + nvm.banks.bank0.vendor_id, + nvm.banks.bank0.product_id, + nvm.banks.bank0.bcd_device_id, + nvm.banks.bank0.port_role_ctrl, + nvm.banks.bank0.device_power_role_ctrl + ); + break; + + case 1: + fprintf(file, "\tgpio_cfg=%u vbus_dchg_mask=%u vbus_disch_time_to_pdo=%u discharge_time_to_0v=%u", + nvm.banks.bank1.gpio_cfg, + nvm.banks.bank1.vbus_dchg_mask, + nvm.banks.bank1.vbus_disch_time_to_pdo, + nvm.banks.bank1.discharge_time_to_0v + ); + + case 2: + // nothing interesting + break; + + case 3: + fprintf(file, "\tusb_comm_capable=%u dpm_snk_pdo_numb=%u snk_uncons_power=%u", + nvm.banks.bank3.usb_comm_capable, + nvm.banks.bank3.dpm_snk_pdo_numb, + nvm.banks.bank3.snk_uncons_power + ); + + fprintf(file, "\tpdo1(i=%u ll=%u hl=%u) pdo2(i=%u ll=%u hl=%u) pdo3(i=%u ll=%u hl=%u)", + nvm.banks.bank3.lut_snk_pdo1_i, + nvm.banks.bank3.snk_ll1, + nvm.banks.bank3.snk_hl1, + nvm.banks.bank3.lut_snk_pdo2_i, + nvm.banks.bank3.snk_ll2, + nvm.banks.bank3.snk_hl2, + nvm.banks.bank3.lut_snk_pdo3_i, + nvm.banks.bank3.snk_ll3, + nvm.banks.bank3.snk_hl3 + ); + break; + + case 4: + fprintf(file, "\tsnk_pdo_flex1_v=%u snk_pdo_flex2_v=%u snk_pdo_flex_i=%u power_ok_cfg=%u power_only_above_5v=%u req_src_current=%u", + nvm.banks.bank4.snk_pdo_flex1_v, + nvm.banks.bank4.snk_pdo_flex2_v, + nvm.banks.bank4.snk_pdo_flex_i, + nvm.banks.bank4.power_ok_cfg, + nvm.banks.bank4.power_only_above_5v, + nvm.banks.bank4.req_src_current + ); + break; + } + + fprintf(file, "\n"); + } +} + +int stusb4500_print(struct stusb4500 *stusb4500, FILE *file) +{ + stusb4500_print_status(stusb4500, file); + stusb4500_print_ctrl(stusb4500, file); + stusb4500_print_pdo(stusb4500, file); + stusb4500_print_rdo(stusb4500, file); + + stusb4500_print_nvm(stusb4500, file); + + return 0; +} diff --git a/components/usb_pd_sink/usb_pd_sink.c b/components/usb_pd_sink/usb_pd_sink.c new file mode 100644 index 00000000..482fa203 --- /dev/null +++ b/components/usb_pd_sink/usb_pd_sink.c @@ -0,0 +1,85 @@ +#include "usb_pd_sink.h" + +#include + +#include + +int usb_pd_sink_init(struct usb_pd_sink *sink, const struct usb_pd_sink_options *options) +{ + switch ((sink->type = options->type)) { + case USB_PD_SINK_STUSB4500: + return stusb4500_init(&sink->state.stusb4500, options); + + default: + LOG_FATAL("invalid type=%d", options->type); + } +} + +int usb_pd_sink_new(struct usb_pd_sink **sinkp, const struct usb_pd_sink_options *options) +{ + struct usb_pd_sink *sink; + int err; + + if (!(sink = calloc(1, sizeof(*sink)))) { + LOG_ERROR("calloc"); + return -1; + } + + if ((err = usb_pd_sink_init(sink, options))) { + goto error; + } + + *sinkp = sink; + + return 0; + +error: + free(sink); + + return err; +} + +int usb_pd_sink_setup(struct usb_pd_sink *sink) +{ + switch (sink->type) { + case USB_PD_SINK_STUSB4500: + return stusb4500_setup(&sink->state.stusb4500); + + default: + LOG_FATAL("invalid type=%d", sink->type); + } + +} + +int usb_pd_sink_start(struct usb_pd_sink *sink) +{ + switch (sink->type) { + case USB_PD_SINK_STUSB4500: + return stusb4500_start(&sink->state.stusb4500); + + default: + LOG_FATAL("invalid type=%d", sink->type); + } +} + +int usb_pd_sink_status(struct usb_pd_sink *sink, struct usb_pd_sink_status *status) +{ + switch (sink->type) { + case USB_PD_SINK_STUSB4500: + return stusb4500_status(&sink->state.stusb4500, status); + + default: + LOG_FATAL("invalid type=%d", sink->type); + } +} + +int usb_pd_sink_print(struct usb_pd_sink *sink, FILE *file) +{ + switch (sink->type) { + case USB_PD_SINK_STUSB4500: + return stusb4500_print(&sink->state.stusb4500, file); + + default: + LOG_FATAL("invalid type=%d", sink->type); + } +} diff --git a/components/usb_pd_sink/usb_pd_sink.h b/components/usb_pd_sink/usb_pd_sink.h new file mode 100644 index 00000000..9dbb9a1d --- /dev/null +++ b/components/usb_pd_sink/usb_pd_sink.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "stusb4500.h" + +struct usb_pd_sink { + enum usb_pd_sink_type type; + + union { + struct stusb4500 stusb4500; + } state; +}; diff --git a/main/Kconfig b/main/Kconfig index 445e31f2..6759717a 100644 --- a/main/Kconfig +++ b/main/Kconfig @@ -424,3 +424,26 @@ menu "qmsk-esp-sdcard" bool "SD Card support" endmenu + +menu "qmsk-esp-usb-pd-sink" + config USB_PD_SINK_ENABLED + bool "USB-PD Sink support" + + choice USB_PD_SINK_TYPE + prompt "USB-PD Sink type" + + config USB_PD_SINK_TYPE_NONE + bool "Disabled" + + config USB_PD_SINK_TYPE_STUSB4500 + bool "STUSB4500" + select USB_PD_SINK_ENABLED + endchoice + + config USB_PD_SINK_TYPE_STUSB4500_I2C_ADDR + depends on USB_PD_SINK_TYPE_STUSB4500 + int "Configurable STUSB4500 I2C address bits (0-3)" + range 0 3 + default 0 + +endmenu diff --git a/main/console_cmd.c b/main/console_cmd.c index 414d6ab9..bf942c83 100644 --- a/main/console_cmd.c +++ b/main/console_cmd.c @@ -9,8 +9,9 @@ #include "log.h" #include "sdcard.h" #include "spiffs.h" -#include "user_leds.h" #include "system.h" +#include "usb_pd_cmd.h" +#include "user_leds.h" #include "vfs.h" #include "wifi.h" @@ -49,6 +50,11 @@ const struct cmd console_cli_commands[] = { { "config", .describe = "Configuration", .subcommands = &config_cmdtab, }, +#if CONFIG_USB_PD_SINK_ENABLED + { "usb-pd", .describe = "USB-PD Sink", + .subcommands = &usb_pd_cmdtab, + }, +#endif #if CONFIG_SDCARD_ENABLED { "sdcard", .describe = "SD Card", .subcommands = &sdcard_cmdtab, diff --git a/main/main.c b/main/main.c index b1b63383..267e3aeb 100644 --- a/main/main.c +++ b/main/main.c @@ -14,6 +14,7 @@ #include "sdcard.h" #include "system.h" #include "user.h" +#include "usb_pd_sink.h" #include "user_events.h" #include "user_leds.h" #include "wifi.h" @@ -111,6 +112,13 @@ void app_main(void) } #endif +#if CONFIG_USB_PD_SINK_ENABLED + if ((err = init_usb_pd_sink())) { + LOG_ERROR("init_usb_pd_sink"); + user_alert(USER_ALERT_ERROR_SETUP); + } +#endif + LOG_INFO("config"); if ((err = init_config()) < 0) { @@ -133,6 +141,13 @@ void app_main(void) } #endif +#if CONFIG_USB_PD_SINK_ENABLED + if ((err = start_usb_pd_sink())) { + LOG_ERROR("start_usb_pd_sink"); + user_alert(USER_ALERT_ERROR_START); + } +#endif + LOG_INFO("setup"); if ((err = init_wifi())) { diff --git a/main/usb_pd_cmd.c b/main/usb_pd_cmd.c new file mode 100644 index 00000000..70d5d68f --- /dev/null +++ b/main/usb_pd_cmd.c @@ -0,0 +1,64 @@ +#include "usb_pd_cmd.h" +#include "usb_pd_state.h" + +#include + +#include + +#if CONFIG_USB_PD_SINK_ENABLED + int usb_pd_sink_status_cmd(int argc, char **argv, void *ctx) + { + struct usb_pd_sink_status status; + int err; + + if (!usb_pd_sink) { + LOG_ERROR("not initialized"); + return -1; + } + + if ((err = usb_pd_sink_status(usb_pd_sink, &status))) { + LOG_ERROR("usb_pd_sink_status"); + return -1; + } + + printf("USB-PD Version %d.%d\n", status.usb_pd_version_major, status.usb_pd_version_minor); + printf("Sink:\n"); + printf("\t%-25s: %d\n", "Port Attached", status.port_attached); + printf("\t%-25s: %d\n", "Port Powered", status.port_power); + printf("\t%-25s: %d\n", "VBUS Ready", status.vbus_ready); + + printf("\t%-25s: %d\n", "PDO #", status.pdo_number); + printf("\t%-25s: %d\n", "PD Capability Mismatch", status.pd_capability_mismatch); + printf("\t%-25s: %.2f\n", "Voltage", status.voltage_mV / 1000.0f); + printf("\t%-25s: %.2f - %.2f\n", "Current", status.operating_current_mA / 1000.0f, status.maximum_current_mA / 1000.0f); + + return 0; + } + + int usb_pd_sink_debug_cmd(int argc, char **argv, void *ctx) + { + int err; + + if (!usb_pd_sink) { + LOG_ERROR("not initialized"); + return -1; + } + + if ((err = usb_pd_sink_print(usb_pd_sink, stdout))) { + LOG_ERROR("usb_pd_sink_print"); + return -1; + } + + return 0; + } + + const struct cmd usb_pd_commands[] = { + { "status", usb_pd_sink_status_cmd, .describe = "Show USB-PD Sink Status" }, + { "debug", usb_pd_sink_debug_cmd, .describe = "Print detailed USB-PD Sink Status" }, + {} + }; + + const struct cmdtab usb_pd_cmdtab = { + .commands = usb_pd_commands, + }; +#endif diff --git a/main/usb_pd_cmd.h b/main/usb_pd_cmd.h new file mode 100644 index 00000000..a341a785 --- /dev/null +++ b/main/usb_pd_cmd.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +#if CONFIG_USB_PD_SINK_ENABLED + extern const struct cmdtab usb_pd_cmdtab; +#endif diff --git a/main/usb_pd_sink.c b/main/usb_pd_sink.c new file mode 100644 index 00000000..f6874599 --- /dev/null +++ b/main/usb_pd_sink.c @@ -0,0 +1,52 @@ +#include "usb_pd_sink.h" +#include "usb_pd_state.h" + +#include "i2c_master.h" + +#include + +#if CONFIG_USB_PD_SINK_ENABLED + #include + + struct usb_pd_sink *usb_pd_sink; + + int init_usb_pd_sink() + { + struct usb_pd_sink_options options = { + #if CONFIG_USB_PD_SINK_TYPE_STUSB4500 + .type = USB_PD_SINK_STUSB4500, + .i2c_port = I2C_MASTER_PORT, + .i2c_addr = CONFIG_USB_PD_SINK_TYPE_STUSB4500_I2C_ADDR, + #else + #error "No USB-PD Sink Type selected" + #endif + }; + int err; + + LOG_INFO("type=%d i2c_port=%d i2c_addr=%d", options.type, options.i2c_port, options.i2c_addr); + + if ((err = usb_pd_sink_new(&usb_pd_sink, &options))) { + LOG_ERROR("usb_pd_sink_new"); + return err; + } + + if ((err = usb_pd_sink_setup(usb_pd_sink))) { + LOG_ERROR("usb_pd_sink_setup"); + return err; + } + + return 0; + } + + int start_usb_pd_sink() + { + int err; + + if ((err = usb_pd_sink_start(usb_pd_sink))) { + LOG_ERROR("usb_pd_sink_start"); + return err; + } + + return 0; + } +#endif diff --git a/main/usb_pd_sink.h b/main/usb_pd_sink.h new file mode 100644 index 00000000..a6fa5ef8 --- /dev/null +++ b/main/usb_pd_sink.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#if CONFIG_USB_PD_SINK_ENABLED + int init_usb_pd_sink(); + int start_usb_pd_sink(); +#endif diff --git a/main/usb_pd_state.h b/main/usb_pd_state.h new file mode 100644 index 00000000..99555157 --- /dev/null +++ b/main/usb_pd_state.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#if CONFIG_USB_PD_SINK_ENABLED + #include + + extern struct usb_pd_sink *usb_pd_sink; +#endif diff --git a/projects/esp8266/Makefile b/projects/esp8266/Makefile index 2bed4eca..3bbe3380 100644 --- a/projects/esp8266/Makefile +++ b/projects/esp8266/Makefile @@ -2,6 +2,7 @@ PROJECT_NAME := qmsk-esp EXTRA_COMPONENT_DIRS := +EXCLUDE_COMPONENTS := usb_pd_sink COMPONENT_DIRS := $(abspath ../../components) ${EXTRA_COMPONENT_DIRS} ${IDF_PATH}/components $(abspath ../../main) include $(IDF_PATH)/make/project.mk