arch\arm\mach-zynq\pl330.c
#define
PL330_DBGSTATUS_OFFSET 0xD00 /* Debug Status
Register */
#define PL330_DBGCMD_OFFSET 0xD04 /* Debug Command Register
*/
module_init(pl330_init); -> pl330_init()
->
pl330_driver_init();
//////////////////////////////////////////////////////////////////////
/**
*
pl330_driver_init - Initialize the dma_struct array and store the
pointer
* to array
*/
static void
pl330_driver_init(void)
{
unsigned int
i;
PDEBUG("inside pl330_driver_init, dma_chan is %x\n",
(unsigned
int)dma_chan);
driver_data.dma_chan = dma_chan;
memset(dma_chan, 0, sizeof(dma_chan[MAX_DMA_CHANNELS]));
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
dma_chan[i].d_ops =
&pl330_ops;
isa_dma_add(i, dma_chan
+ i);
}
}
///////////////////////////////////////////////////////////////////////
/*
*
Platform bus binding
*/
static struct dma_ops pl330_ops = {
.request =
pl330_request_dma,
.free = pl330_free_dma,
.enable = pl330_enable_dma,
.disable =
pl330_disable_dma,
.setspeed = pl330_setspeed_dma,
.residue =
pl330_get_residue_dma,
.type =
"PL330",
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
pl330_enable_dma - Implementation of enable_dma. It translates the
* DMA
parameters to a DMA program if the DMA program is not provided,
* then
starts the DMA program on a channel thread.
* @channel: DMA channel
number
* @indexed_dma_chan: Instance of the dma_struct.
*/
static
void pl330_enable_dma(unsigned int
channel,
struct dma_struct *indexed_dma_chan)
{
struct
dma_struct *dma = indexed_dma_chan;
struct pl330_channel_data
*channel_data;
struct pl330_channel_static_data
*channel_static_data;
struct pl330_client_data *client_data;
struct
pl330_device_data *device_data;
unsigned int dev_chan;
struct pl330_bus_des *src_bus_des = NULL;
struct pl330_bus_des
*dst_bus_des = NULL;
struct default_src_bus_des;
struct
default_dst_bus_des;
unsigned src_inc = 1;
unsigned dst_inc = 1;
u32 src_addr;
u32 dst_addr;
u32 dma_prog;
char *dma_prog_buf;
int dma_prog_bytes;
u32 inten;
unsigned long spin_flags;
struct prog_build_args build_args;
channel_static_data = driver_data.channel_static_data +
channel;
device_data = driver_data.device_data +
channel_static_data->dev_id;
channel_data = driver_data.channel_data +
channel;
client_data =
driver_data.channel_data[channel].client_data;
if (!client_data) {
printk(KERN_ERR
"client data is
not set for DMA channel %d\n",
channel);
BUG();
return;
}
/*
* find out which one is source which one is destination
*/
if (dma->dma_mode == DMA_MODE_READ) {
PDEBUG("dma_mode is
DMA_MODE_READ\n");
src_bus_des = &client_data->dev_bus_des;
dst_bus_des =
&client_data->mem_bus_des;
src_addr = (u32)client_data->dev_addr;
dst_addr =
(u32)virt_to_bus(dma->addr);
src_inc = channel_data->incr_dev_addr;
dst_inc = 1;
}
else if (dma->dma_mode == DMA_MODE_WRITE) {
PDEBUG("dma_mode is
DMA_MODE_WRITE\n");
src_bus_des = &client_data->mem_bus_des;
dst_bus_des =
&client_data->dev_bus_des;
src_addr = (u32)virt_to_bus(dma->addr);
dst_addr =
(u32)client_data->dev_addr;
src_inc = 1;
dst_inc =
channel_data->incr_dev_addr;
} else {
printk(KERN_ERR "Error: mode
%x is not supported\n",
dma->dma_mode);
return;
}
if (dma->count == 0) {
printk(KERN_ERR "Error: DMA count for
channel %d is zero",
channel);
return;
}
/* print some debugging messages */
PDEBUG("count is %ld\n",
dma->count);
PDEBUG("dev_addr = %x\n", (unsigned
int)client_data->dev_addr);
PDEBUG("dev_bus_des =
{\n");
print_pl330_bus_des(&client_data->dev_bus_des);
PDEBUG("}\n");
PDEBUG("mem_bus_des =
{\n");
print_pl330_bus_des(&client_data->mem_bus_des);
PDEBUG("}\n");
PDEBUG("endian_swap_size = %d\n",
client_data->endian_swap_size);
PDEBUG("incr_dev_addr = %d\n",
channel_data->incr_dev_addr);
dma_prog = channel_data->dma_program;
dev_chan = channel_static_data->dev_chan;
if (dma_prog == 0) {
/*
* if the DMA program is not set
by a user,
* construct the dma program
*/
PDEBUG("constructing
DMA program\n");
if (!channel_data->dma_prog_buf) {
/* allocate
the dma prog buffer */
channel_data->dma_prog_buf
=
dma_alloc_coherent(device_data->dev,
0x1000,
&channel_data->dma_prog_phy,
GFP_KERNEL);
}
PDEBUG("channel %d DMA program: vir %#08x, phy
%#08x\n",
channel,
(u32)channel_data->dma_prog_buf,
(u32)channel_data->dma_prog_phy);
dma_prog_buf = (char *)channel_data->dma_prog_buf;
/*
* setup the arguments
*/
build_args.channel =
channel;
build_args.dma_prog_buf = dma_prog_buf;
build_args.dev_chan =
dev_chan;
build_args.dma_count = dma->count;
build_args.src_addr =
src_addr;
build_args.src_bus_des = src_bus_des;
build_args.src_inc =
src_inc;
build_args.dst_addr = dst_addr;
build_args.dst_bus_des =
dst_bus_des;
build_args.dst_inc = dst_inc;
build_args.src_is_mem =
dma->dma_mode == DMA_MODE_WRITE;
build_args.endian_swap_size =
client_data->endian_swap_size;
build_args.cache_length =
device_data->i_cache_len;
dma_prog_bytes =
pl330_build_dma_prog(&build_args);
/*
* using physical address for DMA prog
*/
dma_prog = channel_data->dma_prog_phy;
channel_data->dma_prog_len = dma_prog_bytes;
PDEBUG("DMA program constructed\n");
} else
{
PDEBUG("channel %d user defined DMA program %#08x\n",
channel, (u32)dma_prog);
}
PDEBUG("enable_dma:
spin_lock_irqsave\n");
spin_lock_irqsave(&device_data->lock,
spin_flags);
/* enable the interrupt */
PDEBUG("enable_dma: enabling
interrupt\n");
inten = pl330_readreg(device_data->base,
PL330_INTEN_OFFSET);
inten |= 0x01 << dev_chan; /* set the
correpsonding bit */
pl330_writereg(inten, device_data->base,
PL330_INTEN_OFFSET);
PDEBUG("pl330 interrupt enabled for channel %d\n",
channel);
pl330_exec_dmago(device_data->dev_id,
device_data->base,
dev_chan,
dma_prog);
spin_unlock_irqrestore(&device_data->lock,
spin_flags);
return;
}
/////////////////////////////////////////////////////////////////////////////////
/**
*
pl33_exec_dmago - Execute the DMAGO to start a channel.
* @dev_id: PL330
device ID indicating which PL330, the ID starts at 0.
* @base: PL330 device
base address
* @dev_chan: Channel number for the device
* @dma_prog: DMA
program starting address, this should be DMA address
*
* Returns 0 on
success, -1 on time out
*/
int pl330_exec_dmago(unsigned int dev_id,
void __iomem *base,
unsigned int dev_chan,
u32
dma_prog)
{
char dma_go_prog[8];
u32 dbginst0;
u32
dbginst1;
int wait_count;
PDEBUG("pl330_exec_dmago: entering\n");
pl330_instr_dmago(dma_go_prog, dev_chan, dma_prog, 0);
dbginst0 = PL330_DBGINST0(*(dma_go_prog + 1), *dma_go_prog, 0,
0);
dbginst1 = (u32)dma_prog;
PDEBUG("inside pl330_exec_dmago: base %x, dev_chan %d, dma_prog
%x\n",
(u32)base, dev_chan, dma_prog);
/* wait while debug status is busy */
wait_count = 0;
while
(pl330_readreg(base, PL330_DBGSTATUS_OFFSET)
& PL330_DBGSTATUS_BUSY
&& wait_count < PL330_MAX_WAIT) {
PDEBUG("dbgstatus
%x\n",
pl330_readreg(base, PL330_DBGSTATUS_OFFSET));
wait_count++;
}
if (wait_count >= PL330_MAX_WAIT)
{
printk(KERN_ERR
"PL330 device %d debug status busy time
out\n",
dev_id);
return -1;
}
PDEBUG("dbgstatus idle\n");
/* write debug instruction 0 */
pl330_writereg(dbginst0, base,
PL330_DBGINST0_OFFSET);
/* write debug instruction 1
*/
pl330_writereg(dbginst1, base, PL330_DBGINST1_OFFSET);
/* wait while the DMA Manager is busy */
wait_count = 0;
while
((pl330_readreg(base, PL330_DS_OFFSET) & PL330_DS_DMA_STATUS)
!=
PL330_DS_DMA_STATUS_STOPPED
&& wait_count <=
PL330_MAX_WAIT) {
PDEBUG("ds %x\n",
pl330_readreg(base,
PL330_DS_OFFSET));
wait_count++;
}
if (wait_count >= PL330_MAX_WAIT) {
printk(KERN_ERR
"PL330 device %d debug status busy time out\n",
dev_id);
return
-1;
}
/* run the command in dbginst0 and dbginst1 */
pl330_writereg(0, base, PL330_DBGCMD_OFFSET);
PDEBUG("pl330_exec_dmago done\n");
return 0;
}
////////////////////
#define pl330_writereg(data, base, offset)
\
__raw_writel(data, (void __iomem *)((u32)(base) +
(u32)(offset)))
//////////////////////////////////////////////////////