diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /arch/arm/mach-wmt/dma.c | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'arch/arm/mach-wmt/dma.c')
-rwxr-xr-x | arch/arm/mach-wmt/dma.c | 1361 |
1 files changed, 1361 insertions, 0 deletions
diff --git a/arch/arm/mach-wmt/dma.c b/arch/arm/mach-wmt/dma.c new file mode 100755 index 00000000..ad6db63d --- /dev/null +++ b/arch/arm/mach-wmt/dma.c @@ -0,0 +1,1361 @@ +/*++ + arch/arm/mach-wmt/dma.c - DMA 5 driver + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + 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, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/proc_fs.h> +#include <linux/syscore_ops.h> + +#include <linux/pm.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <mach/hardware.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <linux/dma-mapping.h> +#include <mach/dma.h> + +#undef DMA4_ERRATA +#undef DEBUG +/*#define DEBUG*/ + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ## args) +#else +#define DPRINTK(x...) +#endif + +#define MAX_DESCRIPT_SIZE SIZE_1KB + +int revise_descript( dmach_t ch, struct dma_device_cfg_s device_cfg, struct dma_mem_reg_group_s dma_mem_reg); + +extern unsigned int wmt_read_oscr(void); + +/*-----------------------------------------------------------------------------*/ +/* DMA channel structure.*/ +/*-----------------------------------------------------------------------------*/ +struct dma_info_s { + dmach_t channel_no ; /* channel no*/ + struct dma_regs_s *regs; /* points to appropriate DMA registers*/ + int irq; /* IRQ Index used by the channel*/ + const char *device_id; /* device name*/ + void (*callback)(void *data); /* to call when buffers are done*/ + void *callback_data; /* with private data ptr*/ + enum dma_device_e device_no ; /* device no*/ + struct dma_device_cfg_s device_cfg ; /* device cfg*/ + int in_use; /* Does someone own the channel*/ + int des0cnt; /* descript 0 current index*/ + int des1cnt; /*descript 1 current index*/ + int max_des0cnt; /*the largest descript 0 number*/ + int max_des1cnt; /*the largest descript 1 number*/ + int residue_des0cnt ; /*residue descript 0 count need to be xfer*/ + int residue_des1cnt ; /*residue descript 1 count need to be xfer*/ + struct dma_descript_addr des_addr; + dma_addr_t des0_phy_addr ; + dma_addr_t des1_phy_addr ; + unsigned int accu_size; /*accumlate size between the dma interrupt*/ + unsigned int descript_size; /*the max descript size*/ + +} ; + +struct dma_int_s { + unsigned int request_chans ; + struct dma_regs_s *regs; + +}; + +struct des_attribute { + unsigned int end_descript; + unsigned int interrupt_en; + unsigned int size; + unsigned int fmt; + dma_addr_t data_addr; + dma_addr_t branch_addr; +}; + + +/*-----------------------------------------------------------------------------*/ +/* variable*/ +/*-----------------------------------------------------------------------------*/ +static struct dma_int_s dma_int ; + +static struct dma_info_s dma_chan[MAX_DMA_CHANNELS]; + +static struct dma_mem_reg_s *dma_mem_regs; + +static dma_addr_t dma_mem_phy; + +/* static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;*/ +static DEFINE_SPINLOCK(dma_list_lock); + +static unsigned int dma_irq_no[] = { + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, + IRQ_DMA_NONS, +}; + +struct dma_device_cfg_s dma_device_cfg_table[] = { + /* DeviceReq , DefaultCCR , Source_0, Destination_0 */ + { SPI0_DMA_TX_REQ , 0x00000100 , 0, 0 , SIZE_4KB}, /*spi0*/ + { SPI0_DMA_RX_REQ , 0x00000100 , 0, 0 , SIZE_4KB}, /*spi1*/ + { SPI1_DMA_TX_REQ , 0x00000100 , 0, 0 , SIZE_4KB}, + { SPI1_DMA_RX_REQ , 0x00000100 , 0, 0 , SIZE_4KB}, + { PCM1_TX_DMA_REQ , 0x00000100, 0, 0 , SIZE_4KB}, + { PCM1_RX_DMA_REQ , 0x00000100, 0, 0 , SIZE_4KB}, + /* start from 0, above 5, below 6 */ + { UART_0_TX_DMA_REQ , UART_TX_DMA_CFG, 0, UART0_TX_FIFO , SIZE_1B}, /*uart0*/ + { UART_0_RX_DMA_REQ , UART_RX_DMA_CFG, 0, UART0_RX_FIFO , SIZE_4KB}, /*uart0*/ + { UART_1_TX_DMA_REQ , UART_TX_DMA_CFG, 0, UART1_TX_FIFO , SIZE_1B}, /*uart1*/ + { UART_1_RX_DMA_REQ , UART_RX_DMA_CFG, 0, UART1_RX_FIFO , SIZE_4KB}, /*uart1*/ + { UART_2_TX_DMA_REQ , UART_TX_DMA_CFG, 0, UART2_TX_FIFO , SIZE_1B}, /*uart2*/ + /*start from 0, above 10, below 11 */ + { UART_2_RX_DMA_REQ , UART_RX_DMA_CFG, 0, UART2_RX_FIFO , SIZE_4KB}, /*uart2*/ + { UART_3_TX_DMA_REQ , UART_TX_DMA_CFG, 0, UART3_TX_FIFO , SIZE_1B}, /*uart3*/ + { UART_3_RX_DMA_REQ , UART_RX_DMA_CFG, 0, UART3_RX_FIFO , SIZE_4KB}, /*uart3*/ + { PCM_TX_DMA_REQ, PCM_TX_DMA_CFG, 0, PCM_TX_FIFO, SIZE_4KB}, + { PCM_RX_DMA_REQ, PCM_RX_DMA_CFG, 0, PCM_RX_FIFO, SIZE_4KB}, + /*start from 0, above 15, below 16 */ + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + /*start from 0, above 20, below 21 */ + { AHB1_AUD_DMA_REQ_0 , I2S_RX_DMA_CFG, 0, I2S_RX_FIFO , SIZE_16KB}, + { AHB1_AUD_DMA_REQ_1 , I2S_TX_DMA_CFG, 0, I2S_TX_FIFO , SIZE_16KB}, + { AHB1_AUD_DMA_REQ_2 , I2S_RX_DMA_CFG, 0, SPDIF_RX_FIFO, SIZE_16KB}, + { AHB1_AUD_DMA_REQ_3 , 0x00000100, 0, 0 , SIZE_4KB}, + { AHB1_AUD_DMA_REQ_4 , 0x00000100, 0, 0 , SIZE_4KB}, + { AHB1_AUD_DMA_REQ_5 , 0x00000100, 0, 0 , SIZE_4KB}, + { AHB1_AUD_DMA_REQ_6 , 0x00000100, 0, 0 , SIZE_4KB}, + { AHB1_AUD_DMA_REQ_7 , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + /*start from 0, above 30, below 31 */ + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, + { MEMORY_DMA_REQ , 0x2a800000, 0x0a200000 , 0x0a220000 , SIZE_4KB}, + { DEVICE_RESERVED , 0x00000100, 0, 0 , SIZE_4KB}, +}; +EXPORT_SYMBOL(dma_device_cfg_table); + +/*===========================================================================*/ +/* dma_irq_handler*/ +/**/ +/* return: 0*/ +/*===========================================================================*/ +static irqreturn_t +dma_irq_handler(int irq, void *dev_id) +{ + int ch ; + unsigned int global_st ; + unsigned char channel_st ; + struct dma_info_s *dma = NULL ; + struct dma_mem_reg_group_s dma_mem_reg ; + + global_st = dma_int.regs->DMA_ISR & 0xFFFF ; + + for (ch = 0 ; ch < MAX_DMA_CHANNELS ; ++ch) { + if (global_st & 1 << ch) { + channel_st = dma_int.regs->DMA_CCR_CH[ch] & DMA_EVT_ID_MASK; + break ; + } + } + DPRINTK("%s :dma ch = %d\n", __func__, ch); + + + if (ch >= MAX_DMA_CHANNELS) { + printk(KERN_ERR "DMA : unknown DMA IRQ\n\r") ; + return IRQ_HANDLED ; + } + /* + * ch active handling + */ + dma = &dma_chan[ch] ; + dma_int.regs->DMA_ISR = 1 << ch ; + /* + * Handle channel DMA error + */ + if ((channel_st == DMA_EVT_NO_STATUS)) { + /* + * DMA request finished with no error + * Vincent 2009/05/19 + */ + dma_mem_reg = wmt_get_dma_pos_info(ch); + revise_descript(ch, dma->device_cfg, dma_mem_reg); + } else if ((channel_st != DMA_EVT_SUCCESS) && (channel_st != DMA_EVT_NO_STATUS)) { + /* 1. clear error/abort status*/ + /* 2. re-program src/des/cnt reg 0 and 1*/ + /* 3. write "1" to csr bit6 to reset the buffer pointer to 0*/ + /* 4. re-enable dma channle*/ + printk(KERN_ERR "ch=%d status=0x%.2x err\n\r", + ch, channel_st) ; + /* + * dma->callback(dma->callback_data) ; + * if callback runs, audio driver think this descp is done + * Vincent 2009/05/19 + */ + wmt_resume_dma(ch); + /* free buffer and callback to handle error*/ + return IRQ_HANDLED ; + } + /* + * Decrease the channel descript usage indicator. + */ + if (dma->residue_des0cnt > 0) + --dma->residue_des0cnt; + if (dma->callback) + dma->callback(dma->callback_data) ; + + return IRQ_HANDLED; +} + +int create_fmt0_descript( + dmach_t ch, + struct dma_device_cfg_s device_cfg, + struct des_attribute descript_attr) +{ + struct dma_info_s *dma ; + struct dma_des_fmt0 descript; + unsigned int ReqCnt = 0; + unsigned int des_offset; + descript.DataAddr = 0; + descript.ReqCnt = 0; + dma = &dma_chan[ch] ; + des_offset = dma->des0cnt * sizeof(struct dma_des_fmt0)/sizeof(unsigned long); + + DPRINTK("[%s] : create fmt 0 descript size=%x\n", __func__, descript_attr.size); + DPRINTK("[%s] : des0cnt = %x\n", __func__, dma->des0cnt); + + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } + + descript.DataAddr = (unsigned long)descript_attr.data_addr; + if (descript_attr.interrupt_en == 1) + ReqCnt |= DMA_INTEN_DES; + if (descript_attr.end_descript == 1) + ReqCnt |= DMA_DES_END; + if (descript_attr.size > (SIZE_64KB - 1)) + return -EOVERFLOW ; + descript.ReqCnt = ReqCnt | descript_attr.size; + DPRINTK("[%s]:des_offset = %d , des0 = 0x%x\n", + __func__, des_offset, dma->des_addr.des_0 + des_offset); + *(dma->des_addr.des_0 + des_offset) = descript.ReqCnt; + *(dma->des_addr.des_0 + des_offset + 1) = descript.DataAddr; + ++dma->des0cnt; + return 0; + +} + +int create_fmt1_descript( + dmach_t ch, + struct dma_device_cfg_s device_cfg, + struct des_attribute descript_attr) +{ + struct dma_info_s *dma ; + struct dma_des_fmt1 descript; + unsigned int ReqCnt = 0; + unsigned int des_offset; + descript.DataAddr = 0; + descript.ReqCnt = 0; + dma = &dma_chan[ch] ; + des_offset = (dma->max_des0cnt - 1) * sizeof(struct dma_des_fmt0) / sizeof(unsigned long); + + DPRINTK("[%s]:create fmt 1 descript size=%x\n", __func__, descript_attr.size); + DPRINTK("[%s]:des0cnt = %x\n", __func__, dma->des0cnt); + DPRINTK("[%s]:branch_addr = %x\n", __func__, descript_attr.branch_addr); + + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } + /*if (descript_attr.size > (SIZE_64KB - 1))*/ + descript.DataAddr = (unsigned long)descript_attr.data_addr; + descript.BrAddr = (unsigned long)descript_attr.branch_addr; + if (descript_attr.interrupt_en == 1) + ReqCnt |= DMA_INTEN_DES; + if (descript_attr.end_descript == 1) + ReqCnt |= DMA_DES_END; + if (descript_attr.fmt == 1) + ReqCnt |= DMA_FORMAT_DES1; + if (descript_attr.size > (SIZE_64KB - 1)) + return -EOVERFLOW ; + DPRINTK("[%s] : fmt_1 des_offset = %d , des0 = 0x%x\n", + __func__, des_offset, dma->des_addr.des_0 + des_offset); + descript.ReqCnt = ReqCnt | descript_attr.size; + *(dma->des_addr.des_0 + des_offset) = descript.ReqCnt; + *(dma->des_addr.des_0 + des_offset + 1) = descript.DataAddr; + *(dma->des_addr.des_0 + des_offset + 2) = descript.BrAddr ; + dma->des0cnt = 0; + return 0; +} + +int clear_last_descript( + dmach_t ch, + struct dma_device_cfg_s device_cfg) +{ + struct dma_info_s *dma ; + unsigned int des_offset; + dma = &dma_chan[ch] ; + + if ((dma->des0cnt - 1 >= 0)) + des_offset = (dma->des0cnt - 1) * sizeof(struct dma_des_fmt0) / sizeof(unsigned long); + else + des_offset = (dma->max_des0cnt - 1) * sizeof(struct dma_des_fmt0) / sizeof(unsigned long); + + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } + *(dma->des_addr.des_0 + des_offset) &= ~(DMA_DES_END); + return 0; +} +int add_descript( + dmach_t ch, + struct dma_device_cfg_s device_cfg, + dma_addr_t dma_ptr , + unsigned int size) +{ + struct dma_info_s *dma ; + unsigned int residue_size; + unsigned int xfer_size; + unsigned int xfer_index; + unsigned int ret = 0; + struct des_attribute descript_attr; + int need_add_descript_count = 0; + dma = &dma_chan[ch] ; + residue_size = size; + xfer_index = 0; + need_add_descript_count = size/SIZE_32KB ; + if (size%SIZE_32KB) + ++need_add_descript_count; + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } + if ((dma->max_des0cnt - dma->residue_des0cnt) < need_add_descript_count) { + printk("%s:dma descripts are full\n",__func__); + return -EBUSY ; + } + while (residue_size > 0) { + if (residue_size == size) + ret = clear_last_descript(ch, device_cfg); + + xfer_size = residue_size; + if (residue_size > SIZE_32KB) { + //xfer_size = residue_size - SIZE_32KB; + xfer_size = SIZE_32KB;//vincent + dma->accu_size += xfer_size; + residue_size -= SIZE_32KB; + dma_ptr += xfer_size * xfer_index; + } else { + xfer_size = residue_size; + dma_ptr += xfer_size * xfer_index; + dma->accu_size += xfer_size; + residue_size = 0; + } + + if (dma->des0cnt < dma->max_des0cnt - 1) { + if (residue_size <= SIZE_32KB) + descript_attr.end_descript = 1; + if (dma->accu_size >= device_cfg.ChunkSize) { + descript_attr.interrupt_en = 1; + dma->accu_size = 0; + dma->accu_size += xfer_size; + } + descript_attr.data_addr = dma_ptr; + descript_attr.size = xfer_size ; + descript_attr.fmt = 0; + ret = create_fmt0_descript(ch, + device_cfg, + descript_attr); + } else { + if (residue_size <= SIZE_32KB) + descript_attr.end_descript = 1; + if (dma->accu_size >= device_cfg.ChunkSize) { + descript_attr.interrupt_en = 1; + dma->accu_size = 0; + dma->accu_size += xfer_size; + } + descript_attr.data_addr = dma_ptr; + descript_attr.branch_addr = dma->des0_phy_addr; + descript_attr.size = xfer_size ; + descript_attr.fmt = 1; + ret = create_fmt1_descript(ch, + device_cfg, + descript_attr); + } + xfer_index++; + } + return xfer_index; +} + +int revise_descript( + dmach_t ch, + struct dma_device_cfg_s device_cfg, + struct dma_mem_reg_group_s dma_mem_reg) +{ + struct dma_info_s *dma ; + unsigned int ret = 0; + unsigned int des_offset = 0; + unsigned int req_count = 0; + unsigned int data_address = 0; +#ifdef DMA4_ERRATA + unsigned long flags; + unsigned int now_time = 0; + unsigned int delay_time = 0; +#endif + dma = &dma_chan[ch] ; + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } +#ifdef DMA4ERRATA + if (dma->regs->DMA_CCR_CH[ch] & (SYSTEM_DMA_RUN)) { + spin_lock_irqsave(&dma_list_lock, flags); + dma->regs->DMA_CCR_CH[ch] |= (DMA_UP_MEMREG_EN);/*update memory register before reading*/ + now_time = wmt_read_oscr(); + while (dma->regs->DMA_CCR_CH[ch] & (DMA_UP_MEMREG_EN)) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 15) {/*5us*/ + DPRINTK("[%d]Warnning:up_mem_reg did not clear[%x]\n", ch, dma->regs->DMA_CCR_CH[ch]); + dma->regs->DMA_CCR_CH[ch] &= ~DMA_UP_MEMREG_EN;/*clear DMA_UP_MEMREG_EN*/ + break; + } + + } + spin_unlock_irqrestore(&dma_list_lock, flags); + } +#endif + req_count = dma_mem_reg.DMA_IF0RBR_CH; + req_count &= (DMA_DES_REQCNT_MASK) ;/*Vincent 2009.5.4*/ + data_address = dma_mem_reg.DMA_IF0DAR_CH; + des_offset = dma_mem_reg.DMA_IF0CPR_CH - dma->des0_phy_addr ; + if (req_count > 0) { + *(dma->des_addr.des_0 + (des_offset / sizeof(unsigned long))) &= ~(DMA_DES_REQCNT_MASK); + *(dma->des_addr.des_0 + (des_offset / sizeof(unsigned long))) |= req_count; + *(dma->des_addr.des_0 + (des_offset / sizeof(unsigned long)) + 1) = 0; + *(dma->des_addr.des_0 + (des_offset / sizeof(unsigned long)) + 1) = data_address; + } else { + /*Vincent 2009/05/19*/ + if (des_offset < (dma->max_des0cnt - 1) * DMA_DES0_SIZE) + dma_mem_reg.DMA_IF0CPR_CH += 8; + else + dma_mem_reg.DMA_IF0CPR_CH = dma->des0_phy_addr; + + } + return ret ; +} +/*=============================================================================*/ +/**/ +/* wmt_start_dma - submit a data buffer for DMA*/ +/* Memory To Device or Device To Memory*/ +/* @ch: identifier for the channel to use*/ +/* @dma_ptr: buffer physical (or bus) start address*/ +/* @dma_ptr2: device FIFO address*/ +/* @size: buffer size*/ +/**/ +/* Memory To Memory*/ +/* @ch: identifier for the channel to use*/ +/* @dma_ptr: buffer physical (or bus) source start address*/ +/* @dma_ptr2: buffer physical (or bus) destination start address*/ +/* @size: buffer size*/ +/**/ +/* This function hands the given data buffer to the hardware for DMA*/ +/* access. If another buffer is already in flight then this buffer*/ +/* will be queued so the DMA engine will switch to it automatically*/ +/* when the previous one is done. The DMA engine is actually toggling*/ +/* between two buffers so at most 2 successful calls can be made before*/ +/* one of them terminates and the callback function is called.*/ +/**/ +/* The @ch identifier is provided by a successful call to*/ +/* wmt_request_dma().*/ +/**/ +/* The @size must not be larger than %MAX_DMA_SIZE. If a given buffer*/ +/* is larger than that then it's the caller's responsibility to split*/ +/* it into smaller chunks and submit them separately. If this is the*/ +/* case then a @size of %CUT_DMA_SIZE is recommended to avoid ending*/ +/* up with too small chunks. The callback function can be used to chain*/ +/* submissions of buffer chunks.*/ +/**/ +/* Error return values:*/ +/* %-EOVERFLOW: Given buffer size is too big.*/ +/* %-EBUSY: Both DMA buffers are already in use.*/ +/* %-EAGAIN: Both buffers were busy but one of them just completed*/ +/* but the interrupt handler has to execute first.*/ +/**/ +/* This function returs 0 on success.*/ +/**/ +/*=============================================================================*/ +int wmt_start_dma(dmach_t ch, dma_addr_t dma_ptr, dma_addr_t dma_prt2, unsigned int size) +{ + unsigned long flags; + int count ; + int ret = 0; + int descript_count = 0; + struct dma_info_s *dma = &dma_chan[ch] ; + /*dump_dma_regs(ch);*/ + + DPRINTK("size = %x, chunksize=%x\n", size, dma->device_cfg.ChunkSize); + + if (size == 0) + return -EINVAL ; + + local_irq_save(flags); + + descript_count = add_descript(ch, dma->device_cfg, dma_ptr, size); + if (descript_count < 0){ + ret = -EBUSY; + goto start_dma_out; + } + + dma->residue_des0cnt += descript_count ; + if (dma->residue_des0cnt > dma->max_des0cnt - 1) + { + DPRINTK("%s: dma->residue_des0cnt(%d) is too large\n", + __func__,dma->residue_des0cnt ); + dma->residue_des0cnt = dma->max_des0cnt - 1; + + } + DPRINTK("%s: ch = %d, dma_ptr = 0x%8.8x, size = %d (0x%8.8X)\n", + __func__, ch , dma_ptr, size , size); + + /* Calculate burst count*/ + if (dma->regs->DMA_CCR_CH[ch] & SYSTEM_DMA_RUN) {/*still run*/ + wmb(); + dma->regs->DMA_CCR_CH[ch] |= DMA_WAKE; + wmb(); + } else { + count = size ; + dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH = dma->des0_phy_addr; + DPRINTK("dma descript 0 phy addr = 0x%x\n", dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH); + if (dma->device_cfg.DeviceReqType == MEMORY_DMA_REQ) + dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH = dma->des0_phy_addr; + wmb(); + dma->regs->DMA_CCR_CH[ch] |= (SYSTEM_DMA_RUN | SYSTEM_DMA_REQ_EN); + wmb(); + } + +start_dma_out: + local_irq_restore(flags); + return ret ; +} + +int wmt_wake_dma( + dmach_t ch, + dma_addr_t dma_ptr, + dma_addr_t dma_prt2, + unsigned int size) +{ + int ret = 0; + struct dma_info_s *dma = &dma_chan[ch] ; + /*dump_dma_regs(ch);*/ + + DPRINTK("size = %x, chunksize=%x\n", size, dma->device_cfg.ChunkSize); + + if (size == 0) + return -EINVAL ; + if (size > dma->device_cfg.ChunkSize) + return -EOVERFLOW ; + if (dma->regs->DMA_CCR_CH[ch] & SYSTEM_DMA_RUN) { /*still run*/ + ret = add_descript(ch, dma->device_cfg, dma_ptr, size); + dma->residue_des0cnt += ret ; + wmb(); + dma->regs->DMA_CCR_CH[ch] |= DMA_WAKE; + wmb(); + } + return ret ; +} + +/*=============================================================================*/ +/**/ +/* wmt_request_dma - allocate one of the DMA chanels*/ +/* @channel: Pointer to the location of the allocated channel's identifier*/ +/* @device_id: An ascii name for the claiming device*/ +/* @device: The WMT peripheral targeted by this request*/ +/* @callback: Function to be called when the DMA completes*/ +/* @data: A cookie passed back to the callback function*/ +/**/ +/* This function will search for a free DMA channel and returns the*/ +/* address of the hardware registers for that channel as the channel*/ +/* identifier. This identifier is written to the location pointed by*/ +/* @dma_regs. The list of possible values for @device are listed into*/ +/* linux/include/asm-arm/arch-wmt/dma.h as a dma_device_t enum.*/ +/**/ +/* Note that reading from a port and writing to the same port are*/ +/* actually considered as two different streams requiring separate*/ +/* DMA registrations.*/ +/**/ +/* The @callback function is called from interrupt context when one*/ +/* of the two possible DMA buffers in flight has terminated. That*/ +/* function has to be small and efficient while posponing more complex*/ +/* processing to a lower priority execution context.*/ +/**/ +/* If no channels are available, or if the desired @device is already in*/ +/* use by another DMA channel, then an error code is returned. This*/ +/* function must be called before any other DMA calls.*/ +/**/ +/* return: 0 if successful*/ +/**/ +/*=============================================================================*/ +int wmt_request_dma(dmach_t *channel, const char *device_id, enum dma_device_e device, + void (*callback)(void *data), void *callback_data) +{ + int ch ; + int descript_size = MAX_DESCRIPT_SIZE; + struct dma_info_s *dma = NULL; + *channel = -1; + + /* Ask for Free Channels*/ + spin_lock(&dma_list_lock); + for (ch = 1 ; ch < MAX_DMA_CHANNELS ; ++ch) { + dma = &dma_chan[ch]; + if (dma->in_use == 0) + break ; + } + if (ch >= MAX_DMA_CHANNELS) { + DPRINTK("DMA %s: no free DMA channel available\n", device_id); + return -EBUSY; + } + spin_unlock(&dma_list_lock); + + dma_int.request_chans |= (1 << ch) ; + + /* Configure DMA channel settings.*/ + *channel = ch; + dma->device_id = device_id; + dma->device_no = device ; + dma->callback = callback; + dma->callback_data = callback_data ; + dma->in_use = 1 ; + dma->des0cnt = 0 ; + dma->des1cnt = 0; + dma->accu_size = 0; + dma->residue_des0cnt = 0; + dma->residue_des1cnt = 0; + dma->descript_size = descript_size; + + /* clear status register*/ + dma->regs->DMA_ISR = 1 << ch; + dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH = 0x0; /*reset descript*/ + dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH = 0x0; /*reset descript*/ + dma->des_addr.des_0 = (unsigned long *)dma_alloc_coherent( + NULL, + descript_size, + &dma->des0_phy_addr, + GFP_KERNEL); + dma->max_des0cnt = (descript_size - DMA_DES1_SIZE) / DMA_DES0_SIZE + 1; + DPRINTK("descript 0 addr--- virt:0x%x , phy:0x%x\n", dma->des_addr.des_0, dma->des0_phy_addr); + + /* setup default device*/ + dma->device_cfg = dma_device_cfg_table[device] ; + + dma->regs->DMA_CCR_CH[ch] = (dma_device_cfg_table[device].DefaultCCR & DMA_USER_SET_MASK) ; + + if (device != MEMORY_DMA_REQ) + /*need to set device req number*/ + dma->regs->DMA_CCR_CH[ch] |= device << DMA_REQ_ID_SHIFT; + + else { + dma->des_addr.des_1 = (unsigned long *)dma_alloc_coherent(NULL, + descript_size, &dma->des0_phy_addr, GFP_KERNEL); + dma->max_des1cnt = (descript_size - DMA_DES1_SIZE)/DMA_DES0_SIZE + 1; + } + + + DPRINTK("requested dma ch=%d to device=%d\n", ch, device); + + return 0 ; /* No error*/ +} + +/*=============================================================================*/ +/**/ +/* wmt_clear_dma - clear DMA pointers*/ +/* @ch:identifier for the channel to use*/ +/**/ +/* This clear any DMA state so the DMA engine is ready to restart*/ +/* with new buffers through wmt_start_dma(). Any buffers in flight*/ +/* are discarded.*/ +/**/ +/* The @regs identifier is provided by a successful call to*/ +/* wmt_request_dma().*/ +/**/ +/* return: NULL*/ +/*=============================================================================*/ +void wmt_clear_dma(dmach_t ch) +{ + unsigned long flags; + struct dma_info_s *dma ; + dma = &dma_chan[ch] ; + + local_irq_save(flags); + + /* clear status register*/ + dma->regs->DMA_CCR_CH[ch] &= ~(SYSTEM_DMA_REQ_EN); + udelay(5); + dma->regs->DMA_CCR_CH[ch] &= ~(SYSTEM_DMA_RUN); + dma->regs->DMA_ISR = 1 << ch; /*write 1 clear*/ + dma->des0cnt = 0; + dma->des1cnt = 0; + dma->accu_size = 0; + dma->residue_des0cnt = 0; + dma->residue_des0cnt = 0; + local_irq_restore(flags); +} + +/*=============================================================================*/ +/**/ +/* wmt_free_dma - free a WMT DMA channel*/ +/* @ch: identifier for the channel to free*/ +/**/ +/* This clears all activities on a given DMA channel and releases it*/ +/* for future requests. The @ch identifier is provided by a*/ +/* successful call to wmt_request_dma().*/ +/**/ +/* return: NULL*/ +/**/ +/*=============================================================================*/ +void wmt_free_dma(dmach_t ch) +{ + struct dma_info_s *dma; + enum dma_device_e dev_no ; + + if ((unsigned) ch >= MAX_DMA_CHANNELS) { + DPRINTK("%s: bad DMA identifier\n", __func__); + return ; + } + + dma = &dma_chan[ch]; + if (dma->in_use == 0) { + DPRINTK("%s: Trying to free DMA%d\n", __func__, ch); + return; + } + + if (dma->device_no == DEVICE_RESERVED) { + DPRINTK("%s: Trying to free free DMA\n", __func__); + return ; + } + + wmt_clear_dma(ch); + + /* Int*/ + dma_int.request_chans &= ~(1 << ch) ; + + dev_no = dma->device_no ; + dma_free_coherent(NULL, + dma->descript_size, + (void *)dma->des_addr.des_0, + (dma_addr_t)dma->des0_phy_addr); + if (dma->device_no == MEMORY_DMA_REQ) + dma_free_coherent(NULL, + dma->descript_size, + (void *)dma->des_addr.des_1, + (dma_addr_t)dma->des1_phy_addr); + + dma->device_no = DEVICE_RESERVED ; + dma_chan[ch].device_id = NULL ; + dma_chan[ch].des0cnt = 0; + dma_chan[ch].des1cnt = 0; + dma_chan[ch].accu_size = 0; + dma_chan[ch].residue_des0cnt = 0; + dma_chan[ch].residue_des0cnt = 0; + dma_chan[ch].max_des0cnt = 0; + dma_chan[ch].max_des1cnt = 0; + dma_chan[ch].in_use = 0; +} + +/*=============================================================================*/ +/**/ +/* wmt_reset_dma - reset a DMA channel*/ +/* @ch: identifier for the channel to use*/ +/**/ +/* This function resets and reconfigure the given DMA channel. This is*/ +/* particularly useful after a sleep/wakeup event.*/ +/**/ +/* The @ch identifier is provided by a successful call to*/ +/* request_dma().*/ +/**/ +/* return: NULL*/ +/**/ +/*=============================================================================*/ +void wmt_reset_dma(dmach_t ch) +{ + if (ch >= MAX_DMA_CHANNELS) { + DPRINTK("%s: bad DMA identifier\n", __func__); + return; + } + + wmt_clear_dma(ch); +} + +/*===========================================================================*/ +/* wmt_setup_dma*/ +/**/ +/* Don't setup Dma channel while Dma is busy*/ +/**/ +/* return: 0: success*/ +/*===========================================================================*/ +int wmt_setup_dma(dmach_t ch, struct dma_device_cfg_s device_cfg) +{ + struct dma_info_s *dma ; + enum dma_device_e dev_no ; + + dma = &dma_chan[ch] ; + + if ((ch >= MAX_DMA_CHANNELS) || (dma_chan[ch].in_use == 0)) { + printk("%s: bad DMA identifier\n", __func__) ; + return -EINVAL ; + } + + if (dma->device_no != device_cfg.DeviceReqType) { + printk("%s: bad Device_NO\n", __func__) ; + return -ENODEV ; + } + + /* Apply new device config to DMA interface.*/ + dev_no = dma->device_no ; + dma_device_cfg_table[dev_no] = device_cfg ; + dma->device_cfg = dma_device_cfg_table[dev_no] ; + + /* Clear status register.*/ + dma->regs->DMA_ISR = 1<<ch; + + DPRINTK("%s old CCR=0x%.8x\n", __func__, dma->regs->DMA_CCR_CH[ch]) ; + + /* Apply new DMA config to DMA controller.*/ + dma->regs->DMA_CCR_CH[ch] = dma_device_cfg_table[dev_no].DefaultCCR ; + DPRINTK("%s new CCR=0x%.8x\n", __func__, dma_device_cfg_table[dev_no].DefaultCCR) ; + DPRINTK("%s old CCR=0x%.8x\n", __func__, dma->regs->DMA_CCR_CH[ch].CCR) ; + if (device_cfg.DeviceReqType != MEMORY_DMA_REQ) + dma->regs->DMA_CCR_CH[ch] |= device_cfg.DeviceReqType << DMA_REQ_ID_SHIFT; + /*Device -> Memory(Read) && Memory(Write)i-->Device*/ + if (dev_no != MEMORY_DMA_REQ) { + dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH = dma->device_cfg.MIF1addr; + /*dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH = 0 ;*/ + } + /* + if (dev_no == MEMORY_DMA_REQ) { + dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH = dma->des0_phy_addr; + dma_mem_regs->mem_reg_group[ch].DMA_IF1CPR_CH = dma->des1_phy_addr; + } + */ + DPRINTK("%s new CCR=0x%.8x\n", __func__, dma->regs->DMA_CCR_CH[ch]) ; + + return 0; +} + +/*=============================================================================*/ +/**/ +/* wmt_stop_dma - stop DMA in progress*/ +/* @regs: identifier for the channel to use*/ +/**/ +/* This stops DMA without clearing buffer pointers. Unlike*/ +/* clear_dma() this allows subsequent use of resume_dma()*/ +/* or get_dma_pos().*/ +/**/ +/* The @regs identifier is provided by a successful call to*/ +/* request_dma().*/ +/**/ +/*=============================================================================*/ +void wmt_stop_dma(dmach_t ch) +{ + struct dma_info_s *dma; +#ifdef DMA4_ERRATA + unsigned int now_time = 0; + unsigned int delay_time = 0; +#endif + + if ((ch >= MAX_DMA_CHANNELS) || + (dma_chan[ch].in_use == 0)) { + DPRINTK("%s: bad DMA identifier\n", __func__); + return; + } + + dma = &dma_chan[ch]; + dma->regs->DMA_CCR_CH[ch] &= ~(SYSTEM_DMA_REQ_EN); + udelay(5); +#ifdef DMA4_ERRATA + if (dma->regs->DMA_CCR_CH[ch] & (SYSTEM_DMA_RUN)) { + dma->regs->DMA_CCR_CH[ch] |= (DMA_UP_MEMREG_EN);/*update memory register before reading*/ + now_time = wmt_read_oscr(); + while (dma->regs->DMA_CCR_CH[ch] & (DMA_UP_MEMREG_EN)) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 15) {/*5us*/ + DPRINTK("[%d]Warnning:up_mem_reg did not clear[%x]\n", ch, dma->regs->DMA_CCR_CH[ch]); + dma->regs->DMA_CCR_CH[ch] &= ~DMA_UP_MEMREG_EN;/*clear DMA_UP_MEMREG_EN*/ + break; + } + } + } +#endif + dma->regs->DMA_CCR_CH[ch] &= ~SYSTEM_DMA_RUN; +} + +/*=============================================================================*/ +/**/ +/* wmt_resume_dma - resume DMA on a stopped channel*/ +/* @regs: identifier for the channel to use*/ +/**/ +/* This resumes DMA on a channel previously stopped with*/ +/* wmt_stop_dma().*/ +/**/ +/* The @regs identifier is provided by a successful call to*/ +/* wmt_request_dma().*/ +/**/ +/*=============================================================================*/ +void wmt_resume_dma(dmach_t ch) +{ + struct dma_info_s *dma; + struct dma_mem_reg_group_s dma_mem_reg ; + dma = &dma_chan[ch] ; + if (dma->regs->DMA_CCR_CH[ch] & DMA_ACTIVE) {/*if dma was active , disable dma first*/ + dma->regs->DMA_CCR_CH[ch] &= ~(SYSTEM_DMA_REQ_EN); + udelay(5); + dma->regs->DMA_CCR_CH[ch] &= ~(SYSTEM_DMA_RUN); + } + if ((ch >= MAX_DMA_CHANNELS) || + (dma_chan[ch].in_use == 0)) { + DPRINTK("%s: bad DMA identifier\n", __func__); + return; + } + dma_mem_reg = wmt_get_dma_pos_info(ch); + revise_descript(ch, dma->device_cfg, dma_mem_reg); + dma->regs->DMA_CCR_CH[ch] |= (SYSTEM_DMA_RUN | SYSTEM_DMA_REQ_EN); +} + +/*=============================================================================*/ +/**/ +/* wmt_get_dma_pos_info - return current DMA position*/ +/* @ch: identifier for the channel to use*/ +/**/ +/* This function returns the current physical (or bus) address for the*/ +/* given DMA channel. If the channel is running i.e. not in a stopped*/ +/* state then the caller must disable interrupts prior calling this*/ +/* function and process the returned value before re-enabling them to*/ +/* prevent races with the completion interrupt handler and the callback*/ +/* function. The validation of the returned value is the caller's*/ +/* responsibility as well -- the hardware seems to return out of range*/ +/* values when the DMA engine completes a buffer.*/ +/**/ +/* The @ch identifier is provided by a successful call to*/ +/* wmt_request_dma().*/ +/**/ +/*=============================================================================*/ +struct dma_mem_reg_group_s wmt_get_dma_pos_info(dmach_t ch) +{ + struct dma_mem_reg_group_s dma_mem_reg; +#ifdef DMA4_ERRATA + struct dma_info_s *dma; + unsigned int now_time = 0; + unsigned long flags; + unsigned int delay_time = 0; + dma = &dma_chan[ch]; +#endif + + if ((ch >= MAX_DMA_CHANNELS) || + (dma_chan[ch].in_use == 0)) { + DPRINTK("%s: bad DMA identifier\n", __func__); + goto out; + } +#ifdef DMA4_ERRATA + if (dma->regs->DMA_CCR_CH[ch] & (SYSTEM_DMA_RUN)) { + spin_lock_irqsave(&dma_list_lock, flags); + dma->regs->DMA_CCR_CH[ch] |= (DMA_UP_MEMREG_EN);/*update memory register before reading*/ + + now_time = wmt_read_oscr(); + while (dma->regs->DMA_CCR_CH[ch] & (DMA_UP_MEMREG_EN)) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 15) {/*5us*/ + DPRINTK("[%d]Warnning:up_mem_reg did not clear[%x]\n", ch, dma->regs->DMA_CCR_CH[ch]); + dma->regs->DMA_CCR_CH[ch] &= ~DMA_UP_MEMREG_EN;/*clear DMA_UP_MEMREG_EN*/ + break; + } + + } + + spin_unlock_irqrestore(&dma_list_lock, flags); + } +#endif + + dma_mem_reg.DMA_IF0BAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0BAR_CH; + dma_mem_reg.DMA_IF0CPR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH; + dma_mem_reg.DMA_IF0RBR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0RBR_CH; + dma_mem_reg.DMA_IF0DAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0DAR_CH; + dma_mem_reg.DMA_IF1BAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1BAR_CH; + dma_mem_reg.DMA_IF1CPR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1CPR_CH; + dma_mem_reg.DMA_IF1RBR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1RBR_CH; + dma_mem_reg.DMA_IF1DAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH; + +out: + return dma_mem_reg; +} + +/*=============================================================================*/ +/**/ +/* wmt_get_dma_pos - return current DMA position*/ +/* @ch: identifier for the channel to use*/ +/**/ +/* This function returns the current physical (or bus) address for the*/ +/* given DMA channel. If the channel is running i.e. not in a stopped*/ +/* state then the caller must disable interrupts prior calling this*/ +/* function and process the returned value before re-enabling them to*/ +/* prevent races with the completion interrupt handler and the callback*/ +/* function. The validation of the returned value is the caller's*/ +/* responsibility as well -- the hardware seems to return out of range*/ +/* values when the DMA engine completes a buffer.*/ +/**/ +/* The @ch identifier is provided by a successful call to*/ +/* wmt_request_dma().*/ +/**/ +/*=============================================================================*/ +unsigned int wmt_get_dma_pos(dmach_t ch) +{ + struct dma_mem_reg_group_s dma_mem_reg; + struct dma_info_s *dma; +#ifdef DMA4_ERRATA + unsigned long flags; + unsigned int now_time; + unsigned int delay_time; +#endif + dma = &dma_chan[ch]; + + if ((ch >= MAX_DMA_CHANNELS) || + (dma_chan[ch].in_use == 0)) { + DPRINTK("%s: bad DMA identifier\n", __func__); + goto out; + } +#ifdef DAM4_ERRATA + if (dma->regs->DMA_CCR_CH[ch] & (SYSTEM_DMA_RUN)) { + spin_lock_irqsave(&dma_list_lock, flags); + dma->regs->DMA_CCR_CH[ch] |= (DMA_UP_MEMREG_EN);/*update memory register before reading*/ + now_time = wmt_read_oscr(); + while (dma->regs->DMA_CCR_CH[ch] & (DMA_UP_MEMREG_EN)) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 15) {/*5us*/ + dma->regs->DMA_CCR_CH[ch] &= ~DMA_UP_MEMREG_EN;/*clear DMA_UP_MEMREG_EN*/ + break; + } + } + spin_unlock_irqrestore(&dma_list_lock, flags); + } +#endif + + dma_mem_reg.DMA_IF0BAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0BAR_CH; + dma_mem_reg.DMA_IF0CPR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH; + dma_mem_reg.DMA_IF0RBR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0RBR_CH; + dma_mem_reg.DMA_IF0DAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF0DAR_CH; + dma_mem_reg.DMA_IF1BAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1BAR_CH; + dma_mem_reg.DMA_IF1CPR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1CPR_CH; + dma_mem_reg.DMA_IF1RBR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1RBR_CH; + dma_mem_reg.DMA_IF1DAR_CH = dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH; + +out: + return dma_mem_reg.DMA_IF0DAR_CH; +} + + +/*===========================================================================*/ +/* wmt_dma_busy*/ +/**/ +/* return: 1: busy , 0: not busy , other: fail*/ +/*===========================================================================*/ +int wmt_dma_busy(dmach_t ch) +{ + struct dma_info_s *dma; + + dma = &dma_chan[ch]; + if (ch >= MAX_DMA_CHANNELS || (dma_chan[ch].in_use == 0)) { + DPRINTK("%s: bad DMA identifier\n", __func__); + return -1 ; + } + + if (dma->regs->DMA_ISR & (1<<ch)) { + dma->regs->DMA_ISR = (1<<ch); /*write 1 clear*/ + return 0 ; + } else + return 1; +} + +/*===========================================================================*/ +/* wmt_dump_dma_regs*/ +/**/ +/* return: NULL*/ +/*===========================================================================*/ +void wmt_dump_dma_regs(dmach_t ch) +{ + struct dma_info_s *dma = &dma_chan[ch] ; + struct dma_regs_s *regs = dma->regs ; +#ifdef DMA4_ERRATA + unsigned long flags; + unsigned int now_time = 0; + unsigned int delay_time = 0; +#endif + + printk("0x%8.8X : [0x%8.8X] GCR \n", \ + (unsigned int)&(regs->DMA_GCR) , (unsigned int)regs->DMA_GCR) ; + printk("0x%8.8X : [0x%8.8X] MPRP \n", \ + (unsigned int)&(regs->DMA_MRPR) , (unsigned int)regs->DMA_MRPR) ; + printk("0x%8.8X : [0x%8.8X] IER \n", \ + (unsigned int)&(regs->DMA_IER) , (unsigned int)regs->DMA_IER) ; + printk("0x%8.8X : [0x%8.8X] ISR \n", \ + (unsigned int)&(regs->DMA_ISR) , (unsigned int)regs->DMA_ISR) ; + printk("0x%8.8X : [0x%8.8X] TMR \n", \ + (unsigned int)&(regs->DMA_TMR) , (unsigned int)regs->DMA_TMR) ; + printk("0x%8.8X : [0x%8.8X] CCR \n", \ + (unsigned int)&(regs->DMA_CCR_CH[ch]) , (unsigned int)regs->DMA_CCR_CH[ch]) ; + +#ifdef DMA4_ERRATA + if (dma->regs->DMA_CCR_CH[ch] & (SYSTEM_DMA_RUN)) { + spin_lock_irqsave(&dma_list_lock, flags); + dma->regs->DMA_CCR_CH[ch] |= (DMA_UP_MEMREG_EN);/*update memory register before reading*/ + + now_time = wmt_read_oscr(); + while (dma->regs->DMA_CCR_CH[ch] & (DMA_UP_MEMREG_EN)) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 15) {/*5us*/ + DPRINTK("[%d]Warnning:up_mem_reg did not clear[%x]\n", ch, dma->regs->DMA_CCR_CH[ch]); + dma->regs->DMA_CCR_CH[ch] &= ~DMA_UP_MEMREG_EN;/*clear DMA_UP_MEMREG_EN*/ + break; + } + + } + + spin_unlock_irqrestore(&dma_list_lock, flags); + } +#endif + printk("0x%8.8X : [0x%8.8X] Residue Bytes 0 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF0RBR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF0RBR_CH) ; + printk("0x%8.8X : [0x%8.8X] Data Address 0 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF0DAR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF0DAR_CH) ; + printk("0x%8.8X : [0x%8.8X] Branch Address 0 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF0BAR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF0BAR_CH) ; + printk("0x%8.8X : [0x%8.8X] Command Pointer 0 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH) ; + + printk("0x%8.8X : [0x%8.8X] Residue Bytes 1 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF1RBR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF1RBR_CH) ; + printk("0x%8.8X : [0x%8.8X] Data Address 1 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH) ; + printk("0x%8.8X : [0x%8.8X] Branch Address 1 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF1BAR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF1BAR_CH) ; + printk("0x%8.8X : [0x%8.8X] Command Pointer 1 \n", \ + (unsigned int)&(dma_mem_regs->mem_reg_group[ch].DMA_IF1CPR_CH) + , (unsigned int)dma_mem_regs->mem_reg_group[ch].DMA_IF1CPR_CH) ; + +} + + +#ifdef CONFIG_PM +static int dma_suspend(void) +{ + return 0; +} + +static void dma_resume(void) +{ + struct dma_regs_s *dma_regs ; + auto_pll_divisor(DEV_DMA, CLK_ENABLE, 0, 0); + dma_regs = (struct dma_regs_s *) (io_p2v(DMA_CTRL_CFG_BASE_ADDR)) ; + /*dma_regs->GCR = ( DMA_GCR_GDMA_ENABLE | DMA_GCR_PRIORITY_FIXED | DMA_GCR_GINT_ENABLE ) ;*/ + dma_regs->DMA_GCR |= DMA_GLOBAL_EN; + dma_regs->DMA_ISR = ALL_INT_CLEAR; + dma_regs->DMA_IER |= ALL_INT_EN; + dma_regs->DMA_TMR &= ~SCHEDULE_RR_DISABLE; /*use RR schedule*/ + dma_regs->DMA_MRPR = (unsigned int)dma_mem_phy; +} + +#else +#define dma_suspend NULL +#define dma_resume NULL +#endif /* CONFIG_PM */ + + +static struct syscore_ops wmt_dma_syscore_ops = { + .suspend = dma_suspend, + .resume = dma_resume, +}; + +static int __init wmt_dma_init_devicefs(void) +{ + register_syscore_ops(&wmt_dma_syscore_ops); + return 0; +} + +device_initcall(wmt_dma_init_devicefs); + +/*===========================================================================*/ +/* wmt_dma_init*/ +/**/ +/* return: 0*/ +/*===========================================================================*/ +static int __init +wmt_dma_init(void) +{ + int ch ; + int ret = 0; + struct dma_regs_s *dma_regs ; + /* + *(volatile unsigned int *)(0xD8130254) |= BIT5; + */ + auto_pll_divisor(DEV_DMA, CLK_ENABLE, 0, 0); + + dma_regs = (struct dma_regs_s *) (io_p2v(DMA_CTRL_CFG_BASE_ADDR)) ; + + /**/ + /* software initial*/ + /**/ + dma_int.request_chans = 0 ; + dma_int.regs = (struct dma_regs_s *) (io_p2v(DMA_CTRL_CFG_BASE_ADDR)) ; + + for (ch = 0 ; ch < MAX_DMA_CHANNELS ; ++ch) { + dma_chan[ch].channel_no = ch ; + dma_chan[ch].regs = (struct dma_regs_s *) (io_p2v(DMA_CTRL_CFG_BASE_ADDR)) ; + dma_chan[ch].irq = ch ; + dma_chan[ch].device_no = DEVICE_RESERVED ; + dma_chan[ch].in_use = 0 ; + } + + dma_mem_regs = (struct dma_mem_reg_s *) (io_p2v((DMA_CTRL_CFG_BASE_ADDR + DMA_MEM_REG_OFFSET))); + if (!dma_mem_regs) { + printk("dma memory register allocate failed\n"); + ret = -1; + return ret; + } + dma_mem_phy = DMA_CTRL_CFG_BASE_ADDR + DMA_MEM_REG_OFFSET; + DPRINTK("MEM_REGS ADDR:Virt = 0x%x , Phy = 0x%x\n", dma_mem_regs , dma_mem_phy); + + if (dma_mem_phy & 0x000000FF) {/*8 DW alignment*/ + printk("dma memory registers did not 8 DW alignment"); + ret = -1; + return ret; + } + + /**/ + /* hardware initial*/ + /**/ + dma_regs->DMA_GCR |= DMA_SW_RST ; + dma_regs->DMA_GCR |= DMA_GLOBAL_EN; + dma_regs->DMA_ISR = ALL_INT_CLEAR; + dma_regs->DMA_IER |= ALL_INT_EN; + dma_regs->DMA_TMR &= ~SCHEDULE_RR_DISABLE; /*use RR schedule*/ + dma_regs->DMA_MRPR = (unsigned int)dma_mem_phy; + DPRINTK("0x%8.8X : [0x%8.8X] DMA_GSR_REG \n", \ + (unsigned int)&dma_regs->GSR , dma_regs->GSR) ; + + for (ch = 0 ; ch < MAX_DMA_CHANNELS ; ++ch) { + dma_mem_regs->mem_reg_group[ch].DMA_IF0CPR_CH = 0x100; + dma_mem_regs->mem_reg_group[ch].DMA_IF1DAR_CH = 0x100; + dma_regs->DMA_CCR_CH[ch] = 0x0; + } + DPRINTK("0x%8.8X : [0x%8.8X] DMA_GCR_REG \n", \ + (unsigned int)&dma_regs->DMA_GCR , dma_regs->DMA_GCR) ; + /* + for (ch = 0; ch < MAX_DMA_CHANNELS ; ++ch) + request_irq(dma_irq_no[ch], dma_irq_handler, IRQF_DISABLED, "dma", NULL); + */ + request_irq(IRQ_DMA_NONS, dma_irq_handler, IRQF_DISABLED, "dma", NULL); + + return ret; +} + +/*===========================================================================*/ +/* dma_exit*/ +/**/ +/* return: 0*/ +/*===========================================================================*/ +static void __exit +wmt_dma_exit(void) +{ + int ch; + /*remove_proc_entry("driver/dma", NULL);*/ + dma_free_coherent(NULL, + sizeof(struct dma_mem_reg_s), + dma_mem_regs, + dma_mem_phy); + + for (ch = 0; ch < MAX_DMA_CHANNELS ; ++ch) + free_irq(dma_irq_no[ch], NULL); +} + +__initcall(wmt_dma_init); +__exitcall(wmt_dma_exit); + +EXPORT_SYMBOL(wmt_request_dma); +EXPORT_SYMBOL(wmt_setup_dma); +EXPORT_SYMBOL(wmt_free_dma); +EXPORT_SYMBOL(wmt_clear_dma); +EXPORT_SYMBOL(wmt_reset_dma); +EXPORT_SYMBOL(wmt_start_dma); +EXPORT_SYMBOL(wmt_stop_dma); +EXPORT_SYMBOL(wmt_resume_dma); +EXPORT_SYMBOL(wmt_get_dma_pos_info); +EXPORT_SYMBOL(wmt_dma_busy); +EXPORT_SYMBOL(wmt_dump_dma_regs); |