/*
* 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 <linux/delay.h>
#include <asm/cacheflush.h>
#include "ppc460ex_4chan_dma.h"
#include <asm/page.h>
#include <asm/time.h>
#include <linux/pipe_fs_i.h>
#include <linux/splice.h>
#define SGDMA_MAX_POLL_COUNT 100000000
#define SGDMA_POLL_DELAY 5
static phys_addr_t splice_src_dma_addrs[PIPE_BUFFERS];
static dma_addr_t splice_dst_dma_addrs[PIPE_BUFFERS];
//#define DEBUG_SPLICE_DMA 1
//#define SPLICE_DMA_COHERENT 1
//#define DEBUG_SPLICE_DMA_TIMECAL 1
extern int ppc460ex_disable_dma_interrupt(ppc460ex_plb_dma_dev_t *adev, unsigned int ch_id);
extern int ppc460ex_disable_burst(ppc460ex_plb_dma_dev_t *adev, unsigned int ch_id);
extern int ppc460ex_enable_burst(ppc460ex_plb_dma_dev_t *adev, unsigned int ch_id);
void *dma_mem_page = NULL;
#define dma_pr(x) printk(KERN_DEBUG,x)
int ppc460ex_set_sg_addr(int ch_id, phys_addr_t sg_addr)
{
if (unlikely(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_SGH0 + ch_id*8, sg_addr >> 32);
#endif
mtdcr(DCR_DMA2P40_SGL0 + ch_id*8, (u32)sg_addr);
return 0;
}
static int
poll_for_sgdma_done(int chan)
{
int i;
volatile int status = 0;
#ifdef DEBUG_SPLICE_DMA_TIMECAL
u64 time1=0, time2=0, timediff=0;
#endif
#ifdef DEBUG_SPLICE_DMA_TIMECAL
time1 = get_tbl() | (get_tbu() << 32);
#endif
for(i = 0; i < SGDMA_MAX_POLL_COUNT; i++) {
#ifdef DEBUG_SPLICE_DMA
if(i%16 == 0)
printk("%s:%s:%d - waiting %d\n", __FILE__, __FUNCTION__, __LINE__, i);
#endif
status = PPC460EX_DMA_CHAN_SGXFR_COMPLETE(chan);
if(status) {
#ifdef DEBUG_SPLICE_DMA
printk("%s:%s:%d - Breaking\n", __FILE__, __FUNCTION__, __LINE__);
#endif
break;
}
#ifdef DEBUG_SPLICE_DMA
printk("status = %d dma_status = 0x%08x\n", status, DMA_STATUS(chan));
#endif
//udelay(SGDMA_POLL_DELAY);
}
#ifdef DEBUG_SPLICE_DMA_TIMECAL
time2 = get_tbl() | (get_tbu() << 32);
#endif
#ifdef DEBUG_SPLICE_DMA_TIMECAL
printk("%s:%s:%d time taken for transfer is %llu\n",
__FILE__, __FUNCTION__, __LINE__, time2-time1);
#endif
if(unlikely(i >= SGDMA_MAX_POLL_COUNT)) {
printk("%s:%s:%d - timeout\n",
__FILE__, __FUNCTION__, __LINE__);
return -ETIME;
}
return 0;
}
static int
get_transfer_width(u64 align)
{
if(!(align & 0xF))
return 128;
if(!(align & 0x7))
return 64;
if(!(align & 0x3))
return 32;
if(!(align & 0x1))
return 16;
return 8;
}
/*
* Add a new sgl descriptor to the end of a scatter/gather list
* which was created by alloc_dma_handle().
*
* For a memory to memory transfer, both dma addresses must be
* valid. For a peripheral to memory transfer, one of the addresses
* must be set to NULL, depending on the direction of the transfer:
* memory to peripheral: set dst_addr to NULL,
* peripheral to memory: set src_addr to NULL.
*/
int ppc460ex_add_dma_sgl(ppc460ex_plb_dma_dev_t *adev,
sgl_handle_t handle,
phys_addr_t src_addr,
phys_addr_t dst_addr,
unsigned int count)
{
sgl_list_info_t *psgl = (sgl_list_info_t *)handle;
ppc460ex_plb_dma_ch_t *p_dma_ch;
u64 align;
int tr_width = 8; /* initial value 8 bits */
#ifdef DEBUG_SPLICE_DMA
printk("%s:%s:%d - Filling in dma sgl list\n", __func__, __FILE__, __LINE__);
#endif
if (unlikely(!handle)) {
printk("%s: null handle\n", __FUNCTION__);
return DMA_STATUS_BAD_HANDLE;
}
if (unlikely(psgl->ch_id >= MAX_PPC460EX_DMA_CHANNELS)) {
printk("%s: bad channel %d\n", __FUNCTION__, psgl->ch_id);
return DMA_STATUS_BAD_CHANNEL;