Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I3C DMA Transfer and Abort Support #2460

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

SwaroopPKADI
Copy link
Collaborator

Pull Request Description

I added DMA transfer and abort support for the I3C interface. This allows efficient data transfers and the ability to stop transfers when needed. The DMA transaction of multiple transfers occur sequentially and I3C data complete callback waits for the DMA callback to be completed and the next transfer to be configured before proceeding to configure the next write to control register.

PR Type

  • Bug fix (change that fixes an issue)
  • New feature (change that adds new functionality)
  • Breaking change (has dependencies in other repos or will cause CI to fail)

PR Checklist

  • I have followed the Coding style guidelines
  • I have performed a self-review of the changes
  • I have commented my code, at least hard-to-understand parts
  • I have build all projects affected by the changes in this PR
  • I have tested in hardware affected projects, at the relevant boards
  • I have signed off all commits from this PR
  • I have updated the documentation (wiki pages, ReadMe etc), if applies

@gastmaier
Copy link
Contributor

Is this tested on hw? can you provide a basic_example.c along-side?
Maybe on top of innersource commit 88916d72a87f1f57fc02d195dddd65886acc1460?

int32_t stm32_i3c_dma_transfer(struct no_os_i3c_desc *desc,
struct no_os_i3c_msg *msgs,
uint32_t len,
void (*callback)(void *),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

callback is not used anywhere in this function. Am I missing something?

Comment on lines 677 to 898
else if (tx_count == 0)
hi3c->State = HAL_I3C_STATE_BUSY_RX;
else
hi3c->State = HAL_I3C_STATE_BUSY_TX_RX;

/* Enable Tx process (frame complete and error) interrupts */
hi3c->Instance->IER |= (HAL_I3C_IT_FCIE | HAL_I3C_IT_ERRIE);

/* Enable control DMA Request */
LL_I3C_EnableDMAReq_Control(hi3c->Instance);

/* Check if Rx counter different from zero */
if (rx_count != 0U)
{
/* Enable Rx data DMA Request */
LL_I3C_EnableDMAReq_RX(hi3c->Instance);
}

/* Check if Tx counter different from zero */
if (tx_count != 0U)
{
/* Enable Tx data DMA Request */
LL_I3C_EnableDMAReq_TX(hi3c->Instance);
}

/******************** DMA Configuration ************************/
dma_ctx->desc = desc;

ret = no_os_dma_config_xfer(xdesc->i3c_dma_desc->dma_desc, &dma_ctx->cr_ch_xfer, 1, xdesc->i3c_dma_desc->crdma_ch);
if (ret)
goto free_tx_ch_xfer;
if (dma_ctx->rx_ch_xfer) {
ret = no_os_dma_config_xfer(xdesc->i3c_dma_desc->dma_desc, dma_ctx->rx_ch_xfer, rx_count, xdesc->i3c_dma_desc->rxdma_ch);
if (ret)
goto free_tx_ch_xfer;
}
if (dma_ctx->tx_ch_xfer) {
ret = no_os_dma_config_xfer(xdesc->i3c_dma_desc->dma_desc, dma_ctx->tx_ch_xfer, tx_count, xdesc->i3c_dma_desc->txdma_ch);
if (ret)
goto free_tx_ch_xfer;
}

/********************** Priority Configuration ************************/
/*
* Note: The priority of the I3C interrupt has to lower than DMA interrupt.
* The I3C interrupt is made to wait until the DMA interrupt is received
* and then continues to write the next control word once the DMA transfer is
* configured.
*/
if (xdesc->i3c_dma_desc->txdma_ch)
no_os_irq_get_priority(xdesc->i3c_dma_desc->dma_desc->irq_ctrl,
xdesc->i3c_dma_desc->txdma_ch->irq_num,
&txdma_priority);
if (xdesc->i3c_dma_desc->rxdma_ch)
no_os_irq_get_priority(xdesc->i3c_dma_desc->dma_desc->irq_ctrl,
xdesc->i3c_dma_desc->rxdma_ch->irq_num,
&rxdma_priority);
i3c_priority = txdma_priority > rxdma_priority ? txdma_priority+1 : rxdma_priority+1;
HAL_NVIC_SetPriority(xdesc->irq_id, i3c_priority, 0);
i3c_dma_cplt[desc->bus->device_id] = false;

/********************* DMA Start ***********************/
if (dma_ctx->rx_ch_xfer) {
ret = no_os_dma_xfer_start(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->rxdma_ch);
if (ret)
goto abort_transfer;
}
if (dma_ctx->tx_ch_xfer) {
ret = no_os_dma_xfer_start(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->txdma_ch);
if (ret)
goto abort_transfer;
}

/* Write Control buffer data to control register */
ret = no_os_dma_xfer_start(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->crdma_ch);
if (ret)
goto abort_transfer;

/* Decrement remaining control buffer data counter */
hi3c->ControlXferCount--;

/* Initiate a Start condition */
LL_I3C_RequestTransfer(hi3c->Instance);

return ret;

abort_transfer:
no_os_dma_xfer_abort(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->crdma_ch);
no_os_dma_xfer_abort(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->txdma_ch);
no_os_dma_xfer_abort(xdesc->i3c_dma_desc->dma_desc, xdesc->i3c_dma_desc->rxdma_ch);
free_tx_ch_xfer:
no_os_free(dma_ctx->tx_ch_xfer);
free_rx_ch_xfer:
no_os_free(dma_ctx->rx_ch_xfer);
free_cr_src:
no_os_free(dma_ctx->cr_ch_xfer.src);
free_ctx:
no_os_free(dma_ctx);
return ret;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this into multiple functions. 250 LOC is too much...

Added the DMA transfer and abort functionalities for i3c.
This will enable the user to configure the DMA for I3C transaction
asynchronously and abort it when necessary.

Signed-off-by: Swaroop PrakashKumar <[email protected]>
…I3C.

The driver supports usage of DMA channels for the Control register, Tx and Rx.

Signed-off-by: Swaroop PrakashKumar <[email protected]>
@SwaroopPKADI SwaroopPKADI marked this pull request as draft February 18, 2025 12:07
@kister-jimenez
Copy link
Collaborator

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 2 pipeline(s).

* Transfer a list of messages using DMA. Return once the first one started and
* invoke a callback when they are done.
*/
int32_t no_os_i3c_dma_transfer(struct no_os_i3c_desc *desc,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A word about naming inconsistency, there is some existing DMA support in SPI and I think we should follow the naming convention there:

/* Iterate over the spi_msg array and send all messages at once */
int32_t no_os_spi_transfer(struct no_os_spi_desc *desc,
			   struct no_os_spi_msg *msgs,
			   uint32_t len);
/* Transfer a list of messages using DMA. Wait until all transfers are done */
int32_t no_os_spi_transfer_dma_sync(struct no_os_spi_desc *desc,
				    struct no_os_spi_msg *msgs,
				    uint32_t len);
/*
 * Transfer a list of messages using DMA. Return once the first one started and
 * invoke a callback when they are done.
 */
int32_t no_os_spi_transfer_dma_async(struct no_os_spi_desc *desc,
				     struct no_os_spi_msg *msgs,
				     uint32_t len,
				     void (*callback)(void *),
				     void *ctx);

If we follow that, your function should be named no_os_i3c_transfer_dma_async. I think it would be useful to include a synchronous version too, no_os_i3c_transfer_dma which uses the async version internally but also takes care of the callback.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the mismatch in naming convention and will fix that but, I couldn't understand the usefulness of having a synchronous version of DMA transfer. Isn't it equivalent to a normal I3C transfer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants