msm: ipa3: add support for SMMU to USB

Add support to IPA USB when IPA SMMU is enabled.
IPA USB will create a mapping for USB registers and
USB data structures for GSI.

CRs-Fixed: 1046497
Change-Id: Ib177606acdfa9b3826c929578d1c8094242f90cd
Acked-by: Ady Abraham <adya@qti.qualcomm.com>
Signed-off-by: Skylar Chang <chiaweic@codeaurora.org>
This commit is contained in:
Skylar Chang 2016-07-22 11:37:46 -07:00 committed by Gerrit - the friendly Code Review server
parent 8b42db2259
commit 072a88c698
3 changed files with 169 additions and 0 deletions

View file

@ -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");

View file

@ -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)
{

View file

@ -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_ */