Skip to contentSkip to conversation

sDMA: descriptor autoloading feature

PATCH New Venkatraman S 1 participant 1 message Apr 29, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/2 sDMA: descriptor autoloading feature

Add sDMA driver support for descriptor autoloading feature.
Descriptor autoloading is OMAP sDMA v5 hardware capability that can
be exploited for scatter gather
scenarios, currently available in OMAP3630 and OMAP4430.

The feature works as described below.
1) A sDMA channel is programmed to be in 'linked list' mode.
2) The client (sDMA user) provides a list of descriptors in a linked
list format.
3) Each of the 'descriptor' (element in the linked list) contains an
updated set of DMA
configuration register values.
4) Client starts DMA transfer.
5) sDMA controller loads the first element to its register
configuration memory and executes the
transfer.
6) After completion, loads the next element (in linked list) to
configuration memory and executes
the transfer, without MCU intervention.
7) Interrupt is generated after all transfers are completed; this
can be configured to be done
differently.

Configurations and additional features
1) Fast mode & non-fast mode
Fast mode/non-fast decides on how the first transfer begins.
In non-fast mode, the first
element in the linked list is loaded only after completing the
transfer according to the
configurations already in the sDMA channel registers. In fast mode,
the loading of the first
element precedes the transfer.

2) Pause / resume of transfers
A transfer can be paused after a descriptor set has been loaded,
provided the 'pause
bit' is set in the linked list element.
An ongoing transfer cannot be paused. If the 'pause bit' is set,
transfer is not
started after loading the register set from memory. Such a transfer can be resumed later.

3) Descriptor types
3 possible configurations of descriptors (initialized as linked list elements) are possible. Type 1 provides the maximum flexibility, which contains most register definitions of a DMA logical channel. Fewer options are
present in type
2. Type 3 can just modify source/destinations address of transfers. In all transfers, unmodified registers settings are maintained for the next transfer.

Patch provides options / API for
1) Setting up a descriptor loading for DMA channel for sg type transfers
2) configuration with linked list elements
3) Starting / pause and resume of the said transfers, query state
4) Clearing the sglist mode

CC Adrian Hunter <[email protected]>
CC Madhusudhan C <[email protected]>
CC Shilimkar Santosh <[email protected]>
CC Tony Lindgren <[email protected]>

---
Changes from v7:
* Added wmb() calls between descriptor data structure update and
start/resume of transfer
* Added proper commit log about the feature

 arch/arm/plat-omap/dma.c              |  289 ++++++++++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/dma.h |  170 +++++++++++++++++++
 2 files changed, 458 insertions(+), 1 deletions(-)
arch/arm/plat-omap/dma.c +0 -0
index f6c9bdc..d8d4df8 100644
@@ -46,12 +46,40 @@ enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE,

DMA_CH_STARTED,
enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; #endif

+/* CDP Register bitmaps */
+#define DMA_LIST_CDP_DST_VALID (BIT(0))
+#define DMA_LIST_CDP_SRC_VALID (BIT(2))
+#define DMA_LIST_CDP_TYPE1 (BIT(4))
+#define DMA_LIST_CDP_TYPE2 (BIT(5))
+#define DMA_LIST_CDP_TYPE3 (BIT(4) | BIT(5))
+#define DMA_LIST_CDP_PAUSEMODE (BIT(7))
+#define DMA_LIST_CDP_LISTMODE (BIT(8))
+#define DMA_LIST_CDP_FASTMODE (BIT(10))
+/* CAPS register bitmaps */
+#define DMA_CAPS_SGLIST_SUPPORT (BIT(20))
+
+#define DMA_LIST_DESC_PAUSE (BIT(0))
+#define DMA_LIST_DESC_SRC_VALID (BIT(24))
+#define DMA_LIST_DESC_DST_VALID (BIT(26))
+#define DMA_LIST_DESC_BLK_END (BIT(28))
+
#define OMAP_DMA_ACTIVE 0x01
#define OMAP2_DMA_CSR_CLEAR_MASK 0xffe

#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec)
+#define OMAP_DMA_INVALID_FRAME_COUNT (0xffff)
+#define OMAP_DMA_INVALID_ELEM_COUNT (0xffffff)
+#define OMAP_DMA_INVALID_DESCRIPTOR_POINTER (0xfffffffc)

static int enable_1510_mode;
+static int dma_caps0_status;
+
+struct omap_dma_list_config_params {
+ unsigned int num_elem;
+ struct omap_dma_sglist_node *sghead;
+ dma_addr_t sgheadphy;
+ unsigned int pausenode;
+};

static struct omap_dma_global_context_registers {
u32 dma_irqenable_l0;
@@ -74,9 +102,10 @@ struct omap_dma_lch {
int next_linked_ch;
int state;
int chain_id;
-
int status;
+ struct omap_dma_list_config_params list_config;
#endif
+
long flags;
};

@@ -214,6 +243,76 @@ static void clear_lch_regs(int lch)
__raw_writew(0, lch_base + i);
}

+static inline void omap_dma_list_set_ntype(struct omap_dma_sglist_node *node,
+ int value)
+{
+ node->num_of_elem |= ((value) << 29);
+}
+
+static void omap_set_dma_sglist_pausebit(
+ struct omap_dma_list_config_params *lcfg, int nelem, int set)
+{
+ struct omap_dma_sglist_node *sgn = lcfg->sghead;
+
+ if (nelem > 0 && nelem < lcfg->num_elem) {
+ lcfg->pausenode = nelem;
+ sgn += nelem;
+
+ if (set)
+ sgn->next_desc_add_ptr |= DMA_LIST_DESC_PAUSE;
+ else
+ sgn->next_desc_add_ptr &= ~(DMA_LIST_DESC_PAUSE);
+ }
+}
+
+static int dma_sglist_set_phy_params(struct omap_dma_sglist_node *sghead,
+ dma_addr_t phyaddr, int nelem)
+{
+ struct omap_dma_sglist_node *sgcurr, *sgprev;
+ dma_addr_t elem_paddr = phyaddr;
+
+ for (sgprev = sghead;
+ sgprev < sghead + nelem;
+ sgprev++) {
+
+ sgcurr = sgprev + 1;
+ sgprev->next = sgcurr;
+ elem_paddr += (int)sizeof(*sgcurr);
+ sgprev->next_desc_add_ptr = elem_paddr;
+
+ switch (sgcurr->desc_type) {
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1:
+ omap_dma_list_set_ntype(sgprev, 1);
+ break;
+
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
+ /* intentional no break */
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
+ omap_dma_list_set_ntype(sgprev, 2);
+ break;
+
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
+ /* intentional no break */
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
+ omap_dma_list_set_ntype(sgprev, 3);
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ if (sgcurr->flags & OMAP_DMA_LIST_SRC_VALID)
+ sgprev->num_of_elem |= DMA_LIST_DESC_SRC_VALID;
+ if (sgcurr->flags & OMAP_DMA_LIST_DST_VALID)
+ sgprev->num_of_elem |= DMA_LIST_DESC_DST_VALID;
+ if (sgcurr->flags & OMAP_DMA_LIST_NOTIFY_BLOCK_END)
+ sgprev->num_of_elem |= DMA_LIST_DESC_BLK_END;
+ }
+ sgprev--;
+ sgprev->next_desc_add_ptr = OMAP_DMA_INVALID_DESCRIPTOR_POINTER;
+ return 0;
+}
+
void omap_set_dma_priority(int lch, int dst_port, int priority) {
unsigned long reg;
@@ -825,6 +924,7 @@ void omap_free_dma(int lch)
/* Make sure the DMA transfer is stopped. */ dma_write(0, CCR(lch));
omap_clear_dma(lch);
+ omap_clear_dma_sglist_mode(lch);
}

spin_lock_irqsave(&dma_chan_lock, flags);
@@ -1818,6 +1918,192 @@ int omap_get_dma_chain_src_pos(int chain_id)
EXPORT_SYMBOL(omap_get_dma_chain_src_pos);
#endif /* ifndef CONFIG_ARCH_OMAP1 */

+int omap_set_dma_sglist_mode(int lch, struct omap_dma_sglist_node *sgparams,
+ dma_addr_t padd, int nelem, struct omap_dma_channel_params *chparams)
+{
+ struct omap_dma_list_config_params *lcfg;
+ int l = DMA_LIST_CDP_LISTMODE; /* Enable Linked list mode in CDP */
+
+ if ((dma_caps0_status & DMA_CAPS_SGLIST_SUPPORT) == 0) {
+ printk(KERN_ERR "omap DMA: sglist feature not supported\n");
+ return -EPERM;
+ }
+ if (dma_chan[lch].flags & OMAP_DMA_ACTIVE) {
+ printk(KERN_ERR "omap DMA: configuring active DMA channel\n");
+ return -EPERM;
+ }
+
+ if (padd == 0) {
+ printk(KERN_ERR "omap DMA: sglist invalid dma_addr\n");
+ return -EINVAL;
+ }
+ lcfg = &dma_chan[lch].list_config;
+
+ lcfg->sghead = sgparams;
+ lcfg->num_elem = nelem;
+ lcfg->sgheadphy = padd;
+ lcfg->pausenode = -1;
+
+
+ if (NULL == chparams)
+ l |= DMA_LIST_CDP_FASTMODE;
+ else
+ omap_set_dma_params(lch, chparams);
+
+ dma_write(l, CDP(lch));
+ dma_write(0, CCDN(lch)); /* Reset List index numbering */
+ /* Initialize frame and element counters to invalid values */
+ dma_write(OMAP_DMA_INVALID_FRAME_COUNT, CCFN(lch));
+ dma_write(OMAP_DMA_INVALID_ELEM_COUNT, CCEN(lch));
+
+ return dma_sglist_set_phy_params(sgparams, lcfg->sgheadphy, nelem);
+
+}
+EXPORT_SYMBOL(omap_set_dma_sglist_mode);
+
+void omap_clear_dma_sglist_mode(int lch)
+{
+ /* Clear entire CDP which is related to sglist handling */
+ dma_write(0, CDP(lch));
+ dma_write(0, CCDN(lch));
+ /**
+ * Put back the original enabled irqs, which
+ * could have been overwritten by type 1 or type 2
+ * descriptors
+ */
+ dma_write(dma_chan[lch].enabled_irqs, CICR(lch));
+ return;
+}
+EXPORT_SYMBOL(omap_clear_dma_sglist_mode);
+
+int omap_start_dma_sglist_transfers(int lch, int pauseafter)
+{
+ struct omap_dma_list_config_params *lcfg;
+ struct omap_dma_sglist_node *sgn;
+ unsigned int l, type_id;
+
+ lcfg = &dma_chan[lch].list_config;
+ sgn = lcfg->sghead;
+
+ lcfg->pausenode = 0;
+ omap_set_dma_sglist_pausebit(lcfg, pauseafter, 1);
+
+ /* Program the head descriptor's properties into CDP */
+ switch (lcfg->sghead->desc_type) {
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1:
+ type_id = DMA_LIST_CDP_TYPE1;
+ break;
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
+ type_id = DMA_LIST_CDP_TYPE2;
+ break;
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
+ case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
+ type_id = DMA_LIST_CDP_TYPE3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ l = dma_read(CDP(lch));
+ l |= type_id;
+ if (lcfg->sghead->flags & OMAP_DMA_LIST_SRC_VALID)
+ l |= DMA_LIST_CDP_SRC_VALID;
+ if (lcfg->sghead->flags & OMAP_DMA_LIST_DST_VALID)
+ l |= DMA_LIST_CDP_DST_VALID;
+
+ dma_write(l, CDP(lch));
+ dma_write((lcfg->sgheadphy), CNDP(lch));
+ /**
+ * Barrier needed as writes to the
+ * descriptor memory needs to be flushed
+ * before it's used by DMA controller
+ */
+ wmb();
+ omap_start_dma(lch);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_start_dma_sglist_transfers);
+
+int omap_resume_dma_sglist_transfers(int lch, int pauseafter)
+{
+ struct omap_dma_list_config_params *lcfg;
+ struct omap_dma_sglist_node *sgn;
+ int l, sys_cf;
+
+ lcfg = &dma_chan[lch].list_config;
+ sgn = lcfg->sghead;
+
+ /* Maintain the pause state in descriptor */
+ omap_set_dma_sglist_pausebit(lcfg, lcfg->pausenode, 0);
+ omap_set_dma_sglist_pausebit(lcfg, pauseafter, 1);
+
+ /**
+ * Barrier needed as writes to the
+ * descriptor memory needs to be flushed
+ * before it's used by DMA controller
+ */
+ wmb();
+
+ /* Errata i557 - pausebit should be cleared in no standby mode */
+ sys_cf = dma_read(OCP_SYSCONFIG);
+ l = sys_cf;
+ /* Middle mode reg set no Standby */
+ l &= ~(BIT(12) | BIT(13));
+ dma_write(l, OCP_SYSCONFIG);
+
+ /* Clear pause bit in CDP */
+ l = dma_read(CDP(lch));
+ l &= ~(DMA_LIST_CDP_PAUSEMODE);
+ dma_write(l, CDP(lch));
+
+ omap_start_dma(lch);
+
+ /* Errata i557 - put in the old value */
+ dma_write(sys_cf, OCP_SYSCONFIG);
+ return 0;
+}
+EXPORT_SYMBOL(omap_resume_dma_sglist_transfers);
+
+void omap_release_dma_sglist(int lch)
+{
+ omap_clear_dma_sglist_mode(lch);
+ omap_free_dma(lch);
+
+ return;
+}
+EXPORT_SYMBOL(omap_release_dma_sglist);
+
+int omap_get_completed_sglist_nodes(int lch)
+{
+ int list_count;
+
+ list_count = dma_read(CCDN(lch));
+ return list_count & 0xffff; /* only 16 LSB bits are valid */
+}
+EXPORT_SYMBOL(omap_get_completed_sglist_nodes);
+
+int omap_dma_sglist_is_paused(int lch)
+{
+ int list_state;
+ list_state = dma_read(CDP(lch));
+ return (list_state & DMA_LIST_CDP_PAUSEMODE) ? 1 : 0;
+}
+EXPORT_SYMBOL(omap_dma_sglist_is_paused);
+
+void omap_dma_set_sglist_fastmode(int lch, int fastmode)
+{
+ int l = dma_read(CDP(lch));
+
+ if (fastmode)
+ l |= DMA_LIST_CDP_FASTMODE;
+ else
+ l &= ~(DMA_LIST_CDP_FASTMODE);
+ dma_write(l, CDP(lch));
+}
+EXPORT_SYMBOL(omap_dma_set_sglist_fastmode);
+
/*----------------------------------------------------------------------------*/

#ifdef CONFIG_ARCH_OMAP1
@@ -2066,6 +2352,7 @@ static int __init omap_init_dma(void)
r = -ENOMEM;
goto out_free;
}
+ dma_caps0_status = dma_read(CAPS_0);
}

if (cpu_is_omap15xx()) {

(quoted diff) +0 -0

b/arch/arm/plat-omap/include/plat/dma.h index 02232ca..7490df9 100644

arch/arm/plat-omap/include/plat/dma.h +170 -0

196 lines not on this page · raw message

Signature · 1 line
1.6.3.3