/* Field masks for ipa_cmd_hw_hdr_init_local structure fields */ #define HDR_INIT_LOCAL_FLAGS_TABLE_SIZE_FMASK GENMASK(11, 0) #define HDR_INIT_LOCAL_FLAGS_HDR_ADDR_FMASK GENMASK(27, 12)
/* IPA_CMD_REGISTER_WRITE */
/* For IPA v4.0+, the pipeline clear options are encoded in the opcode */ #define REGISTER_WRITE_OPCODE_SKIP_CLEAR_FMASK GENMASK(8, 8) #define REGISTER_WRITE_OPCODE_CLEAR_OPTION_FMASK GENMASK(10, 9)
/* Field masks for ipa_cmd_register_write structure fields */ /* The next field is present for IPA v4.0+ */ #define REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK GENMASK(14, 11) /* The next field is not present for IPA v4.0+ */ #define REGISTER_WRITE_FLAGS_SKIP_CLEAR_FMASK GENMASK(15, 15)
/* The next field and its values are not present for IPA v4.0+ */ #define REGISTER_WRITE_CLEAR_OPTIONS_FMASK GENMASK(1, 0)
/* IPA_CMD_IP_PACKET_INIT */
struct ipa_cmd_ip_packet_init {
u8 dest_endpoint; /* Full 8 bits used for IPA v5.0+ */
u8 reserved[7];
};
/* Field mask for ipa_cmd_ip_packet_init dest_endpoint field (unused v5.0+) */ #define IPA_PACKET_INIT_DEST_ENDPOINT_FMASK GENMASK(4, 0)
/* IPA_CMD_DMA_SHARED_MEM */
/* For IPA v4.0+, this opcode gets modified with pipeline clear options */
/* Flag allowing atomic clear of target region after reading data (v4.0+)*/ #define DMA_SHARED_MEM_CLEAR_AFTER_READ GENMASK(15, 15)
/* Field masks for ipa_cmd_hw_dma_mem_mem structure fields */ #define DMA_SHARED_MEM_FLAGS_DIRECTION_FMASK GENMASK(0, 0) /* The next two fields are not present for IPA v4.0+ */ #define DMA_SHARED_MEM_FLAGS_SKIP_CLEAR_FMASK GENMASK(1, 1) #define DMA_SHARED_MEM_FLAGS_CLEAR_OPTIONS_FMASK GENMASK(3, 2)
staticvoid ipa_cmd_validate_build(void)
{ /* The size of a filter table needs to fit into fields in the *ipa_cmd_hw_ip_fltrt_initstructure.Althoughhashedtables *mightnotbeused,non-hashedandhashedtableshavethesame *maximumsize.IPv4andIPv6filtertableshavethesamenumber *ofentries.
*/ /* Hashed and non-hashed fields are assumed to be the same size */
BUILD_BUG_ON(field_max(IP_FLTRT_FLAGS_HASH_SIZE_FMASK) !=
field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK));
BUILD_BUG_ON(field_max(IP_FLTRT_FLAGS_HASH_ADDR_FMASK) !=
field_max(IP_FLTRT_FLAGS_NHASH_ADDR_FMASK));
/* Prior to IPA v5.0, we supported no more than 32 endpoints, *andthiswasreflectedinsome5-bitfieldsthatheld *endpointnumbers.StartingwithIPAv5.0,thewidthsof *thesefieldswereextendedto8bits,meaningupto256 *endpoints.Ifthedriverclaimstosupportmorethan *thatit'sanerror.
*/
BUILD_BUG_ON(IPA_ENDPOINT_MAX - 1 > U8_MAX);
}
/* Size must fit in the immediate command field that holds it */ if (size > size_max) {
dev_err(dev, "%s table region size too large\n", table);
dev_err(dev, " (0x%04x > 0x%04x)\n", size, size_max);
returnfalse;
}
/* Offset must fit in the immediate command field that holds it */ if (mem->offset > offset_max ||
ipa->mem_offset > offset_max - mem->offset) {
dev_err(dev, "%s table region offset too large\n", table);
dev_err(dev, " (0x%04x + 0x%04x > 0x%04x)\n",
ipa->mem_offset, mem->offset, offset_max);
returnfalse;
}
returntrue;
}
/* Validate the memory region that holds headers */ staticbool ipa_cmd_header_init_local_valid(struct ipa *ipa)
{ struct device *dev = ipa->dev; conststruct ipa_mem *mem;
u32 offset_max;
u32 size_max;
u32 offset;
u32 size;
/* In ipa_cmd_hdr_init_local_add() we record the offset and size of *theheadertablememoryareainanimmediatecommand.Makesure *theoffsetandsizefitinthefieldsthatneedtoholdthem,and *thattheentirerangeiswithintheoverallIPAmemoryrange.
*/
offset_max = field_max(HDR_INIT_LOCAL_FLAGS_HDR_ADDR_FMASK);
size_max = field_max(HDR_INIT_LOCAL_FLAGS_TABLE_SIZE_FMASK);
/* The header memory area contains both the modem and AP header *regions.Themodemportiondefinestheaddressoftheregion.
*/
mem = ipa_mem_find(ipa, IPA_MEM_MODEM_HEADER);
offset = mem->offset;
size = mem->size;
/* Make sure the offset fits in the IPA command */ if (offset > offset_max || ipa->mem_offset > offset_max - offset) {
dev_err(dev, "header table region offset too large\n");
dev_err(dev, " (0x%04x + 0x%04x > 0x%04x)\n",
ipa->mem_offset, offset, offset_max);
returnfalse;
}
/* Add the size of the AP portion (if defined) to the combined size */
mem = ipa_mem_find(ipa, IPA_MEM_AP_HEADER); if (mem)
size += mem->size;
/* Make sure the combined size fits in the IPA command */ if (size > size_max) {
dev_err(dev, "header table region size too large\n");
dev_err(dev, " (0x%04x > 0x%08x)\n", size, size_max);
returnfalse;
}
returntrue;
}
/* Indicate whether an offset can be used with a register_write command */ staticbool ipa_cmd_register_write_offset_valid(struct ipa *ipa, constchar *name, u32 offset)
{ struct ipa_cmd_register_write *payload; struct device *dev = ipa->dev;
u32 offset_max;
u32 bit_count;
/* The maximum offset in a register_write immediate command depends *ontheversionofIPA.A16bitoffsetisalwayssupported, *butstartingwithIPAv4.0someadditionalhigh-orderbitsare *allowed.
*/
bit_count = BITS_PER_BYTE * sizeof(payload->offset); if (ipa->version >= IPA_VERSION_4_0)
bit_count += hweight32(REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK);
BUILD_BUG_ON(bit_count > 32);
offset_max = ~0U >> (32 - bit_count);
/* Make sure the offset can be represented by the field(s) *thatholdsit.Alsomakesuretheoffsetisnotoutside *theoverallIPAmemoryrange.
*/ if (offset > offset_max || ipa->mem_offset > offset_max - offset) {
dev_err(dev, "%s offset too large 0x%04x + 0x%04x > 0x%04x)\n",
name, ipa->mem_offset, offset, offset_max); returnfalse;
}
/* If hashed tables are supported, ensure the hash flush register *offsetwillfitinaregisterwriteIPAimmediatecommand.
*/ if (ipa_table_hash_support(ipa)) { if (ipa->version < IPA_VERSION_5_0)
reg = ipa_reg(ipa, FILT_ROUT_HASH_FLUSH); else
reg = ipa_reg(ipa, FILT_ROUT_CACHE_FLUSH);
offset = reg_offset(reg);
name = "filter/route hash flush"; if (!ipa_cmd_register_write_offset_valid(ipa, name, offset)) returnfalse;
}
/* Each endpoint can have a status endpoint associated with it, *andthisisrecordedinanendpointregister.Ifthemodem *crashes,weresetthestatusendpointforallmodemendpoints *usingaregisterwriteIPAimmediatecommand.Makesurethe *worstcase(highestendpointnumber)offsetofthatendpoint *fitsintheregisterwritecommandfield(s)thatmustholdit.
*/
reg = ipa_reg(ipa, ENDP_STATUS);
offset = reg_n_offset(reg, IPA_ENDPOINT_COUNT - 1);
name = "maximal endpoint status"; if (!ipa_cmd_register_write_offset_valid(ipa, name, offset)) returnfalse;
/* Command payloads are allocated one at a time, but a single *transactioncanrequireuptothemaximumsupportedbythe *channel;treatthemasiftheywereallocatedallatonce.
*/ return gsi_trans_pool_init_dma(dev, &trans_info->cmd_pool, sizeof(union ipa_cmd_payload),
tre_max, channel->trans_tre_max);
}
/* Record the non-hash table offset and size */
offset += ipa->mem_offset;
val = u64_encode_bits(offset, IP_FLTRT_FLAGS_NHASH_ADDR_FMASK);
val |= u64_encode_bits(size, IP_FLTRT_FLAGS_NHASH_SIZE_FMASK);
/* The hash table offset and address are zero if its size is 0 */ if (hash_size) { /* Record the hash table offset and size */
hash_offset += ipa->mem_offset;
val |= u64_encode_bits(hash_offset,
IP_FLTRT_FLAGS_HASH_ADDR_FMASK);
val |= u64_encode_bits(hash_size,
IP_FLTRT_FLAGS_HASH_SIZE_FMASK);
}
/* Fill in all offsets and sizes and the non-hash table address */ if (hash_size)
payload->hash_rules_addr = cpu_to_le64(hash_addr);
payload->flags = cpu_to_le64(val);
payload->nhash_rules_addr = cpu_to_le64(addr);
/* With this command we tell the IPA where in its local memory the *headertablesreside.Thecontentofthebufferprovidedis *alsowrittenviaDMAintothatspace.TheIPAhardwareowns *thetable,buttheAPmustinitializeit.
*/
cmd_payload = ipa_cmd_payload_alloc(ipa, &payload_addr);
payload = &cmd_payload->hdr_init_local;
/* pipeline_clear_src_grp is not used */
clear_option = clear_full ? pipeline_clear_full : pipeline_clear_hps;
/* IPA v4.0+ represents the pipeline clear options in the opcode. It *alsosupportsalargeroffsetbyencodingadditionalhigh-order *bitsinthepayloadflagsfield.
*/ if (ipa->version >= IPA_VERSION_4_0) {
u16 offset_high;
u32 val;
/* Extract the high 4 bits from the offset */
offset_high = (u16)u32_get_bits(offset, GENMASK(19, 16));
offset &= (1 << 16) - 1;
/* Extract the top 4 bits and encode it into the flags field */
flags = u16_encode_bits(offset_high,
REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK);
options = 0; /* reserved */
} else {
flags = 0; /* SKIP_CLEAR flag is always 0 */
options = u16_encode_bits(clear_option,
REGISTER_WRITE_CLEAR_OPTIONS_FMASK);
}
/* Skip IP packet processing on the next data transfer on a TX channel */ staticvoid ipa_cmd_ip_packet_init_add(struct gsi_trans *trans, u8 endpoint_id)
{ struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); enum ipa_cmd_opcode opcode = IPA_CMD_IP_PACKET_INIT; struct ipa_cmd_ip_packet_init *payload; union ipa_cmd_payload *cmd_payload;
dma_addr_t payload_addr;
/* Use a DMA command to read or write a block of IPA-resident memory */ void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset, u16 size,
dma_addr_t addr, bool toward_ipa)
{ struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); enum ipa_cmd_opcode opcode = IPA_CMD_DMA_SHARED_MEM; struct ipa_cmd_hw_dma_mem_mem *payload; union ipa_cmd_payload *cmd_payload;
dma_addr_t payload_addr;
u16 flags;
/* size and offset must fit in 16 bit fields */
WARN_ON(!size);
WARN_ON(size > U16_MAX);
WARN_ON(offset > U16_MAX || ipa->mem_offset > U16_MAX - offset);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.