mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 16:45:45 +00:00
1653 lines
46 KiB
C
1653 lines
46 KiB
C
/*
|
|
* Copyright (C) 2020 Cypress Semiconductor Corporation.
|
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "fu-ccgx-common.h"
|
|
#include "fu-ccgx-firmware.h"
|
|
#include "fu-ccgx-hpi-common.h"
|
|
#include "fu-ccgx-hpi-device.h"
|
|
|
|
struct _FuCcgxHpiDevice {
|
|
FuUsbDevice parent_instance;
|
|
guint8 inf_num; /* USB interface number */
|
|
guint8 scb_index;
|
|
guint16 silicon_id;
|
|
guint16 fw_app_type;
|
|
guint8 hpi_addrsz; /* hpiv1: 1 byte, hpiv2: 2 byte */
|
|
guint8 num_ports; /* max number of ports */
|
|
FWMode fw_mode;
|
|
FWImageType fw_image_type;
|
|
guint8 target_address;
|
|
guint8 ep_bulk_in;
|
|
guint8 ep_bulk_out;
|
|
guint8 ep_intr_in;
|
|
guint32 flash_row_size;
|
|
guint32 flash_size;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuCcgxHpiDevice, fu_ccgx_hpi_device, FU_TYPE_USB_DEVICE)
|
|
|
|
#define HPI_CMD_REG_READ_WRITE_DELAY_US 10000
|
|
#define HPI_CMD_ENTER_FLASH_MODE_DELAY_US 20000
|
|
#define HPI_CMD_SETUP_EVENT_WAIT_TIME_MS 200
|
|
#define HPI_CMD_SETUP_EVENT_CLEAR_TIME_MS 150
|
|
#define HPI_CMD_COMMAND_RESPONSE_TIME_MS 500
|
|
#define HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS 30
|
|
#define HPI_CMD_RESET_COMPLETE_DELAY_US 150000
|
|
#define HPI_CMD_RETRY_DELAY 30 /* ms */
|
|
#define HPI_CMD_RESET_RETRY_CNT 3
|
|
#define HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT 3
|
|
#define HPI_CMD_FLASH_WRITE_RETRY_CNT 3
|
|
#define HPI_CMD_FLASH_READ_RETRY_CNT 3
|
|
#define HPI_CMD_VALIDATE_FW_RETRY_CNT 3
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_to_string(FuDevice *device, guint idt, GString *str)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
fu_common_string_append_kx(str, idt, "InfNum", self->inf_num);
|
|
fu_common_string_append_kx(str, idt, "ScbIndex", self->scb_index);
|
|
fu_common_string_append_kx(str, idt, "SiliconId", self->silicon_id);
|
|
fu_common_string_append_kx(str, idt, "FwAppType", self->fw_app_type);
|
|
fu_common_string_append_kx(str, idt, "HpiAddrsz", self->hpi_addrsz);
|
|
fu_common_string_append_kx(str, idt, "NumPorts", self->num_ports);
|
|
fu_common_string_append_kv(str, idt, "FWMode", fu_ccgx_fw_mode_to_string(self->fw_mode));
|
|
fu_common_string_append_kv(str,
|
|
idt,
|
|
"FwImageType",
|
|
fu_ccgx_fw_image_type_to_string(self->fw_image_type));
|
|
fu_common_string_append_kx(str, idt, "EpBulkIn", self->ep_bulk_in);
|
|
fu_common_string_append_kx(str, idt, "EpBulkOut", self->ep_bulk_out);
|
|
fu_common_string_append_kx(str, idt, "EpIntrIn", self->ep_intr_in);
|
|
if (self->flash_row_size > 0)
|
|
fu_common_string_append_kx(str, idt, "CcgxFlashRowSize", self->flash_row_size);
|
|
if (self->flash_size > 0)
|
|
fu_common_string_append_kx(str, idt, "CcgxFlashSize", self->flash_size);
|
|
}
|
|
|
|
typedef struct {
|
|
guint8 mode;
|
|
guint16 addr;
|
|
guint8 *buf;
|
|
gsize bufsz;
|
|
} FuCcgxHpiDeviceRetryHelper;
|
|
|
|
typedef struct {
|
|
guint16 addr;
|
|
const guint8 *buf;
|
|
gsize bufsz;
|
|
} FuCcgxHpiFlashWriteRetryHelper;
|
|
|
|
typedef struct {
|
|
guint16 addr;
|
|
guint8 *buf;
|
|
gsize bufsz;
|
|
} FuCcgxHpiFlashReadRetryHelper;
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_i2c_reset_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data;
|
|
g_autoptr(GError) error_local = NULL;
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_RESET_CMD,
|
|
(self->scb_index << CY_SCB_INDEX_POS) | helper->mode,
|
|
0x0,
|
|
NULL,
|
|
0x0,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to reset i2c: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_check_i2c_status(FuCcgxHpiDevice *self, guint8 mode, GError **error)
|
|
{
|
|
guint8 buf[CY_I2C_GET_STATUS_LEN] = {0x0};
|
|
g_autoptr(GError) error_local = NULL;
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_GET_STATUS_CMD,
|
|
(((guint16)self->scb_index) << CY_SCB_INDEX_POS) | mode,
|
|
0x0,
|
|
buf,
|
|
sizeof(buf),
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to get i2c status: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
if (buf[0] & CY_I2C_ERROR_BIT) {
|
|
if (buf[0] & 0x80) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
"i2c status write error: 0x%x",
|
|
buf[0]);
|
|
return FALSE;
|
|
}
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"i2c status read error: 0x%x",
|
|
buf[0]);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_get_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error)
|
|
{
|
|
g_autoptr(GError) error_local = NULL;
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_GET_CONFIG_CMD,
|
|
((guint16)self->scb_index) << CY_SCB_INDEX_POS,
|
|
0x0,
|
|
(guint8 *)i2c_config,
|
|
sizeof(*i2c_config),
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"i2c get config error: control xfer: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_set_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error)
|
|
{
|
|
g_autoptr(GError) error_local = NULL;
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_SET_CONFIG_CMD,
|
|
((guint16)self->scb_index) << CY_SCB_INDEX_POS,
|
|
0x0,
|
|
(guint8 *)i2c_config,
|
|
sizeof(*i2c_config),
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"i2c set config error: control xfer: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_wait_for_notify(FuCcgxHpiDevice *self, guint16 *bytes_pending, GError **error)
|
|
{
|
|
guint8 buf[CY_I2C_EVENT_NOTIFICATION_LEN] = {0x0};
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
if (!g_usb_device_interrupt_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
self->ep_intr_in,
|
|
buf,
|
|
sizeof(buf),
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to get i2c event: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
|
|
/* @bytes_pending available on failure */
|
|
if (buf[0] & CY_I2C_ERROR_BIT) {
|
|
if (bytes_pending != NULL) {
|
|
if (!fu_common_read_uint16_safe(buf,
|
|
sizeof(buf),
|
|
0x01,
|
|
bytes_pending,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
if (buf[0] & 0x80) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
"i2c status write error: 0x%x",
|
|
buf[0]);
|
|
return FALSE;
|
|
}
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"i2c status read error: 0x%x",
|
|
buf[0]);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_i2c_read(FuCcgxHpiDevice *self,
|
|
guint8 *buf,
|
|
gsize bufsz,
|
|
CyI2CDataConfigBits cfg_bits,
|
|
GError **error)
|
|
{
|
|
guint8 target_address = 0;
|
|
|
|
if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_READ, error)) {
|
|
g_prefix_error(error, "i2c read error: ");
|
|
return FALSE;
|
|
}
|
|
target_address = (self->target_address & 0x7F) | (self->scb_index << 7);
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_READ_CMD,
|
|
(((guint16)target_address) << 8) | cfg_bits,
|
|
bufsz,
|
|
NULL,
|
|
0x0,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "i2c read error: control xfer: ");
|
|
return FALSE;
|
|
}
|
|
if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
self->ep_bulk_in,
|
|
buf,
|
|
bufsz,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "i2c read error: bulk xfer: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 10 msec delay */
|
|
g_usleep(I2C_READ_WRITE_DELAY_US);
|
|
if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) {
|
|
g_prefix_error(error, "i2c read error: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_i2c_write(FuCcgxHpiDevice *self,
|
|
guint8 *buf,
|
|
gsize bufsz,
|
|
CyI2CDataConfigBits cfg_bits,
|
|
GError **error)
|
|
{
|
|
guint8 target_address;
|
|
|
|
if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) {
|
|
g_prefix_error(error, "i2c get status error: ");
|
|
return FALSE;
|
|
}
|
|
target_address = (self->target_address & 0x7F) | (self->scb_index << 7);
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_WRITE_CMD,
|
|
((guint16)target_address << 8) |
|
|
(cfg_bits & CY_I2C_DATA_CONFIG_STOP),
|
|
bufsz, /* idx */
|
|
NULL,
|
|
0x0,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "i2c write error: control xfer: ");
|
|
return FALSE;
|
|
}
|
|
if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
self->ep_bulk_out,
|
|
buf,
|
|
bufsz,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "i2c write error: bulk xfer: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 10 msec delay */
|
|
g_usleep(I2C_READ_WRITE_DELAY_US);
|
|
if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) {
|
|
g_prefix_error(error, "i2c wait for notification error: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_i2c_write_no_resp(FuCcgxHpiDevice *self,
|
|
guint8 *buf,
|
|
gsize bufsz,
|
|
CyI2CDataConfigBits cfg_bits,
|
|
GError **error)
|
|
{
|
|
guint8 target_address = 0;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) {
|
|
g_prefix_error(error, "i2c write error: ");
|
|
return FALSE;
|
|
}
|
|
target_address = (self->target_address & 0x7F) | (self->scb_index << 7);
|
|
if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
CY_I2C_WRITE_CMD,
|
|
((guint16)target_address << 8) |
|
|
(cfg_bits & CY_I2C_DATA_CONFIG_STOP),
|
|
bufsz,
|
|
NULL,
|
|
0x0,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "i2c write error: control xfer: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* device will reboot after this, so txfer will fail */
|
|
if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)),
|
|
self->ep_bulk_out,
|
|
buf,
|
|
bufsz,
|
|
NULL,
|
|
FU_CCGX_HPI_WAIT_TIMEOUT,
|
|
NULL,
|
|
&error_local)) {
|
|
g_debug("ignoring i2c write error: bulk xfer: %s", error_local->message);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_reg_read_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data;
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
g_autofree guint8 *bufhw = g_malloc0(self->hpi_addrsz);
|
|
|
|
for (guint32 i = 0; i < self->hpi_addrsz; i++)
|
|
bufhw[i] = (guint8)(helper->addr >> (8 * i));
|
|
if (!fu_ccgx_hpi_device_i2c_write(self,
|
|
bufhw,
|
|
self->hpi_addrsz,
|
|
CY_I2C_DATA_CONFIG_NAK,
|
|
error)) {
|
|
g_prefix_error(error, "write error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_i2c_read(self,
|
|
helper->buf,
|
|
helper->bufsz,
|
|
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
|
|
error)) {
|
|
g_prefix_error(error, "read error: ");
|
|
return FALSE;
|
|
}
|
|
g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_reg_read(FuCcgxHpiDevice *self,
|
|
guint16 addr,
|
|
guint8 *buf,
|
|
gsize bufsz,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiDeviceRetryHelper helper = {
|
|
.addr = addr,
|
|
.mode = CY_I2C_MODE_READ,
|
|
.buf = buf,
|
|
.bufsz = bufsz,
|
|
};
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_device_reg_read_cb,
|
|
HPI_CMD_RESET_RETRY_CNT,
|
|
&helper,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_reg_write_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data;
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
g_autofree guint8 *bufhw = g_malloc0(helper->bufsz + self->hpi_addrsz);
|
|
|
|
for (guint32 i = 0; i < self->hpi_addrsz; i++)
|
|
bufhw[i] = (guint8)(helper->addr >> (8 * i));
|
|
memcpy(&bufhw[self->hpi_addrsz], helper->buf, helper->bufsz);
|
|
if (!fu_ccgx_hpi_device_i2c_write(self,
|
|
bufhw,
|
|
helper->bufsz + self->hpi_addrsz,
|
|
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
|
|
error)) {
|
|
g_prefix_error(error, "reg write error: ");
|
|
return FALSE;
|
|
}
|
|
g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_reg_write(FuCcgxHpiDevice *self,
|
|
guint16 addr,
|
|
const guint8 *buf,
|
|
gsize bufsz,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiDeviceRetryHelper helper = {
|
|
.addr = addr,
|
|
.mode = CY_I2C_MODE_WRITE,
|
|
.buf = (guint8 *)buf,
|
|
.bufsz = bufsz,
|
|
};
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_device_reg_write_cb,
|
|
HPI_CMD_RESET_RETRY_CNT,
|
|
&helper,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_reg_write_no_resp(FuCcgxHpiDevice *self,
|
|
guint16 addr,
|
|
guint8 *buf,
|
|
guint16 bufsz,
|
|
GError **error)
|
|
{
|
|
g_autofree guint8 *bufhw = g_malloc0(bufsz + self->hpi_addrsz);
|
|
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_no_resp(self,
|
|
bufhw,
|
|
bufsz + self->hpi_addrsz,
|
|
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
|
|
error)) {
|
|
g_prefix_error(error, "reg write no-resp error: ");
|
|
return FALSE;
|
|
}
|
|
g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US);
|
|
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_clear_all_events(FuCcgxHpiDevice *self, guint32 io_timeout, GError **error)
|
|
{
|
|
HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = {0x0};
|
|
if (io_timeout == 0) {
|
|
return fu_ccgx_hpi_device_app_read_intr_reg(self,
|
|
HPI_REG_SECTION_ALL,
|
|
event_array,
|
|
NULL,
|
|
error);
|
|
}
|
|
for (guint8 i = 0; i < self->num_ports; i++) {
|
|
g_autoptr(GError) error_local = NULL;
|
|
if (!fu_ccgx_hpi_device_wait_for_event(self,
|
|
i,
|
|
event_array,
|
|
io_timeout,
|
|
&error_local)) {
|
|
if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
|
|
g_propagate_prefixed_error(error,
|
|
g_steal_pointer(&error_local),
|
|
"failed to clear events: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_validate_fw_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
guint8 *fw_index = (guint8 *)user_data;
|
|
CyPDResp hpi_event = 0;
|
|
|
|
g_return_val_if_fail(fw_index != NULL, FALSE);
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
|
|
if (!fu_ccgx_hpi_device_reg_write(self, CY_PD_REG_VALIDATE_FW_ADDR, fw_index, 1, error)) {
|
|
g_prefix_error(error, "validate fw error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_get_event(self,
|
|
HPI_REG_SECTION_DEV,
|
|
&hpi_event,
|
|
HPI_CMD_COMMAND_RESPONSE_TIME_MS,
|
|
error)) {
|
|
g_prefix_error(error, "validate fw resp error: ");
|
|
return FALSE;
|
|
}
|
|
if (hpi_event != CY_PD_RESP_SUCCESS) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"validate failed: %s [0x%x]",
|
|
fu_ccgx_pd_resp_to_string(hpi_event),
|
|
hpi_event);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_validate_fw(FuCcgxHpiDevice *self, guint8 fw_index, GError **error)
|
|
{
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_validate_fw_cb,
|
|
HPI_CMD_VALIDATE_FW_RETRY_CNT,
|
|
&fw_index,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_enter_flash_mode_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
CyPDResp hpi_event = 0;
|
|
guint8 buf[] = {CY_PD_ENTER_FLASHING_MODE_CMD_SIG};
|
|
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
if (!fu_ccgx_hpi_device_reg_write(self,
|
|
CY_PD_REG_ENTER_FLASH_MODE_ADDR,
|
|
buf,
|
|
sizeof(buf),
|
|
error)) {
|
|
g_prefix_error(error, "enter flash mode error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_get_event(self,
|
|
HPI_REG_SECTION_DEV,
|
|
&hpi_event,
|
|
HPI_CMD_COMMAND_RESPONSE_TIME_MS,
|
|
error)) {
|
|
g_prefix_error(error, "enter flash mode resp error: ");
|
|
return FALSE;
|
|
}
|
|
if (hpi_event != CY_PD_RESP_SUCCESS) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"enter flash failed: %s [0x%x]",
|
|
fu_ccgx_pd_resp_to_string(hpi_event),
|
|
hpi_event);
|
|
return FALSE;
|
|
}
|
|
|
|
/* wait 10 msec */
|
|
g_usleep(HPI_CMD_ENTER_FLASH_MODE_DELAY_US);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_enter_flash_mode(FuCcgxHpiDevice *self, GError **error)
|
|
{
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_enter_flash_mode_cb,
|
|
HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT,
|
|
NULL,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_leave_flash_mode_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
CyPDResp hpi_event = 0;
|
|
guint8 buf = {0x0};
|
|
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
|
|
if (!fu_ccgx_hpi_device_reg_write(self,
|
|
CY_PD_REG_ENTER_FLASH_MODE_ADDR,
|
|
&buf,
|
|
sizeof(buf),
|
|
error)) {
|
|
g_prefix_error(error, "leave flash mode error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_get_event(self,
|
|
HPI_REG_SECTION_DEV,
|
|
&hpi_event,
|
|
HPI_CMD_COMMAND_RESPONSE_TIME_MS,
|
|
error)) {
|
|
g_prefix_error(error, "leave flash mode resp error: ");
|
|
return FALSE;
|
|
}
|
|
if (hpi_event != CY_PD_RESP_SUCCESS) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"leave flash mode failed: %s [0x%x]",
|
|
fu_ccgx_pd_resp_to_string(hpi_event),
|
|
hpi_event);
|
|
return FALSE;
|
|
}
|
|
|
|
/* wait 10 msec */
|
|
g_usleep(HPI_CMD_ENTER_FLASH_MODE_DELAY_US);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_leave_flash_mode(FuCcgxHpiDevice *self, GError **error)
|
|
{
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_leave_flash_mode_cb,
|
|
HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT,
|
|
NULL,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_write_flash_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
FuCcgxHpiFlashWriteRetryHelper *helper = (FuCcgxHpiFlashWriteRetryHelper *)user_data;
|
|
CyPDResp hpi_event = 0;
|
|
guint16 addr_tmp = 0;
|
|
guint8 bufhw[] = {
|
|
CY_PD_FLASH_READ_WRITE_CMD_SIG,
|
|
CY_PD_REG_FLASH_ROW_WRITE_CMD,
|
|
helper->addr & 0xFF,
|
|
helper->addr >> 8,
|
|
};
|
|
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
|
|
/* write data to memory */
|
|
addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM : CY_PD_REG_BOOTDATA_MEMORY_ADDR;
|
|
if (!fu_ccgx_hpi_device_reg_write(self, addr_tmp, helper->buf, helper->bufsz, error)) {
|
|
g_prefix_error(error, "write buf to memory error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_reg_write(self,
|
|
CY_PD_REG_FLASH_READ_WRITE_ADDR,
|
|
bufhw,
|
|
sizeof(bufhw),
|
|
error)) {
|
|
g_prefix_error(error, "write flash error: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* wait until flash is written */
|
|
if (!fu_ccgx_hpi_device_get_event(self,
|
|
HPI_REG_SECTION_DEV,
|
|
&hpi_event,
|
|
HPI_CMD_COMMAND_RESPONSE_TIME_MS,
|
|
error)) {
|
|
g_prefix_error(error, "write flash resp error: ");
|
|
return FALSE;
|
|
}
|
|
if (hpi_event != CY_PD_RESP_SUCCESS) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"write flash failed: %s [0x%x]",
|
|
fu_ccgx_pd_resp_to_string(hpi_event),
|
|
hpi_event);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_write_flash(FuCcgxHpiDevice *self,
|
|
guint16 addr,
|
|
const guint8 *buf,
|
|
guint16 bufsz,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiFlashWriteRetryHelper helper = {
|
|
.addr = addr,
|
|
.buf = buf,
|
|
.bufsz = bufsz,
|
|
};
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_write_flash_cb,
|
|
HPI_CMD_FLASH_WRITE_RETRY_CNT,
|
|
&helper,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_read_flash_cb(FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
FuCcgxHpiFlashReadRetryHelper *helper = (FuCcgxHpiFlashReadRetryHelper *)user_data;
|
|
CyPDResp hpi_event = 0;
|
|
guint16 addr_tmp;
|
|
guint8 bufhw[] = {
|
|
CY_PD_FLASH_READ_WRITE_CMD_SIG,
|
|
CY_PD_REG_FLASH_ROW_READ_CMD,
|
|
helper->addr & 0xFF,
|
|
helper->addr >> 8,
|
|
};
|
|
|
|
/* set address */
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
if (!fu_ccgx_hpi_device_reg_write(self,
|
|
CY_PD_REG_FLASH_READ_WRITE_ADDR,
|
|
bufhw,
|
|
sizeof(bufhw),
|
|
error)) {
|
|
g_prefix_error(error, "read flash error: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* wait until flash is read */
|
|
if (!fu_ccgx_hpi_device_get_event(self,
|
|
HPI_REG_SECTION_DEV,
|
|
&hpi_event,
|
|
HPI_CMD_COMMAND_RESPONSE_TIME_MS,
|
|
error)) {
|
|
g_prefix_error(error, "read flash resp error: ");
|
|
return FALSE;
|
|
}
|
|
if (hpi_event != CY_PD_RESP_FLASH_DATA_AVAILABLE) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"read flash failed: %s [0x%x]",
|
|
fu_ccgx_pd_resp_to_string(hpi_event),
|
|
hpi_event);
|
|
return FALSE;
|
|
}
|
|
addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM : CY_PD_REG_BOOTDATA_MEMORY_ADDR;
|
|
if (!fu_ccgx_hpi_device_reg_read(self, addr_tmp, helper->buf, helper->bufsz, error)) {
|
|
g_prefix_error(error, "read data from memory error: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_read_flash(FuCcgxHpiDevice *self,
|
|
guint16 addr,
|
|
guint8 *buf,
|
|
guint16 bufsz,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiFlashReadRetryHelper helper = {
|
|
.addr = addr,
|
|
.buf = buf,
|
|
.bufsz = bufsz,
|
|
};
|
|
return fu_device_retry(FU_DEVICE(self),
|
|
fu_ccgx_hpi_read_flash_cb,
|
|
HPI_CMD_FLASH_READ_RETRY_CNT,
|
|
&helper,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_detach(FuDevice *device, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
guint8 buf[] = {
|
|
CY_PD_JUMP_TO_ALT_FW_CMD_SIG,
|
|
};
|
|
|
|
/* not required */
|
|
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER) ||
|
|
self->fw_image_type == FW_IMAGE_TYPE_DUAL_SYMMETRIC)
|
|
return TRUE;
|
|
|
|
/* jump to Alt FW */
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_RESTART);
|
|
if (!fu_ccgx_hpi_device_reg_write(self,
|
|
CY_PD_JUMP_TO_BOOT_REG_ADDR,
|
|
buf,
|
|
sizeof(buf),
|
|
error)) {
|
|
g_prefix_error(error, "jump to alt mode error: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* sym not required */
|
|
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_attach(FuDevice *device, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
guint8 buf[] = {
|
|
CY_PD_DEVICE_RESET_CMD_SIG,
|
|
CY_PD_REG_RESET_DEVICE_CMD,
|
|
};
|
|
if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error))
|
|
return FALSE;
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_RESTART);
|
|
if (!fu_ccgx_hpi_device_reg_write_no_resp(self,
|
|
CY_PD_REG_RESET_ADDR,
|
|
buf,
|
|
sizeof(buf),
|
|
error)) {
|
|
g_prefix_error(error, "reset device error: ");
|
|
return FALSE;
|
|
}
|
|
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
|
return TRUE;
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_ccgx_hpi_device_prepare_firmware(FuDevice *device,
|
|
GBytes *fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
FWMode fw_mode;
|
|
guint16 fw_app_type;
|
|
guint16 fw_silicon_id;
|
|
g_autoptr(FuFirmware) firmware = fu_ccgx_firmware_new();
|
|
|
|
/* parse all images */
|
|
if (!fu_firmware_parse(firmware, fw, flags, error))
|
|
return NULL;
|
|
|
|
/* check the silicon ID */
|
|
fw_silicon_id = fu_ccgx_firmware_get_silicon_id(FU_CCGX_FIRMWARE(firmware));
|
|
if (fw_silicon_id != self->silicon_id) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"silicon id mismatch, expected 0x%x, got 0x%x",
|
|
self->silicon_id,
|
|
fw_silicon_id);
|
|
return NULL;
|
|
}
|
|
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) {
|
|
fw_app_type = fu_ccgx_firmware_get_app_type(FU_CCGX_FIRMWARE(firmware));
|
|
if (fw_app_type != self->fw_app_type) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"app type mismatch, expected 0x%x, got 0x%x",
|
|
self->fw_app_type,
|
|
fw_app_type);
|
|
return NULL;
|
|
}
|
|
}
|
|
fw_mode = fu_ccgx_firmware_get_fw_mode(FU_CCGX_FIRMWARE(firmware));
|
|
if (fw_mode != fu_ccgx_fw_mode_get_alternate(self->fw_mode)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"FWMode mismatch, expected %s, got %s",
|
|
fu_ccgx_fw_mode_to_string(fu_ccgx_fw_mode_get_alternate(self->fw_mode)),
|
|
fu_ccgx_fw_mode_to_string(fw_mode));
|
|
return NULL;
|
|
}
|
|
return g_steal_pointer(&firmware);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_get_metadata_offset(FuCcgxHpiDevice *self,
|
|
FWMode fw_mode,
|
|
guint32 *addr,
|
|
guint32 *offset,
|
|
GError **error)
|
|
{
|
|
guint32 addr_max;
|
|
|
|
/* sanity check */
|
|
if (self->flash_row_size == 0x0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"unset support row size");
|
|
return FALSE;
|
|
}
|
|
|
|
/* get the row offset for the flash size */
|
|
addr_max = self->flash_size / self->flash_row_size;
|
|
if (self->flash_row_size == 128) {
|
|
*offset = HPI_META_DATA_OFFSET_ROW_128;
|
|
} else if (self->flash_row_size == 256) {
|
|
*offset = HPI_META_DATA_OFFSET_ROW_256;
|
|
} else {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"unsupported support row size: 0x%x",
|
|
self->flash_row_size);
|
|
return FALSE;
|
|
}
|
|
|
|
/* get the row offset in the flash */
|
|
switch (fw_mode) {
|
|
case FW_MODE_FW1:
|
|
*addr = addr_max - 1;
|
|
break;
|
|
case FW_MODE_FW2:
|
|
*addr = addr_max - 2;
|
|
break;
|
|
default:
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"boot recovery not supported");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* this will only work after fu_ccgx_hpi_enter_flash_mode() has been used */
|
|
static gboolean
|
|
fu_ccgx_hpi_load_metadata(FuCcgxHpiDevice *self,
|
|
FWMode fw_mode,
|
|
CCGxMetaData *metadata,
|
|
GError **error)
|
|
{
|
|
guint32 addr = 0x0;
|
|
guint32 md_offset = 0x0;
|
|
g_autofree guint8 *buf = NULL;
|
|
|
|
/* read flash at correct address */
|
|
if (!fu_ccgx_hpi_get_metadata_offset(self, fw_mode, &addr, &md_offset, error))
|
|
return FALSE;
|
|
buf = g_malloc0(self->flash_row_size);
|
|
if (!fu_ccgx_hpi_read_flash(self, addr, buf, self->flash_row_size, error)) {
|
|
g_prefix_error(error, "fw metadata read error: ");
|
|
return FALSE;
|
|
}
|
|
return fu_memcpy_safe((guint8 *)metadata,
|
|
sizeof(*metadata),
|
|
0x0,
|
|
buf,
|
|
self->flash_row_size,
|
|
md_offset,
|
|
sizeof(metadata),
|
|
error);
|
|
}
|
|
|
|
/* this will only work after fu_ccgx_hpi_enter_flash_mode() has been used */
|
|
static gboolean
|
|
fu_ccgx_hpi_save_metadata(FuCcgxHpiDevice *self,
|
|
FWMode fw_mode,
|
|
CCGxMetaData *metadata,
|
|
GError **error)
|
|
{
|
|
guint32 addr = 0x0;
|
|
guint32 md_offset = 0x0;
|
|
g_autofree guint8 *buf = NULL;
|
|
|
|
/* read entire row of flash at correct address */
|
|
if (!fu_ccgx_hpi_get_metadata_offset(self, fw_mode, &addr, &md_offset, error))
|
|
return FALSE;
|
|
buf = g_malloc0(self->flash_row_size);
|
|
if (!fu_ccgx_hpi_read_flash(self, addr, buf, self->flash_row_size, error)) {
|
|
g_prefix_error(error, "fw metadata read existing error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_memcpy_safe(buf,
|
|
self->flash_row_size,
|
|
md_offset,
|
|
(guint8 *)metadata,
|
|
sizeof(*metadata),
|
|
0x0,
|
|
sizeof(metadata),
|
|
error))
|
|
return FALSE;
|
|
if (!fu_ccgx_hpi_write_flash(self, addr, buf, self->flash_row_size, error)) {
|
|
g_prefix_error(error, "fw metadata write error: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_write_firmware(FuDevice *device,
|
|
FuFirmware *firmware,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
CCGxMetaData metadata = {0x0};
|
|
GPtrArray *records = fu_ccgx_firmware_get_records(FU_CCGX_FIRMWARE(firmware));
|
|
FWMode fw_mode_alt = fu_ccgx_fw_mode_get_alternate(self->fw_mode);
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
|
|
/* enter flash mode */
|
|
locker = fu_device_locker_new_full(self,
|
|
(FuDeviceLockerFunc)fu_ccgx_hpi_enter_flash_mode,
|
|
(FuDeviceLockerFunc)fu_ccgx_hpi_leave_flash_mode,
|
|
error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
|
|
/* invalidate metadata for alternate image */
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_READ);
|
|
if (!fu_ccgx_hpi_load_metadata(self, fw_mode_alt, &metadata, error))
|
|
return FALSE;
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_ERASE);
|
|
metadata.metadata_valid = 0x00;
|
|
if (!fu_ccgx_hpi_save_metadata(self, fw_mode_alt, &metadata, error))
|
|
return FALSE;
|
|
|
|
/* write new image */
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_WRITE);
|
|
for (guint i = 0; i < records->len; i++) {
|
|
FuCcgxFirmwareRecord *rcd = g_ptr_array_index(records, i);
|
|
|
|
/* write chunk */
|
|
if (!fu_ccgx_hpi_write_flash(self,
|
|
rcd->row_number,
|
|
g_bytes_get_data(rcd->data, NULL),
|
|
g_bytes_get_size(rcd->data),
|
|
error)) {
|
|
g_prefix_error(error, "fw write error @0x%x: ", rcd->row_number);
|
|
return FALSE;
|
|
}
|
|
|
|
/* update progress */
|
|
fu_device_set_progress_full(device, (gsize)i, (gsize)records->len - 1);
|
|
}
|
|
|
|
/* validate fw */
|
|
fu_device_set_status(device, FWUPD_STATUS_DEVICE_VERIFY);
|
|
if (!fu_ccgx_hpi_validate_fw(self, fw_mode_alt, error)) {
|
|
g_prefix_error(error, "fw validate error: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* this is a good time to leave the flash mode *before* rebooting */
|
|
if (!fu_device_locker_close(locker, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_ensure_silicon_id(FuCcgxHpiDevice *self, GError **error)
|
|
{
|
|
guint8 buf[2] = {0x0};
|
|
g_autofree gchar *instance_id = NULL;
|
|
|
|
if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_SILICON_ID, buf, sizeof(buf), error)) {
|
|
g_prefix_error(error, "get silicon id error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_common_read_uint16_safe(buf,
|
|
sizeof(buf),
|
|
0x0,
|
|
&self->silicon_id,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* add quirks */
|
|
instance_id = g_strdup_printf("CCGX\\SID_%X", self->silicon_id);
|
|
fu_device_add_instance_id_full(FU_DEVICE(self),
|
|
instance_id,
|
|
FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
|
|
/* sanity check */
|
|
if (self->flash_row_size == 0x0 || self->flash_size == 0x0 ||
|
|
self->flash_size % self->flash_row_size != 0) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"invalid row size for Instance ID %s: 0x%x/0x%x",
|
|
instance_id,
|
|
self->flash_row_size,
|
|
self->flash_size);
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_set_version_raw(FuCcgxHpiDevice *self, guint32 version_raw)
|
|
{
|
|
g_autofree gchar *version = fu_ccgx_version_to_string(version_raw);
|
|
fu_device_set_version(FU_DEVICE(self), version);
|
|
fu_device_set_version_raw(FU_DEVICE(self), version_raw);
|
|
}
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_setup_with_fw_mode(FuCcgxHpiDevice *self)
|
|
{
|
|
fu_device_set_logical_id(FU_DEVICE(self), fu_ccgx_fw_mode_to_string(self->fw_mode));
|
|
}
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_setup_with_app_type(FuCcgxHpiDevice *self)
|
|
{
|
|
if (self->silicon_id != 0x0 && self->fw_app_type != 0x0) {
|
|
g_autofree gchar *instance_id1 = NULL;
|
|
g_autofree gchar *instance_id2 = NULL;
|
|
|
|
/* we get fw_image_type from the quirk */
|
|
instance_id1 = g_strdup_printf("USB\\VID_%04X&PID_%04X&SID_%04X&APP_%04X",
|
|
fu_usb_device_get_vid(FU_USB_DEVICE(self)),
|
|
fu_usb_device_get_pid(FU_USB_DEVICE(self)),
|
|
self->silicon_id,
|
|
self->fw_app_type);
|
|
fu_device_add_instance_id(FU_DEVICE(self), instance_id1);
|
|
instance_id2 = g_strdup_printf("USB\\VID_%04X&PID_%04X&SID_%04X&APP_%04X&MODE_%s",
|
|
fu_usb_device_get_vid(FU_USB_DEVICE(self)),
|
|
fu_usb_device_get_pid(FU_USB_DEVICE(self)),
|
|
self->silicon_id,
|
|
self->fw_app_type,
|
|
fu_ccgx_fw_mode_to_string(self->fw_mode));
|
|
fu_device_add_instance_id(FU_DEVICE(self), instance_id2);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
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_autoptr(GError) error_local = NULL;
|
|
|
|
/* FuUsbDevice->setup */
|
|
if (!FU_DEVICE_CLASS(fu_ccgx_hpi_device_parent_class)->setup(device, error))
|
|
return FALSE;
|
|
|
|
/* set the new config */
|
|
if (!fu_ccgx_hpi_device_get_i2c_config(self, &i2c_config, error)) {
|
|
g_prefix_error(error, "get config error: ");
|
|
return FALSE;
|
|
}
|
|
i2c_config.frequency = FU_CCGX_HPI_FREQ;
|
|
i2c_config.is_initiator = TRUE;
|
|
i2c_config.is_msb_first = TRUE;
|
|
if (!fu_ccgx_hpi_device_set_i2c_config(self, &i2c_config, error)) {
|
|
g_prefix_error(error, "set config error: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_REG_DEVICE_MODE_ADDR, &mode, 1, error)) {
|
|
g_prefix_error(error, "get device mode error: ");
|
|
return FALSE;
|
|
}
|
|
self->hpi_addrsz = mode & 0x80 ? 2 : 1;
|
|
self->num_ports = (mode >> 2) & 0x03 ? 2 : 1;
|
|
self->fw_mode = (FWMode)(mode & 0x03);
|
|
fu_ccgx_hpi_device_setup_with_fw_mode(self);
|
|
|
|
/* get silicon ID */
|
|
if (!fu_ccgx_hpi_device_ensure_silicon_id(self, error))
|
|
return FALSE;
|
|
|
|
/* get correct version if not in boot mode */
|
|
if (self->fw_mode != FW_MODE_BOOT) {
|
|
guint16 bufsz;
|
|
guint32 versions[FW_MODE_LAST] = {0x0};
|
|
guint8 bufver[HPI_DEVICE_VERSION_SIZE_HPIV2] = {0x0};
|
|
|
|
bufsz = self->hpi_addrsz == 1 ? HPI_DEVICE_VERSION_SIZE_HPIV1
|
|
: HPI_DEVICE_VERSION_SIZE_HPIV2;
|
|
if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_GET_VERSION, bufver, bufsz, error)) {
|
|
g_prefix_error(error, "get version error: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* fw1 */
|
|
if (!fu_common_read_uint32_safe(bufver,
|
|
sizeof(bufver),
|
|
0x0c,
|
|
&versions[FW_MODE_FW1],
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* fw2 */
|
|
if (!fu_common_read_uint32_safe(bufver,
|
|
sizeof(bufver),
|
|
0x14,
|
|
&versions[FW_MODE_FW2],
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* add GUIDs that are specific to the firmware app type */
|
|
self->fw_app_type = versions[self->fw_mode] & 0xffff;
|
|
fu_ccgx_hpi_device_setup_with_app_type(self);
|
|
|
|
/* if running in bootloader force an upgrade to any version */
|
|
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
|
|
fu_ccgx_hpi_device_set_version_raw(self, 0x0);
|
|
} else {
|
|
fu_ccgx_hpi_device_set_version_raw(self, versions[self->fw_mode]);
|
|
}
|
|
}
|
|
|
|
/* not supported in boot mode */
|
|
if (self->fw_mode == FW_MODE_BOOT) {
|
|
fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
} else {
|
|
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);
|
|
}
|
|
|
|
/* start with no events in the queue */
|
|
return fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_SETUP_EVENT_CLEAR_TIME_MS, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_set_quirk_kv(FuDevice *device,
|
|
const gchar *key,
|
|
const gchar *value,
|
|
GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
if (g_strcmp0(key, "SiliconId") == 0) {
|
|
guint64 tmp = fu_common_strtoull(value);
|
|
if (tmp < G_MAXUINT16) {
|
|
self->silicon_id = tmp;
|
|
return TRUE;
|
|
}
|
|
g_set_error_literal(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid SiliconId");
|
|
return FALSE;
|
|
}
|
|
if (g_strcmp0(key, "CcgxFlashRowSize") == 0) {
|
|
guint64 tmp = fu_common_strtoull(value);
|
|
if (tmp < G_MAXUINT32) {
|
|
self->flash_row_size = tmp;
|
|
return TRUE;
|
|
}
|
|
g_set_error_literal(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid CcgxFlashRowSize");
|
|
return FALSE;
|
|
}
|
|
if (g_strcmp0(key, "CcgxFlashSize") == 0) {
|
|
guint64 tmp = fu_common_strtoull(value);
|
|
if (tmp < G_MAXUINT32) {
|
|
self->flash_size = tmp;
|
|
return TRUE;
|
|
}
|
|
g_set_error_literal(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid CcgxFlashSize");
|
|
return FALSE;
|
|
}
|
|
if (g_strcmp0(key, "CcgxImageKind") == 0) {
|
|
self->fw_image_type = fu_ccgx_fw_image_type_from_string(value);
|
|
if (self->fw_image_type != FW_IMAGE_TYPE_UNKNOWN)
|
|
return TRUE;
|
|
g_set_error_literal(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid CcgxImageKind");
|
|
return FALSE;
|
|
}
|
|
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported");
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_open(FuDevice *device, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* FuUsbDevice->open */
|
|
if (!FU_DEVICE_CLASS(fu_ccgx_hpi_device_parent_class)->open(device, error))
|
|
return FALSE;
|
|
|
|
if (!g_usb_device_claim_interface(fu_usb_device_get_dev(FU_USB_DEVICE(device)),
|
|
self->inf_num,
|
|
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"cannot claim interface: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ccgx_hpi_device_close(FuDevice *device, GError **error)
|
|
{
|
|
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device);
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* do not close handle when device restarts */
|
|
if (fu_device_get_status(device) == FWUPD_STATUS_DEVICE_RESTART)
|
|
return TRUE;
|
|
if (!g_usb_device_release_interface(fu_usb_device_get_dev(FU_USB_DEVICE(device)),
|
|
self->inf_num,
|
|
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
|
|
&error_local)) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"cannot release interface: %s",
|
|
error_local->message);
|
|
return FALSE;
|
|
}
|
|
/* FuUsbDevice->close */
|
|
return FU_DEVICE_CLASS(fu_ccgx_hpi_device_parent_class)->close(device, error);
|
|
}
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_init(FuCcgxHpiDevice *self)
|
|
{
|
|
self->inf_num = 0x0;
|
|
self->hpi_addrsz = 1;
|
|
self->num_ports = 1;
|
|
self->target_address = PD_I2C_TARGET_ADDRESS;
|
|
self->ep_bulk_out = PD_I2C_USB_EP_BULK_OUT;
|
|
self->ep_bulk_in = PD_I2C_USB_EP_BULK_IN;
|
|
self->ep_intr_in = PD_I2C_USB_EP_INTR_IN;
|
|
fu_device_add_protocol(FU_DEVICE(self), "com.cypress.ccgx");
|
|
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET);
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY);
|
|
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
|
|
fu_device_retry_set_delay(FU_DEVICE(self), HPI_CMD_RETRY_DELAY);
|
|
|
|
/* we can recover the I²C link using reset */
|
|
fu_device_retry_add_recovery(FU_DEVICE(self),
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
fu_ccgx_hpi_device_i2c_reset_cb);
|
|
fu_device_retry_add_recovery(FU_DEVICE(self),
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
fu_ccgx_hpi_device_i2c_reset_cb);
|
|
|
|
/* this might not be true for future hardware */
|
|
if (self->inf_num > 0)
|
|
self->scb_index = 1;
|
|
}
|
|
|
|
static void
|
|
fu_ccgx_hpi_device_class_init(FuCcgxHpiDeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
|
klass_device->to_string = fu_ccgx_hpi_device_to_string;
|
|
klass_device->write_firmware = fu_ccgx_hpi_write_firmware;
|
|
klass_device->prepare_firmware = fu_ccgx_hpi_device_prepare_firmware;
|
|
klass_device->detach = fu_ccgx_hpi_device_detach;
|
|
klass_device->attach = fu_ccgx_hpi_device_attach;
|
|
klass_device->setup = fu_ccgx_hpi_device_setup;
|
|
klass_device->set_quirk_kv = fu_ccgx_hpi_device_set_quirk_kv;
|
|
klass_device->open = fu_ccgx_hpi_device_open;
|
|
klass_device->close = fu_ccgx_hpi_device_close;
|
|
}
|