[RFC 4/7][PATCH] AMBA DMA: Add a driver module for the DMA controller.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The ARM PL080 PrimeCell AMBA DMA controller is used as an example.

In addition to the driver files, an access functions is added 
to kernel/module.c. This allows the driver to increment/decrement 
its usage count since the DMA controller may be used by multiple 
AMBA peripherals.

Signed-off-by: Peter M Pearse <[email protected]> 

---

diff -purN arm_amba_dma/drivers/amba/Kconfig
arm_amba_pl080/drivers/amba/Kconfig
--- arm_amba_dma/drivers/amba/Kconfig	2006-10-17 17:10:15.000000000 +0100
+++ arm_amba_pl080/drivers/amba/Kconfig	2006-10-17 17:14:20.000000000 +0100
@@ -28,8 +28,20 @@ config HAS_ARM_AMBA_PL080
 	 This board has an AMBA PrimeCell PL080 DMA Controller.
 	 There is no driver implemented in this kernel.
 
+config ARM_AMBA_PL080_BUILTIN
+	bool "AMBA PrimeCell DMAC PL080 driver built in" if
(ARCH_VERSATILE_PB || MACH_VERSATILE_AB) && ARM_AMBA_DMA
+	---help---
+	 This board has an AMBA PrimeCell PL080 DMA Controller.
+	 Select whether it's driver should be built into the kernel or as a
module.
+	 Say yes to build it in.
+
 endmenu
 
+config ARM_AMBA_PL080_SUPPORT
+	def_tristate y  if (ARCH_VERSATILE_PB || MACH_VERSATILE_AB) &&
ARM_AMBA_DMA && ARM_AMBA_PL080_BUILTIN
+	def_tristate m  if (ARCH_VERSATILE_PB || MACH_VERSATILE_AB) &&
ARM_AMBA_DMA && !ARM_AMBA_PL080_BUILTIN
+	def_tristate n  
+
 config ARM_AMBA_DMA
 	bool "AMBA DMA support" if(ARCH_VERSATILE_PB || MACH_VERSATILE_AB)
 	depends on ARM_AMBA
diff -purN arm_amba_dma/drivers/amba/Makefile
arm_amba_pl080/drivers/amba/Makefile
--- arm_amba_dma/drivers/amba/Makefile	2006-10-17 17:10:15.000000000 +0100
+++ arm_amba_pl080/drivers/amba/Makefile	2006-10-17
17:14:20.000000000 +0100
@@ -1,4 +1,5 @@
-obj-y				+= bus.o
-obj-$(CONFIG_ARM_AMBA_DMA)	+= dma.o
+obj-y					+= bus.o
+obj-$(CONFIG_ARM_AMBA_DMA)		+= dma.o
+obj-$(CONFIG_ARM_AMBA_PL080_SUPPORT)	+= pl080.o
 
 
diff -purN arm_amba_dma/drivers/amba/pl080.c
arm_amba_pl080/drivers/amba/pl080.c
--- arm_amba_dma/drivers/amba/pl080.c	1970-01-01 01:00:00.000000000 +0100
+++ arm_amba_pl080/drivers/amba/pl080.c	2006-10-17 17:14:20.000000000 +0100
@@ -0,0 +1,567 @@
+/*
+ * drivers/amba/pl080.c - ARM PrimeCell DMA Controller PL080 driver
+ *
+ * Copyright (C) 2006 ARM Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Documentation: ARM DDI 0196F
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <linux/amba/dma.h>
+#include <linux/amba/pl080.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#define DRIVER_NAME	"dmac-pl080"
+#define MODULE_NAME	"pl080"
+char pl080_type[0x10] = "PL080 DMAC";
+
+/*
+ * Power Management.
+ * PM support is not complete. Turn it off.
+ */
+#undef CONFIG_PM
+
+#ifdef CONFIG_PM
+#else
+# define pl080_do_suspend	NULL
+# define pl080_do_resume 	NULL
+# define pl080_suspend		NULL
+# define pl080_resume		NULL
+#endif
+
+
+static int	pl080_request(dmach_t cnum, dma_t * cdata);
+static void	pl080_free(dmach_t cnum, dma_t * cdata);
+static void 	pl080_enable(dmach_t cnum, dma_t * cdata);
+static void 	pl080_disable(dmach_t cnum, dma_t * cdata);
+static int	pl080_residue(dmach_t cnum, dma_t * cdata);
+static int	pl080_setspeed(dmach_t cnum, dma_t * cdata, int newspeed);
+
+static struct pl080_tag {
+	/* pl080 register base */
+	void __iomem *	base;
+	/*
+	 * - dma_pool for the LLIs
+	 * (can't use dma_alloc_coherent/free because of the requirement for
+	 * free() to have interrupts enabled when it is called)
+	 */
+	struct dma_pool * pool;
+	/* LLI details for each DMA channel */
+	chanlli * chanllis;
+} pl080;
+
+/* Predeclare the driver */
+static struct amba_driver pl080_driver;
+
+/* All int functions below return 0 for success unless detailed */
+
+static int pl080_request(dmach_t cnum, dma_t * cdata){
+	int retval = -EINVAL;
+
+	/* Increase the usage */
+	if(try_module_get(pl080_driver.drv.owner)){
+		retval = 0;
+	}
+
+	return retval;
+}
+
+static void pl080_free(dmach_t chan_num, dma_t * cdata){
+	unsigned int i, active_count = 0;
+
+	/*
+	 * Free any DMA memory in use for the LLIs
+	 */
+	if((pl080.chanllis[chan_num].va_list) &&
(pl080.chanllis[chan_num].bus_list)){
+		dma_pool_free(pl080.pool, pl080.chanllis[chan_num].va_list,
pl080.chanllis[chan_num].bus_list);
+		pl080.chanllis[chan_num].num_entries = 0;
+		pl080.chanllis[chan_num].va_list = NULL;
+		pl080.chanllis[chan_num].bus_list = (dma_addr_t)NULL;
+	}
+
+	/*
+	 * If no DMA channels active then deconfigure the DMAC entirely
+	 */
+	for(i = 0; i < MAX_DMA_CHANNELS; i++)
+		active_count += dma_channel_active((dmach_t)i);
+
+	if(!active_count)
+	{
+		unsigned int r;
+
+		r = readl(pl080.base + PL080_OS_CFG);
+		r &= PL080_MASK_CFG;
+		r &= ~PL080_MASK_EN;
+		writel(r, pl080.base + PL080_OS_CFG);
+	}
+	/* Decrease the usage */
+	module_put(pl080_driver.drv.owner);
+}
+
+/*
+ * Enable the DMA channel & terminal count interrupts on it
+ * TODO:: Currently hard coded for memory to peripheral transfer (DMA in
control)
+ *				- flow control setting should be stored in
cdata
+ */
+inline void pl080_enable(dmach_t cnum, dma_t * cdata){
+	void __iomem * cbase = pl080.base + PL080_OS_CHAN_BASE + (cnum *
PL080_OS_CHAN);
+	volatile unsigned int r = 0;
+
+	/*
+	 * Do not access config register until channel shows as disabled
+	 */
+	while((readl(pl080.base + PL080_OS_ENCHNS) & (1<< cnum)) &
PL080_MASK_ENCHNS){
+		;
+	}
+	mb();
+	r = readl(cbase + PL080_OS_CCFG );
+	mb();
+	writel((r | PL080_MASK_CEN | PL080_MASK_INTTC | PL080_MASK_INTERR |
PL080_FCMASK_M2P_DMA) &~(PL080_MASK_HALT), cbase + PL080_OS_CCFG );
+}
+
+/*
+ * Disable without losing data in the channel's FIFO:
+ * - Set the Halt bit so that subsequent DMA requests are ignored.
+ * - Poll the Active bit until it reaches 0, indicating that there is no
data
+ * 	remaining in the channel's FIFO.
+ * - Clear the Channel Enable bit.
+ *
+ * Currently not implemented correctly in the hardware
+ *
+ */
+/*
+ * TODO:: Currently hard coded for memory to peripheral transfer (DMA in
control)
+ *				- flow control setting should be stored in
cdata
+ */
+inline void pl080_disable(dmach_t cnum, dma_t * cdata){
+	void __iomem * cbase = pl080.base + PL080_OS_CHAN_BASE + (cnum *
PL080_OS_CHAN);
+	volatile unsigned int r = readl(cbase + PL080_OS_CCFG );
+
+#ifdef ERRATUM_FIXED
+	mb();
+	writel(r | PL080_MASK_HALT, cbase + PL080_OS_CCFG );
+	mb();
+	while(readl(cbase + PL080_OS_CCFG ) & PL080_MASK_ACTIVE){
+		;
+	}
+#else
+	// printk("pl080_disable() - check if we can use the active
flag\n");
+	// printk(" - this code doesn't\n");
+#endif
+	r = readl(cbase + PL080_OS_CCFG );
+	mb();
+	writel(r & ~(PL080_MASK_CEN) & ~(PL080_MASK_INTTC) &
~(PL080_MASK_INTERR & ~(PL080_FCMASK_M2P_DMA)), cbase + PL080_OS_CCFG );
+	mb();
+	while(readl(cbase + PL080_OS_CCFG ) & PL080_MASK_CEN){
+		;
+	}
+}
+
+/* 
+ * Disable the channel, read the control register, re-enable the channel
+ * May not be an accurate value - see TRM
+ * ASSUME returns bytes 
+ */
+static int pl080_residue(dmach_t cnum, dma_t * cdata){
+	void __iomem * cbase = pl080.base + PL080_OS_CHAN_BASE + (cnum *
PL080_OS_CHAN) + PL080_OS_CCTL;
+	volatile unsigned int r = 0;
+
+	pl080_disable(cnum, cdata);
+	mb();
+
+	/* The number of source width transfers (AACI == 32 bits) completed
*/
+	r = readl(cbase + PL080_OS_CCTL ) & PL080_MASK_TSFR_SIZE;
+	mb();
+
+	pl080_enable(cnum, cdata);
+
+	return r * 4;
+}
+
+/* Not implemented - should return the new speed */
+static int pl080_setspeed(dmach_t cnum, dma_t * cdata, int newspeed){
+	return 0;
+}
+
+#ifdef PL080_IRQ_REQUIRED
+/* 
+ * Each AMBA device requesting DMA chains its interrupt handler to the DMA
interrupt,
+ * rather than this handler attaching this interrupt....
+ */
+static irqreturn_t pl080_irq(int irq, void *devid, struct pt_regs *regs)
+{
+	u32 mask = 0;
+
+	return mask ? IRQ_HANDLED : IRQ_NONE;
+}
+#endif
+
+/* 
+ * The operations required for the pl080
+ * to control DMA transfers, in addition
+ * to the standard ISA DMA API
+ */
+pl080_extra_ops pl080_ops = {
+	.reset_cycle		= pl080_reset_cycle ,
+	.configure_chan		= pl080_configure_chan ,
+	.make_llis		= pl080_make_llis	 , 
+	.transfer_configure	= pl080_transfer_configure,
+};
+
+/*
+ * Complete the DMA channel initialization
+ * started by the AMBA DMA code
+ * - Set the dma ops for the board to call
+ */
+static int __devinit pl080_probe(struct amba_device *dev, void *id)
+{
+	int ret,i;
+
+	ret = amba_request_regions(dev, NULL);
+	if (ret)
+		return ret;
+
+	pl080.base = ioremap(dev->res.start, SZ_4K);
+	if (!pl080.base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Make one struct for each DMA channel
+	 * to hold details of its LLIs
+	 */
+	pl080.chanllis = kmalloc(sizeof(chanlli) * MAX_DMA_CHANNELS,
GFP_KERNEL);
+	if (!pl080.chanllis) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for(i = 0; i < MAX_DMA_CHANNELS; i++){
+		pl080.chanllis[i].num_entries = 0;
+		pl080.chanllis[i].va_list = NULL;
+		pl080.chanllis[i].bus_list = (dma_addr_t)NULL;
+	}
+
+	if(!(pl080.pool = dma_pool_create(pl080_driver.drv.name, (struct
device *)dev,
+			PL080_MAX_LLIS_SIZE, PL080_ALIGN, PL080_ALLOC))){
+
+		kfree(pl080.chanllis);
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* TODO:: Get the parameters from amb_device */
+	pl080_init_dma(dev->chans, dev->dmac_ops);
+	pl080_driver.drv.owner = try_find_module(MODULE_NAME);
+	
+	return ret;
+
+ out:
+	amba_release_regions(dev);
+	return ret;
+}
+
+static struct amba_id pl080_ids[] = {
+	{
+		.id	= 0x00041080,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+static int __devexit pl080_remove(struct amba_device *dev);
+
+static struct amba_driver pl080_driver = {
+	.drv		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= pl080_probe,
+	.remove		= __devexit_p(pl080_remove),
+	.suspend	= pl080_suspend,
+	.resume		= pl080_resume,
+	.id_table	= pl080_ids,
+};
+
+int pl080_init_dma(dma_t * dma_chan, struct dma_ops * ops){
+	int i;
+
+	for(i = 0; i < MAX_DMA_CHANNELS; i++){
+		dma_chan[i].dma_base = (unsigned int)pl080.base;
+	}
+	ops->request 	= pl080_request;
+	ops->free	= pl080_free;
+	ops->enable	= pl080_enable ;
+	ops->disable 	= pl080_disable;
+	ops->residue 	= pl080_residue;
+	ops->setspeed 	= pl080_setspeed;
+	ops->type 	= pl080_type;
+	
+	/* PL080 specific */
+	ops->extra_ops = &pl080_ops;
+
+	pl080_driver.dmac_ops = ops;
+
+	amba_init_dma(dma_chan);
+
+	return 0;
+}
+
+/*
+ * Note that the module usage count ensures that exit code
+ * is not called with a transfer active,,,
+ */
+static void pl080_close_dma(void){
+	unsigned long flags = claim_dma_lock();
+	pl080_driver.dmac_ops->request 	= NULL;
+	release_dma_lock(flags);
+}
+
+static int __devexit pl080_remove(struct amba_device *dev)
+{
+	int retval = -EINVAL;
+	pl080_close_dma();
+	if(pl080.pool){
+		dma_pool_destroy(pl080.pool);
+		pl080.pool = NULL;
+	}
+	amba_release_regions(dev);
+	return retval;
+}
+
+/*
+ * Module initialization
+ */
+static int __init pl080_init(void)
+{
+	pl080.base = NULL;
+	pl080.chanllis = NULL; /* LLI details for each DMA channel */
+	pl080.pool = NULL;
+	
+	return amba_driver_register(&pl080_driver);
+}
+/*
+ * Module destruction
+ */
+static void __exit pl080_exit(void)
+{
+	amba_driver_unregister(&pl080_driver);
+}
+
+/*
+ * Set up a circular linked list of period sized packets
+ * We loop until stopped by another entity
+ *
+ * TODO Abstract this function for use by any device.
+ * It has only been tested with VersatilePB/AACI
+ *
+ * - CAUTION ASSUMES FIXED dest, cword ?? other??
+ *
+ * All addresses stored in the LLI must be bus addresses
+ * Set the lower bit of the bus address to ensure the correct bus is used
+ * dma_chan[chan_num] holds the DMA buffer info
+ *
+ * Return bus address of first LLI
+ */
+dma_addr_t pl080_make_llis(dmach_t chan_num, unsigned int address, unsigned
int length, unsigned int packet_size, pl080_lli * setting){
+
+	int i;
+	unsigned int num_llis = 0;
+	/*
+	 * Whether we increment the lli address to indicate the bus
+	 * for this transfer
+	 */
+	unsigned int bus_increment = setting->next;
+	pl080_lli * llis_bus = NULL;
+	pl080_lli * llis_va = NULL;
+
+	if(NULL != pl080.chanllis[chan_num].va_list){
+		/*
+		 * Repeated call - destroy previous LLIs
+		 */
+		dma_pool_free(pl080.pool, pl080.chanllis[chan_num].va_list,
pl080.chanllis[chan_num].bus_list);
+		pl080.chanllis[chan_num].num_entries = 0;
+		pl080.chanllis[chan_num].va_list = NULL;
+		pl080.chanllis[chan_num].bus_list = (dma_addr_t)NULL;
+	}
+	/*
+ 	 * dma_chan[chan_num] holds the DMA buffer info
+	 */
+	setting->start = address;
+	num_llis = length / packet_size;
+
+	/* Get memory for the LLIs */
+	if(PL080_MAX_LLIS_SIZE < num_llis * sizeof(pl080_lli)){
+		printk(KERN_ERR "pl080.c::make_lli_aaci() - 0x%08x bytes
needed for the LLIs, consider rebuilding with PL080_MAX_LLIS_SIZE (0x%08x)
increased\n", 
+		num_llis * sizeof(pl080_lli), PL080_MAX_LLIS_SIZE);
+	} else {
+		pl080.chanllis[chan_num].va_list =
dma_pool_alloc(pl080.pool, GFP_KERNEL, &pl080.chanllis[chan_num].bus_list);
+	}
+	if(NULL == pl080.chanllis[chan_num].va_list){
+		printk(KERN_ERR "pl080.c::make_lli_aaci() - Failed to get
DMA memory for the LLIs\n");
+		return (dma_addr_t)NULL;
+	}
+
+	pl080.chanllis[chan_num].num_entries = num_llis;
+
+	llis_va	= (pl080_lli *) pl080.chanllis[chan_num].va_list;
+	llis_bus = (pl080_lli *) pl080.chanllis[chan_num].bus_list;
+
+	for(i = 0; i < num_llis - 1; i++){
+		llis_va[i].start = (dma_addr_t)((unsigned int)setting->start
+ (i * packet_size));
+		llis_va[i].dest	 = setting->dest;
+		llis_va[i].cword = setting->cword;
+
+		/*
+		 * Adjust to access the memory on the correct DMA bus
+		 */
+		llis_va[i].next = (dma_addr_t)((unsigned int) &llis_bus[i +
1] + bus_increment);
+	}
+	llis_va[i].start = (dma_addr_t)((unsigned int)setting->start + (i *
packet_size));
+	llis_va[i].dest	= setting->dest;
+	llis_va[i].cword = setting->cword;
+	llis_va[i].next = (dma_addr_t)((unsigned int) &llis_bus[0] +
bus_increment);
+
+	/*
+	 * Initial register setting
+	 */
+	setting->next = llis_va[0].next;
+
+	return pl080.chanllis[chan_num].bus_list;
+}
+
+/*
+ *	Generalized interrupt handling
+ *	i.e. not the terminal count part which will be device specific
+ */
+/* Handle only interrupts on the correct channel */
+static int pl080_ignore_this_irq(dmach_t dma_chan){
+	unsigned int r = readl(pl080.base + PL080_OS_ISR);
+	return !(r & 1 << dma_chan);
+}
+
+/*
+ * TODO:: Report the errors, rather than just clearing them
+ */
+static unsigned int errCtr = 0;
+static void pl080_pre_irq(dmach_t dma_chan){
+
+	unsigned int isr_err = readl(pl080.base + PL080_OS_ISR_ERR);
+	if((1 << dma_chan) & isr_err){
+		errCtr++;
+	}
+}
+/* Clear any interrupts on the correct channel */
+static void pl080_post_irq(dmach_t dma_chan){
+
+	/* Finally clear the interrupt of both kinds */
+	writel((1 << dma_chan), pl080.base + PL080_OS_ICLR_TC);
+	writel((1 << dma_chan), pl080.base + PL080_OS_ICLR_ERR);
+}
+
+/*
+ * Configure the DMAC
+ *
+ * LittleEndian, LittleEndian, disabled
+ */
+void pl080_configure(void){
+	unsigned int reg;
+
+	reg = readl(pl080.base + PL080_OS_CFG);
+	reg &= PL080_MASK_CFG;
+	reg |= PL080_MASK_EN;
+	writel(reg, pl080.base + PL080_OS_CFG);
+}
+
+/*
+ * Configure the DMA channel
+ *
+ * Set up interrupt calls for devices to use
+ * TODO:: Find a neater way.....
+ */
+void pl080_configure_chan(dmach_t chan_num, struct amba_dma_data * data){
+	/*
+	 * Set address of DMA channel registers
+	 */
+	unsigned int chan_base = (unsigned int)pl080.base +
PL080_OS_CHAN_BASE;
+	chan_base += chan_num * PL080_OS_CHAN;
+
+	/*
+	 * The interrupt handlers & destination are known
+	 * Interrupt data
+	 * - DMAC interrupt status register address
+	 * - mask to use
+	 * - DMAC interrupt clear address
+	 * - mask(s) to use
+	 */
+	 data->irq_ignore = pl080_ignore_this_irq;
+	 data->irq_pre = pl080_pre_irq;
+	 data->irq_post = pl080_post_irq;
+
+	/*
+	 * Always configure the pl080 itself,
+	 * in case this is the first channel configuration
+	 */
+	pl080_configure();
+
+}
+
+/*
+ * Restart the LLIs by setting the channel LLI
+ * register to point to the first entry
+ *
+ * Useful where e.g. the data supplier
+ * restarts due to perceived underrun
+ */
+void pl080_reset_cycle(dmach_t cnum){
+	void __iomem * cbase = (void __iomem * )((unsigned int)pl080.base +
(unsigned int)PL080_OS_CHAN_BASE + (unsigned int)(cnum * PL080_OS_CHAN));
+	writel((unsigned int)(((unsigned
int)(pl080.chanllis[cnum].bus_list)) + 1), cbase + PL080_OS_CLLI);
+}
+
+/*
+ * Set up the DMA channel registers for a transfer
+ * whose LLIs are ready
+ */
+void pl080_transfer_configure(dmach_t chan_num, pl080_lli *setting,
unsigned int ccfg){
+
+	unsigned int chan_base = (unsigned int)pl080.base +
PL080_OS_CHAN_BASE;
+	chan_base += chan_num * PL080_OS_CHAN;
+
+	writel(setting->start, chan_base + PL080_OS_CSRC);
+	writel(setting->dest , chan_base + PL080_OS_CDST);
+	writel(setting->next , chan_base + PL080_OS_CLLI);
+	writel(setting->cword, chan_base + PL080_OS_CCTL);
+	writel(ccfg , chan_base + PL080_OS_CCFG);
+}
+
+module_init(pl080_init);
+module_exit(pl080_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM PrimeCell PL080 DMA Controller driver");
+
+
diff -purN arm_amba_dma/include/linux/amba/pl080.h
arm_amba_pl080/include/linux/amba/pl080.h
--- arm_amba_dma/include/linux/amba/pl080.h	1970-01-01
01:00:00.000000000 +0100
+++ arm_amba_pl080/include/linux/amba/pl080.h	2006-10-17
17:14:20.000000000 +0100
@@ -0,0 +1,142 @@
+/*
+ *	linux/amba/pl080.h - ARM PrimeCell AACI DMA Controller driver
+ *
+ *	Copyright (C) 2005 ARM Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Please credit ARM.com
+ * Documentation: ARM DDI 0196D
+ */
+
+#ifndef AMBA_PL080_H
+#define AMBA_PL080_H 
+
+
+/*
+ * Memory to peripheral transfer may be visualized as
+ * 	Source burst data from memory to DMAC
+ *	Until no data left
+ *		On burst request from peripheral 
+ *			Destination burst from DMAC to peripheral
+ *			Clear burst request
+ *	Raise terminal count interrupt
+ *
+ * For peripherals with a FIFO:
+ * Source      burst size == half the depth of the peripheral FIFO
+ * Destination burst size == width of the peripheral FIFO
+ *
+ *
+ */
+
+/*
+ * Header for pl080 drivers
+ */
+
+
+/*
+ *  dma_pool defines
+ */
+
+#define PL080_MAX_LLIS_SIZE	0x2000	/* Size (bytes) of each buffer
allocated */
+#define PL080_ALIGN		8	/* Alignment each buffer allocated
*/
+#define PL080_ALLOC		0	/* Boundary not to cross 0 == N/A */
+ 
+#define	PL080_OS_ISR		0x00
+#define	PL080_OS_ISR_TC		0x04
+#define	PL080_OS_ICLR_TC	0x08
+#define	PL080_OS_ISR_ERR	0x0C
+#define	PL080_OS_ICLR_ERR	0x10
+#define	PL080_OS_ENCHNS		0x1C
+#define	PL080_MASK_ENCHNS	0x000000FF
+#define	PL080_OS_CFG		0x30
+#define	PL080_MASK_CFG		0xFFFFFFF1
+#define	PL080_MASK_EN		0x00000001
+#define	PL080_OS_CHAN_BASE	0x100
+#define	PL080_OS_CHAN		0x20
+#define	PL080_OS_CSRC		0x00
+#define	PL080_OS_CDST		0x04
+#define	PL080_OS_CLLI		0x08
+#define	PL080_MASK_CLLI		0x00000002
+#define	PL080_OS_CCTL		0x0C
+/*
+ *	The DMA channel control register entries (see pl080 TRM) have the
following units
+ *	Width 			- bits
+ *	Burst size		- number of transfers
+ *	Transfer (packet) size	- number of (destination width) transfers
+ *	See e.g include/asm-arm/arch-versatile/hardware.h
+ */
+#define	PL080_MASK_TSFR_SIZE	0x00000FFF
+#define	PL080_OS_CCFG		0x10
+#define	PL080_MASK_CEN		0x00000001
+#define	PL080_MASK_INTTC	0x00008000
+#define	PL080_MASK_INTERR	0x00004000
+#define	PL080_MASK_HALT		0x00040000
+#define	PL080_MASK_ACTIVE	0x00020000
+#define	PL080_MASK_CCFG		0x00000000
+/*
+ * Flow control bit masks
+ */
+/*
+ * Bit values for  Transfer type Controller
+ * [13 - 11]
+ */
+#define PL080_FCMASK_M2M_DMA	0x00000000	/* Memory-to-memory DMA */
+#define PL080_FCMASK_M2P_DMA	0x00000800	/* Memory-to-peripheral DMA
*/
+#define PL080_FCMASK_P2M_DMA	0x00001000	/* Peripheral-to-memory DMA
*/
+#define PL080_FCMASK_P2P_DMA	0x00001800	/* Source
peripheral-to-destination peripheral DMA */
+#define PL080_FCMASK_P2P_DST	0x00002000	/* Source
peripheral-to-destination peripheral Destination peripheral */
+#define PL080_FCMASK_M2P_PER	0x00002800	/* Memory-to-peripheral
Peripheral */
+#define PL080_FCMASK_P2P_PER	0x00003000	/* Peripheral-to-memory
Peripheral */
+#define PL080_FCMASK_P2P_SRC	0x00003800	/* Source
peripheral-to-destination peripheral Source peripheral */
+
+typedef struct _chan_lli {
+	dma_addr_t bus_list;	/* the linked lli list bus     address for
use in the LLI */
+	void	*  va_list;	/* the linked lli list virtual address for
use by the driver */
+	unsigned int num_entries; /* number of entries - might not be
circular */
+	unsigned int index_next; /* Index of next lli to load */
+} chanlli;
+
+/*
+ * An LLI struct - see pl080 TRM
+ */
+typedef struct _lli{
+	dma_addr_t start;
+	dma_addr_t dest;
+	dma_addr_t next;
+	unsigned int cword;
+} pl080_lli;
+
+/*
+ *  Channel register settings
+ */
+typedef struct _periph_id_dma_channel_settings {
+	unsigned int periphid;
+	unsigned int ctl;
+	unsigned int cfg;
+} setting;
+
+/*
+ *	One structure for each DMA channel
+ */
+extern chanlli * chanllis;
+
+int		pl080_init_dma		(dma_t * dma_chan, struct dma_ops *
ops);
+void		pl080_reset_cycle	(dmach_t chan_num);
+dma_addr_t	pl080_make_llis		(dmach_t chan_num, unsigned int
address, unsigned int length, unsigned int packet_size, pl080_lli *
setting);
+void		pl080_configure_chan	(dmach_t chan_num, struct
amba_dma_data * data);
+void		pl080_transfer_configure(dmach_t chan_num, pl080_lli *
setting, unsigned int ccfg);
+
+typedef struct _pl080_extra_ops {
+	void 	(*reset_cycle)(dmach_t cnum);
+	void 	(*configure_chan)(dmach_t chan_num, struct amba_dma_data *
data);
+	dma_addr_t (*make_llis)(dmach_t chan_num, unsigned int address,
unsigned int length, unsigned int packet_size, pl080_lli * setting);
+	void 	(*transfer_configure)(dmach_t chan_num, pl080_lli *setting,
unsigned int ccfg);
+} pl080_extra_ops;
+
+
+#endif	/* AMBA_PL080_H */
+
+
diff -purN arm_amba_dma/include/linux/module.h
arm_amba_pl080/include/linux/module.h
--- arm_amba_dma/include/linux/module.h	2006-10-17 13:29:42.000000000 +0100
+++ arm_amba_pl080/include/linux/module.h	2006-10-17
17:14:20.000000000 +0100
@@ -378,6 +378,11 @@ extern void __module_put_and_exit(struct
 	__attribute__((noreturn));
 #define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code);
 
+/*
+ *  Allow drivers access to their owners by name
+ */
+extern struct module *try_find_module(const char *name);
+
 #ifdef CONFIG_MODULE_UNLOAD
 unsigned int module_refcount(struct module *mod);
 void __symbol_put(const char *symbol);
diff -purN arm_amba_dma/kernel/module.c arm_amba_pl080/kernel/module.c
--- arm_amba_dma/kernel/module.c	2006-10-17 13:29:46.000000000 +0100
+++ arm_amba_pl080/kernel/module.c	2006-10-17 17:14:20.000000000 +0100
@@ -297,6 +297,20 @@ static struct module *find_module(const 
 	}
 	return NULL;
 }
+/* 
+ * Export wrapped find_module to allow drivers to find their modules
+ * Useful for e.g. controlling the usage count
+ */ 
+struct module *try_find_module(const char *name)
+{
+	struct module *mod;
+	mutex_lock(&module_mutex);
+	mod = find_module(name);
+	mutex_unlock(&module_mutex);
+	return mod;
+}
+EXPORT_SYMBOL(try_find_module);
+
 
 #ifdef CONFIG_SMP
 /* Number of blocks used and allocated. */



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux