Commit 5d20c2cd authored by Peter Pearse's avatar Peter Pearse Committed by Catalin Marinas

Add DMA support for the Versatile AB and PB926 platforms

This patch also enables the DMA use for the AACI driver
Signed-off-by: default avatarPeter Pearse <peter.pearse@arm.com>
parent 6a457c22
......@@ -235,6 +235,8 @@ config ARCH_OMAP
config ARCH_VERSATILE
bool "Versatile"
select ARM_AMBA
select ARM_AMBA_PL080
select ARM_AMBA_PL041
select ARM_VIC
select ICST307
help
......@@ -337,8 +339,7 @@ config FORCE_MAX_ZONEORDER
menu "Bus support"
config ARM_AMBA
bool
source "drivers/amba/Kconfig"
config ISA
bool
......
......@@ -15,6 +15,8 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <asm/dma.h>
......@@ -25,23 +27,6 @@ EXPORT_SYMBOL(dma_spin_lock);
static dma_t dma_chan[MAX_DMA_CHANNELS];
/*
* Get dma list for /proc/dma
*/
int get_dma_list(char *buf)
{
dma_t *dma;
char *p = buf;
int i;
for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++)
if (dma->lock)
p += sprintf(p, "%2d: %14s %s\n", i,
dma->d_ops->type, dma->device_id);
return p - buf;
}
/*
* Request DMA channel
*
......@@ -140,8 +125,15 @@ void __set_dma_addr (dmach_t channel, void *addr)
printk(KERN_ERR "dma%d: altering DMA address while "
"DMA active\n", channel);
#ifdef CONFIG_ARM_AMBA_DMA
dma->sg = &dma->buf;
dma->sgcount = 1;
dma->buf.dma_address = virt_to_bus(addr);
#else
dma->sg = NULL;
dma->addr = addr;
#endif
dma->invalid = 1;
}
EXPORT_SYMBOL(__set_dma_addr);
......@@ -264,4 +256,45 @@ static int __init init_dma(void)
return 0;
}
#ifdef CONFIG_PROC_FS
static int proc_dma_show(struct seq_file *m, void *v)
{
int i;
for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) {
if (dma_chan[i].lock) {
seq_printf(m, "%2d: %14s %s\n", i,
dma_chan[i].d_ops->type, dma_chan[i].device_id);
}
}
return 0;
}
static int proc_dma_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_dma_show, NULL);
}
static struct file_operations proc_dma_operations = {
.open = proc_dma_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init proc_dma_init(void)
{
if(MAX_DMA_CHANNELS > 0){
struct proc_dir_entry *e;
e = create_proc_entry("dma", 0, NULL);
if (e)
e->proc_fops = &proc_dma_operations;
}
return 0;
}
__initcall(proc_dma_init);
#endif
core_initcall(init_dma);
......@@ -6,3 +6,5 @@ obj-y := core.o clock.o
obj-$(CONFIG_ARCH_VERSATILE_PB) += versatile_pb.o
obj-$(CONFIG_MACH_VERSATILE_AB) += versatile_ab.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_ARM_AMBA_DMA) += dma.o
......@@ -47,13 +47,16 @@
#include "core.h"
#include "clock.h"
#ifdef CONFIG_ARM_AMBA_DMA
# include "dma.h"
#endif
/*
* All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
* is the (PA >> 12).
*
* Setup a VA for the Versatile Vectored Interrupt Controller.
*/
#define __io_address(n) __io(IO_ADDRESS(n))
#define VA_VIC_BASE __io_address(VERSATILE_VIC_BASE)
#define VA_SIC_BASE __io_address(VERSATILE_SIC_BASE)
......@@ -707,7 +710,13 @@ AMBA_DEVICE(uart1, "dev:f2", UART1, NULL);
AMBA_DEVICE(uart2, "dev:f3", UART2, NULL);
AMBA_DEVICE(ssp0, "dev:f4", SSP, NULL);
static struct amba_device *amba_devs[] __initdata = {
static struct amba_device *amba_devs[]
#ifdef CONFIG_ARM_AMBA_DMA
// Do not discard - used by AMBA DMA code
// These devices are common to Versatile AB && PB
__initdata
#endif
= {
&dmac_device,
&uart0_device,
&uart1_device,
......
......@@ -30,6 +30,12 @@ extern void __init versatile_map_io(void);
extern struct sys_timer versatile_timer;
extern unsigned int mmc_status(struct device *dev);
/*
* All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
* is the (PA >> 12).
*/
#define __io_address(n) __io(IO_ADDRESS(n))
#define AMBA_DEVICE(name,busid,base,plat) \
static struct amba_device name##_device = { \
.dev = { \
......@@ -44,7 +50,6 @@ static struct amba_device name##_device = { \
}, \
.dma_mask = ~0, \
.irq = base##_IRQ, \
/* .dma = base##_DMA,*/ \
}
#endif
/*
* linux/arch/arm/mach-versatile/dma.c
*
* 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Board specific dma code.
* This board knows
* - it has an AMBA bus with
* AMBA pl080 bus DMA controller
* as device dev:30
*
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl080.h>
#include <asm/arch/hardware.h>
#include <asm/mach-types.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>
#include "core.h"
#include "dma.h"
/* The pl080 ops to be called by the versatile */
static struct dma_ops dmac_ops;
variant_ops vops;
/*
* Entries must be in the same order as the amba_devs array
* - only aaci is implemented at present
* TODO:: Some parts of the values should be constructed
* from the parts which might vary.....
*/
setting settings[] = {
{0x80,0,0}, /* dmac_device */
{0,0}, /* uart0_devic */
{0,0}, /* uart1_devic */
{0,0}, /* uart2_devic */
{0,0}, /* smc_device, */
{0,0}, /* mpmc_device */
{0,0}, /* clcd_device */
{0,0}, /* sctl_device */
{0,0}, /* wdog_device */
{0,0}, /* gpio0_devic */
{0,0}, /* gpio1_devic */
{0,0}, /* rtc_device, */
{0,0}, /* sci0_device */
{0,0}, /* ssp0_device */
{0x41,VDMA_AACI_CCTL, VDMA_AACI_CCFG}, /* aaci_device */
{0,0}, /* mmc0_device */
{0,0}, /* kmi0_device */
{0,0} /* kmi1_device */
};
/*
* Return 0 for success
*/
static int versatile_request(dmach_t chan_num, dma_t * chan_data)
{
int status = -EINVAL;
struct amba_device * client;
/*
* Find the driver data for the named device
*/
client = amba_get_device_with_name((char*)chan_data->device_id);
/* Configure the DMA mechanism e.g. the AMBA DMA controller */
if(!vops.versatile_dma_configure(chan_num, chan_data, client))
status = -EINVAL;
else {
/*
* Check pl080 is happy before doing anything complicated
*/
if(!dmac_ops.request(chan_num, chan_data)) {
if(!vops.versatile_dma_transfer_setup(chan_num, chan_data, client))
status = -EINVAL;
else
status = 0;
}
}
return status;
}
static void versatile_free(dmach_t chan_num, dma_t * chan_data)
{
struct amba_device * client;
/*
* Find the driver data for the named device
*/
client = amba_get_device_with_name((char*)chan_data->device_id);
/*
*Deconfigure the DMA mechanism e.g. the AMBA DMA controller
* and any machine settings e.g. DMA mapping register
*/
switch(amba_part(client)) {
case AMBA_PERIPHID_AACI_97:
switch(chan_num){
case 0:
case 1:
case 2:
{
#ifdef CONFIG_ARCH_VERSATILE_PB
if(machine_is_versatile_pb()){
void __iomem **map_base = __io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_DMAPSR0_OFFSET + (chan_num * 4);
/*
* Clear down the V/PB DMA mapping register connection
*/
writel(
// [31:8] Reserved
(0 << 7) | // 0 = Disable this mapping
// [6:5] Reserved
(0 << 0) // 0b00000 = AACI Tx
, &map_base[chan_num]);
}
#endif
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_free() Invalid DMA channel 0x%02x for AACI \n", chan_num);
break;
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_free() Unexpected device %p, periphid part number 0x%03x - AACI is 0x%03x\n", client, amba_part(client), AMBA_PERIPHID_AACI_97);
break;
}
dmac_ops.free(chan_num, chan_data);
}
void versatile_enable(dmach_t chan_num, dma_t * chan_data)
{
pl080_reset_cycle(chan_num);
dmac_ops.enable(chan_num, chan_data);
}
void versatile_disable(dmach_t chan_num, dma_t * chan_data)
{
dmac_ops.disable(chan_num, chan_data);
}
/* ASSUME returns bytes */
int versatile_residue(dmach_t chan_num, dma_t * chan_data)
{
int res = 0;
return res;
}
int versatile_setspeed(dmach_t chan_num, dma_t * chan_data, int speed)
{
int new_speed = 0;
return new_speed;
}
/*
* Board ops to be called by AMBA functions
*/
static struct dma_ops versatile_dma_ops = {
versatile_request, /* optional */
versatile_free, /* optional */
versatile_enable, /* mandatory */
versatile_disable, /* mandatory */
versatile_residue, /* optional */
versatile_setspeed, /* optional */
"VERSATILE DMA"
};
void __init arch_dma_init(dma_t *dma)
{
#ifdef CONFIG_ARM_AMBA_DMA
/* Adjust the DMA channel configuration values for the peripherals */
if(machine_is_versatile_ab()){
/*
* AACI is peripheral 1 on the VAB, 0 on the VPB
* VDMA_AACI_CCFG is VPB (peripheral 0) value
*/
settings[0x0E].cfg |= VDMA_CCFG_PERIPHAL_NUM_1;
}
amba_init_dma(dma);
pl080_init_dma(dma, &dmac_ops);
amba_set_ops(&versatile_dma_ops);
#endif
}
/*
* linux/include/asm-arm/hardware/amba.h
*
* 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.
*
* Pass dma symbols to the core.c code
*/
extern int versatile_init_dma_channels(void __iomem *base);
/*
* Board variant specific functions
*/
typedef struct _variant_ops{
const int (*versatile_dma_configure )(dmach_t chan_num, dma_t * chan_data, struct amba_device * client);
const int (*versatile_dma_transfer_setup)(dmach_t chan_num, dma_t * chan_data, struct amba_device * client);
} variant_ops;
extern variant_ops vops;
#define VA_SYS_BASE (__io(IO_ADDRESS(VERSATILE_SYS_BASE)))
......@@ -44,3 +44,131 @@ MACHINE_START(VERSATILE_AB, "ARM-Versatile AB")
.timer = &versatile_timer,
.init_machine = versatile_init,
MACHINE_END
#ifdef CONFIG_ARM_AMBA_DMA
#include "dma.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>
#include <linux/amba/pl080.h>
extern setting settings[];
/*
* Configure the board && the dma controller channel for the requesting peripheral
* as far as possible when the actual transfer
* (source address, size, dest address, etc.) is not known
*
* Versatile AB :
*
* DMA Peripheral
*
* 15 UART0 Tx
* 14 UART0 Rx
* 13 UART1 Tx
* 12 UART1 Rx
* 11 UART2 Tx
* 10 UART2 Rx
* 9 SSP Tx
* 8 SSP Rx
* 7 SCI Tx
* 6 SCI Rx
*
* 5-3 Unused
*
* FPGA peripherals using DMA channels 0,1,2
*
* 2 MMCI
* 1 AACI Tx
* 0 AACI Rx
*
* Return 1 for success
*
*/
const int versatileab_dma_configure(dmach_t chan_num, dma_t * chan_data, struct amba_device * client){
int retval = 0;
/* Switch by client AMBA device part number*/
switch(amba_part(client)) {
case AMBA_PERIPHID_AACI_97:
/*
* Only DMA channel 1 can be used for AACI DMA TX
*/
switch(chan_num){
case 1:
pl080_configure_chan(chan_num, &client->dma_data);
retval = 1;
break;
default:
break;
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_configure() Unexpected device %p, periphid part number 0x%03x\n", client, amba_part(client));
}
return retval;
}
const int versatileab_dma_transfer_setup(dmach_t chan_num, dma_t * chan_data, struct amba_device * client){
pl080_lli setting;
unsigned int ccfg = 0;
dma_addr_t cllis = 0;
int retval = 0;
unsigned int periph_index = 0;
switch(amba_part(client)) {
case AMBA_PERIPHID_AACI_97:
{
// TODO:: RX DMA
while(amba_part(client) != settings[periph_index].periphid){
periph_index++;
}
ccfg = settings[periph_index].cfg;
/* Destination is the FIFO read/write register */
setting.dest = VERSATILE_AACI_BASE + (unsigned int)client->dma_data.dmac_data;
setting.cword = settings[periph_index].ctl;
/*
* Construct the LLIs
*/
// TODO:: determine whether the bus address needs the bus distinguishing bit set
// - hard code the 1 for now
setting.next = 1;
cllis = pl080_make_llis(chan_num, chan_data->buf.dma_address, chan_data->count ,client->dma_data.packet_size, &setting);
if(cllis) {
retval = 1;
} else {
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_transfer_setup() No memory for LLIs\n");
}
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_transfer_setup() - Unexpected device %p, periphid part number 0x%03x\n", client, amba_part(client));
return 0;
break;
}
if(retval)
pl080_transfer_configure(chan_num, &setting, ccfg);
return retval;
}
static int __init versatile_ab_init(void)
{
if (machine_is_versatile_ab()) {
vops.versatile_dma_configure = versatileab_dma_configure ;
vops.versatile_dma_transfer_setup = versatileab_dma_transfer_setup;
}
return 0;
}
arch_initcall(versatile_ab_init);
#endif
......@@ -82,6 +82,155 @@ static struct amba_device *amba_devs[] __initdata = {
&mmc1_device,
};
#ifdef CONFIG_ARM_AMBA_DMA
#include "dma.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>
#include <linux/amba/pl080.h>
extern setting settings[];
/*
* Configure the board && the dma controller channel for the requesting peripheral
* as far as possible when the actual transfer
* (source address, size, dest address, etc.) is not known
*
* Versatile PB :
*
* Other possible assignments:
* DMA Peripheral
*
* 15 UART0 Tx
* 14 UART0 Rx
* 13 UART1 Tx
* 12 UART1 Rx
* 11 UART2 Tx
* 10 UART2 Rx
* 9 SSP Tx
* 8 SSP Rx
* 7 SCI Tx
* 6 SCI Rx
*
* 5-3 I/O device in RealView Logic Tile
*
* 2-0 I/O device in RealView Logic Tile or FPGA
*
* FPGA peripherals using DMA channels 0,1,2
*
* AACI Tx
* AACI Rx
* USB A
* USB B
* MCI 0
* MCI 1
* UART3 Tx
* UART3 Rx
* SCI0 int A
* SCI0 int B
*
* Return 1 for success
*
*/
const int versatilepb_dma_configure(dmach_t chan_num, dma_t * chan_data, struct amba_device * client){
int retval = 0;
/*
* Versatile DMA mapping register for assigned DMA channel
*/
void __iomem **map_base = __io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_DMAPSR0_OFFSET + (chan_num * 4);
struct amba_dma_data * data = &client->dma_data;
/* Switch by client AMBA device part number*/
switch(amba_part(client)) {
case AMBA_PERIPHID_AACI_97:
/*
* Only DMA channels 0,1,2 can be used for AACI DMA
*/
switch(chan_num){
case 0:
case 1:
case 2:
/*
* Set V/PB DMA mapping register to connect
* AACI Tx DMAC request signals to DMAC peripheral #0 request lines
*
* ASSUMES Tx
* TODO:: Distinguish Tx/Rx
*/
writel(
// [31:8] Reserved
(1 << 7) | // 1 = Enable this mapping
// [6:5] Reserved
(0 << 0) // 0b00000 = AACI Tx
, &map_base[chan_num]);
pl080_configure_chan(chan_num, data);
retval = 1;
break;
default:
break;
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_configure() Unexpected device %p, periphid part number 0x%03x\n", client, amba_part(client));
}
return retval;
}
const int versatilepb_dma_transfer_setup(dmach_t chan_num, dma_t * chan_data, struct amba_device * client){
pl080_lli setting;
unsigned int ccfg = 0;
dma_addr_t cllis = 0;
int retval = 0;
unsigned int periph_index = 0;
switch(amba_part(client)) {
case AMBA_PERIPHID_AACI_97:
{
while(amba_part(client) != settings[periph_index].periphid){
periph_index++;
}
ccfg = settings[periph_index].cfg;
/* Destination is the FIFO read/write register */
setting.dest = VERSATILE_AACI_BASE + (unsigned int)client->dma_data.dmac_data;
setting.cword = settings[periph_index].ctl;
/*
* Construct the LLIs
*/
// TODO:: determine whether the bus address needs the bus distinguishing bit set
// - hard code the 1 for now
setting.next = 1;
cllis = pl080_make_llis(chan_num, chan_data->buf.dma_address, chan_data->count ,client->dma_data.packet_size, &setting);
if(cllis) {
retval = 1;
} else {
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_transfer_setup() No memory for LLIs\n");
}
}
break;
default:
printk(KERN_ERR "mach-versatile/dma.c::versatile_dma_transfer_setup() - Unexpected device %p, periphid part number 0x%03x\n", client, amba_part(client));
return 0;
break;
}
if(retval)
pl080_transfer_configure(chan_num, &setting, ccfg);
return retval;
}
#endif
static int __init versatile_pb_init(void)
{
int i;
......@@ -91,8 +240,30 @@ static int __init versatile_pb_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
}
#ifdef CONFIG_ARM_AMBA_DMA
{
volatile unsigned int r;
/*
* Initial disposition of the DMA select signals
* - later a contention mechanism must be implemented to allow
* apps/drivers of the 10 FPGA sources to contend for the 3 lines
*/
/* AACI TX is line 0 */
r = readl(VA_SYS_BASE + VERSATILE_SYS_DMAPSR0_OFFSET);
mb();
r &= VSYSMASK_DMAPSR;
r |= VSYS_VAL_DMAPSR_AACI_TX;
r |= VSYS_VAL_DMAPSR_ENABLE;
writel(r, VA_SYS_BASE + VERSATILE_SYS_DMAPSR0_OFFSET);
mb();
vops.versatile_dma_configure = versatilepb_dma_configure ;
vops.versatile_dma_transfer_setup = versatilepb_dma_transfer_setup;
}
#endif
}
return 0;
}
......
config ARM_AMBA
bool "ARM AMBA bus"
---help---
This motherboard has AMBA bus support.
config ARM_AMBA_DMA
bool "AMBA DMA"
depends on ARM_AMBA && (ARCH_VERSATILE_PB || MACH_VERSATILE_AB)
select ISA_DMA_API
default y
---help---
This motherboard has AMBA DMA support.
If you wish DMA capable drivers to use it,
say Y, otherwise N.
config ARM_AMBA_PL080
bool "AMBA PrimeCell DMAC PL080" if (ARCH_VERSATILE_PB || MACH_VERSATILE_AB)
depends on ARM_AMBA_DMA
---help---
This board has an AMBA PrimeCell PL080 DMA Controller.
It will be used if AMBA DMA is selected.
config ARM_AMBA_PL041
bool "AMBA PrimeCell AACI AC'97" if (ARCH_VERSATILE_PB || MACH_VERSATILE_AB)
depends on ARM_AMBA
---help---
This board has an AMBA PrimeCell Advanced Audio Codec Interface.
It can be selected under sound/Advanced Linux Sound Architecture/ALSA ARM devices.
obj-y += bus.o
obj-$(CONFIG_ARM_AMBA_DMA) += dma.o
obj-$(CONFIG_ARM_AMBA_PL080) += pl080.o
......@@ -349,6 +349,57 @@ void amba_release_regions(struct amba_device *dev)
release_mem_region(dev->res.start, SZ_4K);
}
#ifdef CONFIG_ARCH_VERSATILE
/*
* In the request & free we want to call versatile routines with the amba_device *
* We need this so we can pass the driver the interrupt etc DMAC driver routines
* & to identify the amba_device for e.g setting the DMA mapping register
*/
typedef struct _query_data {
struct amba_device * ad;
char * name;
} query_data;
static int match_name(struct device * dev, void * data){
query_data * d = (query_data*)data;
int ret = 0;
if(dev && data){
if(dev->driver){
if(dev->driver->name){
if(0 == strncmp(dev->driver->name, (const char *)d->name, strlen(dev->driver->name))){
get_device(dev);
d->ad = to_amba_device(dev);
ret = 1;
}
} else {
printk(KERN_ERR "amba.c::match_name() - no driver name for device %p, driver %p\n", dev, dev->driver);
}
} /* else no driver attached */
} else {
printk(KERN_ERR "amba.c::match_name() - void pointer(s) dev %p, data %p\n", dev, data);
}
return ret;
}
/*
* Attempt to match the passed name to the driver, if any, of each AMBA device on the bus
*/
struct amba_device * amba_get_device_with_name(char * name){
query_data d;
d.ad = NULL;
d.name = name;
bus_for_each_dev(&amba_bustype, NULL, &d, match_name);
return d.ad;
}
EXPORT_SYMBOL(amba_get_device_with_name);
#endif
EXPORT_SYMBOL(amba_driver_register);
EXPORT_SYMBOL(amba_driver_unregister);
EXPORT_SYMBOL(amba_device_register);
......
/*
* drivers/amba/dma.c
*
* 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.
*
* Wrapper for the AMBA DMA API
* Calls the board functions which may also call the controller, if any.
*
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/sizes.h>
DEFINE_SPINLOCK(amba_dma_spin_lock);
/* DMA operations of the board */
struct dma_ops * board_ops = NULL;
void amba_set_ops(struct dma_ops * ops){
board_ops = ops;
}
/*
* Successful int functions return 0
* - unless otherwise detailed
*/
int amba_request(dmach_t chan_num, dma_t * chan_data)
{
int status = 0;
if((board_ops) && (board_ops->request)){
status = board_ops->request(chan_num, chan_data);
}
return status;
}
void amba_free(dmach_t chan_num, dma_t * chan_data)
{
if((board_ops) && (board_ops->free)){
board_ops->free(chan_num, chan_data);
}
}
void amba_enable(dmach_t chan_num, dma_t * chan_data)
{
if((board_ops) && (board_ops->enable)){
board_ops->enable(chan_num, chan_data);
}
}
void amba_disable(dmach_t chan_num, dma_t * chan_data)
{
if((board_ops) && (board_ops->disable)){
board_ops->disable(chan_num, chan_data);
}
}
/* ASSUME returns number of bytes */
int amba_residue(dmach_t chan_num, dma_t * chan_data)
{
int res = 0;
if((board_ops) && (board_ops->residue)){
res = board_ops->residue(chan_num, chan_data);
}
return res;
}
int amba_setspeed(dmach_t chan_num, dma_t * chan_data, int speed)
{
int new_speed = 0;
if((board_ops) && (board_ops->setspeed)){
new_speed = board_ops->setspeed(chan_num, chan_data, speed);
}
return new_speed;
}
/*
* AMBA ops to be called by kernel DMA functions
*/
static struct dma_ops amba_dma_ops = {
amba_request, /* optional */
amba_free, /* optional */
amba_enable, /* mandatory */
amba_disable, /* mandatory */
amba_residue, /* optional */
amba_setspeed, /* optional */
"AMBA DMA"
};
/*
* Initialize the dma channels, as far as we can
*/
void __init amba_init_dma(dma_t *channels)
{
int i;
for(i = 0; i < MAX_DMA_CHANNELS; i++){
channels[i].sgcount = 0;
channels[i].sg = NULL;
channels[i].active = 0;
channels[i].invalid = 1;
// Disappeared channels[i].using_sg = 0;
channels[i].dma_mode = DMA_NONE;
channels[i].speed = 0;
channels[i].lock = 0;
channels[i].device_id = "UNASSIGNED";
channels[i].dma_base = (unsigned int)NULL;
channels[i].dma_irq = IRQ_DMAINT;
channels[i].state = 0;
channels[i].d_ops = &amba_dma_ops;
}
}
This diff is collapsed.
......@@ -18,3 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __ASM_ARCH_DMA_H
#define __ASM_ARCH_DMA_H
#define MAX_DMA_ADDRESS 0xffffffff
#ifdef CONFIG_ARM_AMBA_DMA
# define MAX_DMA_CHANNELS 8
#else
# define MAX_DMA_CHANNELS 0
#endif
#endif /* _ASM_ARCH_DMA_H */
......@@ -49,4 +49,75 @@
/* macro to get at IO space when running virtually */
#define IO_ADDRESS(x) (((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000)
#ifdef CONFIG_ARM_AMBA_DMA
/* DMA defines for AACI/DMAC transfers */
// Packet is the data transferred during the run of one LLI
// Memory boundary size means the byte size of the data packet transferred by one LLI
// Burst sizes are in bytes
// Transfer size is destination burst sizes in a source burst == run of one LLI
/* Register settings - see PrimeCell DMA Controller (PL080) TRM && AACI PL041 TRM */
#define DMABlockSize 0x40 // 64 transfers @ 32 bit width == 256(0x100) bytes
#define DWidth 2 // 32 bits 0/1/2
#define SWidth 2 // 32 bits 0/1/2
#define DBSize 3 // 16 transfers @ 16 bit width == 32(0x20) bytes AACI FIFO width /0 == 1/1 == 4/2 == 8/3 == 16/4 == 32/5 == 64/6 == 128/7 == 256/
#define SBSize 5 // Set to half of the AACI 512 byte FIFO depth == 64 transfers @ 32 bit width == 256(0x100) bytes Memory
// - later check with FIFO status whether we could use full depth
/*
* TODO::
* - some from DMAC == same for all device DMA
* - some AACI device specific
*/
#define VDMA_AACI_CCTL \
((1 << 31) | /* ALL 'I' - Terminal count interrupt enable */ \
(0 << 28) | /* ALL 'Prot' - AHB HPROT information to destination slave. (Set to Not Cacheable, Not bufferable, User mode). */ \
(0 << 27) | /* AACI 'DI' - Destination address NOT incremented after each transfer */ \
(1 << 26) | /* AACI 'SI' - Source address incremented after each transfer */ \
(0 << 25) | /* AACI 'D' - Destination AHB master select; 0=AHB1, 1=AHB2. (Transferring to AACI on V/PB M2 bus) */ \
(1 << 24) | /* AACI 'S' - Source AHB master select; 0=AHB1, 1=AHB2. (Transferring from memory on V/PB DMA1 bus) */ \
(DWidth << 21) | /* AACI 'DWidth' - Destination transfer width. */ \
(SWidth << 18) | /* ALL 'SWidth' - Source transfer width. */ \
(DBSize << 15) | /* AACI 'DBSize' - Destination burst size. */ \
/* The number of bytes transferred when the peripheral requests a burst of data. */ \
/* AACI requests a burst when the FIFO has four or less words. */ \
(SBSize << 12) | /* AACI 'SBSize' - Source burst size. (Memory boundary size when transferring from memory). (Set to 256 bytes) */ \
DMABlockSize) /* AACI Number of 'destination width' transfers in one DMA 'packet'. (2^12)-1 is the maximum. */
#define VDMA_AACI_CCFG \
/* [31:19] ALL 'Reserved' - Write as zero. */ \
((0 << 18) | /* 'Halt' ALL - 0 = Allow DMA requests, 1 = Finish current request and ignore others */ \
(0 << 17) | /* [17] ALL 'Active' - Read only bit. 0 = No data in FIFO. */ \
(0 << 16) | /* 'L' ALL - Lock. Generates locked tranfers on the AHB. */ \
(1 << 15) | /* 'ITC' ALL - Terminal Count Interrupt mask. Masks TC interrupt when cleared. */ \
(1 << 14) | /* 'IE' ALL - Interrupt error mask. Masks error interrupt when cleared. */ \
(1 << 11) | /* 'FlowCntrl' AACI - Flow control method. We're using Mem -> Peripheral, with the DMAC as the flow controller. */ \
(0 << 10) | /* 'Reserved' ALL - Write as zero */ \
(0 << 6) | /* 'DestPeripheral' AACI - Destination peripheral number to associate with this channel. */ \
/* Set zero here, OR in the correct value in the machine initialisation */ \
/* VPB uses #0, which is shared with a logic tile DMA line on the V/PB. */ \
/* AACI Must be selected in V/PB SYS_DMAPSR0 reg. */ \
/* VAB uses #1 AACI TX which is fixed to DMA channel 1 */ \
(0 << 5) | /* 'Reserved' ALL - Write as zero */ \
(0 << 1) | /* 'SrcPeripheral' AACI - Source peripheral number to associate with this channel. Ignored for 'from memory' transfers.*/ \
(0 << 0)) /* 'E' ALL - Enables this DMA channel. (Channels automatically disabled when a transfer completes) */
#endif /* CONFIG_ARM_AMBA_DMA */
#define VDMA_CCFG_PERIPHAL_NUM_0 0x00000000
#define VDMA_CCFG_PERIPHAL_NUM_1 0x00000040
#define VDMA_CCFG_PERIPHAL_NUM_2 0x00000080
#define VDMA_CCFG_PERIPHAL_NUM_3 0x000000C0
#define VDMA_CCFG_PERIPHAL_NUM_4 0x00000100
#define VDMA_CCFG_PERIPHAL_NUM_5 0x00000140
#define VDMA_CCFG_PERIPHAL_NUM_6 0x00000180
#define VDMA_CCFG_PERIPHAL_NUM_7 0x000001C0
#define VDMA_CCFG_PERIPHAL_NUM_8 0x00000200
#define VDMA_CCFG_PERIPHAL_NUM_9 0x00000240
#define VDMA_CCFG_PERIPHAL_NUM_A 0x00000280
#define VDMA_CCFG_PERIPHAL_NUM_B 0x000002C0
#define VDMA_CCFG_PERIPHAL_NUM_C 0x00000300
#define VDMA_CCFG_PERIPHAL_NUM_D 0x00000340
#define VDMA_CCFG_PERIPHAL_NUM_E 0x00000380
#define VDMA_CCFG_PERIPHAL_NUM_F 0x000003C0
#endif
......@@ -84,6 +84,19 @@
#define VERSATILE_SYS_BOOTCS_OFFSET 0x58
#define VERSATILE_SYS_24MHz_OFFSET 0x5C
#define VERSATILE_SYS_MISC_OFFSET 0x60
#if defined(CONFIG_ARCH_VERSATILE_PB)
# define VERSATILE_SYS_DMAPSR0_OFFSET 0x64
# define VERSATILE_SYS_DMAPSR1_OFFSET 0x68
# define VERSATILE_SYS_DMAPSR2_OFFSET 0x6C
# define VSYSMASK_DMAPSR 0xFFFFFF60
# define VSYS_VAL_DMAPSR_ENABLE (1 << 7)
# define VSYS_VAL_DMAPSR_AACI_TX 0x00000000
# define VSYS_VAL_DMAPSR_AACI_RX 0x00000001
# define VSYS_VAL_DMAPSR_USB_A 0x00000002
# define VSYS_VAL_DMAPSR_USB_B 0x00000003
#endif
#define VERSATILE_SYS_TEST_OSC0_OFFSET 0x80
#define VERSATILE_SYS_TEST_OSC1_OFFSET 0x84
#define VERSATILE_SYS_TEST_OSC2_OFFSET 0x88
......
......@@ -11,18 +11,8 @@
* (dma.c) and the architecture-specific DMA backends (dma-*.c)
*/
struct dma_struct;
typedef struct dma_struct dma_t;
struct dma_ops {
int (*request)(dmach_t, dma_t *); /* optional */
void (*free)(dmach_t, dma_t *); /* optional */
void (*enable)(dmach_t, dma_t *); /* mandatory */
void (*disable)(dmach_t, dma_t *); /* mandatory */
int (*residue)(dmach_t, dma_t *); /* optional */
int (*setspeed)(dmach_t, dma_t *, int); /* optional */
char *type;
};
#ifndef _ASMARM_MACH_DMA_H_
#define _ASMARM_MACH_DMA_H_
struct dma_struct {
void *addr; /* single DMA address */
......@@ -48,6 +38,20 @@ struct dma_struct {
struct dma_ops *d_ops;
};
typedef struct dma_struct dma_t;
struct dma_ops {
int (*request)(dmach_t, dma_t *); /* optional */
void (*free)(dmach_t, dma_t *); /* optional */
void (*enable)(dmach_t, dma_t *); /* mandatory */
void (*disable)(dmach_t, dma_t *); /* mandatory */
int (*residue)(dmach_t, dma_t *); /* optional */
int (*setspeed)(dmach_t, dma_t *, int); /* optional */
char *type;
};
/* Prototype: void arch_dma_init(dma)
* Purpose : Initialise architecture specific DMA
* Params : dma - pointer to array of DMA structures
......@@ -55,3 +59,10 @@ struct dma_struct {
extern void arch_dma_init(dma_t *dma);
extern void isa_init_dma(dma_t *dma);
#ifdef CONFIG_ARM_AMBA_DMA
extern void amba_init_dma(dma_t *dma);
#endif
#endif
......@@ -10,6 +10,12 @@
#ifndef ASMARM_AMBA_H
#define ASMARM_AMBA_H
#ifdef CONFIG_ARM_AMBA_DMA
# include <asm/dma.h>
# include <asm/mach/dma.h>
# include <linux/amba/dma.h>
#endif
#define AMBA_NR_IRQS 2
struct amba_device {
......@@ -18,6 +24,9 @@ struct amba_device {
u64 dma_mask;
unsigned int periphid;
unsigned int irq[AMBA_NR_IRQS];
#ifdef CONFIG_ARM_AMBA_DMA
struct amba_dma_data dma_data;
#endif
};
struct amba_id {
......@@ -47,9 +56,32 @@ struct amba_device *amba_find_device(const char *, struct device *, unsigned int
int amba_request_regions(struct amba_device *, const char *);
void amba_release_regions(struct amba_device *);
#ifdef CONFIG_ARM_AMBA_DMA
struct amba_device * amba_get_device_with_name(char * name);
#endif
#define amba_config(d) (((d)->periphid >> 24) & 0xff)
#define amba_rev(d) (((d)->periphid >> 20) & 0x0f)
#define amba_manf(d) (((d)->periphid >> 12) & 0xff)
#define amba_part(d) ((d)->periphid & 0xfff)
/* AMBA PrimeCell peripheral ids */
/*
* Note that the hex number is that from the prime cell id
* e.g. pl041 has id 0x41 NOT 0x29
* hence OEM peripherals may use 0x<n>[a - f]
*/
#define AMBA_PERIPHID_UART 0x11
#define AMBA_PERIPHID_SSI 0x22
#define AMBA_PERIPHID_RTC 0x31
#define AMBA_PERIPHID_AACI 0x40
#define AMBA_PERIPHID_AACI_97 0x41
#define AMBA_PERIPHID_KMI 0x50
#define AMBA_PERIPHID_GPIO 0x61
#define AMBA_PERIPHID_DMAC2 0x80
#define AMBA_PERIPHID_DMAC1 0x81
#define AMBA_PERIPHID_CLCD 0x110
#define AMBA_PERIPHID_VIC 0x190
#define AMBA_PERIPHID_CIM 0x321
#endif
/*
* linux/include/linux/amba/dma.h
*
* 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.
*
* DMA API for any AMBA system supporting DMA
* i.e. there may or may not be an AMBA DMA controller in the system
*/
#ifndef LINUX_AMBA_DMA_H
#define LINUX_AMBA_DMA_H
/*
* Data from the peripheral using DMA, required by the AMBA DMA manager
*/
struct amba_dma_data {
/* DMAC interrupt data as known by the board */
int (*irq_ignore)(dmach_t chan); /* Requesting device should ignore this interrupt */
void (*irq_pre)(dmach_t chan); /* Any necessary DMA operations e.g error checking */
void (*irq_post)(dmach_t chan); /* Clear the interrupt and carry out any necessary DMA operations e.g re-enable DMAC channel */
unsigned int packet_size; /* Size, in bytes, of each DMA transfer packet */
void * dmac_data; /* DMA controller specific data e.g. for pl080/pl041 TX transfers, the pl040 TX FIFO offset */
};
extern void amba_set_ops(struct dma_ops * board_ops);
#endif
/*
* 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);
#endif /* AMBA_PL080_H */
This diff is collapsed.
......@@ -12,7 +12,7 @@
/*
* Control and status register offsets
* P39.
* - see Programmer's Model in TRM
*/
#define AACI_CSCH1 0x000
#define AACI_CSCH2 0x014
......@@ -209,6 +209,10 @@ struct aaci_runtime {
u32 cr;
struct snd_pcm_substream *substream;
#ifdef CONFIG_ARM_AMBA_DMA
dmach_t dma_chan;
#endif
/*
* PIO support
*/
......@@ -223,7 +227,7 @@ struct aaci_runtime {
struct aaci {
struct amba_device *dev;
struct snd_card *card;
void __iomem *base;
void __iomem *base; /* AACI register base */
unsigned int fifosize;
/* AC'97 */
......@@ -243,4 +247,17 @@ struct aaci {
#define ACSTREAM_SURROUND 1
#define ACSTREAM_LFE 2
#define TIME_TO_RESPOND 42 /* 42us = ~2 frames at 48kHz. */
/* This should give the AC'97 codec */
/* sufficient time to respond */
#ifdef CONFIG_ARM_AMBA_DMA
/*
* DMA only possible on audio channel 1
* - the first of four
*/
# define AACI_INDEX_DMACHAN 0
# define AACI_DMACHAN 1
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment