Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94a51817 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa3: add support for SMMU to USB"

parents b649d7e1 072a88c6
Loading
Loading
Loading
Loading
+89 −0
Original line number Diff line number Diff line
@@ -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");
+77 −0
Original line number Diff line number Diff line
@@ -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)
{
+3 −0
Original line number Diff line number Diff line
@@ -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_ */