// SPDX-License-Identifier: GPL-2.0-or-later /* * DMA driver for Altera mSGDMA IP core * * Copyright (C) 2017 Stefan Roese <sr@denx.de> * * Based on drivers/dma/xilinx/zynqmp_dma.c, which is: * Copyright (C) 2016 Xilinx, Inc. All rights reserved.
*/
/** * struct msgdma_extended_desc - implements an extended descriptor * @read_addr_lo: data buffer source address low bits * @write_addr_lo: data buffer destination address low bits * @len: the number of bytes to transfer per descriptor * @burst_seq_num: bit 31:24 write burst * bit 23:16 read burst * bit 15:00 sequence number * @stride: bit 31:16 write stride * bit 15:00 read stride * @read_addr_hi: data buffer source address high bits * @write_addr_hi: data buffer destination address high bits * @control: characteristics of the transfer
*/ struct msgdma_extended_desc {
u32 read_addr_lo;
u32 write_addr_lo;
u32 len;
u32 burst_seq_num;
u32 stride;
u32 read_addr_hi;
u32 write_addr_hi;
u32 control;
};
/** * struct msgdma_sw_desc - implements a sw descriptor * @async_tx: support for the async_tx api * @hw_desc: associated HW descriptor * @node: node to move from the free list to the tx list * @tx_list: transmit list node
*/ struct msgdma_sw_desc { struct dma_async_tx_descriptor async_tx; struct msgdma_extended_desc hw_desc; struct list_head node; struct list_head tx_list;
};
/** * msgdma_get_descriptor - Get the sw descriptor from the pool * @mdev: Pointer to the Altera mSGDMA device structure * * Return: The sw descriptor
*/ staticstruct msgdma_sw_desc *msgdma_get_descriptor(struct msgdma_device *mdev)
{ struct msgdma_sw_desc *desc; unsignedlong flags;
/** * msgdma_free_desc_list - Free descriptors list * @mdev: Pointer to the Altera mSGDMA device structure * @list: List to parse and delete the descriptor
*/ staticvoid msgdma_free_desc_list(struct msgdma_device *mdev, struct list_head *list)
{ struct msgdma_sw_desc *desc, *next;
/** * msgdma_desc_config - Configure the descriptor * @desc: Hw descriptor pointer * @dst: Destination buffer address * @src: Source buffer address * @len: Transfer length * @stride: Read/write stride value to set
*/ staticvoid msgdma_desc_config(struct msgdma_extended_desc *desc,
dma_addr_t dst, dma_addr_t src, size_t len,
u32 stride)
{ /* Set lower 32bits of src & dst addresses in the descriptor */
desc->read_addr_lo = lower_32_bits(src);
desc->write_addr_lo = lower_32_bits(dst);
/* Set upper 32bits of src & dst addresses in the descriptor */
desc->read_addr_hi = upper_32_bits(src);
desc->write_addr_hi = upper_32_bits(dst);
desc->len = len;
desc->stride = stride;
desc->burst_seq_num = 0; /* 0 will result in max burst length */
/* * Don't set interrupt on xfer end yet, this will be done later * for the "last" descriptor
*/
desc->control = MSGDMA_DESC_CTL_TR_ERR_IRQ | MSGDMA_DESC_CTL_GO |
MSGDMA_DESC_CTL_END_ON_LEN;
}
/** * msgdma_desc_config_eod - Mark the descriptor as end descriptor * @desc: Hw descriptor pointer
*/ staticvoid msgdma_desc_config_eod(struct msgdma_extended_desc *desc)
{
desc->control |= MSGDMA_DESC_CTL_TR_COMP_IRQ;
}
/* * Check if the DESC FIFO it not full. If its full, we need to wait * for at least one entry to become free again
*/ while (ioread32(mdev->csr + MSGDMA_CSR_STATUS) &
MSGDMA_CSR_STAT_DESC_BUF_FULL)
mdelay(1);
/* * The descriptor needs to get copied into the descriptor FIFO * of the DMA controller. The descriptor will get flushed to the * FIFO, once the last word (control word) is written. Since we * are not 100% sure that memcpy() writes all word in the "correct" * order (address from low to high) on all architectures, we make * sure this control word is written last by single coding it and * adding some write-barriers here.
*/
memcpy((void __force *)hw_desc, &desc->hw_desc, sizeof(desc->hw_desc) - sizeof(u32));
/* Write control word last to flush this descriptor into the FIFO */
mdev->idle = false;
wmb();
iowrite32(desc->hw_desc.control, hw_desc +
offsetof(struct msgdma_extended_desc, control));
wmb();
}
/** * msgdma_copy_desc_to_fifo - copy descriptor(s) into controller FIFO * @mdev: Pointer to the Altera mSGDMA device structure * @desc: Transaction descriptor pointer
*/ staticvoid msgdma_copy_desc_to_fifo(struct msgdma_device *mdev, struct msgdma_sw_desc *desc)
{ struct msgdma_sw_desc *sdesc, *next;
/** * msgdma_start_transfer - Initiate the new transfer * @mdev: Pointer to the Altera mSGDMA device structure
*/ staticvoid msgdma_start_transfer(struct msgdma_device *mdev)
{ struct msgdma_sw_desc *desc;
if (!mdev->idle) return;
desc = list_first_entry_or_null(&mdev->pending_list, struct msgdma_sw_desc, node); if (!desc) return;
/* Run any dependencies, then free the descriptor */
msgdma_free_descriptor(mdev, desc);
}
spin_unlock_irqrestore(&mdev->lock, irqflags);
}
/** * msgdma_complete_descriptor - Mark the active descriptor as complete * @mdev: Pointer to the Altera mSGDMA device structure
*/ staticvoid msgdma_complete_descriptor(struct msgdma_device *mdev)
{ struct msgdma_sw_desc *desc;
if (mdev->resp) { /* Read number of responses that are available */
count = ioread32(mdev->csr + MSGDMA_CSR_RESP_FILL_LEVEL);
dev_dbg(mdev->dev, "%s (%d): response count=%d\n",
__func__, __LINE__, count);
} else {
count = 1;
}
while (count--) { /* * Read both longwords to purge this response from the FIFO * On Avalon-MM implementations, size and status do not * have any real values, like transferred bytes or error * bits. So we need to just drop these values.
*/ if (mdev->resp) {
size = ioread32(mdev->resp +
MSGDMA_RESP_BYTES_TRANSFERRED);
status = ioread32(mdev->resp +
MSGDMA_RESP_STATUS);
}
msgdma_complete_descriptor(mdev);
}
spin_unlock_irqrestore(&mdev->lock, flags);
msgdma_chan_desc_cleanup(mdev);
}
/** * msgdma_irq_handler - Altera mSGDMA Interrupt handler * @irq: IRQ number * @data: Pointer to the Altera mSGDMA device structure * * Return: IRQ_HANDLED/IRQ_NONE
*/ static irqreturn_t msgdma_irq_handler(int irq, void *data)
{ struct msgdma_device *mdev = data;
u32 status;
status = ioread32(mdev->csr + MSGDMA_CSR_STATUS); if ((status & MSGDMA_CSR_STAT_BUSY) == 0) { /* Start next transfer if the DMA controller is idle */
spin_lock(&mdev->lock);
mdev->idle = true;
msgdma_start_transfer(mdev);
spin_unlock(&mdev->lock);
}
/** * msgdma_dev_remove() - Device remove function * @mdev: Pointer to the Altera mSGDMA device structure
*/ staticvoid msgdma_dev_remove(struct msgdma_device *mdev)
{ if (!mdev) return;
/* Set DMA mask to 64 bits */
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (ret) {
dev_warn(&pdev->dev, "unable to set coherent mask to 64"); goto fail;
}
msgdma_reset(mdev);
ret = dma_async_device_register(dma_dev); if (ret) goto fail;
ret = of_dma_controller_register(pdev->dev.of_node,
of_dma_xlate_by_chan_id, dma_dev); if (ret == -EINVAL)
dev_warn(&pdev->dev, "device was not probed from DT"); elseif (ret && ret != -ENODEV) goto fail;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.