summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c89
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c77
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h3
3 files changed, 169 insertions, 0 deletions
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 11daafe43d7d..8e583203abda 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -162,6 +162,12 @@ struct ipa3_usb_transport_type_ctx {
void *user_data;
enum ipa3_usb_state state;
struct finish_suspend_work_context finish_suspend_work;
+ struct ipa_usb_xdci_chan_params ch_params;
+};
+
+struct ipa3_usb_smmu_reg_map {
+ int cnt;
+ phys_addr_t addr;
};
struct ipa3_usb_context {
@@ -179,6 +185,7 @@ struct ipa3_usb_context {
ttype_ctx[IPA_USB_TRANSPORT_MAX];
struct dentry *dfile_state_info;
struct dentry *dent;
+ struct ipa3_usb_smmu_reg_map smmu_reg_map;
};
enum ipa3_usb_op {
@@ -1112,6 +1119,74 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params)
return true;
}
+static int ipa3_usb_smmu_map_xdci_channel(
+ struct ipa_usb_xdci_chan_params *params, bool map)
+{
+ int result;
+ u32 gevntcount_r = rounddown(params->gevntcount_low_addr, PAGE_SIZE);
+ u32 xfer_scratch_r =
+ rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE);
+
+ if (gevntcount_r != xfer_scratch_r) {
+ IPA_USB_ERR("No support more than 1 page map for USB regs\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (map) {
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) {
+ ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r;
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, true);
+ if (result) {
+ IPA_USB_ERR("failed to map USB regs %d\n",
+ result);
+ return result;
+ }
+ } else {
+ if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) {
+ IPA_USB_ERR(
+ "No support for map different reg\n");
+ return -EINVAL;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt++;
+ } else {
+ if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) {
+ IPA_USB_ERR(
+ "No support for map different reg\n");
+ return -EINVAL;
+ }
+
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) {
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, false);
+ if (result) {
+ IPA_USB_ERR("failed to unmap USB regs %d\n",
+ result);
+ return result;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt--;
+ }
+
+ result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
+ params->xfer_ring_base_addr, params->xfer_ring_len, map);
+ if (result) {
+ IPA_USB_ERR("failed to map Xfer ring %d\n", result);
+ return result;
+ }
+
+ result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
+ params->data_buff_base_addr, params->data_buff_base_len, map);
+ if (result) {
+ IPA_USB_ERR("failed to map TRBs buff %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
static int ipa3_usb_request_xdci_channel(
struct ipa_usb_xdci_chan_params *params,
struct ipa_req_chan_out_params *out_params)
@@ -1186,6 +1261,16 @@ static int ipa3_usb_request_xdci_channel(
default:
break;
}
+
+ result = ipa3_usb_smmu_map_xdci_channel(params, true);
+ if (result) {
+ IPA_USB_ERR("failed to smmu map %d\n", result);
+ return result;
+ }
+
+ /* store channel params for SMMU unmap */
+ ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params;
+
chan_params.keep_ipa_awake = params->keep_ipa_awake;
chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV;
chan_params.evt_ring_params.intr = GSI_INTR_IRQ;
@@ -1243,6 +1328,7 @@ static int ipa3_usb_request_xdci_channel(
result = ipa3_request_gsi_channel(&chan_params, out_params);
if (result) {
IPA_USB_ERR("failed to allocate GSI channel\n");
+ ipa3_usb_smmu_map_xdci_channel(params, false);
return result;
}
@@ -1273,6 +1359,9 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
return result;
}
+ result = ipa3_usb_smmu_map_xdci_channel(
+ &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false);
+
/* Change ipa_usb state to INITIALIZED */
if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
IPA_USB_ERR("failed to change state to initialized\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 7b0376ecba7e..8326c3fdd9d1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1055,6 +1055,83 @@ static bool ipa3_is_legal_params(struct ipa_request_gsi_channel_params *params)
return true;
}
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map)
+{
+ struct iommu_domain *smmu_domain;
+ int res;
+
+ if (ipa3_ctx->smmu_s1_bypass)
+ return 0;
+
+ smmu_domain = ipa3_get_smmu_domain();
+ if (!smmu_domain) {
+ IPAERR("invalid smmu domain\n");
+ return -EINVAL;
+ }
+
+ if (map) {
+ res = ipa3_iommu_map(smmu_domain, phys_addr, phys_addr,
+ PAGE_SIZE, IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ } else {
+ res = iommu_unmap(smmu_domain, phys_addr, PAGE_SIZE);
+ res = (res != PAGE_SIZE);
+ }
+ if (res) {
+ IPAERR("Fail to %s reg 0x%pa\n", map ? "map" : "unmap",
+ &phys_addr);
+ return -EINVAL;
+ }
+
+ IPADBG("Peer reg 0x%pa %s\n", &phys_addr, map ? "map" : "unmap");
+
+ return 0;
+}
+
+int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
+{
+ struct iommu_domain *smmu_domain;
+ int res;
+
+ if (ipa3_ctx->smmu_s1_bypass)
+ return 0;
+
+ smmu_domain = ipa3_get_smmu_domain();
+ if (!smmu_domain) {
+ IPAERR("invalid smmu domain\n");
+ return -EINVAL;
+ }
+
+ if (map) {
+ res = ipa3_iommu_map(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ rounddown(phys_addr, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE),
+ IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr);
+ return -EINVAL;
+ }
+ } else {
+ res = iommu_unmap(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE));
+ if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE)) {
+ IPAERR("Fail to unmap 0x%llx->0x%pa\n",
+ iova, &phys_addr);
+ return -EINVAL;
+ }
+ }
+
+ IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap",
+ iova, &phys_addr);
+
+ return 0;
+}
+
+
int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params,
struct ipa_req_chan_out_params *out_params)
{
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index d2c605d7627c..cce05cf31b3c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2194,4 +2194,7 @@ const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
int ipa_gsi_ch20_wa(void);
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
+int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
+ u32 size, bool map);
#endif /* _IPA3_I_H_ */