[PATCH v7 1/3] dmaengine: Add support for APM X-Gene SoC DMA engine driver
From: Vinod Koul <hidden>
Date: 2015-03-16 09:31:47
Also in:
linux-devicetree, lkml
On Thu, Mar 12, 2015 at 04:45:19PM +0530, Rameshwar Prasad Sahu wrote:
+/* DMA ring csr registers and bit definations */ +#define DMA_RING_CONFIG 0x04 +#define DMA_RING_ENABLE BIT(31) +#define DMA_RING_ID 0x08 +#define DMA_RING_ID_SETUP(v) ((v) | BIT(31)) +#define DMA_RING_ID_BUF 0x0C +#define DMA_RING_ID_BUF_SETUP(v) (((v) << 9) | BIT(21)) +#define DMA_RING_THRESLD0_SET1 0x30 +#define DMA_RING_THRESLD0_SET1_VAL 0X64 +#define DMA_RING_THRESLD1_SET1 0x34 +#define DMA_RING_THRESLD1_SET1_VAL 0xC8 +#define DMA_RING_HYSTERESIS 0x68 +#define DMA_RING_HYSTERESIS_VAL 0xFFFFFFFF +#define DMA_RING_STATE 0x6C +#define DMA_RING_STATE_WR_BASE 0x70 +#define DMA_RING_NE_INT_MODE 0x017C +#define DMA_RING_NE_INT_MODE_SET(m, v) \ + ((m) = ((m) & ~BIT(31 - (v))) | BIT(31 - (v))) +#define DMA_RING_NE_INT_MODE_RESET(m, v) \ + ((m) &= (~BIT(31 - (v)))) +#define DMA_RING_CLKEN 0xC208 +#define DMA_RING_SRST 0xC200 +#define DMA_RING_MEM_RAM_SHUTDOWN 0xD070 +#define DMA_RING_BLK_MEM_RDY 0xD074 +#define DMA_RING_BLK_MEM_RDY_VAL 0xFFFFFFFF +#define DMA_RING_DESC_CNT(v) (((v) & 0x0001FFFE) >> 1) +#define DMA_RING_ID_GET(owner, num) (((owner) << 6) | (num)) +#define DMA_RING_DST_ID(v) ((1 << 10) | (v)) +#define DMA_RING_CMD_OFFSET 0x2C +#define DMA_RING_CMD_BASE_OFFSET(v) ((v) << 6) +#define DMA_RING_COHERENT_SET(m) (((u32 *)(m))[2] |= BIT(4)) +#define DMA_RING_ADDRL_SET(m, v) (((u32 *)(m))[2] |= (((v) >> 8) << 5)) +#define DMA_RING_ADDRH_SET(m, v) (((u32 *)(m))[3] |= ((v) >> 35)) +#define DMA_RING_ACCEPTLERR_SET(m) (((u32 *)(m))[3] |= BIT(19)) +#define DMA_RING_SIZE_SET(m, v) (((u32 *)(m))[3] |= ((v) << 23)) +#define DMA_RING_RECOMBBUF_SET(m) (((u32 *)(m))[3] |= BIT(27)) +#define DMA_RING_RECOMTIMEOUTL_SET(m) (((u32 *)(m))[3] |= (0x7 << 28)) +#define DMA_RING_RECOMTIMEOUTH_SET(m) (((u32 *)(m))[4] |= 0x3) +#define DMA_RING_SELTHRSH_SET(m) (((u32 *)(m))[4] |= BIT(3)) +#define DMA_RING_TYPE_SET(m, v) (((u32 *)(m))[4] |= ((v) << 19))
These are very generic name as can conflicts, can you please namespace these...
+/* DMA device csr registers and bit definitions */ +#define DMA_IPBRR 0x0 +#define DMA_DEV_ID_RD(v) ((v) & 0x00000FFF) +#define DMA_BUS_ID_RD(v) (((v) >> 12) & 3) +#define DMA_REV_NO_RD(v) (((v) >> 14) & 3) +#define DMA_GCR 0x10 +#define DMA_CH_SETUP(v) ((v) = ((v) & ~0x000FFFFF) | 0x000AAFFF) +#define DMA_ENABLE(v) ((v) |= BIT(31)) +#define DMA_DISABLE(v) ((v) &= ~BIT(31)) +#define DMA_RAID6_CONT 0x14 +#define DMA_RAID6_MULTI_CTRL(v) ((v) << 24) +#define DMA_INT 0x70 +#define DMA_INT_MASK 0x74 +#define DMA_INT_ALL_MASK 0xFFFFFFFF +#define DMA_INT_ALL_UNMASK 0x0 +#define DMA_INT_MASK_SHIFT 0x14 +#define DMA_RING_INT0_MASK 0x90A0 +#define DMA_RING_INT1_MASK 0x90A8 +#define DMA_RING_INT2_MASK 0x90B0 +#define DMA_RING_INT3_MASK 0x90B8 +#define DMA_RING_INT4_MASK 0x90C0 +#define DMA_CFG_RING_WQ_ASSOC 0x90E0 +#define DMA_ASSOC_RING_MNGR1 0xFFFFFFFF +#define DMA_MEM_RAM_SHUTDOWN 0xD070 +#define DMA_BLK_MEM_RDY 0xD074 +#define DMA_BLK_MEM_RDY_VAL 0xFFFFFFFF
same here and throughout the driver..
+static void xgene_dma_free_descriptor(struct xgene_dma_chan *chan,
+ struct xgene_dma_desc_sw *desc)
+{
+ list_del(&desc->node);
+ chan_dbg(chan, "LD %p free\n", desc);
+ dma_pool_free(chan->desc_pool, desc, desc->tx.phys);where is the descriptor freed? Perhpas we can say clean rather than free here to not confuse...
+}
+
+static struct xgene_dma_desc_sw *xgene_dma_alloc_descriptor(
+ struct xgene_dma_chan *chan)
+{
+ struct xgene_dma_desc_sw *desc;
+ dma_addr_t phys;
+
+ desc = dma_pool_alloc(chan->desc_pool, GFP_NOWAIT, &phys);
+ if (!desc) {
+ chan_dbg(chan, "Failed to allocate LDs\n");not error?
+static void xgene_dma_free_desc_list_reverse(struct xgene_dma_chan *chan, + struct list_head *list)
do we really care about free order?
+{
+ struct xgene_dma_desc_sw *desc, *_desc;
+
+ list_for_each_entry_safe_reverse(desc, _desc, list, node)
+ xgene_dma_free_descriptor(chan, desc);
+}
+
+static void xgene_dma_free_chan_resources(struct dma_chan *dchan)
+{
+ struct xgene_dma_chan *chan = to_dma_chan(dchan);
+
+ chan_dbg(chan, "Free all resources\n");
+
+ if (!chan->desc_pool)
+ return;
+
+ spin_lock_bh(&chan->lock);
+
+ /* Process all running descriptor */
+ xgene_dma_cleanup_descriptors(chan);
+
+ /* Clean all link descriptor queues */
+ xgene_dma_free_desc_list(chan, &chan->ld_pending);
+ xgene_dma_free_desc_list(chan, &chan->ld_running);
+ xgene_dma_free_desc_list(chan, &chan->ld_completed);
+
+ spin_unlock_bh(&chan->lock);
+
+ /* Delete this channel DMA pool */
+ dma_pool_destroy(chan->desc_pool);
+ chan->desc_pool = NULL;
+}
+
+static struct dma_async_tx_descriptor *xgene_dma_prep_memcpy(
+ struct dma_chan *dchan, dma_addr_t dst, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct xgene_dma_desc_sw *first = NULL, *new;
+ struct xgene_dma_chan *chan;
+ size_t copy;
+ u32 desc_id;
+
+ if (unlikely(!dchan || !len))
+ return NULL;
+
+ chan = to_dma_chan(dchan);
+
+ /* Get the id for this group of descriptors */
+ desc_id = atomic_inc_return(&chan->desc_id);
+
+ do {
+ /* Allocate the link descriptor from DMA pool */
+ new = xgene_dma_alloc_descriptor(chan);
+ if (!new)
+ goto fail;
+
+ /* Create the largest transaction possible */
+ copy = min_t(size_t, len, DMA_MAX_64B_DESC_BYTE_CNT);
+
+ /* Prepare DMA descriptor */
+ xgene_dma_prep_cpy_desc(chan, new, dst, src, copy, desc_id);
+
+ if (!first)
+ first = new;
+
+ new->first = first;
+ first->desc_cnt++;
+ new->desc_id = desc_id;
+
+ new->tx.cookie = 0;
+ async_tx_ack(&new->tx);
+
+ /* Update metadata */
+ len -= copy;
+ dst += copy;
+ src += copy;
+
+ /* Insert the link descriptor to the LD ring */
+ list_add_tail(&new->node, &first->tx_list);
+ } while (len);
+
+ first->tx.flags = flags; /* client is in control of this ack */
+ first->tx.cookie = -EBUSY;
+ first->flags |= DMA_FLAG_FIRST_DESC;
+
+ return &first->tx;where are you mapping dma buffers?
+static enum dma_status xgene_dma_find_tx_desc_status(
+ struct xgene_dma_chan *chan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct xgene_dma_desc_sw *desc;
+
+ spin_lock_bh(&chan->lock);
+
+ /* Check if this tx descriptor is still in pending queue */
+ list_for_each_entry(desc, &chan->ld_pending, node) {
+ if (desc->tx.cookie == cookie) {
+ xgene_chan_xfer_ld_pending(chan);why are you calling this here, status check shouldnt do this...
+ spin_unlock_bh(&chan->lock); + return DMA_IN_PROGRESS;
residue here is size of transacation.
+ }
+ }
+
+ /* Check if this descriptor is in running queue */
+ list_for_each_entry(desc, &chan->ld_running, node) {
+ if (desc->tx.cookie == cookie) {
+ /* Cleanup any running and executed descriptors */
+ xgene_dma_cleanup_descriptors(chan);ditto?
+ spin_unlock_bh(&chan->lock); + return dma_cookie_status(&chan->dma_chan, + cookie, txstate);
and you havent touched txstate so what is the intent here...?
+ }
+ }
+
+ spin_unlock_bh(&chan->lock);
+
+ return DMA_COMPLETE;
+}
+
+static enum dma_status xgene_dma_tx_status(struct dma_chan *dchan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct xgene_dma_chan *chan = to_dma_chan(dchan);
+
+ enum dma_status status;
+
+ status = dma_cookie_status(dchan, cookie, txstate);
+ if (status == DMA_COMPLETE)you should do this if txstate in NULL, no point doing calculations..
+ return status;
+
+ return xgene_dma_find_tx_desc_status(chan, cookie, txstate);
+}
+
+static void xgene_dma_tasklet_cb(unsigned long data)
+{
+ struct xgene_dma_chan *chan = (struct xgene_dma_chan *)data;
+
+ spin_lock_bh(&chan->lock);
+
+ /* Run all cleanup for descriptors which have been completed */
+ xgene_dma_cleanup_descriptors(chan);
+
+ /* Re-enable DMA channel IRQ */
+ enable_irq(chan->rx_irq);
+
+ spin_unlock_bh(&chan->lock);
+}
+
+static irqreturn_t xgene_dma_chan_ring_isr(int irq, void *id)
+{
+ struct xgene_dma_chan *chan = (struct xgene_dma_chan *)id;
+
+ BUG_ON(!chan);
+
+ /*
+ * Disable DMA channel IRQ until we process completed
+ * descriptors
+ */
+ disable_irq_nosync(chan->rx_irq);and why is that?
+ + /* + * Schedule the tasklet to handle all cleanup of the current + * transaction. It will start a new transaction if there is + * one pending. + */ + tasklet_schedule(&chan->tasklet);
for better results why not schedule the next transaction here..?
+ + return IRQ_HANDLED; +} +
+static void xgene_dma_async_unregister(struct xgene_dma *pdma)
+{
+ int i;
+
+ for (i = 0; i < DMA_MAX_CHANNEL; i++)
+ dma_async_device_unregister(&pdma->dma_dev[i]);how do you ensure irq is disabled and tasklets killed?
+MODULE_DESCRIPTION("APM X-Gene SoC DMA driver");
+MODULE_AUTHOR("Rameshwar Prasad Sahu [off-list ref]");
+MODULE_AUTHOR("Loc Ho [off-list ref]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");why do you need this?
-- 1.8.2.1
--