acpi: acpi_table: Add IORT support

The SoC can implement acpi_fill_iort to update the IORT table.
Add a helper function to fill out the NAMED_COMPONENT node.

TEST=Run FWTS V24.03.00 on RPi4 and round no problems.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Cc: Simon Glass <sjg@chromium.org>
This commit is contained in:
Patrick Rudolph 2024-10-23 15:19:54 +02:00 committed by Tom Rini
parent 7f91bcac1e
commit bf5d37662d
2 changed files with 426 additions and 0 deletions

View file

@ -832,6 +832,117 @@ struct acpi_pptt_cache {
u16 line_size;
} __packed;
/** IORT - IO Remapping Table revision 6
* Document number: ARM DEN 0049E.e, Sep 2022
*/
struct acpi_table_iort {
struct acpi_table_header header;
u32 node_count;
u32 node_offset;
u32 reserved;
} __packed;
/*
* IORT subtables
*/
struct acpi_iort_node {
u8 type;
u16 length;
u8 revision;
u32 identifier;
u32 mapping_count;
u32 mapping_offset;
char node_data[];
} __packed;
/* Values for subtable Type above */
enum acpi_iort_node_type {
ACPI_IORT_NODE_ITS_GROUP = 0x00,
ACPI_IORT_NODE_NAMED_COMPONENT = 0x01,
ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02,
ACPI_IORT_NODE_SMMU = 0x03,
ACPI_IORT_NODE_SMMU_V3 = 0x04,
ACPI_IORT_NODE_PMCG = 0x05,
ACPI_IORT_NODE_RMR = 0x06,
};
/* ITS Group revision 1 */
struct acpi_iort_its_group {
u32 its_count;
u32 identifiers[]; /* GIC ITS identifier array */
} __packed;
/* PCI root complex node revision 2 */
struct acpi_iort_rc {
u64 mem_access_properties;
u32 ats_attributes;
u32 pci_segment_number;
u8 memory_address_size_limit;
u8 reserved[3];
} __packed;
/* SMMUv3 revision 5 */
struct acpi_iort_smmu_v3 {
u64 base_address; /* SMMUv3 base address */
u32 flags;
u32 reserved;
u64 vatos_address;
u32 model;
u32 event_gsiv;
u32 pri_gsiv;
u32 gerr_gsiv;
u32 sync_gsiv;
u32 pxm;
u32 id_mapping_index;
} __packed;
/* Masks for Flags field above */
#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE (1)
#define ACPI_IORT_SMMU_V3_HTTU_OVERRIDE (3 << 1)
#define ACPI_IORT_SMMU_V3_PXM_VALID (1 << 3)
#define ACPI_IORT_SMMU_V3_DEVICEID_VALID (1 << 4)
struct acpi_iort_id_mapping {
u32 input_base; /* Lowest value in input range */
u32 id_count; /* Number of IDs */
u32 output_base; /* Lowest value in output range */
u32 output_reference; /* A reference to the output node */
u32 flags;
} __packed;
/* Masks for Flags field above for IORT subtable */
#define ACPI_IORT_ID_SINGLE_MAPPING (1)
/* Named Component revision 4 */
struct acpi_iort_named_component {
u32 node_flags;
u64 memory_properties; /* Memory access properties */
u8 memory_address_limit; /* Memory address size limit */
char device_name[]; /* Path of namespace object */
} __packed;
/* Masks for Flags field above */
#define ACPI_IORT_NC_STALL_SUPPORTED (1)
#define ACPI_IORT_NC_PASID_BITS (31 << 1)
struct acpi_iort_root_complex {
u64 memory_properties; /* Memory access properties */
u32 ats_attribute;
u32 pci_segment_number;
u8 memory_address_limit;/* Memory address size limit */
u16 pasid_capabilities; /* PASID Capabilities */
u8 reserved; /* Reserved, must be zero */
u32 flags; /* Flags */
} __packed;
/* Masks for ats_attribute field above */
#define ACPI_IORT_ATS_SUPPORTED (1) /* The root complex ATS support */
#define ACPI_IORT_PRI_SUPPORTED (1 << 1) /* The root complex PRI support */
#define ACPI_IORT_PASID_FWD_SUPPORTED (1 << 2) /* The root complex PASID forward support */
/* Masks for pasid_capabilities field above */
#define ACPI_IORT_PASID_MAX_WIDTH (0x1F) /* Bits 0-4 */
/* Tables defined/reserved by ACPI and generated by U-Boot */
enum acpi_tables {
ACPITAB_BERT,
@ -1000,6 +1111,108 @@ int acpi_fill_csrt(struct acpi_ctx *ctx);
*/
void acpi_fill_fadt(struct acpi_fadt *fadt);
/**
* acpi_fill_iort() - Fill out the body of the IORT table
*
* Should be implemented in SoC specific code.
*
* @ctx: ACPI context to write to
* @offset: Offset from the start of the IORT
*/
int acpi_fill_iort(struct acpi_ctx *ctx);
/**
* acpi_iort_add_its_group() - Add ITS group node to IORT table
*
* Called by SoC specific code within acpi_fill_iort().
*
* @ctx: ACPI context to write to
* @its_count: Elements in identifiers
* @identifiers: The array of ITS identifiers. These IDs must match the value
* used in the Multiple APIC Description Table (MADT) GIC ITS
* structure for each relevant ITS unit.
* @return Offset of table within parent
*/
int acpi_iort_add_its_group(struct acpi_ctx *ctx,
const u32 its_count,
const u32 *identifiers);
/**
* acpi_iort_add_named_component() - Add named component to IORT table
*
* Called by SoC specific code within acpi_fill_iort().
*
* @ctx: ACPI context to write to
* @node_flags: Node flags
* @memory_properties: Memory properties
* @memory_address_limit: Memory address limit
* @device_name: ACPI device path
* @return Offset of table within parent
*/
int acpi_iort_add_named_component(struct acpi_ctx *ctx,
const u32 node_flags,
const u64 memory_properties,
const u8 memory_address_limit,
const char *device_name);
/**
* acpi_iort_add_rc() - Add PCI root complex node to IORT table
*
* Called by SoC specific code within acpi_fill_iort().
*
* @ctx: ACPI context to write to
* @mem_access_properties: Memory access properties
* @ats_attributes: Support for ATS and its ancillary feature
* @pci_segment_number: The PCI segment number, as in MCFG
* @memory_address_size_limit: The number of address bits, starting from LSB
* @num_mappings: Number of elements in map
* @map: ID mappings for this node
* @return Offset of table within parent
*/
int acpi_iort_add_rc(struct acpi_ctx *ctx,
const u64 mem_access_properties,
const u32 ats_attributes,
const u32 pci_segment_number,
const u8 memory_address_size_limit,
const int num_mappings,
const struct acpi_iort_id_mapping *map);
/**
* acpi_iort_add_smmu_v3() - Add PCI root complex node to IORT table
*
* Called by SoC specific code within acpi_fill_iort().
*
* @ctx: ACPI context to write to
* @base_address: Base address of SMMU
* @flags: SMMUv3 flags
* @vatos_address: Optional, set to zero if not supported
* @model: Model ID
* @event_gsiv: GSIV of the Event interrupt if SPI based
* @pri_gsiv: GSIV of the PRI interrupt if SPI based
* @gerr_gsiv: GSIV of the GERR interrupt if GSIV based
* @sync_gsiv: TGSIV of the Sync interrupt if GSIV based
* @pxm: Proximity Domain
* @id_mapping_index: If all the SMMU control interrupts are GSIV based,
* this field is ignored. Index into the array of ID
* mapping otherwise.
* @num_mappings: Number of elements in map
* @map: ID mappings for this node
* @return Offset of table within parent
*/
int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx,
const u64 base_address,
const u32 flags,
const u64 vatos_address,
const u32 model,
const u32 event_gsiv,
const u32 pri_gsiv,
const u32 gerr_gsiv,
const u32 sync_gsiv,
const u32 pxm,
const u32 id_mapping_index,
const int num_mappings,
const struct acpi_iort_id_mapping *map);
/**
* acpi_fill_madt() - Fill out the body of the MADT
*

View file

@ -520,3 +520,216 @@ static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry
}
ACPI_WRITER(5spcr, "SPCR", acpi_write_spcr, 0);
__weak int acpi_fill_iort(struct acpi_ctx *ctx)
{
return 0;
}
int acpi_iort_add_its_group(struct acpi_ctx *ctx,
const u32 its_count,
const u32 *identifiers)
{
struct acpi_iort_node *node;
struct acpi_iort_its_group *group;
int offset;
offset = ctx->current - ctx->tab_start;
node = ctx->current;
memset(node, '\0', sizeof(struct acpi_iort_node));
node->type = ACPI_IORT_NODE_ITS_GROUP;
node->revision = 1;
node->length = sizeof(struct acpi_iort_node);
node->length += sizeof(struct acpi_iort_its_group);
node->length += sizeof(u32) * its_count;
group = (struct acpi_iort_its_group *)node->node_data;
group->its_count = its_count;
memcpy(&group->identifiers, identifiers, sizeof(u32) * its_count);
ctx->current += node->length;
return offset;
}
int acpi_iort_add_named_component(struct acpi_ctx *ctx,
const u32 node_flags,
const u64 memory_properties,
const u8 memory_address_limit,
const char *device_name)
{
struct acpi_iort_node *node;
struct acpi_iort_named_component *comp;
int offset;
offset = ctx->current - ctx->tab_start;
node = ctx->current;
memset(node, '\0', sizeof(struct acpi_iort_node));
node->type = ACPI_IORT_NODE_NAMED_COMPONENT;
node->revision = 4;
node->length = sizeof(struct acpi_iort_node);
node->length += sizeof(struct acpi_iort_named_component);
node->length += strlen(device_name) + 1;
comp = (struct acpi_iort_named_component *)node->node_data;
comp->node_flags = node_flags;
comp->memory_properties = memory_properties;
comp->memory_address_limit = memory_address_limit;
memcpy(comp->device_name, device_name, strlen(device_name) + 1);
ctx->current += node->length;
return offset;
}
int acpi_iort_add_rc(struct acpi_ctx *ctx,
const u64 mem_access_properties,
const u32 ats_attributes,
const u32 pci_segment_number,
const u8 memory_address_size_limit,
const int num_mappings,
const struct acpi_iort_id_mapping *map)
{
struct acpi_iort_id_mapping *mapping;
struct acpi_iort_node *node;
struct acpi_iort_rc *rc;
int offset;
offset = ctx->current - ctx->tab_start;
node = ctx->current;
memset(node, '\0', sizeof(struct acpi_iort_node));
node->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
node->revision = 2;
node->length = sizeof(struct acpi_iort_node);
node->length += sizeof(struct acpi_iort_rc);
node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings;
rc = (struct acpi_iort_rc *)node->node_data;
rc->mem_access_properties = mem_access_properties;
rc->ats_attributes = ats_attributes;
rc->pci_segment_number = pci_segment_number;
rc->memory_address_size_limit = memory_address_size_limit;
mapping = (struct acpi_iort_id_mapping *)(rc + 1);
for (int i = 0; i < num_mappings; i++) {
memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping));
mapping++;
}
ctx->current += node->length;
return offset;
}
int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx,
const u64 base_address,
const u32 flags,
const u64 vatos_address,
const u32 model,
const u32 event_gsiv,
const u32 pri_gsiv,
const u32 gerr_gsiv,
const u32 sync_gsiv,
const u32 pxm,
const u32 id_mapping_index,
const int num_mappings,
const struct acpi_iort_id_mapping *map)
{
struct acpi_iort_node *node;
struct acpi_iort_smmu_v3 *smmu;
struct acpi_iort_id_mapping *mapping;
int offset;
offset = ctx->current - ctx->tab_start;
node = ctx->current;
memset(node, '\0', sizeof(struct acpi_iort_node));
node->type = ACPI_IORT_NODE_SMMU_V3;
node->revision = 5;
node->mapping_count = num_mappings;
node->mapping_offset = sizeof(struct acpi_iort_node) + sizeof(struct acpi_iort_smmu_v3);
node->length = sizeof(struct acpi_iort_node);
node->length += sizeof(struct acpi_iort_smmu_v3);
node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings;
smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
smmu->base_address = base_address;
smmu->flags = flags;
smmu->vatos_address = vatos_address;
smmu->model = model;
smmu->event_gsiv = event_gsiv;
smmu->pri_gsiv = pri_gsiv;
smmu->gerr_gsiv = gerr_gsiv;
smmu->sync_gsiv = sync_gsiv;
smmu->pxm = pxm;
smmu->id_mapping_index = id_mapping_index;
mapping = (struct acpi_iort_id_mapping *)(smmu + 1);
for (int i = 0; i < num_mappings; i++) {
memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping));
mapping++;
}
ctx->current += node->length;
return offset;
}
static int acpi_write_iort(struct acpi_ctx *ctx, const struct acpi_writer *entry)
{
struct acpi_table_iort *iort;
struct acpi_iort_node *node;
u32 offset;
int ret;
iort = ctx->current;
ctx->tab_start = ctx->current;
memset(iort, '\0', sizeof(struct acpi_table_iort));
acpi_fill_header(&iort->header, "IORT");
iort->header.revision = 1;
iort->header.creator_revision = 1;
iort->header.length = sizeof(struct acpi_table_iort);
iort->node_offset = sizeof(struct acpi_table_iort);
acpi_inc(ctx, sizeof(struct acpi_table_iort));
offset = sizeof(struct acpi_table_iort);
ret = acpi_fill_iort(ctx);
if (ret) {
ctx->current = iort;
return log_msg_ret("fill", ret);
}
/* Count nodes filled in */
for (node = (void *)iort + iort->node_offset;
node->length > 0 && (void *)node < ctx->current;
node = (void *)node + node->length)
iort->node_count++;
/* (Re)calculate length and checksum */
iort->header.length = ctx->current - (void *)iort;
iort->header.checksum = table_compute_checksum((void *)iort, iort->header.length);
log_debug("IORT at %p, length %x\n", iort, iort->header.length);
/* Drop the table if it is empty */
if (iort->header.length == sizeof(struct acpi_table_iort))
return log_msg_ret("fill", -ENOENT);
acpi_add_table(ctx, iort);
return 0;
}
ACPI_WRITER(5iort, "IORT", acpi_write_iort, 0);