lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 30 Oct 2006 12:02:44 -0000
From:	"Peter Pearse" <peter.pearse@....com>
To:	<linux-kernel@...r.kernel.org>
Subject: [RFC 4/7][PATCH] AMBA DMA: Add a driver module for the DMA controller. 

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 <peter.pearse@....com> 

---

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 majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ