Commit 7fb19ea1 authored by Jeff Hugo's avatar Jeff Hugo
Browse files

accel/qaic: Support the new READ_DATA implementation



AIC200 uses the newer "XBL" firmware implementation which changes the
expectations of how READ_DATA is performed. Larger data requests are
supported via streaming the data over the transport instead of requiring
a single transport transfer for everything.

Co-developed-by: default avatarCarl Vanderlip <quic_carlv@quicinc.com>
Signed-off-by: default avatarCarl Vanderlip <quic_carlv@quicinc.com>
Signed-off-by: default avatarJeff Hugo <jeff.hugo@oss.qualcomm.com>
Signed-off-by: default avatarYoussef Samir <youssef.abdulrahman@oss.qualcomm.com>
Reviewed-by: default avatarJeff Hugo <jeff.hugo@oss.qualcomm.com>
Reviewed-by: default avatarCarl Vanderlip <carl.vanderlip@oss.qualcomm.com>
Signed-off-by: default avatarJeff Hugo <jeff.hugo@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20251007224045.605374-1-youssef.abdulrahman@oss.qualcomm.com
parent 4ddf4ddf
Loading
Loading
Loading
Loading
+104 −34
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ struct sahara_context {
	struct sahara_packet		*rx;
	struct work_struct		fw_work;
	struct work_struct		dump_work;
	struct work_struct		read_data_work;
	struct mhi_device		*mhi_dev;
	const char * const		*image_table;
	u32				table_size;
@@ -174,7 +175,10 @@ struct sahara_context {
	u64				dump_image_offset;
	void				*mem_dump_freespace;
	u64				dump_images_left;
	u32				read_data_offset;
	u32				read_data_length;
	bool				is_mem_dump_mode;
	bool				non_streaming;
};

static const char * const aic100_image_table[] = {
@@ -216,6 +220,11 @@ static const char * const aic200_image_table[] = {
	[75] = "qcom/aic200/pvs.bin",
};

static bool is_streaming(struct sahara_context *context)
{
	return !context->non_streaming;
}

static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
	int ret;
@@ -265,6 +274,8 @@ static void sahara_send_reset(struct sahara_context *context)
	int ret;

	context->is_mem_dump_mode = false;
	context->read_data_offset = 0;
	context->read_data_length = 0;

	context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
	context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
@@ -319,9 +330,39 @@ static void sahara_hello(struct sahara_context *context)
		dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
}

static int read_data_helper(struct sahara_context *context, int buf_index)
{
	enum mhi_flags mhi_flag;
	u32 pkt_data_len;
	int ret;

	pkt_data_len = min(context->read_data_length, SAHARA_PACKET_MAX_SIZE);

	memcpy(context->tx[buf_index],
	       &context->firmware->data[context->read_data_offset],
	       pkt_data_len);

	context->read_data_offset += pkt_data_len;
	context->read_data_length -= pkt_data_len;

	if (is_streaming(context) || !context->read_data_length)
		mhi_flag = MHI_EOT;
	else
		mhi_flag = MHI_CHAIN;

	ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
			    context->tx[buf_index], pkt_data_len, mhi_flag);
	if (ret) {
		dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", ret);
		return ret;
	}

	return 0;
}

static void sahara_read_data(struct sahara_context *context)
{
	u32 image_id, data_offset, data_len, pkt_data_len;
	u32 image_id, data_offset, data_len;
	int ret;
	int i;

@@ -357,7 +398,7 @@ static void sahara_read_data(struct sahara_context *context)
	 * and is not needed here on error.
	 */

	if (data_len > SAHARA_TRANSFER_MAX_SIZE) {
	if (context->non_streaming && data_len > SAHARA_TRANSFER_MAX_SIZE) {
		dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n",
			data_len, SAHARA_TRANSFER_MAX_SIZE);
		sahara_send_reset(context);
@@ -378,22 +419,18 @@ static void sahara_read_data(struct sahara_context *context)
		return;
	}

	for (i = 0; i < SAHARA_NUM_TX_BUF && data_len; ++i) {
		pkt_data_len = min(data_len, SAHARA_PACKET_MAX_SIZE);

		memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len);

		data_offset += pkt_data_len;
		data_len -= pkt_data_len;
	context->read_data_offset = data_offset;
	context->read_data_length = data_len;

		ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
				    context->tx[i], pkt_data_len,
				    !data_len ? MHI_EOT : MHI_CHAIN);
		if (ret) {
			dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n",
				ret);
	if (is_streaming(context)) {
		schedule_work(&context->read_data_work);
		return;
	}

	for (i = 0; i < SAHARA_NUM_TX_BUF && context->read_data_length; ++i) {
		ret = read_data_helper(context, i);
		if (ret)
			break;
	}
}

@@ -742,6 +779,13 @@ static void sahara_dump_processing(struct work_struct *work)
	sahara_send_reset(context);
}

static void sahara_read_data_processing(struct work_struct *work)
{
	struct sahara_context *context = container_of(work, struct sahara_context, read_data_work);

	read_data_helper(context, 0);
}

static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
	struct sahara_context *context;
@@ -756,34 +800,56 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
	if (!context->rx)
		return -ENOMEM;

	if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
		context->image_table = aic200_image_table;
		context->table_size = ARRAY_SIZE(aic200_image_table);
	} else {
		context->image_table = aic100_image_table;
		context->table_size = ARRAY_SIZE(aic100_image_table);
		context->non_streaming = true;
	}

	/*
	 * AIC100 defines SAHARA_TRANSFER_MAX_SIZE as the largest value it
	 * will request for READ_DATA. This is larger than
	 * SAHARA_PACKET_MAX_SIZE, and we need 9x SAHARA_PACKET_MAX_SIZE to
	 * cover SAHARA_TRANSFER_MAX_SIZE. When the remote side issues a
	 * READ_DATA, it requires a transfer of the exact size requested. We
	 * can use MHI_CHAIN to link multiple buffers into a single transfer
	 * but the remote side will not consume the buffers until it sees an
	 * EOT, thus we need to allocate enough buffers to put in the tx fifo
	 * to cover an entire READ_DATA request of the max size.
	 * There are two firmware implementations for READ_DATA handling.
	 * The older "SBL" implementation defines a Sahara transfer size, and
	 * expects that the response is a single transport transfer. If the
	 * FW wants to transfer a file that is larger than the transfer size,
	 * the FW will issue multiple READ_DATA commands. For this
	 * implementation, we need to allocate enough buffers to contain the
	 * entire Sahara transfer size.
	 *
	 * The newer "XBL" implementation does not define a maximum transfer
	 * size and instead expects the data to be streamed over using the
	 * transport level MTU. The FW will issue a single READ_DATA command
	 * of whatever size, and consume multiple transport level transfers
	 * until the expected amount of data is consumed. For this
	 * implementation we only need a single buffer of the transport MTU
	 * but we'll need to be able to use it multiple times for a single
	 * READ_DATA request.
	 *
	 * AIC100 is the SBL implementation and defines SAHARA_TRANSFER_MAX_SIZE
	 * and we need 9x SAHARA_PACKET_MAX_SIZE to cover that. We can use
	 * MHI_CHAIN to link multiple buffers into a single transfer but the
	 * remote side will not consume the buffers until it sees an EOT, thus
	 * we need to allocate enough buffers to put in the tx fifo to cover an
	 * entire READ_DATA request of the max size.
	 *
	 * AIC200 is the XBL implementation, and so a single buffer will work.
	 */
	for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
		context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
		context->tx[i] = devm_kzalloc(&mhi_dev->dev,
					      SAHARA_PACKET_MAX_SIZE,
					      GFP_KERNEL);
		if (!context->tx[i])
			return -ENOMEM;
		if (is_streaming(context))
			break;
	}

	context->mhi_dev = mhi_dev;
	INIT_WORK(&context->fw_work, sahara_processing);
	INIT_WORK(&context->dump_work, sahara_dump_processing);

	if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
		context->image_table = aic200_image_table;
		context->table_size = ARRAY_SIZE(aic200_image_table);
	} else {
		context->image_table = aic100_image_table;
		context->table_size = ARRAY_SIZE(aic100_image_table);
	}
	INIT_WORK(&context->read_data_work, sahara_read_data_processing);

	context->active_image_id = SAHARA_IMAGE_ID_NONE;
	dev_set_drvdata(&mhi_dev->dev, context);
@@ -814,6 +880,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev)

static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
{
	struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);

	if (!mhi_result->transaction_status && context->read_data_length && is_streaming(context))
		schedule_work(&context->read_data_work);
}

static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)