pds_core: add PLDM firmware update support via devlink flash

Implement PLDM FW Update in the pds_core driver using the upstream
pldmfw API. This allows updating an entire PLDM FW package at once
or updating specific firmware components by name.

Flash the entire image:
  devlink dev flash pci/0000:b5:00.0 file firmware.pldmfw

Flash a specific component from the PLDM FW package:
  devlink dev flash pci/0000:b5:00.0 \
    file firmware.pldmfw component fw.cpld

Per-component update uses driver-defined component names (fw, fw.cpld,
etc.). Not all components support per-component update - devlink will
reject the request if the specified component cannot be updated.

Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
Link: https://patch.msgid.link/20260730-upstream_v8-v12-3-136cd174ee85@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Brett Creeley
2026-07-30 04:25:20 +00:00
committed by Jakub Kicinski
parent e7960459d9
commit fb918581d4
9 changed files with 1411 additions and 11 deletions

View File

@@ -102,6 +102,95 @@ currently in use, and that bank will used for the next boot::
# devlink dev flash pci/0000:b5:00.0 \
file pensando/dsc_fw_1.63.0-22.tar
Firmware Management (PLDM)
==========================
Firmware that supports PLDM can be updated using the devlink flash command
with a PLDM firmware package. The entire package can be updated at once::
# devlink dev flash pci/0000:b5:00.0 file firmware.pldmfw
Individual components can also be updated by specifying the component name::
# devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.cpld
Per-component update uses driver-defined component names (fw, fw.cpld,
etc.). Not all components support per-component update -
devlink will reject the request if the specified component cannot
be updated.
Gold (recovery) components can be updated by specifying the base component
name (e.g., ``fw`` for ``fw.gold``) with a goldfw package file when the
device supports per-component update. The ``.gold`` suffix in devlink info
output indicates the gold slot version, not a flash target.
Info versions (PLDM)
====================
Firmware that supports PLDM reports component versions using driver-defined
names. The driver reports the following component versions:
.. list-table:: devlink info versions for PLDM-capable firmware
:widths: 5 5 90
* - Name
- Type
- Description
* - ``fw``
- running, stored
- Main firmware
* - ``fw.gold``
- stored
- Gold (recovery) firmware
* - ``fw.bootloader``
- running, stored
- Boot loader
* - ``fw.cpld``
- running, stored
- CPLD
* - ``fw.secure``
- running, stored
- Secure boot firmware
* - ``fw.fpga``
- running, stored
- FPGA configuration
* - ``fw.suc``
- running, stored
- System Unit Controller firmware
* - ``fw.suc.bootloader``
- running, stored
- System Unit Controller bootloader
* - ``fw.uboot``
- running, stored
- U-Boot bootloader
* - ``asic.id``
- fixed
- The ASIC type for this device
* - ``asic.rev``
- fixed
- The revision of the ASIC for this device
Example output::
$ devlink dev info pci/0000:00:05.0
pci/0000:00:05.0:
driver pds_core
serial_number FLM18420073
versions:
fixed:
asic.id 0x0
asic.rev 0x0
running:
fw.bootloader 1.2.3
fw 1.3.0
fw.cpld 3.18
stored:
fw.bootloader 1.2.3
fw.gold 1.2.0
fw 1.3.0
fw.cpld 3.18
Health Reporters
================

View File

@@ -171,6 +171,7 @@ config PDS_CORE
depends on 64BIT && PCI
select AUXILIARY_BUS
select NET_DEVLINK
select PLDMFW
help
This enables the support for the AMD/Pensando Core device family of
adapters. More specific information on this driver can be

View File

@@ -489,6 +489,15 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
pdsc_devcmd_reset(pdsc);
pci_clear_master(pdsc->pdev);
if (!pdsc->pdev->is_virtfn) {
u16 val;
/* Flush any in-flight DMA before freeing buffers.
* A config read completion cannot return until all prior
* device-initiated memory writes have completed.
*/
pci_read_config_word(pdsc->pdev, PCI_VENDOR_ID, &val);
}
pdsc_core_uninit(pdsc);

View File

@@ -23,6 +23,14 @@
#define PDSC_SETUP_RECOVERY false
#define PDSC_SETUP_INIT true
struct pdsc_deferred_dma {
struct list_head list;
dma_addr_t dma_addr;
void *va;
size_t size;
enum dma_data_direction dir;
};
struct pdsc_dev_bar {
void __iomem *vaddr;
phys_addr_t bus_addr;
@@ -185,6 +193,8 @@ struct pdsc {
struct mutex devcmd_lock; /* lock for dev_cmd operations */
struct mutex config_lock; /* lock for configuration operations */
spinlock_t adminq_lock; /* lock for adminq operations */
struct list_head deferred_dma_list;
spinlock_t deferred_dma_lock; /* lock for deferred DMA list */
refcount_t adminq_refcnt;
struct pds_core_dev_info_regs __iomem *info_regs;
struct pds_core_dev_cmd_regs __iomem *cmd_regs;
@@ -199,6 +209,8 @@ struct pdsc {
u64 last_eid;
struct pdsc_viftype *viftype_status;
struct work_struct pci_reset_work;
struct pds_core_component_list_info fw_components;
};
/** enum pds_core_dbell_bits - bitwise composition of dbell values.
@@ -281,8 +293,16 @@ bool pdsc_is_fw_running(struct pdsc *pdsc);
bool pdsc_is_fw_good(struct pdsc *pdsc);
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
const void *data, size_t data_len,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
const void *data, size_t data_len,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_init(struct pdsc *pdsc);
int pdsc_devcmd_reset(struct pdsc *pdsc);
int pdsc_dev_init(struct pdsc *pdsc);
@@ -315,11 +335,20 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq);
void pdsc_work_thread(struct work_struct *work);
irqreturn_t pdsc_adminq_isr(int irq, void *data);
int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
int pdsc_firmware_update(struct pdsc *pdsc,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
int pdsc_get_component_info(struct pdsc *pdsc);
const char *pdsc_fw_type_to_name(u8 type);
void pdsc_fw_components_invalidate(struct pdsc *pdsc);
void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
void pdsc_pci_reset_thread(struct work_struct *work);
void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
dma_addr_t dma_addr, void *va, size_t size,
enum dma_data_direction dir);
void pdsc_deferred_dma_free(struct pdsc *pdsc);
#endif /* _PDSC_H_ */

View File

@@ -206,15 +206,56 @@ static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
else
memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
if (err != -ETIMEDOUT && err != -EAGAIN)
pdsc_deferred_dma_free(pdsc);
return err;
}
void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
dma_addr_t dma_addr, void *va, size_t size,
enum dma_data_direction dir)
{
entry->dma_addr = dma_addr;
entry->va = va;
entry->size = size;
entry->dir = dir;
spin_lock(&pdsc->deferred_dma_lock);
list_add_tail(&entry->list, &pdsc->deferred_dma_list);
spin_unlock(&pdsc->deferred_dma_lock);
}
void pdsc_deferred_dma_free(struct pdsc *pdsc)
{
struct pdsc_deferred_dma *entry, *tmp;
LIST_HEAD(local_list);
spin_lock(&pdsc->deferred_dma_lock);
list_splice_init(&pdsc->deferred_dma_list, &local_list);
spin_unlock(&pdsc->deferred_dma_lock);
list_for_each_entry_safe(entry, tmp, &local_list, list) {
dma_unmap_single(pdsc->dev, entry->dma_addr,
entry->size, entry->dir);
kfree(entry->va);
list_del(&entry->list);
kfree(entry);
}
}
int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, true);
}
int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, false);
}
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
@@ -227,6 +268,47 @@ int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return err;
}
static int __pdsc_devcmd_with_data(struct pdsc *pdsc,
union pds_core_dev_cmd *cmd,
const void *data, size_t data_len,
union pds_core_dev_comp *comp,
int max_seconds, bool do_msg)
{
int err;
mutex_lock(&pdsc->devcmd_lock);
if (!pdsc->cmd_regs) {
err = -ENXIO;
goto unlock;
}
if (data_len > sizeof(pdsc->cmd_regs->data)) {
err = -ENOSPC;
goto unlock;
}
memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
err = __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, do_msg);
unlock:
mutex_unlock(&pdsc->devcmd_lock);
return err;
}
int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
const void *data, size_t data_len,
union pds_core_dev_comp *comp, int max_seconds)
{
return __pdsc_devcmd_with_data(pdsc, cmd, data, data_len,
comp, max_seconds, true);
}
int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
const void *data, size_t data_len,
union pds_core_dev_comp *comp, int max_seconds)
{
return __pdsc_devcmd_with_data(pdsc, cmd, data, data_len,
comp, max_seconds, false);
}
int pdsc_devcmd_init(struct pdsc *pdsc)
{
union pds_core_dev_comp comp = {};

View File

@@ -90,13 +90,7 @@ int pdsc_dl_flash_update(struct devlink *dl,
{
struct pdsc *pdsc = devlink_priv(dl);
if (params->component) {
NL_SET_ERR_MSG_MOD(extack,
"Component update not supported by this device");
return -EOPNOTSUPP;
}
return pdsc_firmware_update(pdsc, params->fw, extack);
return pdsc_firmware_update(pdsc, params, extack);
}
static char *fw_slotnames[] = {

View File

@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2023 Advanced Micro Devices, Inc */
#include <linux/pldmfw.h>
#include <linux/vmalloc.h>
#include "core.h"
/* The worst case wait for the install activity is about 25 minutes when
@@ -14,6 +17,58 @@
/* Number of periodic log updates during fw file download */
#define PDSC_FW_INTERVAL_FRACTION 32
#define PDSC_FW_COMPONENT_PREFIX "fw."
#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
(sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
/* Driver-defined component type to name mapping.
* PDS_CORE_FW_TYPE_MAIN is NULL - handled specially as "fw" without prefix.
*/
static const char * const pdsc_fw_type_names[] = {
[PDS_CORE_FW_TYPE_MAIN] = NULL,
[PDS_CORE_FW_TYPE_BOOT] = "bootloader",
[PDS_CORE_FW_TYPE_CPLD] = "cpld",
[PDS_CORE_FW_TYPE_SECURE] = "secure",
[PDS_CORE_FW_TYPE_FPGA] = "fpga",
[PDS_CORE_FW_TYPE_SUC_MAIN] = "suc",
[PDS_CORE_FW_TYPE_SUC_BOOT] = "suc.bootloader",
[PDS_CORE_FW_TYPE_UBOOT] = "uboot",
};
const char *pdsc_fw_type_to_name(u8 type)
{
if (type < ARRAY_SIZE(pdsc_fw_type_names) && pdsc_fw_type_names[type])
return pdsc_fw_type_names[type];
return NULL;
}
void pdsc_fw_components_invalidate(struct pdsc *pdsc)
{
/* Pairs with READ_ONCE in pdsc_dl_component_info_get() */
WRITE_ONCE(pdsc->fw_components.num_components, 0);
}
static u8 pdsc_name_to_fw_type(const char *name)
{
size_t prefix_len;
int i;
/* "fw" without suffix maps to main firmware */
if (!strcmp(name, "fw"))
return PDS_CORE_FW_TYPE_MAIN;
prefix_len = str_has_prefix(name, PDSC_FW_COMPONENT_PREFIX);
if (prefix_len)
name += prefix_len;
for (i = 1; i < ARRAY_SIZE(pdsc_fw_type_names); i++) {
if (pdsc_fw_type_names[i] &&
!strcmp(name, pdsc_fw_type_names[i]))
return i;
}
return 0;
}
static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
u32 offset, u32 length)
{
@@ -23,7 +78,7 @@ static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
.fw_download.addr = cpu_to_le64(addr),
.fw_download.length = cpu_to_le32(length),
};
union pds_core_dev_comp comp;
union pds_core_dev_comp comp = {};
return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
@@ -95,9 +150,12 @@ static int pdsc_fw_status_long_wait(struct pdsc *pdsc,
return err;
}
int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
struct netlink_ext_ack *extack)
static int
pdsc_legacy_firmware_update(struct pdsc *pdsc,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
const struct firmware *fw = params->fw;
u32 buf_sz, copy_sz, offset;
struct devlink *dl;
int next_interval;
@@ -105,6 +163,12 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
int err = 0;
int fw_slot;
if (params->component) {
NL_SET_ERR_MSG_MOD(extack,
"Component update not supported by this device");
return -EOPNOTSUPP;
}
dev_info(pdsc->dev, "Installing firmware\n");
if (!pdsc->cmd_regs)
@@ -195,3 +259,729 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
NULL, 0, 0);
return err;
}
struct pdsc_component_priv {
u16 component_id;
bool skip;
struct list_head list_entry;
};
struct pds_core_fwu_priv {
struct pldmfw context;
struct devlink_flash_update_params *params;
struct netlink_ext_ack *extack;
struct pdsc *pdsc;
struct list_head components;
bool component_found;
};
static void pdsc_free_fwu_priv(struct pds_core_fwu_priv *priv)
{
struct pdsc_component_priv *component_priv, *tmp;
list_for_each_entry_safe(component_priv, tmp, &priv->components,
list_entry) {
list_del(&component_priv->list_entry);
kfree(component_priv);
}
}
static int pdsc_devcmd_match_record_desc(struct pdsc *pdsc, u16 desc_type,
u16 desc_size, const u8 *desc_data,
u8 *match)
{
union pds_core_dev_cmd cmd = {
.match_record_desc.opcode = PDS_CORE_CMD_MATCH_RECORD_DESC,
.match_record_desc.ver = 1,
.match_record_desc.type = cpu_to_le16(desc_type),
.match_record_desc.size = cpu_to_le16(desc_size),
};
union pds_core_dev_comp comp = {};
int err;
err = pdsc_devcmd_with_data(pdsc, &cmd, desc_data, desc_size,
&comp, pdsc->devcmd_timeout);
*match = comp.match_record_desc.match;
return err;
}
static bool pdsc_match_record_descs(struct pldmfw *context,
struct pldmfw_record *record)
{
struct pds_core_fwu_priv *priv =
container_of(context, struct pds_core_fwu_priv, context);
struct pdsc *pdsc = priv->pdsc;
struct pldmfw_desc_tlv *desc;
if (!pldmfw_op_pci_match_record(context, record))
return false;
list_for_each_entry(desc, &record->descs, entry) {
u8 match;
int err;
switch (desc->type) {
/* skip types checked in pldmfw_op_pci_match_record */
case PLDM_DESC_ID_PCI_VENDOR_ID:
case PLDM_DESC_ID_PCI_DEVICE_ID:
case PLDM_DESC_ID_PCI_SUBVENDOR_ID:
case PLDM_DESC_ID_PCI_SUBDEV_ID:
continue;
}
if (!desc->size)
return false;
err = pdsc_devcmd_match_record_desc(pdsc, desc->type,
desc->size, desc->data,
&match);
if (err) {
dev_err(pdsc->dev,
"match_record_desc failed type: 0x%04x size: %u, err %d\n",
desc->type, desc->size, err);
return false;
}
/* all record descriptors must match */
if (!match)
return false;
}
return true;
}
static int pdsc_devcmd_send_package_data(struct pdsc *pdsc, u64 addr,
u16 length, u16 offset, u16 total_len)
{
union pds_core_dev_cmd cmd = {
.send_pkg_data.opcode = PDS_CORE_CMD_SEND_PKG_DATA,
.send_pkg_data.ver = 1,
.send_pkg_data.data_pa = cpu_to_le64(addr),
.send_pkg_data.data_len = cpu_to_le16(length),
.send_pkg_data.offset = cpu_to_le16(offset),
.send_pkg_data.total_len = cpu_to_le16(total_len),
};
union pds_core_dev_comp comp = {};
return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
static int pdsc_send_package_data(struct pldmfw *context, const u8 *data,
u16 length)
{
struct pds_core_fwu_priv *priv =
container_of(context, struct pds_core_fwu_priv, context);
struct pdsc_deferred_dma *deferred;
struct device *dev = context->dev;
struct pdsc *pdsc = priv->pdsc;
dma_addr_t dma_addr;
u8 *package_data;
u32 offset;
int err;
if (!length)
return 0;
deferred = kmalloc_obj(*deferred, GFP_KERNEL);
if (!deferred)
return -ENOMEM;
package_data = kmemdup(data, length, GFP_KERNEL);
if (!package_data) {
kfree(deferred);
return -ENOMEM;
}
dma_addr = dma_map_single(dev, package_data, length, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "Failed to dma_map package_data length 0x%x\n",
length);
kfree(package_data);
kfree(deferred);
return -ENOMEM;
}
for (offset = 0; offset < length; offset += PDS_PAGE_SIZE) {
u32 copy_sz;
copy_sz = min_t(unsigned int, PDS_PAGE_SIZE, length - offset);
err = pdsc_devcmd_send_package_data(pdsc, dma_addr + offset,
copy_sz, offset, length);
if (err) {
NL_SET_ERR_MSG_MOD(priv->extack,
"Failed to send package data");
break;
}
}
if (err == -ETIMEDOUT || err == -EAGAIN) {
pdsc_deferred_dma_add(pdsc, deferred, dma_addr,
package_data, length, DMA_TO_DEVICE);
return err;
}
kfree(deferred);
dma_unmap_single(dev, dma_addr, length, DMA_TO_DEVICE);
kfree(package_data);
return err;
}
static bool pdsc_component_type_exists(struct pdsc *pdsc, u8 type)
{
int i;
for (i = 0; i < pdsc->fw_components.num_components; i++) {
if (pdsc->fw_components.info[i].component_type == type)
return true;
}
return false;
}
static bool pdsc_component_id_matches_type(struct pdsc *pdsc,
u8 component_id, u8 type)
{
int i;
for (i = 0; i < pdsc->fw_components.num_components; i++) {
struct pds_core_fw_component_info *info =
&pdsc->fw_components.info[i];
if (info->identifier == component_id &&
info->component_type == type)
return true;
}
return false;
}
static u8 pdsc_get_component_type_by_id(struct pdsc *pdsc, u16 component_id)
{
int i;
for (i = 0; i < pdsc->fw_components.num_components; i++) {
struct pds_core_fw_component_info *info =
&pdsc->fw_components.info[i];
if (info->identifier == component_id)
return info->component_type;
}
return 0;
}
static bool pdsc_skip_component(struct pds_core_fwu_priv *priv,
u16 component_id)
{
struct pdsc_component_priv *component_priv;
list_for_each_entry(component_priv, &priv->components, list_entry) {
if (component_priv->component_id == component_id)
return component_priv->skip;
}
return false;
}
static int pdsc_send_component_table(struct pldmfw *context,
struct pldmfw_component *component,
u8 transfer_flag)
{
struct pds_core_fwu_priv *priv =
container_of(context, struct pds_core_fwu_priv, context);
struct pds_core_component_tbl *component_tbl;
struct pdsc_component_priv *component_priv;
struct device *dev = context->dev;
union pds_core_dev_comp comp = {};
union pds_core_dev_cmd cmd = {};
struct pdsc *pdsc = priv->pdsc;
bool skip_component = false;
u8 requested_type = 0;
u16 buf_sz, tbl_sz;
int err = 0;
dev_dbg(dev,
"component name %s classification %u id %u activation_method %u ver_len %d ver_str %.*s index %u size %u transfer_flag 0x%02x\n",
priv->params->component, component->classification,
component->identifier, component->activation_method,
component->version_len, component->version_len,
component->version_string, component->index,
component->component_size, transfer_flag);
component_priv = kzalloc_obj(*component_priv, GFP_KERNEL);
if (!component_priv)
return -ENOMEM;
if (priv->params->component) {
requested_type = pdsc_name_to_fw_type(priv->params->component);
if (component->identifier > U8_MAX ||
!pdsc_component_id_matches_type(pdsc,
component->identifier,
requested_type)) {
skip_component = true;
goto add_component_priv;
}
priv->component_found = true;
}
buf_sz = sizeof(pdsc->cmd_regs->data);
tbl_sz = struct_size(component_tbl, version_str,
component->version_len);
if (tbl_sz > buf_sz) {
dev_err(dev, "component_tbl size %d too big, max size: %d\n",
tbl_sz, buf_sz);
err = -ENOSPC;
goto free_component_priv;
}
component_tbl = kzalloc(tbl_sz, GFP_KERNEL);
if (!component_tbl) {
err = -ENOMEM;
goto free_component_priv;
}
component_tbl->comparison_stamp =
cpu_to_le32(component->comparison_stamp);
component_tbl->classification = cpu_to_le16(component->classification);
component_tbl->identifier = cpu_to_le16(component->identifier);
component_tbl->transfer_flag = transfer_flag;
component_tbl->version_str_type = component->version_type;
component_tbl->version_str_len = component->version_len;
memcpy(component_tbl->version_str, component->version_string,
component->version_len);
cmd.send_component_tbl.opcode = PDS_CORE_CMD_SEND_COMPONENT_TBL;
cmd.send_component_tbl.ver = 1;
cmd.send_component_tbl.slot_id = PDS_CORE_FW_SLOT_INVALID;
err = pdsc_devcmd_with_data(pdsc, &cmd, component_tbl, tbl_sz,
&comp, pdsc->devcmd_timeout);
kfree(component_tbl);
if (err) {
dev_err(dev, "Failed sending component table: %pe\n",
ERR_PTR(err));
goto free_component_priv;
}
skip_component = comp.send_component_tbl.response == 1;
add_component_priv:
component_priv->skip = skip_component;
component_priv->component_id = component->identifier;
list_add(&component_priv->list_entry, &priv->components);
return 0;
free_component_priv:
kfree(component_priv);
return err;
}
int pdsc_get_component_info(struct pdsc *pdsc)
{
union pds_core_dev_cmd cmd = {
.get_component_info.opcode = PDS_CORE_CMD_GET_COMPONENT_INFO,
.get_component_info.ver = 1,
};
struct pds_core_component_list_info *list_info;
struct pdsc_deferred_dma *deferred;
union pds_core_dev_comp comp = {};
dma_addr_t dma_addr;
u8 num_components;
int err, i;
deferred = kmalloc_obj(*deferred);
if (!deferred)
return -ENOMEM;
list_info = kzalloc(PDS_PAGE_SIZE, GFP_KERNEL);
if (!list_info) {
kfree(deferred);
return -ENOMEM;
}
dma_addr = dma_map_single(pdsc->dev, list_info, PDS_PAGE_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(pdsc->dev, dma_addr)) {
dev_err(pdsc->dev,
"Failed to dma_map component_list_info length %d\n",
PDS_PAGE_SIZE);
kfree(list_info);
kfree(deferred);
return -ENOMEM;
}
cmd.get_component_info.data_len = cpu_to_le16(PDS_PAGE_SIZE);
cmd.get_component_info.data_pa = cpu_to_le64(dma_addr);
err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
if (err == -ETIMEDOUT || err == -EAGAIN) {
pdsc_deferred_dma_add(pdsc, deferred, dma_addr, list_info,
PDS_PAGE_SIZE, DMA_FROM_DEVICE);
return err;
}
kfree(deferred);
dma_unmap_single(pdsc->dev, dma_addr, PDS_PAGE_SIZE, DMA_FROM_DEVICE);
if (err)
goto out;
if (comp.get_component_info.ver == 0) {
/* Don't support backward compatibility as version 0 has
* alignment issues, so give a hint to users to update
* their firmware
*/
dev_warn_once(pdsc->dev,
"Incompatible get_component_info version %u reported by firmware\n",
comp.get_component_info.ver);
err = 0;
goto out;
}
num_components = list_info->num_components;
if (num_components > PDS_CORE_FW_COMPONENT_LIST_LEN) {
err = -ENOMEM;
goto out;
}
pdsc->fw_components.num_components = num_components;
for (i = 0; i < num_components; i++) {
struct pds_core_fw_component_info *info =
&pdsc->fw_components.info[i];
memcpy(info, &list_info->info[i], sizeof(*info));
info->version[PDS_CORE_FW_COMPONENT_VER_BUFLEN - 1] = 0;
info->name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN - 1] = 0;
}
out:
kfree(list_info);
return err;
}
static int pdsc_devcmd_send_component(struct pdsc *pdsc,
struct pds_core_flash_component *info,
u16 info_sz, dma_addr_t addr, u32 length,
u32 offset, u16 slot_id,
union pds_core_dev_comp *comp)
{
union pds_core_dev_cmd cmd = {
.send_component.opcode = PDS_CORE_CMD_SEND_COMPONENT,
.send_component.ver = 1,
.send_component.operation = PDS_CORE_SEND_COMPONENT_START,
.send_component.data_pa = cpu_to_le64(addr),
.send_component.data_len = cpu_to_le32(length),
.send_component.offset = cpu_to_le32(offset),
.send_component.slot_id = slot_id,
};
unsigned long timeout = 300 * HZ;
unsigned long start_time;
unsigned long end_time;
int err;
start_time = jiffies;
end_time = start_time + timeout;
do {
/* prevent noisy/benign devcmd failures */
err = pdsc_devcmd_with_data_nomsg(pdsc, &cmd, info, info_sz,
comp, 60);
if (err != -EAGAIN)
break;
/* if required, subsequent commands check status of
* PDS_CORE_CMD_SEND_COMPONENT command, which returns
* EAGAIN while the command is still running,
* else we get the final command status.
*/
cmd.send_component.operation = PDS_CORE_SEND_COMPONENT_STATUS;
msleep(20);
} while (time_before(jiffies, end_time));
if (err == -EAGAIN || err == -ETIMEDOUT)
dev_err(pdsc->dev, "PDS_CORE_CMD_SEND_COMPONENT timed out\n");
return err;
}
static int pdsc_flash_component_chunk(struct pdsc *pdsc, struct device *dev,
struct pds_core_flash_component *info,
u16 info_sz, const u8 *data, u16 copy_sz,
u32 offset, u8 slot_id,
union pds_core_dev_comp *comp)
{
struct pdsc_deferred_dma *deferred;
dma_addr_t dma_addr;
u8 *component_data;
int err;
deferred = kmalloc_obj(*deferred, GFP_KERNEL);
if (!deferred)
return -ENOMEM;
component_data = kmemdup(data, copy_sz, GFP_KERNEL);
if (!component_data) {
kfree(deferred);
return -ENOMEM;
}
dma_addr = dma_map_single(dev, component_data, copy_sz, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev,
"Failed to dma_map component_data at offset 0x%x copy_sz 0x%x\n",
offset, copy_sz);
kfree(component_data);
kfree(deferred);
return -ENOMEM;
}
err = pdsc_devcmd_send_component(pdsc, info, info_sz, dma_addr,
copy_sz, offset, slot_id, comp);
if (err == -ETIMEDOUT || err == -EAGAIN) {
pdsc_deferred_dma_add(pdsc, deferred, dma_addr,
component_data, copy_sz, DMA_TO_DEVICE);
return err;
}
kfree(deferred);
dma_unmap_single(dev, dma_addr, copy_sz, DMA_TO_DEVICE);
kfree(component_data);
return err;
}
static int pdsc_flash_component(struct pldmfw *context,
struct pldmfw_component *component)
{
char component_name_buf[PDSC_FW_COMPONENT_FULL_NAME_BUFLEN];
struct pds_core_fwu_priv *priv =
container_of(context, struct pds_core_fwu_priv, context);
struct pds_core_flash_component *component_info;
const char *component_name = NULL;
struct device *dev = context->dev;
struct pdsc *pdsc = priv->pdsc;
u16 buf_sz, info_sz;
struct devlink *dl;
u8 component_type;
u32 total_len;
u32 offset;
int err;
component_type = pdsc_get_component_type_by_id(pdsc,
component->identifier);
if (component_type) {
const char *type_name = pdsc_fw_type_to_name(component_type);
if (component_type == PDS_CORE_FW_TYPE_MAIN) {
component_name = "fw";
} else if (type_name) {
snprintf(component_name_buf, sizeof(component_name_buf),
"%s%s", PDSC_FW_COMPONENT_PREFIX, type_name);
component_name = component_name_buf;
}
}
dl = priv_to_devlink(pdsc);
if (pdsc_skip_component(priv, component->identifier)) {
devlink_flash_update_status_notify(dl, "Skipped",
component_name, 0, 0);
return 0;
}
total_len = component->component_size;
dev_dbg(dev,
"component name %s class %u id %u act_meth %u ver_str %.*s index %u size %u\n",
component_name ?: "(unknown)", component->classification,
component->identifier, component->activation_method,
component->version_len, component->version_string,
component->index, component->component_size);
buf_sz = sizeof(pdsc->cmd_regs->data);
info_sz = struct_size(component_info, version_str,
component->version_len);
if (info_sz > buf_sz) {
dev_err(dev, "component_info size %d too big, max size: %d\n",
info_sz, buf_sz);
return -ENOSPC;
}
component_info = vzalloc(info_sz);
if (!component_info)
return -ENOMEM;
component_info->comparison_stamp =
cpu_to_le32(component->comparison_stamp);
component_info->image_size = cpu_to_le32(total_len);
component_info->classification = cpu_to_le16(component->classification);
component_info->identifier = cpu_to_le16(component->identifier);
component_info->options = cpu_to_le16(component->options);
component_info->version_str_type = component->version_type;
component_info->version_str_len = component->version_len;
memcpy(component_info->version_str, component->version_string,
component->version_len);
offset = 0;
while (offset < total_len) {
union pds_core_dev_comp comp = {};
u16 copy_sz;
copy_sz = min_t(unsigned int, PDS_PAGE_SIZE,
total_len - offset);
err = pdsc_flash_component_chunk(pdsc, dev, component_info,
info_sz,
component->component_data +
offset, copy_sz, offset,
PDS_CORE_FW_SLOT_INVALID,
&comp);
if (err &&
comp.send_component.compat_response &&
(comp.send_component.compat_response_code ==
PDS_CORE_COMPONENT_STAMP_IDENTICAL ||
comp.send_component.compat_response_code ==
PDS_CORE_COMPONENT_STAMP_LOWER)) {
err = 0;
devlink_flash_update_status_notify(dl, "Skipped",
component_name,
0, 0);
goto skip_component;
}
if (err) {
NL_SET_ERR_MSG_MOD(priv->extack,
"Failed to flash component");
goto err_out;
}
offset += copy_sz;
devlink_flash_update_status_notify(dl,
"Erasing/Flashing",
component_name, offset,
total_len);
}
vfree(component_info);
return 0;
err_out:
devlink_flash_update_status_notify(dl,
"Erasing/Flashing Component Failed",
component_name, 0, 0);
skip_component:
vfree(component_info);
return err;
}
static int pdsc_devcmd_finalize_update(struct pdsc *pdsc)
{
union pds_core_dev_cmd cmd = {
.finalize_update.opcode = PDS_CORE_CMD_FINALIZE_UPDATE,
.finalize_update.ver = 1,
};
union pds_core_dev_comp comp = {};
return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
static int pdsc_finalize_update(struct pldmfw *context)
{
struct pds_core_fwu_priv *priv =
container_of(context, struct pds_core_fwu_priv, context);
const char *component_name = priv->params->component;
unsigned long start_time, end_time;
struct device *dev = context->dev;
struct pdsc *pdsc = priv->pdsc;
struct devlink *dl;
int err;
dl = priv_to_devlink(pdsc);
start_time = jiffies;
end_time = start_time + (PDSC_FW_INSTALL_TIMEOUT * HZ);
do {
err = pdsc_devcmd_finalize_update(pdsc);
if (err != -EAGAIN)
break;
dev_dbg(dev, "retrying finalize_update: %pe\n", ERR_PTR(err));
msleep(20);
} while (time_before(jiffies, end_time) && err == -EAGAIN);
if (err) {
devlink_flash_update_status_notify(dl, "Finalize Update Failed",
component_name, 0, 0);
NL_SET_ERR_MSG_MOD(priv->extack, "Finalize update failed");
return err;
}
devlink_flash_update_status_notify(dl, "Finalized Update",
component_name, 0, 0);
return 0;
}
static const struct pldmfw_ops pdsc_pldmfw_ops = {
.match_record = pdsc_match_record_descs,
.send_package_data = pdsc_send_package_data,
.send_component_table = pdsc_send_component_table,
.flash_component = pdsc_flash_component,
.finalize_update = pdsc_finalize_update
};
static int pdsc_pldm_firmware_update(struct pdsc *pdsc,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack,
const struct firmware *fw)
{
struct pds_core_fwu_priv priv = {};
int err;
if (!pdsc->fw_components.num_components) {
err = pdsc_get_component_info(pdsc);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to get component info");
return err;
}
}
if (params->component) {
u8 type = pdsc_name_to_fw_type(params->component);
if (!type || !pdsc_component_type_exists(pdsc, type)) {
NL_SET_ERR_MSG_MOD(extack, "Unknown component name");
return -ENOENT;
}
}
INIT_LIST_HEAD(&priv.components);
priv.context.ops = &pdsc_pldmfw_ops;
priv.context.dev = pdsc->dev;
priv.params = params;
priv.extack = extack;
priv.pdsc = pdsc;
err = pldmfw_flash_image(&priv.context, fw);
if (!err && params->component && !priv.component_found) {
NL_SET_ERR_MSG_MOD(extack,
"Requested component not present in firmware package");
err = -ENOENT;
}
pdsc_free_fwu_priv(&priv);
return err;
}
int pdsc_firmware_update(struct pdsc *pdsc,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
int err;
if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
pdsc->dev_ident.capabilities &
cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
err = pdsc_pldm_firmware_update(pdsc, params, extack,
params->fw);
else
err = pdsc_legacy_firmware_update(pdsc, params, extack);
/* Invalidate cached component info so next info_get refreshes */
pdsc_fw_components_invalidate(pdsc);
return err;
}

View File

@@ -315,6 +315,7 @@ static int pdsc_init_pf(struct pdsc *pdsc)
destroy_workqueue(pdsc->wq);
mutex_destroy(&pdsc->config_lock);
mutex_destroy(&pdsc->devcmd_lock);
pdsc_deferred_dma_free(pdsc);
pci_free_irq_vectors(pdsc->pdev);
err_out_unmap_bars:
pdsc_unmap_bars(pdsc);
@@ -353,6 +354,8 @@ static int pdsc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pdsc->pdev = pdev;
pdsc->dev = &pdev->dev;
set_bit(PDSC_S_INITING_DRIVER, &pdsc->state);
INIT_LIST_HEAD(&pdsc->deferred_dma_list);
spin_lock_init(&pdsc->deferred_dma_lock);
pci_set_drvdata(pdev, pdsc);
pdsc_debugfs_add_dev(pdsc);
@@ -458,6 +461,7 @@ static void pdsc_remove(struct pci_dev *pdev)
}
pci_disable_device(pdev);
pdsc_deferred_dma_free(pdsc);
ida_free(&pdsc_ida, pdsc->uid);
pdsc_debugfs_del_dev(pdsc);
@@ -505,6 +509,7 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)
pci_release_regions(pdev);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
pdsc_deferred_dma_free(pdsc);
}
static void pdsc_reset_done(struct pci_dev *pdev)

View File

@@ -40,6 +40,13 @@ enum pds_core_cmd_opcode {
PDS_CORE_CMD_FW_DOWNLOAD = 4,
PDS_CORE_CMD_FW_CONTROL = 5,
PDS_CORE_CMD_GET_COMPONENT_INFO = 6,
PDS_CORE_CMD_SEND_PKG_DATA = 7,
PDS_CORE_CMD_SEND_COMPONENT_TBL = 8,
PDS_CORE_CMD_SEND_COMPONENT = 9,
PDS_CORE_CMD_FINALIZE_UPDATE = 10,
PDS_CORE_CMD_MATCH_RECORD_DESC = 11,
/* SR/IOV commands */
PDS_CORE_CMD_VF_GETATTR = 60,
PDS_CORE_CMD_VF_SETATTR = 61,
@@ -100,6 +107,14 @@ struct pds_core_drv_identity {
char driver_ver_str[32];
};
/**
* enum pds_core_dev_capability - Device capabilities
* @PDS_CORE_DEV_CAP_PLDM_FW_UPDATE: Device only supports FW update via PLDM
*/
enum pds_core_dev_capability {
PDS_CORE_DEV_CAP_PLDM_FW_UPDATE = BIT(0),
};
#define PDS_DEV_TYPE_MAX 16
/**
* struct pds_core_dev_identity - Device identity information
@@ -119,6 +134,9 @@ struct pds_core_drv_identity {
* value in usecs to device units using:
* device units = usecs * mult / div
* @vif_types: How many of each VIF device type is supported
* @max_fw_slots: Number of firmware components reported by device
* only supported on version >= PDS_CORE_IDENTITY_VERSION_2
* @rsvd2: Word boundary padding
* @capabilities: Device capabilities
* only supported on version >= PDS_CORE_IDENTITY_VERSION_2
*/
@@ -133,6 +151,8 @@ struct pds_core_dev_identity {
__le32 intr_coal_mult;
__le32 intr_coal_div;
__le16 vif_types[PDS_DEV_TYPE_MAX];
__le16 max_fw_slots;
u8 rsvd2[6];
__le64 capabilities;
};
@@ -279,11 +299,20 @@ enum pds_core_fw_control_oper {
PDS_CORE_FW_GET_LIST = 7,
};
/**
* enum pds_core_fw_slot - Firmware slot identifiers
* @PDS_CORE_FW_SLOT_INVALID: Let firmware select slot based on package metadata
* @PDS_CORE_FW_SLOT_A: Primary firmware slot A
* @PDS_CORE_FW_SLOT_B: Primary firmware slot B
* @PDS_CORE_FW_SLOT_GOLD: Gold/recovery firmware slot
* @PDS_CORE_FW_SLOT_MAX: Sentinel value indicating no slot resolved
*/
enum pds_core_fw_slot {
PDS_CORE_FW_SLOT_INVALID = 0,
PDS_CORE_FW_SLOT_A = 1,
PDS_CORE_FW_SLOT_B = 2,
PDS_CORE_FW_SLOT_GOLD = 3,
PDS_CORE_FW_SLOT_MAX = 0xff,
};
/**
@@ -450,6 +479,364 @@ struct pds_core_vf_ctrl_comp {
u8 status;
};
/**
* struct pds_core_send_pkg_data_cmd - Send package data command
* @opcode: Opcode PDS_CORE_CMD_SEND_PKG_DATA
* @ver: Driver's max support version of this command
* @total_len: Total length of the package data
* @offset: Offset in the package data, non-zero if multiple commands are
* needed for sending the package data
* @data_len: Length of data stored at data_pa
* @data_pa: Data physical address for DMA to device
*
* The package data may be too large to store in a single buffer, so multiple
* PDS_CORE_CMD_SEND_PKG_DATA devcmds may be needed.
*/
struct pds_core_send_pkg_data_cmd {
u8 opcode;
u8 ver;
__le16 total_len;
__le16 offset;
__le16 data_len;
__le64 data_pa;
};
/**
* struct pds_core_send_pkg_data_comp - Send package data completion
* @status: Status of the command (enum pds_core_status_code)
* @ver: Device's max supported version of this command
* @rsvd: Word boundary padding
*/
struct pds_core_send_pkg_data_comp {
u8 status;
u8 ver;
u8 rsvd[2];
};
/**
* struct pds_core_component_tbl - Component table details
* @comparison_stamp: Comparison stamp used for component version checks
* @classification: Vendor specific classification info
* @identifier: Component's ID
* @transfer_flag: Part of the component table this request represents
* @version_str_type: The types of strings used
* @version_str_len: Length of @version_str
* @version_str: Component version information
*/
struct pds_core_component_tbl {
__le32 comparison_stamp;
__le16 classification;
__le16 identifier;
u8 transfer_flag;
u8 version_str_type;
u8 version_str_len;
u8 version_str[];
};
/**
* struct pds_core_send_component_tbl_cmd - Send component table command
* @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT_TBL
* @ver: Driver's max support version of this command
* @slot_id: enum pds_core_fw_slot
* @rsvd: Word boundary padding
*
* Expects to find component table info (struct pds_core_component_tbl)
* in cmd_regs->data. Driver should keep the devcmd interface locked
* while preparing the component table info.
*/
struct pds_core_send_component_tbl_cmd {
u8 opcode;
u8 ver;
u8 slot_id;
u8 rsvd;
};
enum pds_core_component_resp_code {
PDS_CORE_COMPONENT_VALID = 0x0,
PDS_CORE_COMPONENT_STAMP_IDENTICAL = 0x1,
PDS_CORE_COMPONENT_STAMP_LOWER = 0x2,
PDS_CORE_COMPONENT_STAMP_OR_VERSION_INVALID = 0x3,
PDS_CORE_COMPONENT_CONFLICT = 0x4,
PDS_CORE_COMPONENT_PREREQS_NOT_MET = 0x5,
PDS_CORE_COMPONENT_NOT_SUPPORTED = 0x6,
PDS_CORE_COMPONENT_FW_TYPE_INVALID = 0xd0,
};
/**
* struct pds_core_send_component_tbl_comp - Send component table completion
* @status: Status of the command (enum pds_core_status_code)
* @ver: Device's max supported version of this command
* @completion_code: Component completion code
* @response: Component response
* @response_code: Component response code
* @slot_id: Actual slot_id of the component (enum pds_core_fw_slot)
* @rsvd: Word boundary padding
*/
struct pds_core_send_component_tbl_comp {
u8 status;
u8 ver;
u8 completion_code;
u8 response;
u8 response_code;
u8 slot_id;
u8 rsvd[2];
};
/**
* enum pds_core_send_component_op - PDS_CORE_CMD_SEND_COMPONENT operation
* @PDS_CORE_SEND_COMPONENT_START: Initial operation to start transfer
* @PDS_CORE_SEND_COMPONENT_STATUS: Subsequent calls to check on status
*/
enum pds_core_send_component_op {
PDS_CORE_SEND_COMPONENT_START = 0,
PDS_CORE_SEND_COMPONENT_STATUS = 1,
};
#define PDS_CORE_FW_COMPONENT_ID_INVALID 0xFFFF
/**
* struct pds_core_flash_component - Component details
* @comparison_stamp: Comparison stamp used for component version checks
* @image_size: Component image size
* @classification: Vendor specific classification info
* @identifier: Component's ID
* @options: Component options
* @rsvd: Word boundary padding
* @version_str_type: The types of strings used
* @version_str_len: Length of @version_str
* @version_str: Component version information
*/
struct pds_core_flash_component {
__le32 comparison_stamp;
__le32 image_size;
__le16 classification;
__le16 identifier;
__le16 options;
u8 rsvd[3];
u8 version_str_type;
u8 version_str_len;
u8 version_str[];
};
/**
* struct pds_core_send_component_cmd - Send component command
* @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT
* @ver: Driver's max supported version of this command
* @slot_id: enum pds_core_fw_slot
* @operation: enum pds_core_send_component_op
* @offset: Offset into the component, non-zero if multiple commands
* are needed for a single component
* @data_len: Length of this part of the component stored at @data_pa
* @rsvd: Word boundary padding
* @data_pa: DMA address of the component
*
* A component may be too large to store in a single buffer, so multiple
* PDS_CORE_CMD_SEND_COMPONENT devcmds may be needed.
*
* Expects to find flash component info (struct pds_core_flash_component)
* in cmd_regs->data. Driver should keep the devcmd interface locked
* while preparing and sending the flash component info.
*/
struct pds_core_send_component_cmd {
u8 opcode;
u8 ver;
u8 slot_id;
u8 operation;
__le32 offset;
__le32 data_len;
u8 rsvd[4];
__le64 data_pa;
};
/**
* struct pds_core_send_component_comp - Send component completion
* @status: Status of the command (enum pds_core_status_code)
* @ver: Device's max supported version of this command
* @completion_code: Completion code
* @compat_response: Compatibility response (0 = Component can be updated)
* @compat_response_code: Compatibility response code
* @rsvd: Word boundary padding
*/
struct pds_core_send_component_comp {
u8 status;
u8 ver;
u8 completion_code;
u8 compat_response;
u8 compat_response_code;
u8 rsvd[3];
};
/**
* enum pds_core_fw_component_type - Firmware component type
* @PDS_CORE_FW_TYPE_UNKNOWN: Unknown component type
* @PDS_CORE_FW_TYPE_MAIN: Main firmware
* @PDS_CORE_FW_TYPE_BOOT: Boot loader
* @PDS_CORE_FW_TYPE_CPLD: CPLD firmware
* @PDS_CORE_FW_TYPE_SECURE: Secure firmware
* @PDS_CORE_FW_TYPE_FPGA: FPGA configuration
* @PDS_CORE_FW_TYPE_SUC_MAIN: System Unit Controller firmware
* @PDS_CORE_FW_TYPE_SUC_BOOT: System Unit Controller bootloader
* @PDS_CORE_FW_TYPE_UBOOT: U-Boot bootloader
*
* Gold/recovery variants are identified by slot_id == PDS_CORE_FW_SLOT_GOLD
* and reported with a ".gold" suffix (e.g., fw.gold).
*/
enum pds_core_fw_component_type {
PDS_CORE_FW_TYPE_UNKNOWN = 0,
PDS_CORE_FW_TYPE_MAIN = 1,
PDS_CORE_FW_TYPE_BOOT = 2,
PDS_CORE_FW_TYPE_CPLD = 3,
PDS_CORE_FW_TYPE_SECURE = 4,
PDS_CORE_FW_TYPE_FPGA = 5,
PDS_CORE_FW_TYPE_SUC_MAIN = 6,
PDS_CORE_FW_TYPE_SUC_BOOT = 7,
PDS_CORE_FW_TYPE_UBOOT = 8,
};
/**
* enum pds_core_component_info_flags - Component info flags
* @PDS_CORE_FW_COMPONENT_INFO_F_RUNNING: Component is currently running
* @PDS_CORE_FW_COMPONENT_INFO_F_STARTUP: Component version on next FW boot
* @PDS_CORE_FW_COMPONENT_INFO_F_FIXED: Component is fixed and cannot be updated
* @PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME: Component can be updated
* by name
*/
enum pds_core_component_info_flags {
PDS_CORE_FW_COMPONENT_INFO_F_RUNNING = BIT(0),
PDS_CORE_FW_COMPONENT_INFO_F_STARTUP = BIT(1),
PDS_CORE_FW_COMPONENT_INFO_F_FIXED = BIT(2),
PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME = BIT(3),
};
/**
* struct pds_core_fw_component_info - GET_COMPONENT_INFO entry
* @name: Component's name
* @component_type: enum pds_core_fw_component_type
* @rsvd: Word boundary padding
* @flags: enum pds_core_component_info_flags
* @identifier: Component's identifier
* @slot_id: Component's slot identifier
* @version: Component's version
*/
struct pds_core_fw_component_info {
#define PDS_CORE_FW_COMPONENT_NAME_BUFLEN 24
char name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN];
u8 component_type;
u8 rsvd[3];
__le16 flags;
u8 identifier;
u8 slot_id;
#define PDS_CORE_FW_COMPONENT_VER_BUFLEN 32
char version[PDS_CORE_FW_COMPONENT_VER_BUFLEN];
};
#define PDS_CORE_FW_COMPONENT_LIST_LEN ((PDS_PAGE_SIZE - 8) / \
sizeof(struct pds_core_fw_component_info))
/**
* struct pds_core_component_list_info - GET_COMPONENT_INFO completion data
* @num_components: Number of valid components
* @rsvd: Word boundary padding
* @info: List of valid components
*/
struct pds_core_component_list_info {
u8 num_components;
u8 rsvd[7];
struct pds_core_fw_component_info info[PDS_CORE_FW_COMPONENT_LIST_LEN];
};
/**
* struct pds_core_get_component_info_cmd - GET_COMPONENT_INFO command
* @opcode: PDS_CORE_CMD_GET_COMPONENT_INFO
* @ver: Driver's max supported version of this command
* @data_len: Length of data at data_pa
* @rsvd: Word boundary padding
* @data_pa: DMA address of data
*
* FW populates struct pds_core_component_list_info pointed to by @data_pa
*/
struct pds_core_get_component_info_cmd {
u8 opcode;
u8 ver;
__le16 data_len;
u8 rsvd[4];
__le64 data_pa;
};
/**
* struct pds_core_get_component_info_comp - GET_COMPONENT_INFO completion
* @status: enum pds_core_status_code
* @ver: Device's max supported version of this command
* @rsvd: Word boundary padding
*/
struct pds_core_get_component_info_comp {
u8 status;
u8 ver;
u8 rsvd[2];
};
/**
* struct pds_core_finalize_update_cmd - FINALIZE_UPDATE command
* @opcode: PDS_CORE_CMD_FINALIZE_UPDATE
* @ver: Driver's max support version of this command
* @rsvd: Word boundary padding
*
* Driver sends at the end of updating all components to finalize the update
*/
struct pds_core_finalize_update_cmd {
u8 opcode;
u8 ver;
u8 rsvd[2];
};
/**
* struct pds_core_finalize_update_comp - FINALIZE_UPDATE completion
* @status: enum pds_core_status_code
* @ver: Device's max supported version of this command
* @rsvd: Word boundary padding
*/
struct pds_core_finalize_update_comp {
u8 status;
u8 ver;
u8 rsvd[2];
};
/**
* struct pds_core_match_record_desc_cmd - MATCH_RECORD_DESC command
* @opcode: PDS_CORE_CMD_MATCH_RECORD_DESC
* @ver: Driver's max supported version of this command
* @type: PLDM Descriptor Identifier Type
* @size: Length of the Descriptor Identifier Value
* @rsvd: Word boundary padding
*
* Expects to find the Descriptor Identifier Data in cmd_regs->data. Driver
* should keep the devcmd interface locked while preparing and sending this
* command.
*/
struct pds_core_match_record_desc_cmd {
u8 opcode;
u8 ver;
__le16 type;
__le16 size;
u8 rsvd[2];
};
/**
* struct pds_core_match_record_desc_comp - MATCH_RECORD_DESC completion
* @status: enum pds_core_status_code
* @ver: Device's max supported version of this command
* @match: Whether or not the Record Descriptor matches the device
* @rsvd: Word boundary padding
*
* When status is PDS_RC_SUCCESS, then @match is valid, otherwise it's
* undefined.
*/
struct pds_core_match_record_desc_comp {
u8 status;
u8 ver;
u8 match;
u8 rsvd;
};
/*
* union pds_core_dev_cmd - Overlay of core device command structures
*/
@@ -466,6 +853,13 @@ union pds_core_dev_cmd {
struct pds_core_vf_setattr_cmd vf_setattr;
struct pds_core_vf_getattr_cmd vf_getattr;
struct pds_core_vf_ctrl_cmd vf_ctrl;
struct pds_core_get_component_info_cmd get_component_info;
struct pds_core_send_pkg_data_cmd send_pkg_data;
struct pds_core_send_component_tbl_cmd send_component_tbl;
struct pds_core_send_component_cmd send_component;
struct pds_core_finalize_update_cmd finalize_update;
struct pds_core_match_record_desc_cmd match_record_desc;
};
/*
@@ -484,6 +878,13 @@ union pds_core_dev_comp {
struct pds_core_vf_setattr_comp vf_setattr;
struct pds_core_vf_getattr_comp vf_getattr;
struct pds_core_vf_ctrl_comp vf_ctrl;
struct pds_core_get_component_info_comp get_component_info;
struct pds_core_send_pkg_data_comp send_pkg_data;
struct pds_core_send_component_tbl_comp send_component_tbl;
struct pds_core_send_component_comp send_component;
struct pds_core_finalize_update_comp finalize_update;
struct pds_core_match_record_desc_comp match_record_desc;
};
/**