/*
* Copyright(c) 2008 Applied Micro Circuits Corporation(AMCC). 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.
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/async_tx.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <asm/dcr-regs.h>
#include <asm/dcr.h>
#include "ppc460ex_4chan_dma.h"
#ifdef DEBUG_TEST
#define dma_pr printk
#else
#define dma_pr
#endif
#define TEST_SIZE 12
ppc460ex_plb_dma_dev_t *adev;
int ppc460ex_get_dma_channel(void)
{
int i;
unsigned int status = 0;
status = mfdcr(DCR_DMA2P40_SR);
for(i=0; i<MAX_PPC460EX_DMA_CHANNELS; i++) {
if ((status & (1 >> (20+i))) == 0)
return i;
}
return -ENODEV;
}
int ppc460ex_get_dma_status(void)
{
return (mfdcr(DCR_DMA2P40_SR));
}
int ppc460ex_set_src_addr(int ch_id, phys_addr_t src_addr)
{
if (ch_id >= MAX_PPC460EX_DMA_CHANNELS) {
printk("%s: bad channel %d\n", __FUNCTION__, ch_id);
return DMA_STATUS_BAD_CHANNEL;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCR_DMA2P40_SAH0 + ch_id*8, src_addr >> 32);
#endif
mtdcr(DCR_DMA2P40_SAL0 + ch_id*8, (u32)src_addr);
return DMA_STATUS_GOOD;
}
int ppc460ex_set_dst_addr(int ch_id, phys_addr_t dst_addr)
{
if (ch_id >= MAX_PPC460EX_DMA_CHANNELS) {
printk(KERN_ERR "%s: bad channel %d\n", __FUNCTION__, ch_id);
return DMA_STATUS_BAD_CHANNEL;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCR_DMA2P40_DAH0 + ch_id*8, dst_addr >> 32);
#endif
mtdcr(DCR_DMA2P40_DAL0 + ch_id*8, (u32)dst_addr);
return DMA_STATUS_GOOD;
}
/*
* Sets the dma mode for single DMA transfers only.
* For scatter/gather transfers, the mode is passed to the
* alloc_dma_handle() function as one of the parameters.
*
* The mode is simply saved and used later. This allows
* the driver to call set_dma_mode() and set_dma_addr() in
* any order.
*
* Valid mode values are:
*
* DMA_MODE_READ peripheral to memory
* DMA_MODE_WRITE memory to peripheral
* DMA_MODE_MM memory to memory
* DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
* DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
*/
int ppc460ex_set_dma_mode(ppc460ex_plb_dma_dev_t *adev, unsigned int ch_id, unsigned int mode)
{
ppc460ex_plb_dma_ch_t *dma_chan = adev->chan[ch_id];
if (ch_id >= MAX_PPC460EX_DMA_CHANNELS) {
printk("%s: bad channel %d\n", __FUNCTION__, dma_chan->chan_id);
return DMA_STATUS_BAD_CHANNEL;
}
dma_chan->mode = mode;
return DMA_STATUS_GOOD;
}
/*
* Sets the DMA Count register. Note that 'count' is in bytes.
* However, the DMA Count register counts the number of "transfers",
* where each transfer is equal to the bus width. Thus, count
* MUST be a multiple of the bus width.
*/
void ppc460ex_set_dma_count(ppc460ex_plb_dma_dev_t *adev, unsigned int ch_id, unsigned int count)
{
ppc460ex_plb_dma_ch_t *dma_chan = adev->chan[ch_id];
//#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (dma_chan->pwidth) {
case PW_8:
break;
case PW_16:
if (count & 0x1)
error = 1;
break;
case PW_32:
if (count & 0x3)
error = 1;
break;
case PW_64:
if (count & 0x7)
error = 1;
break;
case PW_128:
if (count & 0xf)
error = 1;
break