diff options
-rw-r--r-- | drivers/net/ipa/ipa.h | 2 | ||||
-rw-r--r-- | drivers/net/ipa/ipa_cmd.c | 45 | ||||
-rw-r--r-- | drivers/net/ipa/ipa_cmd.h | 24 | ||||
-rw-r--r-- | drivers/net/ipa/ipa_endpoint.c | 79 | ||||
-rw-r--r-- | drivers/net/ipa/ipa_main.c | 1 |
5 files changed, 109 insertions, 42 deletions
diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index c6c6a7f6909c..802077631371 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -43,6 +43,7 @@ enum ipa_flag { * @flags: Boolean state flags * @version: IPA hardware version * @pdev: Platform device + * @completion: Used to signal pipeline clear transfer complete * @smp2p: SMP2P information * @clock: IPA clocking information * @table_addr: DMA address of filter/route table content @@ -82,6 +83,7 @@ struct ipa { DECLARE_BITMAP(flags, IPA_FLAG_COUNT); enum ipa_version version; struct platform_device *pdev; + struct completion completion; struct notifier_block nb; void *notifier; struct ipa_smp2p *smp2p; diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index 002e51448510..97b50fee6008 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -529,7 +529,7 @@ void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset, u16 size, direction, opcode); } -static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans, u64 tag) +static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans) { struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); enum ipa_cmd_opcode opcode = IPA_CMD_IP_PACKET_TAG_STATUS; @@ -543,14 +543,14 @@ static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans, u64 tag) cmd_payload = ipa_cmd_payload_alloc(ipa, &payload_addr); payload = &cmd_payload->ip_packet_tag_status; - payload->tag = u64_encode_bits(tag, IP_PACKET_TAG_STATUS_TAG_FMASK); + payload->tag = le64_encode_bits(0, IP_PACKET_TAG_STATUS_TAG_FMASK); gsi_trans_cmd_add(trans, payload, sizeof(*payload), payload_addr, direction, opcode); } /* Issue a small command TX data transfer */ -static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size) +static void ipa_cmd_transfer_add(struct gsi_trans *trans) { struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); enum dma_data_direction direction = DMA_TO_DEVICE; @@ -558,8 +558,6 @@ static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size) union ipa_cmd_payload *payload; dma_addr_t payload_addr; - /* assert(size <= sizeof(*payload)); */ - /* Just transfer a zero-filled payload structure */ payload = ipa_cmd_payload_alloc(ipa, &payload_addr); @@ -567,34 +565,53 @@ static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size) direction, opcode); } -void ipa_cmd_tag_process_add(struct gsi_trans *trans) +/* Add immediate commands to a transaction to clear the hardware pipeline */ +void ipa_cmd_pipeline_clear_add(struct gsi_trans *trans) { struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); struct ipa_endpoint *endpoint; - endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]; + /* This will complete when the transfer is received */ + reinit_completion(&ipa->completion); + /* Issue a no-op register write command (mask 0 means no write) */ ipa_cmd_register_write_add(trans, 0, 0, 0, true); + + /* Send a data packet through the IPA pipeline. The packet_init + * command says to send the next packet directly to the exception + * endpoint without any other IPA processing. The tag_status + * command requests that status be generated on completion of + * that transfer, and that it will be tagged with a value. + * Finally, the transfer command sends a small packet of data + * (instead of a command) using the command endpoint. + */ + endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]; ipa_cmd_ip_packet_init_add(trans, endpoint->endpoint_id); - ipa_cmd_ip_tag_status_add(trans, 0xcba987654321); - ipa_cmd_transfer_add(trans, 4); + ipa_cmd_ip_tag_status_add(trans); + ipa_cmd_transfer_add(trans); } -/* Returns the number of commands required for the tag process */ -u32 ipa_cmd_tag_process_count(void) +/* Returns the number of commands required to clear the pipeline */ +u32 ipa_cmd_pipeline_clear_count(void) { return 4; } -void ipa_cmd_tag_process(struct ipa *ipa) +void ipa_cmd_pipeline_clear_wait(struct ipa *ipa) +{ + wait_for_completion(&ipa->completion); +} + +void ipa_cmd_pipeline_clear(struct ipa *ipa) { - u32 count = ipa_cmd_tag_process_count(); + u32 count = ipa_cmd_pipeline_clear_count(); struct gsi_trans *trans; trans = ipa_cmd_trans_alloc(ipa, count); if (trans) { - ipa_cmd_tag_process_add(trans); + ipa_cmd_pipeline_clear_add(trans); gsi_trans_commit_wait(trans); + ipa_cmd_pipeline_clear_wait(ipa); } else { dev_err(&ipa->pdev->dev, "error allocating %u entry tag transaction\n", count); diff --git a/drivers/net/ipa/ipa_cmd.h b/drivers/net/ipa/ipa_cmd.h index 4ed09c486abc..6dd3d35cf315 100644 --- a/drivers/net/ipa/ipa_cmd.h +++ b/drivers/net/ipa/ipa_cmd.h @@ -157,26 +157,30 @@ void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset, u16 size, dma_addr_t addr, bool toward_ipa); /** - * ipa_cmd_tag_process_add() - Add IPA tag process commands to a transaction + * ipa_cmd_pipeline_clear_add() - Add pipeline clear commands to a transaction * @trans: GSI transaction */ -void ipa_cmd_tag_process_add(struct gsi_trans *trans); +void ipa_cmd_pipeline_clear_add(struct gsi_trans *trans); /** - * ipa_cmd_tag_process_add_count() - Number of commands in a tag process + * ipa_cmd_pipeline_clear_count() - # commands required to clear pipeline * * Return: The number of elements to allocate in a transaction - * to hold tag process commands + * to hold commands to clear the pipeline */ -u32 ipa_cmd_tag_process_count(void); +u32 ipa_cmd_pipeline_clear_count(void); /** - * ipa_cmd_tag_process() - Perform a tag process - * - * @Return: The number of elements to allocate in a transaction - * to hold tag process commands + * ipa_cmd_pipeline_clear_wait() - Wait pipeline clear to complete + * @ipa: - IPA pointer + */ +void ipa_cmd_pipeline_clear_wait(struct ipa *ipa); + +/** + * ipa_cmd_pipeline_clear() - Clear the hardware pipeline + * @ipa: - IPA pointer */ -void ipa_cmd_tag_process(struct ipa *ipa); +void ipa_cmd_pipeline_clear(struct ipa *ipa); /** * ipa_cmd_trans_alloc() - Allocate a transaction for the command TX endpoint diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 688a3dd40510..8313220d41e7 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -69,8 +69,11 @@ struct ipa_status { }; /* Field masks for struct ipa_status structure fields */ +#define IPA_STATUS_MASK_TAG_VALID_FMASK GENMASK(4, 4) +#define IPA_STATUS_SRC_IDX_FMASK GENMASK(4, 0) #define IPA_STATUS_DST_IDX_FMASK GENMASK(4, 0) #define IPA_STATUS_FLAGS1_RT_RULE_ID_FMASK GENMASK(31, 22) +#define IPA_STATUS_FLAGS2_TAG_FMASK GENMASK_ULL(63, 16) #ifdef IPA_VALIDATE @@ -399,7 +402,7 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) * That won't happen, and we could be more precise, but this is fine * for now. We need to end the transaction with a "tag process." */ - count = hweight32(initialized) + ipa_cmd_tag_process_count(); + count = hweight32(initialized) + ipa_cmd_pipeline_clear_count(); trans = ipa_cmd_trans_alloc(ipa, count); if (!trans) { dev_err(&ipa->pdev->dev, @@ -428,11 +431,13 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) ipa_cmd_register_write_add(trans, offset, 0, ~0, false); } - ipa_cmd_tag_process_add(trans); + ipa_cmd_pipeline_clear_add(trans); /* XXX This should have a 1 second timeout */ gsi_trans_commit_wait(trans); + ipa_cmd_pipeline_clear_wait(ipa); + return 0; } @@ -1172,11 +1177,45 @@ static bool ipa_endpoint_status_skip(struct ipa_endpoint *endpoint, return false; /* Don't skip this packet, process it */ } +static bool ipa_endpoint_status_tag(struct ipa_endpoint *endpoint, + const struct ipa_status *status) +{ + struct ipa_endpoint *command_endpoint; + struct ipa *ipa = endpoint->ipa; + u32 endpoint_id; + + if (!le16_get_bits(status->mask, IPA_STATUS_MASK_TAG_VALID_FMASK)) + return false; /* No valid tag */ + + /* The status contains a valid tag. We know the packet was sent to + * this endpoint (already verified by ipa_endpoint_status_skip()). + * If the packet came from the AP->command TX endpoint we know + * this packet was sent as part of the pipeline clear process. + */ + endpoint_id = u8_get_bits(status->endp_src_idx, + IPA_STATUS_SRC_IDX_FMASK); + command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX]; + if (endpoint_id == command_endpoint->endpoint_id) { + complete(&ipa->completion); + } else { + dev_err(&ipa->pdev->dev, + "unexpected tagged packet from endpoint %u\n", + endpoint_id); + } + + return true; +} + /* Return whether the status indicates the packet should be dropped */ -static bool ipa_status_drop_packet(const struct ipa_status *status) +static bool ipa_endpoint_status_drop(struct ipa_endpoint *endpoint, + const struct ipa_status *status) { u32 val; + /* If the status indicates a tagged transfer, we'll drop the packet */ + if (ipa_endpoint_status_tag(endpoint, status)) + return true; + /* Deaggregation exceptions we drop; all other types we consume */ if (status->exception) return status->exception == IPA_STATUS_EXCEPTION_DEAGGR; @@ -1213,12 +1252,11 @@ static void ipa_endpoint_status_parse(struct ipa_endpoint *endpoint, continue; } - /* Compute the amount of buffer space consumed by the - * packet, including the status element. If the hardware - * is configured to pad packet data to an aligned boundary, - * account for that. And if checksum offload is is enabled - * a trailer containing computed checksum information will - * be appended. + /* Compute the amount of buffer space consumed by the packet, + * including the status element. If the hardware is configured + * to pad packet data to an aligned boundary, account for that. + * And if checksum offload is enabled a trailer containing + * computed checksum information will be appended. */ align = endpoint->data->rx.pad_align ? : 1; len = le16_to_cpu(status->pkt_len); @@ -1226,16 +1264,21 @@ static void ipa_endpoint_status_parse(struct ipa_endpoint *endpoint, if (endpoint->data->checksum) len += sizeof(struct rmnet_map_dl_csum_trailer); - /* Charge the new packet with a proportional fraction of - * the unused space in the original receive buffer. - * XXX Charge a proportion of the *whole* receive buffer? - */ - if (!ipa_status_drop_packet(status)) { - u32 extra = unused * len / total_len; - void *data2 = data + sizeof(*status); - u32 len2 = le16_to_cpu(status->pkt_len); + if (!ipa_endpoint_status_drop(endpoint, status)) { + void *data2; + u32 extra; + u32 len2; /* Client receives only packet data (no status) */ + data2 = data + sizeof(*status); + len2 = le16_to_cpu(status->pkt_len); + + /* Have the true size reflect the extra unused space in + * the original receive buffer. Distribute the "cost" + * proportionately across all aggregated packets in the + * buffer. + */ + extra = DIV_ROUND_CLOSEST(unused * len, total_len); ipa_endpoint_skb_copy(endpoint, data2, len2, extra); } @@ -1564,7 +1607,7 @@ void ipa_endpoint_suspend(struct ipa *ipa) if (ipa->modem_netdev) ipa_modem_suspend(ipa->modem_netdev); - ipa_cmd_tag_process(ipa); + ipa_cmd_pipeline_clear(ipa); ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]); ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX]); diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index ab0fd5cb4927..c10e7340b031 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -831,6 +831,7 @@ static int ipa_probe(struct platform_device *pdev) dev_set_drvdata(dev, ipa); ipa->clock = clock; ipa->version = data->version; + init_completion(&ipa->completion); ret = ipa_reg_init(ipa); if (ret) |