/* Filter or route rules consist of a set of 32-bit values followed by a *32-bitall-zerorulelistterminator.The"zerorule"issimplyan *all-zerorulefollowedbythelistterminator.
*/ #define IPA_ZERO_RULE_SIZE (2 * sizeof(__le32))
/* Check things that can be validated at build time. */ staticvoid ipa_table_validate_build(void)
{ /* Filter and route tables contain DMA addresses that refer *tofilterorrouterules.Butthesizeofatableentry *is64bitsregardlessofwhatthesizeofanAPDMAaddress *is.Afixedconstantdefinesthesizeofanentry,and *codeinipa_table_init()usesapointerto__le64to *initializetables.
*/
BUILD_BUG_ON(sizeof(dma_addr_t) > sizeof(__le64));
/* A "zero rule" is used to represent no filtering or no routing. *Itisa64-bitblockofzeroedmemory.Codeinipa_table_init() *assumesthatitcanbewrittenusingapointerto__le64.
*/
BUILD_BUG_ON(IPA_ZERO_RULE_SIZE != sizeof(__le64));
}
if (!filtered) {
dev_err(dev, "at least one filtering endpoint is required\n");
returnfalse;
}
count = hweight64(filtered); if (count > ipa->filter_count) {
dev_err(dev, "too many filtering endpoints (%u > %u)\n",
count, ipa->filter_count);
returnfalse;
}
returntrue;
}
/* Zero entry count means no table, so just return a 0 address */ static dma_addr_t ipa_table_addr(struct ipa *ipa, bool filter_mask, u16 count)
{
u32 skip;
/* Reset entries in a single filter table belonging to either the AP or *modemtorefertothezeroentry.Thememoryregionsuppliedwillbe *fortheIPv4andIPv6non-hashedandhashedfiltertables.
*/ staticint
ipa_filter_reset_table(struct ipa *ipa, bool hashed, bool ipv6, bool modem)
{
u64 ep_mask = ipa->filtered; struct gsi_trans *trans; enum gsi_ee_id ee_id;
trans = ipa_cmd_trans_alloc(ipa, hweight64(ep_mask)); if (!trans) {
dev_err(ipa->dev, "no transaction for %s filter reset\n",
modem ? "modem" : "AP"); return -EBUSY;
}
/* Theoretically, each filter table could have more filter slots to *updatethanthemaximumnumberofcommandsinatransaction.So *wedoeachtableseparately.
*/ staticint ipa_filter_reset(struct ipa *ipa, bool modem)
{ int ret;
ret = ipa_filter_reset_table(ipa, false, false, modem); if (ret) return ret;
ret = ipa_filter_reset_table(ipa, false, true, modem); if (ret || !ipa_table_hash_support(ipa)) return ret;
ret = ipa_filter_reset_table(ipa, true, false, modem); if (ret) return ret;
/* Zero the unused space in the filter table */
zero_offset = mem->offset + size;
zero_size = mem->size - size;
ipa_cmd_dma_shared_mem_add(trans, zero_offset, zero_size,
ipa->zero_addr, true); if (!hash_size) return;
/* Zero the unused space in the hashed filter table */
zero_offset = hash_offset + hash_size;
zero_size = hash_mem->size - hash_size;
ipa_cmd_dma_shared_mem_add(trans, zero_offset, zero_size,
ipa->zero_addr, true);
}
int ipa_table_setup(struct ipa *ipa)
{ struct gsi_trans *trans;
/* We will need at most 8 TREs: *-IPv4: *-Oneforroutetableinitialization(non-hashedandhashed) *-Oneforfiltertableinitialization(non-hashedandhashed) *-Onetozerounusedentriesinthenon-hashedfiltertable *-Onetozerounusedentriesinthehashedfiltertable *-IPv6: *-Oneforroutetableinitialization(non-hashedandhashed) *-Oneforfiltertableinitialization(non-hashedandhashed) *-Onetozerounusedentriesinthenon-hashedfiltertable *-Onetozerounusedentriesinthehashedfiltertable *Allplatformssupportatleast8TREsinatransaction.
*/
trans = ipa_cmd_trans_alloc(ipa, 8); if (!trans) {
dev_err(ipa->dev, "no transaction for table setup\n"); return -EBUSY;
}
/* Zero all route-related fields, preserving the rest */
val &= ~reg_fmask(reg, ROUTER_HASH_MSK_ALL);
} else { /* IPA v5.0 separates filter and router cache configuration */
reg = ipa_reg(ipa, ENDP_ROUTER_CACHE_CFG);
offset = reg_n_offset(reg, route_id);
/* Zero all route-related fields */
val = 0;
}
iowrite32(val, ipa->reg_virt + offset);
}
/* Configure a hashed route table; there is no ipa_route_deconfig() */ staticvoid ipa_route_config(struct ipa *ipa, bool modem)
{
u32 route_id;
if (!ipa_table_hash_support(ipa)) return;
for (route_id = 0; route_id < ipa->route_count; route_id++) if (ipa_route_id_modem(ipa, route_id) == modem)
ipa_route_tuple_zero(ipa, route_id);
}
/* Configure a filter and route tables; there is no ipa_table_deconfig() */ void ipa_table_config(struct ipa *ipa)
{
ipa_filter_config(ipa, false);
ipa_filter_config(ipa, true);
ipa_route_config(ipa, false);
ipa_route_config(ipa, true);
}
/* Verify the sizes of all IPA table filter or routing table memory regions *arevalid.Ifvalid,thisrecordsthesizeoftheroutingtable.
*/ bool ipa_table_mem_valid(struct ipa *ipa, bool filter)
{ bool hash_support = ipa_table_hash_support(ipa); conststruct ipa_mem *mem_hashed; conststruct ipa_mem *mem_ipv4; conststruct ipa_mem *mem_ipv6;
u32 count;
/* IPv4 and IPv6 non-hashed tables are expected to be defined and *havethesamesize.Bothmusthaveatleasttwoentries(and *wouldnormallyhavemorethanthat).
*/
mem_ipv4 = ipa_table_mem(ipa, filter, false, false); if (!mem_ipv4) returnfalse;
mem_ipv6 = ipa_table_mem(ipa, filter, false, true); if (!mem_ipv6) returnfalse;
if (mem_ipv4->size != mem_ipv6->size) returnfalse;
/* Compute and record the number of entries for each table type */
count = mem_ipv4->size / sizeof(__le64); if (count < 2) returnfalse; if (filter)
ipa->filter_count = count - 1; /* Filter map in first entry */ else
ipa->route_count = count;
/* Table offset and size must fit in TABLE_INIT command fields */ if (!ipa_cmd_table_init_valid(ipa, mem_ipv4, !filter)) returnfalse;
/* Make sure the regions are big enough */ if (filter) { /* Filter tables must able to hold the endpoint bitmap plus *anentryforeachendpointthatsupportsfiltering
*/ if (count < 1 + hweight64(ipa->filtered)) returnfalse;
} else { /* Routing tables must be able to hold all modem entries, *plusatleastoneentryfortheAP.
*/ if (count < ipa->modem_route_count + 1) returnfalse;
}
/* If hashing is supported, hashed tables are expected to be defined, *andhavethesamesizeasnon-hashedtables.Ifhashingisnot *supported,hashedtablesareexpectedtohavezerosize(ornot *bedefined).
*/
mem_hashed = ipa_table_mem(ipa, filter, true, false); if (hash_support) { if (!mem_hashed || mem_hashed->size != mem_ipv4->size) returnfalse;
} else { if (mem_hashed && mem_hashed->size) returnfalse;
}
/* Same check for IPv6 tables */
mem_hashed = ipa_table_mem(ipa, filter, true, true); if (hash_support) { if (!mem_hashed || mem_hashed->size != mem_ipv6->size) returnfalse;
} else { if (mem_hashed && mem_hashed->size) returnfalse;
}
/* The IPA hardware requires route and filter table rules to be *alignedona128-byteboundary.Weputthe"zerorule"atthe *baseofthetableareaallocatedhere.TheDMAaddressreturned *bydma_alloc_coherent()isguaranteedtobeapower-of-2number *ofpages,whichsatisfiestherulealignmentrequirement.
*/
size = IPA_ZERO_RULE_SIZE + (1 + count) * sizeof(__le64);
virt = dma_alloc_coherent(dev, size, &addr, GFP_KERNEL); if (!virt) return -ENOMEM;
ipa->table_virt = virt;
ipa->table_addr = addr;
/* First slot is the zero rule */
*virt++ = 0;
/* Next is the filter table bitmap. The "soft" bitmap value might *needtobeconvertedtothehardwarerepresentationbyshifting *itleftoneposition.PriortoIPAv5.0,bit0repesentsglobal *filtering,whichispossiblebutnotused.IPAv5.0+eliminated *thatoption,sothere'snoshiftingrequired.
*/ if (ipa->version < IPA_VERSION_5_0)
*virt++ = cpu_to_le64(ipa->filtered << 1); else
*virt++ = cpu_to_le64(ipa->filtered);
/* All the rest contain the DMA address of the zero rule */
le_addr = cpu_to_le64(addr); while (count--)
*virt++ = le_addr;
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.