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: Carl Vanderlip <quic_carlv@quicinc.com>
Signed-off-by: Carl Vanderlip <quic_carlv@quicinc.com>
Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Signed-off-by: Youssef Samir <youssef.abdulrahman@oss.qualcomm.com>
Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Reviewed-by: Carl Vanderlip <carl.vanderlip@oss.qualcomm.com>
Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20251007224045.605374-1-youssef.abdulrahman@oss.qualcomm.com
This commit is contained in:
Jeff Hugo 2025-10-08 00:40:45 +02:00
parent 4ddf4ddfce
commit 7fb19ea1ec

View File

@ -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);
context->read_data_offset = data_offset;
context->read_data_length = data_len;
memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len);
if (is_streaming(context)) {
schedule_work(&context->read_data_work);
return;
}
data_offset += pkt_data_len;
data_len -= pkt_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);
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,35 +800,57 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
if (!context->rx)
return -ENOMEM;
/*
* 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.
*/
for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
if (!context->tx[i])
return -ENOMEM;
}
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);
context->non_streaming = true;
}
/*
* 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);
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);
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)