new file mode 100644
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/interconnect.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "ipa_dma.h"
+#include "ipa_i.h"
+#include "ipahal.h"
+
+/* Interconnect path bandwidths (each times 1000 bytes per second) */
+#define IPA_MEMORY_AVG 80000
+#define IPA_MEMORY_PEAK 600000
+
+#define IPA_IMEM_AVG 80000
+#define IPA_IMEM_PEAK 350000
+
+#define IPA_CONFIG_AVG 40000
+#define IPA_CONFIG_PEAK 40000
+
+#define IPA_BCR_REG_VAL 0x0000003b
+
+#define IPA_GSI_DMA_TASK_TIMEOUT 15 /* milliseconds */
+
+#define IPA_GSI_CHANNEL_STOP_SLEEP_MIN 1000 /* microseconds */
+#define IPA_GSI_CHANNEL_STOP_SLEEP_MAX 2000 /* microseconds */
+
+#define QMB_MASTER_SELECT_DDR 0
+
+enum ipa_rsrc_group {
+ IPA_RSRC_GROUP_LWA_DL, /* currently not used */
+ IPA_RSRC_GROUP_UL_DL,
+ IPA_RSRC_GROUP_MAX,
+};
+
+enum ipa_rsrc_grp_type_src {
+ IPA_RSRC_GRP_TYPE_SRC_PKT_CONTEXTS,
+ IPA_RSRC_GRP_TYPE_SRS_DESCRIPTOR_LISTS,
+ IPA_RSRC_GRP_TYPE_SRC_DESCRIPTOR_BUFF,
+ IPA_RSRC_GRP_TYPE_SRC_HPS_DMARS,
+ IPA_RSRC_GRP_TYPE_SRC_ACK_ENTRIES,
+};
+
+enum ipa_rsrc_grp_type_dst {
+ IPA_RSRC_GRP_TYPE_DST_DATA_SECTORS,
+ IPA_RSRC_GRP_TYPE_DST_DPS_DMARS,
+};
+
+enum ipa_rsrc_grp_type_rx {
+ IPA_RSRC_GRP_TYPE_RX_HPS_CMDQ,
+ IPA_RSRC_GRP_TYPE_RX_MAX
+};
+
+struct rsrc_min_max {
+ u32 min;
+ u32 max;
+};
+
+/* IPA_HW_v3_5_1 */
+static const struct rsrc_min_max ipa_src_rsrc_grp[][IPA_RSRC_GROUP_MAX] = {
+ [IPA_RSRC_GRP_TYPE_SRC_PKT_CONTEXTS] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 1, .max = 63, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 1, .max = 63, },
+ },
+ [IPA_RSRC_GRP_TYPE_SRS_DESCRIPTOR_LISTS] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 10, .max = 10, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 10, .max = 10, },
+ },
+ [IPA_RSRC_GRP_TYPE_SRC_DESCRIPTOR_BUFF] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 12, .max = 12, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 14, .max = 14, },
+ },
+ [IPA_RSRC_GRP_TYPE_SRC_HPS_DMARS] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 0, .max = 63, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 0, .max = 63, },
+ },
+ [IPA_RSRC_GRP_TYPE_SRC_ACK_ENTRIES] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 14, .max = 14, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 20, .max = 20, },
+ },
+};
+
+/* IPA_HW_v3_5_1 */
+static const struct rsrc_min_max ipa_dst_rsrc_grp[][IPA_RSRC_GROUP_MAX] = {
+ [IPA_RSRC_GRP_TYPE_DST_DATA_SECTORS] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 4, .max = 4, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 4, .max = 4, },
+ },
+ [IPA_RSRC_GRP_TYPE_DST_DPS_DMARS] = {
+ [IPA_RSRC_GROUP_LWA_DL] = { .min = 2, .max = 63, },
+ [IPA_RSRC_GROUP_UL_DL] = { .min = 1, .max = 63, },
+ },
+};
+
+/**
+ * struct ipa_gsi_ep_config - GSI endpoint configuration.
+ * @ep_id: IPA endpoint identifier.
+ * @channel_id: GSI channel number used for this endpoint.
+ * @tlv_count: The number of TLV (type-length-value) entries for the channel.
+ * @ee: Execution environment endpoint is associated with.
+ *
+ * Each GSI endpoint has a set of configuration parameters defined within
+ * entries in the ipa_ep_configuration[] array. Its @ep_id field uniquely
+ * defines the endpoint, and @channel_id defines which data channel (ring
+ * buffer) is used for the endpoint.
+ * XXX TLV
+ * XXX ee is never used in the code
+ */
+struct ipa_gsi_ep_config {
+ u32 ep_id;
+ u32 channel_id;
+ u32 tlv_count;
+ u32 ee;
+};
+
+struct ipa_ep_configuration {
+ bool support_flt;
+ enum ipa_seq_type seq_type;
+ struct ipa_gsi_ep_config ipa_gsi_ep_info;
+};
+
+/* IPA_HW_v3_5_1 */
+/* clients not included in the list below are considered as invalid */
+static const struct ipa_ep_configuration ipa_ep_configuration[] = {
+ [IPA_CLIENT_WLAN1_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 7,
+ .channel_id = 1,
+ .tlv_count = 8,
+ .ee = IPA_EE_UC,
+ },
+ },
+ [IPA_CLIENT_USB_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 0,
+ .channel_id = 0,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_APPS_LAN_PROD] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_PKT_PROCESS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 8,
+ .channel_id = 7,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_APPS_WAN_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 2,
+ .channel_id = 3,
+ .tlv_count = 16,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_APPS_CMD_PROD] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_DMA_ONLY,
+ .ipa_gsi_ep_info = {
+ .ep_id = 5,
+ .channel_id = 4,
+ .tlv_count = 20,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_Q6_LAN_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_PKT_PROCESS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 3,
+ .channel_id = 0,
+ .tlv_count = 16,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_Q6_WAN_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_PKT_PROCESS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 6,
+ .channel_id = 4,
+ .tlv_count = 12,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_Q6_CMD_PROD] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_PKT_PROCESS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 4,
+ .channel_id = 1,
+ .tlv_count = 20,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_TEST_CONS] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 14,
+ .channel_id = 5,
+ .tlv_count = 8,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_TEST1_CONS] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 15,
+ .channel_id = 2,
+ .tlv_count = 8,
+ .ee = IPA_EE_UC,
+ },
+ },
+ /* Only for testing */
+ [IPA_CLIENT_TEST_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 0,
+ .channel_id = 0,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_TEST1_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 0,
+ .channel_id = 0,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_TEST2_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 2,
+ .channel_id = 3,
+ .tlv_count = 16,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_TEST3_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 4,
+ .channel_id = 1,
+ .tlv_count = 20,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_TEST4_PROD] = {
+ .support_flt = true,
+ .seq_type = IPA_SEQ_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ .ipa_gsi_ep_info = {
+ .ep_id = 1,
+ .channel_id = 0,
+ .tlv_count = 8,
+ .ee = IPA_EE_UC,
+ },
+ },
+ [IPA_CLIENT_WLAN1_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 16,
+ .channel_id = 3,
+ .tlv_count = 8,
+ .ee = IPA_EE_UC,
+ },
+ },
+ [IPA_CLIENT_WLAN2_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 18,
+ .channel_id = 9,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_WLAN3_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 19,
+ .channel_id = 10,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_USB_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 17,
+ .channel_id = 8,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_USB_DPL_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 11,
+ .channel_id = 2,
+ .tlv_count = 4,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_APPS_LAN_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 9,
+ .channel_id = 5,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_APPS_WAN_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 10,
+ .channel_id = 6,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_Q6_LAN_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 13,
+ .channel_id = 3,
+ .tlv_count = 8,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ [IPA_CLIENT_Q6_WAN_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 12,
+ .channel_id = 2,
+ .tlv_count = 8,
+ .ee = IPA_EE_Q6,
+ },
+ },
+ /* Only for testing */
+ [IPA_CLIENT_TEST2_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 18,
+ .channel_id = 9,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_TEST3_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 19,
+ .channel_id = 10,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+ [IPA_CLIENT_TEST4_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 11,
+ .channel_id = 2,
+ .tlv_count = 4,
+ .ee = IPA_EE_AP,
+ },
+ },
+/* Dummy consumer (endpoint 31) is used in L2TP rt rule */
+ [IPA_CLIENT_DUMMY_CONS] = {
+ .support_flt = false,
+ .seq_type = IPA_SEQ_INVALID,
+ .ipa_gsi_ep_info = {
+ .ep_id = 31,
+ .channel_id = 31,
+ .tlv_count = 8,
+ .ee = IPA_EE_AP,
+ },
+ },
+};
+
+/** ipa_client_ep_id() - provide endpoint mapping
+ * @client: client type
+ *
+ * Return value: endpoint mapping
+ */
+u32 ipa_client_ep_id(enum ipa_client_type client)
+{
+ return ipa_ep_configuration[client].ipa_gsi_ep_info.ep_id;
+}
+
+u32 ipa_client_channel_id(enum ipa_client_type client)
+{
+ return ipa_ep_configuration[client].ipa_gsi_ep_info.channel_id;
+}
+
+u32 ipa_client_tlv_count(enum ipa_client_type client)
+{
+ return ipa_ep_configuration[client].ipa_gsi_ep_info.tlv_count;
+}
+
+enum ipa_seq_type ipa_endp_seq_type(u32 ep_id)
+{
+ return ipa_ep_configuration[ipa_ctx->ep[ep_id].client].seq_type;
+}
+
+/** ipa_sram_settings_read() - Read SRAM settings from HW
+ *
+ * Returns: None
+ */
+void ipa_sram_settings_read(void)
+{
+ struct ipa_reg_shared_mem_size mem_size;
+
+ ipa_read_reg_fields(IPA_SHARED_MEM_SIZE, &mem_size);
+
+ /* reg fields are in 8B units */
+ ipa_ctx->smem_offset = mem_size.shared_mem_baddr * 8;
+ ipa_ctx->smem_size = mem_size.shared_mem_size * 8;
+
+ ipa_debug("sram size 0x%x offset 0x%x\n", ipa_ctx->smem_size,
+ ipa_ctx->smem_offset);
+}
+
+/** ipa_init_hw() - initialize HW */
+void ipa_init_hw(void)
+{
+ struct ipa_reg_qsb_max_writes max_writes;
+ struct ipa_reg_qsb_max_reads max_reads;
+
+ /* SDM845 has IPA version 3.5.1 */
+ ipa_write_reg(IPA_BCR, IPA_BCR_REG_VAL);
+
+ ipa_reg_qsb_max_writes(&max_writes, 8, 4);
+ ipa_write_reg_fields(IPA_QSB_MAX_WRITES, &max_writes);
+
+ ipa_reg_qsb_max_reads(&max_reads, 8, 12);
+ ipa_write_reg_fields(IPA_QSB_MAX_READS, &max_reads);
+}
+
+/** ipa_filter_bitmap_init() - Initialize the bitmap
+ * that represents the End-points that supports filtering
+ */
+u32 ipa_filter_bitmap_init(void)
+{
+ enum ipa_client_type client;
+ u32 filter_bitmap = 0;
+ u32 count = 0;
+
+ for (client = 0; client < IPA_CLIENT_MAX ; client++) {
+ const struct ipa_ep_configuration *ep_config;
+
+ ep_config = &ipa_ep_configuration[client];
+ if (!ep_config->support_flt)
+ continue;
+ if (++count > IPA_MEM_FLT_COUNT)
+ return 0; /* Too many filtering endpoints */
+
+ filter_bitmap |= BIT(ep_config->ipa_gsi_ep_info.ep_id);
+ }
+
+ return filter_bitmap;
+}
+
+/* In IPAv3 only endpoints 0-3 can be configured to deaggregation */
+bool ipa_endp_aggr_support(u32 ep_id)
+{
+ return ep_id < 4;
+}
+
+/** ipa_endp_init_hdr_write()
+ *
+ * @ep_id: endpoint whose header config register should be written
+ */
+static void ipa_endp_init_hdr_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_HDR_N, ep_id, &ep->init_hdr);
+}
+
+/** ipa_endp_init_hdr_ext_write() - write endpoint extended header register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+static void
+ipa_endp_init_hdr_ext_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_HDR_EXT_N, ep_id,
+ &ep->hdr_ext);
+}
+
+/** ipa_endp_init_aggr_write() write endpoint aggregation register
+ *
+ * @ep_id: endpoint whose aggregation config register should be written
+ */
+static void ipa_endp_init_aggr_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_AGGR_N, ep_id, &ep->init_aggr);
+}
+
+/** ipa_endp_init_cfg_write() - write endpoint configuration register
+ *
+ * @ep_id: endpoint whose configuration register should be written
+ */
+static void ipa_endp_init_cfg_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_CFG_N, ep_id, &ep->init_cfg);
+}
+
+/** ipa_endp_init_mode_write() - write endpoint mode register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+static void ipa_endp_init_mode_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_MODE_N, ep_id,
+ &ep->init_mode);
+}
+
+/** ipa_endp_init_seq_write() - write endpoint sequencer register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+static void ipa_endp_init_seq_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_SEQ_N, ep_id, &ep->init_seq);
+}
+
+/** ipa_endp_init_deaggr_write() - write endpoint deaggregation register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+void ipa_endp_init_deaggr_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_DEAGGR_N, ep_id,
+ &ep->init_deaggr);
+}
+
+/** ipa_endp_init_hdr_metadata_mask_write() - endpoint metadata mask register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+static void ipa_endp_init_hdr_metadata_mask_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_HDR_METADATA_MASK_N, ep_id,
+ &ep->metadata_mask);
+}
+
+/** ipa_endp_init_hdr_metadata_mask_write() - endpoint metadata mask register
+ *
+ * @ep_id: endpoint whose register should be written
+ */
+static void ipa_endp_status_write(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+
+ ipa_write_reg_n_fields(IPA_ENDP_STATUS_N, ep_id, &ep->status);
+}
+
+/** ipa_cfg_ep - IPA end-point configuration
+ * @ep_id: [in] endpoint id assigned by IPA to client
+ * @dst: [in] destination client handle (ignored for consumer clients)
+ *
+ * This includes nat, IPv6CT, header, mode, aggregation and route settings and
+ * is a one shot API to configure the IPA end-point fully
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Note: Should not be called from atomic context
+ */
+void ipa_cfg_ep(u32 ep_id)
+{
+ ipa_endp_init_hdr_write(ep_id);
+ ipa_endp_init_hdr_ext_write(ep_id);
+
+ ipa_endp_init_aggr_write(ep_id);
+ ipa_endp_init_cfg_write(ep_id);
+
+ if (ipa_producer(ipa_ctx->ep[ep_id].client)) {
+ ipa_endp_init_mode_write(ep_id);
+ ipa_endp_init_seq_write(ep_id);
+ ipa_endp_init_deaggr_write(ep_id);
+ } else {
+ ipa_endp_init_hdr_metadata_mask_write(ep_id);
+ }
+
+ ipa_endp_status_write(ep_id);
+}
+
+int ipa_interconnect_init(struct device *dev)
+{
+ struct icc_path *path;
+
+ path = of_icc_get(dev, "memory");
+ if (IS_ERR(path))
+ goto err_return;
+ ipa_ctx->memory_path = path;
+
+ path = of_icc_get(dev, "imem");
+ if (IS_ERR(path))
+ goto err_memory_path_put;
+ ipa_ctx->imem_path = path;
+
+ path = of_icc_get(dev, "config");
+ if (IS_ERR(path))
+ goto err_imem_path_put;
+ ipa_ctx->config_path = path;
+
+ return 0;
+
+err_imem_path_put:
+ icc_put(ipa_ctx->imem_path);
+ ipa_ctx->imem_path = NULL;
+err_memory_path_put:
+ icc_put(ipa_ctx->memory_path);
+ ipa_ctx->memory_path = NULL;
+err_return:
+
+ return PTR_ERR(path);
+}
+
+void ipa_interconnect_exit(void)
+{
+ icc_put(ipa_ctx->config_path);
+ ipa_ctx->config_path = NULL;
+
+ icc_put(ipa_ctx->imem_path);
+ ipa_ctx->imem_path = NULL;
+
+ icc_put(ipa_ctx->memory_path);
+ ipa_ctx->memory_path = NULL;
+}
+
+/* Currently we only use bandwidth level, so just "enable" interconnects */
+int ipa_interconnect_enable(void)
+{
+ int ret;
+
+ ret = icc_set(ipa_ctx->memory_path, IPA_MEMORY_AVG, IPA_MEMORY_PEAK);
+ if (ret)
+ return ret;
+
+ ret = icc_set(ipa_ctx->imem_path, IPA_IMEM_AVG, IPA_IMEM_PEAK);
+ if (ret)
+ goto err_disable_memory_path;
+
+ ret = icc_set(ipa_ctx->config_path, IPA_CONFIG_AVG, IPA_CONFIG_PEAK);
+ if (!ret)
+ return 0; /* Success */
+
+ (void)icc_set(ipa_ctx->imem_path, 0, 0);
+err_disable_memory_path:
+ (void)icc_set(ipa_ctx->memory_path, 0, 0);
+
+ return ret;
+}
+
+/* To disable an interconnect, we just its bandwidth to 0 */
+int ipa_interconnect_disable(void)
+{
+ int ret;
+
+ ret = icc_set(ipa_ctx->memory_path, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = icc_set(ipa_ctx->imem_path, 0, 0);
+ if (ret)
+ goto err_reenable_memory_path;
+
+ ret = icc_set(ipa_ctx->config_path, 0, 0);
+ if (!ret)
+ return 0; /* Success */
+
+ /* Re-enable things in the event of an error */
+ (void)icc_set(ipa_ctx->imem_path, IPA_IMEM_AVG, IPA_IMEM_PEAK);
+err_reenable_memory_path:
+ (void)icc_set(ipa_ctx->memory_path, IPA_MEMORY_AVG, IPA_MEMORY_PEAK);
+
+ return ret;
+}
+
+/** ipa_proxy_clk_unvote() - called to remove IPA clock proxy vote
+ *
+ * Return value: none
+ */
+void ipa_proxy_clk_unvote(void)
+{
+ if (ipa_ctx->modem_clk_vote_valid) {
+ ipa_client_remove();
+ ipa_ctx->modem_clk_vote_valid = false;
+ }
+}
+
+/** ipa_proxy_clk_vote() - called to add IPA clock proxy vote
+ *
+ * Return value: none
+ */
+void ipa_proxy_clk_vote(void)
+{
+ if (!ipa_ctx->modem_clk_vote_valid) {
+ ipa_client_add();
+ ipa_ctx->modem_clk_vote_valid = true;
+ }
+}
+
+u32 ipa_get_ep_count(void)
+{
+ return ipa_read_reg(IPA_ENABLED_PIPES);
+}
+
+/** ipa_is_modem_ep()- Checks if endpoint is owned by the modem
+ *
+ * @ep_id: endpoint identifier
+ * Return value: true if owned by modem, false otherwize
+ */
+bool ipa_is_modem_ep(u32 ep_id)
+{
+ int client_idx;
+
+ for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) {
+ if (!ipa_modem_consumer(client_idx) &&
+ !ipa_modem_producer(client_idx))
+ continue;
+ if (ipa_client_ep_id(client_idx) == ep_id)
+ return true;
+ }
+
+ return false;
+}
+
+static void ipa_src_rsrc_grp_init(enum ipa_rsrc_grp_type_src n)
+{
+ struct ipa_reg_rsrc_grp_xy_rsrc_type_n limits;
+ const struct rsrc_min_max *x_limits;
+ const struct rsrc_min_max *y_limits;
+
+ x_limits = &ipa_src_rsrc_grp[n][IPA_RSRC_GROUP_LWA_DL];
+ y_limits = &ipa_src_rsrc_grp[n][IPA_RSRC_GROUP_UL_DL];
+ ipa_reg_rsrc_grp_xy_rsrc_type_n(&limits, x_limits->min, x_limits->max,
+ y_limits->min, y_limits->max);
+
+ ipa_write_reg_n_fields(IPA_SRC_RSRC_GRP_01_RSRC_TYPE_N, n, &limits);
+}
+
+static void ipa_dst_rsrc_grp_init(enum ipa_rsrc_grp_type_dst n)
+{
+ struct ipa_reg_rsrc_grp_xy_rsrc_type_n limits;
+ const struct rsrc_min_max *x_limits;
+ const struct rsrc_min_max *y_limits;
+
+ x_limits = &ipa_dst_rsrc_grp[n][IPA_RSRC_GROUP_LWA_DL];
+ y_limits = &ipa_dst_rsrc_grp[n][IPA_RSRC_GROUP_UL_DL];
+ ipa_reg_rsrc_grp_xy_rsrc_type_n(&limits, x_limits->min, x_limits->max,
+ y_limits->min, y_limits->max);
+
+ ipa_write_reg_n_fields(IPA_DST_RSRC_GRP_01_RSRC_TYPE_N, n, &limits);
+}
+
+void ipa_set_resource_groups_min_max_limits(void)
+{
+ ipa_src_rsrc_grp_init(IPA_RSRC_GRP_TYPE_SRC_PKT_CONTEXTS);
+ ipa_src_rsrc_grp_init(IPA_RSRC_GRP_TYPE_SRS_DESCRIPTOR_LISTS);
+ ipa_src_rsrc_grp_init(IPA_RSRC_GRP_TYPE_SRC_DESCRIPTOR_BUFF);
+ ipa_src_rsrc_grp_init(IPA_RSRC_GRP_TYPE_SRC_HPS_DMARS);
+ ipa_src_rsrc_grp_init(IPA_RSRC_GRP_TYPE_SRC_ACK_ENTRIES);
+
+ ipa_dst_rsrc_grp_init(IPA_RSRC_GRP_TYPE_DST_DATA_SECTORS);
+ ipa_dst_rsrc_grp_init(IPA_RSRC_GRP_TYPE_DST_DPS_DMARS);
+}
+
+static void ipa_gsi_poll_after_suspend(struct ipa_ep_context *ep)
+{
+ ipa_rx_switch_to_poll_mode(ep->sys);
+}
+
+/* Suspend a consumer endpoint */
+static void ipa_ep_cons_suspend(enum ipa_client_type client)
+{
+ struct ipa_reg_endp_init_ctrl init_ctrl;
+ u32 ep_id = ipa_client_ep_id(client);
+
+ ipa_reg_endp_init_ctrl(&init_ctrl, true);
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_CTRL_N, ep_id, &init_ctrl);
+
+ /* Due to a hardware bug, a client suspended with an open
+ * aggregation frame will not generate a SUSPEND IPA interrupt.
+ * We work around this by force-closing the aggregation frame,
+ * then simulating the arrival of such an interrupt.
+ */
+ ipa_suspend_active_aggr_wa(ep_id);
+
+ ipa_gsi_poll_after_suspend(&ipa_ctx->ep[ep_id]);
+}
+
+void ipa_ep_suspend_all(void)
+{
+ ipa_ep_cons_suspend(IPA_CLIENT_APPS_WAN_CONS);
+ ipa_ep_cons_suspend(IPA_CLIENT_APPS_LAN_CONS);
+}
+
+/* Resume a suspended consumer endpoint */
+static void ipa_ep_cons_resume(enum ipa_client_type client)
+{
+ struct ipa_reg_endp_init_ctrl init_ctrl;
+ struct ipa_ep_context *ep;
+ u32 ep_id;
+
+ ep_id = ipa_client_ep_id(client);
+ ep = &ipa_ctx->ep[ep_id];
+
+ ipa_reg_endp_init_ctrl(&init_ctrl, false);
+ ipa_write_reg_n_fields(IPA_ENDP_INIT_CTRL_N, ep_id, &init_ctrl);
+
+ if (!ipa_ep_polling(ep))
+ gsi_channel_intr_enable(ipa_ctx->gsi, ep->channel_id);
+}
+
+void ipa_ep_resume_all(void)
+{
+ ipa_ep_cons_resume(IPA_CLIENT_APPS_LAN_CONS);
+ ipa_ep_cons_resume(IPA_CLIENT_APPS_WAN_CONS);
+}
+
+/** ipa_cfg_route() - configure IPA route
+ * @route: IPA route
+ *
+ * Return codes:
+ * 0: success
+ */
+void ipa_cfg_default_route(enum ipa_client_type client)
+{
+ struct ipa_reg_route route;
+
+ ipa_reg_route(&route, ipa_client_ep_id(client));
+ ipa_write_reg_fields(IPA_ROUTE, &route);
+}
+
+/* In certain cases we need to issue a command to reliably clear the
+ * IPA pipeline. Sending a 1-byte DMA task is sufficient, and this
+ * function preallocates a command to do just that. There are
+ * conditions (process context in KILL state) where DMA allocations
+ * can fail, and we need to be able to issue this command to put the
+ * hardware in a known state. By preallocating the command here we
+ * guarantee it can't fail for that reason.
+ */
+int ipa_gsi_dma_task_alloc(void)
+{
+ struct ipa_dma_mem *mem = &ipa_ctx->dma_task_info.mem;
+
+ if (ipa_dma_alloc(mem, IPA_GSI_CHANNEL_STOP_PKT_SIZE, GFP_KERNEL))
+ return -ENOMEM;
+
+ /* IPA_IMM_CMD_DMA_TASK_32B_ADDR */
+ ipa_ctx->dma_task_info.payload = ipahal_dma_task_32b_addr_pyld(mem);
+ if (!ipa_ctx->dma_task_info.payload) {
+ ipa_dma_free(mem);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void ipa_gsi_dma_task_free(void)
+{
+ struct ipa_dma_mem *mem = &ipa_ctx->dma_task_info.mem;
+
+ ipahal_payload_free(ipa_ctx->dma_task_info.payload);
+ ipa_ctx->dma_task_info.payload = NULL;
+ ipa_dma_free(mem);
+}
+
+/** ipa_gsi_dma_task_inject()- Send DMA_TASK to IPA for GSI stop channel
+ *
+ * Send a DMA_TASK of 1B to IPA to unblock GSI channel in STOP_IN_PROG.
+ * Return value: 0 on success, negative otherwise
+ */
+static int ipa_gsi_dma_task_inject(void)
+{
+ struct ipa_desc desc = { };
+
+ desc.type = IPA_IMM_CMD_DESC;
+ desc.len_opcode = IPA_IMM_CMD_DMA_TASK_32B_ADDR;
+ desc.payload = ipa_ctx->dma_task_info.payload;
+
+ return ipa_send_cmd_timeout(&desc, IPA_GSI_DMA_TASK_TIMEOUT);
+}
+
+/** ipa_stop_gsi_channel()- Stops a GSI channel in IPA
+ * @chan_hdl: GSI channel handle
+ *
+ * This function implements the sequence to stop a GSI channel
+ * in IPA. This function returns when the channel is is STOP state.
+ *
+ * Return value: 0 on success, negative otherwise
+ */
+int ipa_stop_gsi_channel(u32 ep_id)
+{
+ struct ipa_ep_context *ep = &ipa_ctx->ep[ep_id];
+ int ret;
+ int i;
+
+ if (ipa_producer(ep->client))
+ return gsi_channel_stop(ipa_ctx->gsi, ep->channel_id);
+
+ for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
+ ret = gsi_channel_stop(ipa_ctx->gsi, ep->channel_id);
+ if (ret != -EAGAIN && ret != -ETIMEDOUT)
+ return ret;
+
+ /* Send a 1B packet DMA_TASK to IPA and try again */
+ ret = ipa_gsi_dma_task_inject();
+ if (ret)
+ return ret;
+
+ /* sleep for short period to flush IPA */
+ usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN,
+ IPA_GSI_CHANNEL_STOP_SLEEP_MAX);
+ }
+
+ ipa_err("Failed to stop GSI channel with retries\n");
+
+ return -EFAULT;
+}
+
+/** ipa_enable_dcd() - enable dynamic clock division on IPA
+ *
+ * Return value: Non applicable
+ *
+ */
+void ipa_enable_dcd(void)
+{
+ struct ipa_reg_idle_indication_cfg indication;
+
+ /* recommended values for IPA 3.5 according to IPA HPG */
+ ipa_reg_idle_indication_cfg(&indication, 256, 0);
+ ipa_write_reg_fields(IPA_IDLE_INDICATION_CFG, &indication);
+}
+
+/** ipa_set_flt_tuple_mask() - Sets the flt tuple masking for the given
+ * endpoint. Endpoint must be for AP (not modem) and support filtering.
+ * Updates the the filtering masking values without changing the rt ones.
+ *
+ * @ep_id: filter endpoint to configure the tuple masking
+ * @tuple: the tuple members masking
+ * Returns: 0 on success, negative on failure
+ *
+ */
+void ipa_set_flt_tuple_mask(u32 ep_id)
+{
+ struct ipa_ep_filter_router_hsh_cfg hsh_cfg;
+
+ ipa_read_reg_n_fields(IPA_ENDP_FILTER_ROUTER_HSH_CFG_N, ep_id,
+ &hsh_cfg);
+
+ ipa_reg_hash_tuple(&hsh_cfg.flt);
+
+ ipa_write_reg_n_fields(IPA_ENDP_FILTER_ROUTER_HSH_CFG_N, ep_id,
+ &hsh_cfg);
+}
+
+/** ipa_set_rt_tuple_mask() - Sets the rt tuple masking for the given tbl
+ * table index must be for AP EP (not modem)
+ * updates the the routing masking values without changing the flt ones.
+ *
+ * @tbl_idx: routing table index to configure the tuple masking
+ * @tuple: the tuple members masking
+ * Returns: 0 on success, negative on failure
+ *
+ */
+void ipa_set_rt_tuple_mask(int tbl_idx)
+{
+ struct ipa_ep_filter_router_hsh_cfg hsh_cfg;
+
+ ipa_read_reg_n_fields(IPA_ENDP_FILTER_ROUTER_HSH_CFG_N, tbl_idx,
+ &hsh_cfg);
+
+ ipa_reg_hash_tuple(&hsh_cfg.rt);
+
+ ipa_write_reg_n_fields(IPA_ENDP_FILTER_ROUTER_HSH_CFG_N, tbl_idx,
+ &hsh_cfg);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IPA HW device driver");
This patch contains "ipa_utils.c", which provides some miscellaneous supporting code. Included are: - Endpoint configuration. The IPA hardware has a fixed number of endpoints (pipes) available. In some cases the AP and modem must agree on aspects of these. A key example is that they need to agree what particular endpoints are used for (the modem can't send messages to the AP command endpoint, for example). There is a static array that defines these parameters, and this is found in "ipa_utils.c". - Resource group configuration. The IPA has a number of internal resources it uses for its operation. These are configurable, and it is up to the AP to set these configuration values at initialization time. There are some static arrays that define these configuration values. Functions are also defined here to send those values to hardware. - Shared memory. The IPA uses a region of shared memory to hold various data structures. A function ipa_sram_settings_read() fetches the location and size of this shared memory. (The individual regions are currently initialized in "ipa_main.c".) - Endpoint configuration. Each endpoint (or channel) has a number of configurable properties. Functions found in this file are used to configure these properties. - Interconnect handling. The IPA driver depends on the interconnect framework to request that buses it uses be enabled when needed. This is not a complete list, but it covers much of the functionality found in "ipa_utils.c". Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/net/ipa/ipa_utils.c | 1035 +++++++++++++++++++++++++++++++++++ 1 file changed, 1035 insertions(+) create mode 100644 drivers/net/ipa/ipa_utils.c