mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-15 05:05:52 +00:00
ccgx: Wait for hardware to settle when coming out of reset
This commit is contained in:
parent
3990a71401
commit
fd7f13f905
@ -122,6 +122,78 @@ typedef enum {
|
||||
CY_I2C_DATA_CONFIG_NAK = 1 << 1, /* only for read */
|
||||
} CyI2CDataConfigBits;
|
||||
|
||||
typedef enum {
|
||||
HPI_DEV_REG_DEVICE_MODE = 0,
|
||||
HPI_DEV_REG_BOOT_MODE_REASON,
|
||||
HPI_DEV_REG_SI_ID,
|
||||
HPI_DEV_REG_SI_ID_LSB,
|
||||
HPI_DEV_REG_BL_LAST_ROW,
|
||||
HPI_DEV_REG_BL_LAST_ROW_LSB,
|
||||
HPI_DEV_REG_INTR_ADDR,
|
||||
HPI_DEV_REG_JUMP_TO_BOOT,
|
||||
HPI_DEV_REG_RESET_ADDR,
|
||||
HPI_DEV_REG_RESET_CMD,
|
||||
HPI_DEV_REG_ENTER_FLASH_MODE,
|
||||
HPI_DEV_REG_VALIDATE_FW_ADDR,
|
||||
HPI_DEV_REG_FLASH_READ_WRITE,
|
||||
HPI_DEV_REG_FLASH_READ_WRITE_CMD,
|
||||
HPI_DEV_REG_FLASH_ROW,
|
||||
HPI_DEV_REG_FLASH_ROW_LSB,
|
||||
HPI_DEV_REG_ALL_VERSION,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_1,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_2,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_3,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_4,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_5,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_6,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_7,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_8,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_9,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_10,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_11,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_12,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_13,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_14,
|
||||
HPI_DEV_REG_ALL_VERSION_BYTE_15,
|
||||
HPI_DEV_REG_FW_2_VERSION,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_1,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_2,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_3,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_4,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_5,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_6,
|
||||
HPI_DEV_REG_FW_2_VERSION_BYTE_7,
|
||||
HPI_DEV_REG_FW_BIN_LOC,
|
||||
HPI_DEV_REG_FW_1_BIN_LOC_LSB,
|
||||
HPI_DEV_REG_FW_2_BIN_LOC_MSB,
|
||||
HPI_DEV_REG_FW_2_BIN_LOC_LSB,
|
||||
HPI_DEV_REG_PORT_ENABLE,
|
||||
HPI_DEV_SPACE_REG_LEN,
|
||||
HPI_DEV_REG_RESPONSE = 0x007E,
|
||||
HPI_DEV_REG_FLASH_MEM = 0x0200
|
||||
} HPIDevReg;
|
||||
|
||||
typedef enum {
|
||||
HPI_REG_SECTION_DEV = 0, /* device information */
|
||||
HPI_REG_SECTION_PORT_0, /* USB-PD Port 0 related */
|
||||
HPI_REG_SECTION_PORT_1, /* USB-PD Port 1 related */
|
||||
HPI_REG_SECTION_ALL /* select all registers */
|
||||
} HPIRegSection;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint16 event_code;
|
||||
guint16 event_length;
|
||||
guint8 event_data[128];
|
||||
} HPIEvent;
|
||||
|
||||
typedef enum {
|
||||
HPI_REG_PART_REG = 0, /* register region */
|
||||
HPI_REG_PART_DATA = 1, /* data memory */
|
||||
HPI_REG_PART_FLASH = 2, /* flash memory */
|
||||
HPI_REG_PART_PDDATA_READ = 4, /* read data memory */
|
||||
HPI_REG_PART_PDDATA_WRITE = 8, /* write data memory */
|
||||
} HPIRegPart;
|
||||
|
||||
typedef enum {
|
||||
CY_PD_REG_DEVICE_MODE_ADDR,
|
||||
CY_PD_BOOT_MODE_REASON,
|
||||
@ -166,3 +238,107 @@ typedef enum {
|
||||
#define PD_I2CM_USB_EP_BULK_IN 0x83
|
||||
#define PD_I2CM_USB_EP_INTR_IN 0x84
|
||||
|
||||
typedef enum {
|
||||
/* responses */
|
||||
CY_PD_RESP_NO_RESPONSE,
|
||||
CY_PD_RESP_SUCCESS = 0x02,
|
||||
CY_PD_RESP_FLASH_DATA_AVAILABLE,
|
||||
CY_PD_RESP_INVALID_COMMAND = 0x05,
|
||||
CY_PD_RESP_COLLISION_DETECTED,
|
||||
CY_PD_RESP_FLASH_UPDATE_FAILED,
|
||||
CY_PD_RESP_INVALID_FW,
|
||||
CY_PD_RESP_INVALID_ARGUMENTS,
|
||||
CY_PD_RESP_NOT_SUPPORTED,
|
||||
CY_PD_RESP_TRANSACTION_FAILED = 0x0C,
|
||||
CY_PD_RESP_PD_COMMAND_FAILED,
|
||||
CY_PD_RESP_UNDEFINED,
|
||||
CY_PD_RESP_RA_DETECT = 0x10,
|
||||
CY_PD_RESP_RA_REMOVED,
|
||||
|
||||
/* device specific events */
|
||||
CY_PD_RESP_RESET_COMPLETE = 0x80,
|
||||
CY_PD_RESP_MESSAGE_QUEUE_OVERFLOW,
|
||||
|
||||
/* type-c specific events */
|
||||
CY_PD_RESP_OVER_CURRENT_DETECTED,
|
||||
CY_PD_RESP_OVER_VOLTAGE_DETECTED,
|
||||
CY_PD_RESP_TYPC_C_CONNECTED,
|
||||
CY_PD_RESP_TYPE_C_DISCONNECTED,
|
||||
|
||||
/* pd specific events and asynchronous messages */
|
||||
CY_PD_RESP_PD_CONTRACT_ESTABLISHED,
|
||||
CY_PD_RESP_DR_SWAP,
|
||||
CY_PD_RESP_PR_SWAP,
|
||||
CY_PD_RESP_VCON_SWAP,
|
||||
CY_PD_RESP_PS_RDY,
|
||||
CY_PD_RESP_GOTOMIN,
|
||||
CY_PD_RESP_ACCEPT_MESSAGE,
|
||||
CY_PD_RESP_REJECT_MESSAGE,
|
||||
CY_PD_RESP_WAIT_MESSAGE,
|
||||
CY_PD_RESP_HARD_RESET,
|
||||
CY_PD_RESP_VDM_RECEIVED,
|
||||
CY_PD_RESP_SRC_CAP_RCVD,
|
||||
CY_PD_RESP_SINK_CAP_RCVD,
|
||||
CY_PD_RESP_DP_ALTERNATE_MODE,
|
||||
CY_PD_RESP_DP_DEVICE_CONNECTED,
|
||||
CY_PD_RESP_DP_DEVICE_NOT_CONNECTED,
|
||||
CY_PD_RESP_DP_SID_NOT_FOUND,
|
||||
CY_PD_RESP_MULTIPLE_SVID_DISCOVERED,
|
||||
CY_PD_RESP_DP_FUNCTION_NOT_SUPPORTED,
|
||||
CY_PD_RESP_DP_PORT_CONFIG_NOT_SUPPORTED,
|
||||
CY_PD_HARD_RESET_SENT,
|
||||
CY_PD_SOFT_RESET_SENT,
|
||||
CY_PD_CABLE_RESET_SENT,
|
||||
CY_PD_SOURCE_DISBALED_STATE_ENTERED,
|
||||
CY_PD_SENDER_RESPONSE_TIMER_TIMEOUT,
|
||||
CY_PD_NO_VDM_RESPONSE_RECEIVED
|
||||
} CyPDResp;
|
||||
|
||||
typedef enum {
|
||||
HPI_RESPONSE_NO_RESPONSE,
|
||||
HPI_RESPONSE_SUCCESS = 0x02,
|
||||
HPI_RESPONSE_FLASH_DATA_AVAILABLE,
|
||||
HPI_RESPONSE_INVALID_COMMAND = 0x05,
|
||||
HPI_RESPONSE_FLASH_UPDATE_FAILED = 0x07,
|
||||
HPI_RESPONSE_INVALID_FW,
|
||||
HPI_RESPONSE_INVALID_ARGUMENT,
|
||||
HPI_RESPONSE_NOT_SUPPORTED,
|
||||
HPI_RESPONSE_PD_TRANSACTION_FAILED = 0x0C,
|
||||
HPI_RESPONSE_PD_COMMAND_FAILED,
|
||||
HPI_RESPONSE_UNDEFINED_ERROR = 0x0F,
|
||||
HPI_EVENT_RESET_COMPLETE = 0x80,
|
||||
HPI_EVENT_MSG_OVERFLOW,
|
||||
HPI_EVENT_OC_DETECT,
|
||||
HPI_EVENT_OV_DETECT,
|
||||
HPI_EVENT_CONNECT_DETECT,
|
||||
HPI_EVENT_DISCONNECT_DETECT,
|
||||
HPI_EVENT_NEGOTIATION_COMPLETE,
|
||||
HPI_EVENT_SWAP_COMPLETE,
|
||||
HPI_EVENT_PS_RDY_RECEIVED = 0x8A,
|
||||
HPI_EVENT_GOTO_MIN_RECEIVED,
|
||||
HPI_EVENT_ACCEPT_RECEIVED,
|
||||
HPI_EVENT_REJECT_RECEIVED,
|
||||
HPI_EVENT_WAIT_RECEIVED,
|
||||
HPI_EVENT_HARD_RESET_RECEIVED,
|
||||
HPI_EVENT_VDM_RECEIVED = 0x90,
|
||||
HPI_EVENT_SOURCE_CAP_RECEIVED,
|
||||
HPI_EVENT_SINK_CAP_RECEIVED,
|
||||
HPI_EVENT_DP_MODE_ENTERED,
|
||||
HPI_EVENT_DP_STATUS_UPDATE,
|
||||
HPI_EVENT_DP_SID_NOT_FOUND = 0x96,
|
||||
HPI_EVENT_DP_MANY_SID_FOUND,
|
||||
HPI_EVENT_DP_NO_CABLE_SUPPORT,
|
||||
HPI_EVENT_DP_NO_UFP_SUPPORT,
|
||||
HPI_EVENT_HARD_RESET_SENT,
|
||||
HPI_EVENT_SOFT_RESET_SENT,
|
||||
HPI_EVENT_CABLE_RESET_SENT,
|
||||
HPI_EVENT_SOURCE_DISABLED,
|
||||
HPI_EVENT_SENDER_TIMEOUT,
|
||||
HPI_EVENT_VDM_NO_RESPONSE,
|
||||
HPI_EVENT_UNEXPECTED_VOLTAGE,
|
||||
HPI_EVENT_ERROR_RECOVERY,
|
||||
HPI_EVENT_EMCA_DETECT = 0xA6,
|
||||
HPI_EVENT_RP_CHANGE_DETECT = 0xAA,
|
||||
HPI_EVENT_TB_ENTERED = 0xB0,
|
||||
HPI_EVENT_TB_EXITED
|
||||
} HPIResp;
|
||||
|
@ -33,6 +33,9 @@ struct _FuCcgxHpiDevice
|
||||
|
||||
G_DEFINE_TYPE (FuCcgxHpiDevice, fu_ccgx_hpi_device, FU_TYPE_USB_DEVICE)
|
||||
|
||||
#define HPI_CMD_SETUP_EVENT_WAIT_TIME_MS 200
|
||||
#define HPI_CMD_RESET_COMPLETE_DELAY_US 150000
|
||||
|
||||
static void
|
||||
fu_ccgx_hpi_device_to_string (FuDevice *device, guint idt, GString *str)
|
||||
{
|
||||
@ -305,6 +308,212 @@ fu_ccgx_hpi_device_reg_read (FuCcgxHpiDevice *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_reg_write (FuCcgxHpiDevice *self,
|
||||
guint16 addr,
|
||||
guint8 *buf,
|
||||
guint16 bufsz,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree guint8 *bufhw = g_malloc0 (bufsz + self->hpi_addrsz + 1);
|
||||
for (guint32 i = 0; i < self->hpi_addrsz; i++)
|
||||
bufhw[i] = (guint8) (addr >> (8*i));
|
||||
memcpy (&bufhw[self->hpi_addrsz], buf, bufsz);
|
||||
if (!fu_ccgx_hpi_device_i2c_write (self, bufhw, bufsz + self->hpi_addrsz,
|
||||
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
|
||||
error)) {
|
||||
g_prefix_error (error, "reg write error: ");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_clear_intr (FuCcgxHpiDevice *self,
|
||||
HPIRegSection section,
|
||||
GError **error)
|
||||
{
|
||||
guint8 intr = 0;
|
||||
for (guint8 i = 0; i <= self->num_ports; i++) {
|
||||
if (i == section || section == HPI_REG_SECTION_ALL)
|
||||
intr |= 1 << i;
|
||||
}
|
||||
if (!fu_ccgx_hpi_device_reg_write (self, HPI_DEV_REG_INTR_ADDR,
|
||||
&intr, sizeof(intr),
|
||||
error)) {
|
||||
g_prefix_error (error, "failed to clear interrupt: ");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint16
|
||||
fu_ccgx_hpi_device_reg_addr_gen (guint8 section, guint8 part, guint8 addr)
|
||||
{
|
||||
return (((guint16) section) << 12) | (((guint16) part) << 8) | addr;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_read_event_reg (FuCcgxHpiDevice *self,
|
||||
HPIRegSection section,
|
||||
HPIEvent *event,
|
||||
GError **error)
|
||||
{
|
||||
|
||||
if (section != HPI_REG_SECTION_DEV) {
|
||||
guint16 reg_addr;
|
||||
guint8 buf[4] = { 0x0 };
|
||||
|
||||
/* first read the response register */
|
||||
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (section,
|
||||
HPI_REG_PART_PDDATA_READ,
|
||||
0);
|
||||
if (!fu_ccgx_hpi_device_reg_read (self,
|
||||
reg_addr,
|
||||
buf, sizeof(buf),
|
||||
error)) {
|
||||
g_prefix_error (error, "read response reg error:");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* byte 1 is reserved and should read as zero */
|
||||
buf[1] = 0;
|
||||
memcpy ((guint8 *) event, buf, sizeof(buf));
|
||||
if (event->event_length != 0) {
|
||||
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (section,
|
||||
HPI_REG_PART_PDDATA_READ,
|
||||
sizeof(buf));
|
||||
if (!fu_ccgx_hpi_device_reg_read (self,
|
||||
reg_addr,
|
||||
event->event_data,
|
||||
event->event_length,
|
||||
error)) {
|
||||
g_prefix_error (error, "read event data error:");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
guint8 buf[2] = { 0x0 };
|
||||
if (!fu_ccgx_hpi_device_reg_read (self,
|
||||
CY_PD_REG_RESPONSE_ADDR,
|
||||
buf, sizeof(buf),
|
||||
error)) {
|
||||
g_prefix_error (error, "read response reg error:");
|
||||
return FALSE;
|
||||
}
|
||||
event->event_code = buf[0];
|
||||
event->event_length = buf[1];
|
||||
if (event->event_length != 0) {
|
||||
/* read the data memory */
|
||||
if (!fu_ccgx_hpi_device_reg_read (self,
|
||||
CY_PD_REG_BOOTDATA_MEMORY_ADDR,
|
||||
event->event_data,
|
||||
event->event_length,
|
||||
error)) {
|
||||
g_prefix_error (error, "read event data error:");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* success */
|
||||
return fu_ccgx_hpi_device_clear_intr (self, section, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_app_read_intr_reg (FuCcgxHpiDevice *self,
|
||||
HPIRegSection section,
|
||||
HPIEvent *event_array,
|
||||
guint8 *event_count,
|
||||
GError **error)
|
||||
{
|
||||
guint16 reg_addr;
|
||||
guint8 event_count_tmp = 0;
|
||||
guint8 intr_reg = 0;
|
||||
|
||||
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (HPI_REG_SECTION_DEV,
|
||||
HPI_REG_PART_REG,
|
||||
HPI_DEV_REG_INTR_ADDR);
|
||||
if (!fu_ccgx_hpi_device_reg_read (self,
|
||||
reg_addr,
|
||||
&intr_reg,
|
||||
sizeof(intr_reg),
|
||||
error)) {
|
||||
g_prefix_error (error, "read intr reg error: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* device section will not come here */
|
||||
for (guint8 i = 0; i <= self->num_ports; i++) {
|
||||
/* check if this section is needed */
|
||||
if (section == i || section == HPI_REG_SECTION_ALL) {
|
||||
/* check whether this section has any event/response */
|
||||
if ((1 << i) & intr_reg) {
|
||||
if (!fu_ccgx_hpi_device_read_event_reg (self,
|
||||
section,
|
||||
&event_array[i],
|
||||
error)) {
|
||||
g_prefix_error (error, "read event error: ");
|
||||
return FALSE;
|
||||
}
|
||||
event_count_tmp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event_count != NULL)
|
||||
*event_count = event_count_tmp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_wait_for_event (FuCcgxHpiDevice *self,
|
||||
HPIRegSection section,
|
||||
HPIEvent *event_array,
|
||||
guint32 timeout_ms,
|
||||
GError **error)
|
||||
{
|
||||
guint8 event_count = 0;
|
||||
g_autoptr(GTimer) start_time = g_timer_new ();
|
||||
do {
|
||||
if (!fu_ccgx_hpi_device_app_read_intr_reg (self,
|
||||
section,
|
||||
event_array,
|
||||
&event_count,
|
||||
error))
|
||||
return FALSE;
|
||||
if (event_count > 0)
|
||||
return TRUE;
|
||||
} while (g_timer_elapsed (start_time, NULL) * 1000.f <= timeout_ms);
|
||||
|
||||
/* timed out */
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_TIMED_OUT,
|
||||
"failed to wait for event in %ums",
|
||||
timeout_ms);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_get_event (FuCcgxHpiDevice *self,
|
||||
HPIRegSection reg_section,
|
||||
CyPDResp *event,
|
||||
guint32 io_timeout,
|
||||
GError **error )
|
||||
{
|
||||
HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = { 0x0 };
|
||||
if (!fu_ccgx_hpi_device_wait_for_event (self,
|
||||
reg_section,
|
||||
event_array,
|
||||
io_timeout,
|
||||
error)) {
|
||||
g_prefix_error (error, "failed to get event: ");
|
||||
return FALSE;
|
||||
}
|
||||
*event = event_array[reg_section].event_code;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_ccgx_hpi_device_attach (FuDevice *device, GError **error)
|
||||
{
|
||||
@ -412,8 +621,10 @@ fu_ccgx_hpi_device_setup (FuDevice *device, GError **error)
|
||||
{
|
||||
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
|
||||
CyI2CConfig i2c_config = { 0x0 };
|
||||
guint32 hpi_event = 0;
|
||||
guint8 mode = 0;
|
||||
g_autofree gchar *instance_id = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
|
||||
/* set the new config */
|
||||
if (!fu_ccgx_hpi_device_get_i2c_config (self, &i2c_config, error)) {
|
||||
@ -492,6 +703,23 @@ fu_ccgx_hpi_device_setup (FuDevice *device, GError **error)
|
||||
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
||||
}
|
||||
|
||||
/* if we are coming back from reset, wait for hardware to settle */
|
||||
if (!fu_ccgx_hpi_device_get_event (self,
|
||||
HPI_REG_SECTION_DEV,
|
||||
&hpi_event,
|
||||
HPI_CMD_SETUP_EVENT_WAIT_TIME_MS,
|
||||
&error_local)) {
|
||||
if (!g_error_matches (error_local,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_TIMED_OUT)) {
|
||||
g_propagate_error (error, g_steal_pointer (&error_local));
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (hpi_event == CY_PD_RESP_RESET_COMPLETE)
|
||||
g_usleep (HPI_CMD_RESET_COMPLETE_DELAY_US);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user