131650d64SMarek Vasut /* 231650d64SMarek Vasut * Freescale i.MX28 APBH DMA driver 331650d64SMarek Vasut * 431650d64SMarek Vasut * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 531650d64SMarek Vasut * on behalf of DENX Software Engineering GmbH 631650d64SMarek Vasut * 731650d64SMarek Vasut * Based on code from LTIB: 831650d64SMarek Vasut * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. 931650d64SMarek Vasut * 1031650d64SMarek Vasut * This program is free software; you can redistribute it and/or modify 1131650d64SMarek Vasut * it under the terms of the GNU General Public License as published by 1231650d64SMarek Vasut * the Free Software Foundation; either version 2 of the License, or 1331650d64SMarek Vasut * (at your option) any later version. 1431650d64SMarek Vasut * 1531650d64SMarek Vasut * This program is distributed in the hope that it will be useful, 1631650d64SMarek Vasut * but WITHOUT ANY WARRANTY; without even the implied warranty of 1731650d64SMarek Vasut * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1831650d64SMarek Vasut * GNU General Public License for more details. 1931650d64SMarek Vasut * 2031650d64SMarek Vasut * You should have received a copy of the GNU General Public License along 2131650d64SMarek Vasut * with this program; if not, write to the Free Software Foundation, Inc., 2231650d64SMarek Vasut * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 2331650d64SMarek Vasut */ 2431650d64SMarek Vasut 2531650d64SMarek Vasut #include <linux/list.h> 2631650d64SMarek Vasut 2731650d64SMarek Vasut #include <common.h> 2831650d64SMarek Vasut #include <malloc.h> 2931650d64SMarek Vasut #include <asm/errno.h> 3031650d64SMarek Vasut #include <asm/io.h> 3131650d64SMarek Vasut #include <asm/arch/clock.h> 3231650d64SMarek Vasut #include <asm/arch/imx-regs.h> 3331650d64SMarek Vasut #include <asm/arch/sys_proto.h> 3431650d64SMarek Vasut #include <asm/arch/dma.h> 3531650d64SMarek Vasut 3631650d64SMarek Vasut static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; 3731650d64SMarek Vasut 3831650d64SMarek Vasut /* 3931650d64SMarek Vasut * Test is the DMA channel is valid channel 4031650d64SMarek Vasut */ 4131650d64SMarek Vasut int mxs_dma_validate_chan(int channel) 4231650d64SMarek Vasut { 4331650d64SMarek Vasut struct mxs_dma_chan *pchan; 4431650d64SMarek Vasut 4531650d64SMarek Vasut if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) 4631650d64SMarek Vasut return -EINVAL; 4731650d64SMarek Vasut 4831650d64SMarek Vasut pchan = mxs_dma_channels + channel; 4931650d64SMarek Vasut if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED)) 5031650d64SMarek Vasut return -EINVAL; 5131650d64SMarek Vasut 5231650d64SMarek Vasut return 0; 5331650d64SMarek Vasut } 5431650d64SMarek Vasut 5531650d64SMarek Vasut /* 56aa72e43bSMarek Vasut * Return the address of the command within a descriptor. 57aa72e43bSMarek Vasut */ 58aa72e43bSMarek Vasut static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) 59aa72e43bSMarek Vasut { 60aa72e43bSMarek Vasut return desc->address + offsetof(struct mxs_dma_desc, cmd); 61aa72e43bSMarek Vasut } 62aa72e43bSMarek Vasut 63aa72e43bSMarek Vasut /* 64aa72e43bSMarek Vasut * Read a DMA channel's hardware semaphore. 65aa72e43bSMarek Vasut * 66aa72e43bSMarek Vasut * As used by the MXS platform's DMA software, the DMA channel's hardware 67aa72e43bSMarek Vasut * semaphore reflects the number of DMA commands the hardware will process, but 68aa72e43bSMarek Vasut * has not yet finished. This is a volatile value read directly from hardware, 69aa72e43bSMarek Vasut * so it must be be viewed as immediately stale. 70aa72e43bSMarek Vasut * 71aa72e43bSMarek Vasut * If the channel is not marked busy, or has finished processing all its 72aa72e43bSMarek Vasut * commands, this value should be zero. 73aa72e43bSMarek Vasut * 74aa72e43bSMarek Vasut * See mxs_dma_append() for details on how DMA command blocks must be configured 75aa72e43bSMarek Vasut * to maintain the expected behavior of the semaphore's value. 76aa72e43bSMarek Vasut */ 77aa72e43bSMarek Vasut static int mxs_dma_read_semaphore(int channel) 78aa72e43bSMarek Vasut { 799c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 809c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 81aa72e43bSMarek Vasut uint32_t tmp; 82aa72e43bSMarek Vasut int ret; 83aa72e43bSMarek Vasut 84aa72e43bSMarek Vasut ret = mxs_dma_validate_chan(channel); 85aa72e43bSMarek Vasut if (ret) 86aa72e43bSMarek Vasut return ret; 87aa72e43bSMarek Vasut 88aa72e43bSMarek Vasut tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); 89aa72e43bSMarek Vasut 90aa72e43bSMarek Vasut tmp &= APBH_CHn_SEMA_PHORE_MASK; 91aa72e43bSMarek Vasut tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; 92aa72e43bSMarek Vasut 93aa72e43bSMarek Vasut return tmp; 94aa72e43bSMarek Vasut } 95aa72e43bSMarek Vasut 96c3dfe707SMarek Vasut #ifndef CONFIG_SYS_DCACHE_OFF 97c3dfe707SMarek Vasut void mxs_dma_flush_desc(struct mxs_dma_desc *desc) 98c3dfe707SMarek Vasut { 99c3dfe707SMarek Vasut uint32_t addr; 100c3dfe707SMarek Vasut uint32_t size; 101c3dfe707SMarek Vasut 102c3dfe707SMarek Vasut addr = (uint32_t)desc; 103c3dfe707SMarek Vasut size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); 104c3dfe707SMarek Vasut 105c3dfe707SMarek Vasut flush_dcache_range(addr, addr + size); 106c3dfe707SMarek Vasut } 107c3dfe707SMarek Vasut #else 108c3dfe707SMarek Vasut inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {} 109c3dfe707SMarek Vasut #endif 110c3dfe707SMarek Vasut 111aa72e43bSMarek Vasut /* 11231650d64SMarek Vasut * Enable a DMA channel. 11331650d64SMarek Vasut * 11431650d64SMarek Vasut * If the given channel has any DMA descriptors on its active list, this 11531650d64SMarek Vasut * function causes the DMA hardware to begin processing them. 11631650d64SMarek Vasut * 11731650d64SMarek Vasut * This function marks the DMA channel as "busy," whether or not there are any 11831650d64SMarek Vasut * descriptors to process. 11931650d64SMarek Vasut */ 120aa72e43bSMarek Vasut static int mxs_dma_enable(int channel) 12131650d64SMarek Vasut { 1229c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 1239c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 12431650d64SMarek Vasut unsigned int sem; 12531650d64SMarek Vasut struct mxs_dma_chan *pchan; 12631650d64SMarek Vasut struct mxs_dma_desc *pdesc; 12731650d64SMarek Vasut int ret; 12831650d64SMarek Vasut 12931650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 13031650d64SMarek Vasut if (ret) 13131650d64SMarek Vasut return ret; 13231650d64SMarek Vasut 13331650d64SMarek Vasut pchan = mxs_dma_channels + channel; 13431650d64SMarek Vasut 13531650d64SMarek Vasut if (pchan->pending_num == 0) { 13631650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_BUSY; 13731650d64SMarek Vasut return 0; 13831650d64SMarek Vasut } 13931650d64SMarek Vasut 14031650d64SMarek Vasut pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); 14131650d64SMarek Vasut if (pdesc == NULL) 14231650d64SMarek Vasut return -EFAULT; 14331650d64SMarek Vasut 14431650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_BUSY) { 14531650d64SMarek Vasut if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) 14631650d64SMarek Vasut return 0; 14731650d64SMarek Vasut 14831650d64SMarek Vasut sem = mxs_dma_read_semaphore(channel); 14931650d64SMarek Vasut if (sem == 0) 15031650d64SMarek Vasut return 0; 15131650d64SMarek Vasut 15231650d64SMarek Vasut if (sem == 1) { 15331650d64SMarek Vasut pdesc = list_entry(pdesc->node.next, 15431650d64SMarek Vasut struct mxs_dma_desc, node); 15531650d64SMarek Vasut writel(mxs_dma_cmd_address(pdesc), 15631650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); 15731650d64SMarek Vasut } 15831650d64SMarek Vasut writel(pchan->pending_num, 15931650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_sema); 16031650d64SMarek Vasut pchan->active_num += pchan->pending_num; 16131650d64SMarek Vasut pchan->pending_num = 0; 16231650d64SMarek Vasut } else { 16331650d64SMarek Vasut pchan->active_num += pchan->pending_num; 16431650d64SMarek Vasut pchan->pending_num = 0; 16531650d64SMarek Vasut writel(mxs_dma_cmd_address(pdesc), 16631650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); 16731650d64SMarek Vasut writel(pchan->active_num, 16831650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_sema); 16931650d64SMarek Vasut writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), 17031650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 17131650d64SMarek Vasut } 17231650d64SMarek Vasut 17331650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_BUSY; 17431650d64SMarek Vasut return 0; 17531650d64SMarek Vasut } 17631650d64SMarek Vasut 17731650d64SMarek Vasut /* 17831650d64SMarek Vasut * Disable a DMA channel. 17931650d64SMarek Vasut * 18031650d64SMarek Vasut * This function shuts down a DMA channel and marks it as "not busy." Any 18131650d64SMarek Vasut * descriptors on the active list are immediately moved to the head of the 18231650d64SMarek Vasut * "done" list, whether or not they have actually been processed by the 18331650d64SMarek Vasut * hardware. The "ready" flags of these descriptors are NOT cleared, so they 18431650d64SMarek Vasut * still appear to be active. 18531650d64SMarek Vasut * 18631650d64SMarek Vasut * This function immediately shuts down a DMA channel's hardware, aborting any 18731650d64SMarek Vasut * I/O that may be in progress, potentially leaving I/O hardware in an undefined 18831650d64SMarek Vasut * state. It is unwise to call this function if there is ANY chance the hardware 18931650d64SMarek Vasut * is still processing a command. 19031650d64SMarek Vasut */ 191aa72e43bSMarek Vasut static int mxs_dma_disable(int channel) 19231650d64SMarek Vasut { 19331650d64SMarek Vasut struct mxs_dma_chan *pchan; 1949c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 1959c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 19631650d64SMarek Vasut int ret; 19731650d64SMarek Vasut 19831650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 19931650d64SMarek Vasut if (ret) 20031650d64SMarek Vasut return ret; 20131650d64SMarek Vasut 20231650d64SMarek Vasut pchan = mxs_dma_channels + channel; 20331650d64SMarek Vasut 20431650d64SMarek Vasut if (!(pchan->flags & MXS_DMA_FLAGS_BUSY)) 20531650d64SMarek Vasut return -EINVAL; 20631650d64SMarek Vasut 20731650d64SMarek Vasut writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), 20831650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 20931650d64SMarek Vasut 21031650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_BUSY; 21131650d64SMarek Vasut pchan->active_num = 0; 21231650d64SMarek Vasut pchan->pending_num = 0; 21331650d64SMarek Vasut list_splice_init(&pchan->active, &pchan->done); 21431650d64SMarek Vasut 21531650d64SMarek Vasut return 0; 21631650d64SMarek Vasut } 21731650d64SMarek Vasut 21831650d64SMarek Vasut /* 21931650d64SMarek Vasut * Resets the DMA channel hardware. 22031650d64SMarek Vasut */ 221aa72e43bSMarek Vasut static int mxs_dma_reset(int channel) 22231650d64SMarek Vasut { 2239c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2249c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 22531650d64SMarek Vasut int ret; 226*0e5c05efSMarek Vasut #if defined(CONFIG_MX23) 227*0e5c05efSMarek Vasut uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set); 228*0e5c05efSMarek Vasut uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET; 229*0e5c05efSMarek Vasut #elif defined(CONFIG_MX28) 230*0e5c05efSMarek Vasut uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set); 231*0e5c05efSMarek Vasut uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET; 232*0e5c05efSMarek Vasut #endif 23331650d64SMarek Vasut 23431650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 23531650d64SMarek Vasut if (ret) 23631650d64SMarek Vasut return ret; 23731650d64SMarek Vasut 238*0e5c05efSMarek Vasut writel(1 << (channel + offset), setreg); 23931650d64SMarek Vasut 24031650d64SMarek Vasut return 0; 24131650d64SMarek Vasut } 24231650d64SMarek Vasut 24331650d64SMarek Vasut /* 24431650d64SMarek Vasut * Enable or disable DMA interrupt. 24531650d64SMarek Vasut * 24631650d64SMarek Vasut * This function enables the given DMA channel to interrupt the CPU. 24731650d64SMarek Vasut */ 248aa72e43bSMarek Vasut static int mxs_dma_enable_irq(int channel, int enable) 24931650d64SMarek Vasut { 2509c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2519c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 25231650d64SMarek Vasut int ret; 25331650d64SMarek Vasut 25431650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 25531650d64SMarek Vasut if (ret) 25631650d64SMarek Vasut return ret; 25731650d64SMarek Vasut 25831650d64SMarek Vasut if (enable) 25931650d64SMarek Vasut writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), 26031650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl1_set); 26131650d64SMarek Vasut else 26231650d64SMarek Vasut writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), 26331650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl1_clr); 26431650d64SMarek Vasut 26531650d64SMarek Vasut return 0; 26631650d64SMarek Vasut } 26731650d64SMarek Vasut 26831650d64SMarek Vasut /* 26931650d64SMarek Vasut * Clear DMA interrupt. 27031650d64SMarek Vasut * 27131650d64SMarek Vasut * The software that is using the DMA channel must register to receive its 27231650d64SMarek Vasut * interrupts and, when they arrive, must call this function to clear them. 27331650d64SMarek Vasut */ 274aa72e43bSMarek Vasut static int mxs_dma_ack_irq(int channel) 27531650d64SMarek Vasut { 2769c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2779c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 27831650d64SMarek Vasut int ret; 27931650d64SMarek Vasut 28031650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 28131650d64SMarek Vasut if (ret) 28231650d64SMarek Vasut return ret; 28331650d64SMarek Vasut 28431650d64SMarek Vasut writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr); 28531650d64SMarek Vasut writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr); 28631650d64SMarek Vasut 28731650d64SMarek Vasut return 0; 28831650d64SMarek Vasut } 28931650d64SMarek Vasut 29031650d64SMarek Vasut /* 29131650d64SMarek Vasut * Request to reserve a DMA channel 29231650d64SMarek Vasut */ 293aa72e43bSMarek Vasut static int mxs_dma_request(int channel) 29431650d64SMarek Vasut { 29531650d64SMarek Vasut struct mxs_dma_chan *pchan; 29631650d64SMarek Vasut 29731650d64SMarek Vasut if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) 29831650d64SMarek Vasut return -EINVAL; 29931650d64SMarek Vasut 30031650d64SMarek Vasut pchan = mxs_dma_channels + channel; 30131650d64SMarek Vasut if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID) 30231650d64SMarek Vasut return -ENODEV; 30331650d64SMarek Vasut 30431650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED) 30531650d64SMarek Vasut return -EBUSY; 30631650d64SMarek Vasut 30731650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_ALLOCATED; 30831650d64SMarek Vasut pchan->active_num = 0; 30931650d64SMarek Vasut pchan->pending_num = 0; 31031650d64SMarek Vasut 31131650d64SMarek Vasut INIT_LIST_HEAD(&pchan->active); 31231650d64SMarek Vasut INIT_LIST_HEAD(&pchan->done); 31331650d64SMarek Vasut 31431650d64SMarek Vasut return 0; 31531650d64SMarek Vasut } 31631650d64SMarek Vasut 31731650d64SMarek Vasut /* 31831650d64SMarek Vasut * Release a DMA channel. 31931650d64SMarek Vasut * 32031650d64SMarek Vasut * This function releases a DMA channel from its current owner. 32131650d64SMarek Vasut * 32231650d64SMarek Vasut * The channel will NOT be released if it's marked "busy" (see 32331650d64SMarek Vasut * mxs_dma_enable()). 32431650d64SMarek Vasut */ 32596666a39SMarek Vasut int mxs_dma_release(int channel) 32631650d64SMarek Vasut { 32731650d64SMarek Vasut struct mxs_dma_chan *pchan; 32831650d64SMarek Vasut int ret; 32931650d64SMarek Vasut 33031650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 33131650d64SMarek Vasut if (ret) 33231650d64SMarek Vasut return ret; 33331650d64SMarek Vasut 33431650d64SMarek Vasut pchan = mxs_dma_channels + channel; 33531650d64SMarek Vasut 33631650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_BUSY) 33731650d64SMarek Vasut return -EBUSY; 33831650d64SMarek Vasut 33931650d64SMarek Vasut pchan->dev = 0; 34031650d64SMarek Vasut pchan->active_num = 0; 34131650d64SMarek Vasut pchan->pending_num = 0; 34231650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED; 34331650d64SMarek Vasut 34431650d64SMarek Vasut return 0; 34531650d64SMarek Vasut } 34631650d64SMarek Vasut 34731650d64SMarek Vasut /* 34831650d64SMarek Vasut * Allocate DMA descriptor 34931650d64SMarek Vasut */ 35031650d64SMarek Vasut struct mxs_dma_desc *mxs_dma_desc_alloc(void) 35131650d64SMarek Vasut { 35231650d64SMarek Vasut struct mxs_dma_desc *pdesc; 353c3dfe707SMarek Vasut uint32_t size; 35431650d64SMarek Vasut 355c3dfe707SMarek Vasut size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); 356c3dfe707SMarek Vasut pdesc = memalign(MXS_DMA_ALIGNMENT, size); 35731650d64SMarek Vasut 35831650d64SMarek Vasut if (pdesc == NULL) 35931650d64SMarek Vasut return NULL; 36031650d64SMarek Vasut 36131650d64SMarek Vasut memset(pdesc, 0, sizeof(*pdesc)); 36231650d64SMarek Vasut pdesc->address = (dma_addr_t)pdesc; 36331650d64SMarek Vasut 36431650d64SMarek Vasut return pdesc; 36531650d64SMarek Vasut }; 36631650d64SMarek Vasut 36731650d64SMarek Vasut /* 36831650d64SMarek Vasut * Free DMA descriptor 36931650d64SMarek Vasut */ 37031650d64SMarek Vasut void mxs_dma_desc_free(struct mxs_dma_desc *pdesc) 37131650d64SMarek Vasut { 37231650d64SMarek Vasut if (pdesc == NULL) 37331650d64SMarek Vasut return; 37431650d64SMarek Vasut 37531650d64SMarek Vasut free(pdesc); 37631650d64SMarek Vasut } 37731650d64SMarek Vasut 37831650d64SMarek Vasut /* 37931650d64SMarek Vasut * Add a DMA descriptor to a channel. 38031650d64SMarek Vasut * 38131650d64SMarek Vasut * If the descriptor list for this channel is not empty, this function sets the 38231650d64SMarek Vasut * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so 38331650d64SMarek Vasut * it will chain to the new descriptor's command. 38431650d64SMarek Vasut * 38531650d64SMarek Vasut * Then, this function marks the new descriptor as "ready," adds it to the end 38631650d64SMarek Vasut * of the active descriptor list, and increments the count of pending 38731650d64SMarek Vasut * descriptors. 38831650d64SMarek Vasut * 38931650d64SMarek Vasut * The MXS platform DMA software imposes some rules on DMA commands to maintain 39031650d64SMarek Vasut * important invariants. These rules are NOT checked, but they must be carefully 39131650d64SMarek Vasut * applied by software that uses MXS DMA channels. 39231650d64SMarek Vasut * 39331650d64SMarek Vasut * Invariant: 39431650d64SMarek Vasut * The DMA channel's hardware semaphore must reflect the number of DMA 39531650d64SMarek Vasut * commands the hardware will process, but has not yet finished. 39631650d64SMarek Vasut * 39731650d64SMarek Vasut * Explanation: 39831650d64SMarek Vasut * A DMA channel begins processing commands when its hardware semaphore is 39931650d64SMarek Vasut * written with a value greater than zero, and it stops processing commands 40031650d64SMarek Vasut * when the semaphore returns to zero. 40131650d64SMarek Vasut * 40231650d64SMarek Vasut * When a channel finishes a DMA command, it will decrement its semaphore if 40331650d64SMarek Vasut * the DECREMENT_SEMAPHORE bit is set in that command's flags bits. 40431650d64SMarek Vasut * 40531650d64SMarek Vasut * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set, 40631650d64SMarek Vasut * unless it suits the purposes of the software. For example, one could 40731650d64SMarek Vasut * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE 40831650d64SMarek Vasut * bit set only in the last one. Then, setting the DMA channel's hardware 40931650d64SMarek Vasut * semaphore to one would cause the entire series of five commands to be 41031650d64SMarek Vasut * processed. However, this example would violate the invariant given above. 41131650d64SMarek Vasut * 41231650d64SMarek Vasut * Rule: 41331650d64SMarek Vasut * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA 41431650d64SMarek Vasut * channel's hardware semaphore will be decremented EVERY time a command is 41531650d64SMarek Vasut * processed. 41631650d64SMarek Vasut */ 41731650d64SMarek Vasut int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc) 41831650d64SMarek Vasut { 41931650d64SMarek Vasut struct mxs_dma_chan *pchan; 42031650d64SMarek Vasut struct mxs_dma_desc *last; 42131650d64SMarek Vasut int ret; 42231650d64SMarek Vasut 42331650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 42431650d64SMarek Vasut if (ret) 42531650d64SMarek Vasut return ret; 42631650d64SMarek Vasut 42731650d64SMarek Vasut pchan = mxs_dma_channels + channel; 42831650d64SMarek Vasut 42931650d64SMarek Vasut pdesc->cmd.next = mxs_dma_cmd_address(pdesc); 43031650d64SMarek Vasut pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST; 43131650d64SMarek Vasut 43231650d64SMarek Vasut if (!list_empty(&pchan->active)) { 43331650d64SMarek Vasut last = list_entry(pchan->active.prev, struct mxs_dma_desc, 43431650d64SMarek Vasut node); 43531650d64SMarek Vasut 43631650d64SMarek Vasut pdesc->flags &= ~MXS_DMA_DESC_FIRST; 43731650d64SMarek Vasut last->flags &= ~MXS_DMA_DESC_LAST; 43831650d64SMarek Vasut 43931650d64SMarek Vasut last->cmd.next = mxs_dma_cmd_address(pdesc); 44031650d64SMarek Vasut last->cmd.data |= MXS_DMA_DESC_CHAIN; 441c3dfe707SMarek Vasut 442c3dfe707SMarek Vasut mxs_dma_flush_desc(last); 44331650d64SMarek Vasut } 44431650d64SMarek Vasut pdesc->flags |= MXS_DMA_DESC_READY; 44531650d64SMarek Vasut if (pdesc->flags & MXS_DMA_DESC_FIRST) 44631650d64SMarek Vasut pchan->pending_num++; 44731650d64SMarek Vasut list_add_tail(&pdesc->node, &pchan->active); 44831650d64SMarek Vasut 449c3dfe707SMarek Vasut mxs_dma_flush_desc(pdesc); 450c3dfe707SMarek Vasut 45131650d64SMarek Vasut return ret; 45231650d64SMarek Vasut } 45331650d64SMarek Vasut 45431650d64SMarek Vasut /* 45531650d64SMarek Vasut * Clean up processed DMA descriptors. 45631650d64SMarek Vasut * 45731650d64SMarek Vasut * This function removes processed DMA descriptors from the "active" list. Pass 45831650d64SMarek Vasut * in a non-NULL list head to get the descriptors moved to your list. Pass NULL 45931650d64SMarek Vasut * to get the descriptors moved to the channel's "done" list. Descriptors on 46031650d64SMarek Vasut * the "done" list can be retrieved with mxs_dma_get_finished(). 46131650d64SMarek Vasut * 46231650d64SMarek Vasut * This function marks the DMA channel as "not busy" if no unprocessed 46331650d64SMarek Vasut * descriptors remain on the "active" list. 46431650d64SMarek Vasut */ 465aa72e43bSMarek Vasut static int mxs_dma_finish(int channel, struct list_head *head) 46631650d64SMarek Vasut { 46731650d64SMarek Vasut int sem; 46831650d64SMarek Vasut struct mxs_dma_chan *pchan; 46931650d64SMarek Vasut struct list_head *p, *q; 47031650d64SMarek Vasut struct mxs_dma_desc *pdesc; 47131650d64SMarek Vasut int ret; 47231650d64SMarek Vasut 47331650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 47431650d64SMarek Vasut if (ret) 47531650d64SMarek Vasut return ret; 47631650d64SMarek Vasut 47731650d64SMarek Vasut pchan = mxs_dma_channels + channel; 47831650d64SMarek Vasut 47931650d64SMarek Vasut sem = mxs_dma_read_semaphore(channel); 48031650d64SMarek Vasut if (sem < 0) 48131650d64SMarek Vasut return sem; 48231650d64SMarek Vasut 48331650d64SMarek Vasut if (sem == pchan->active_num) 48431650d64SMarek Vasut return 0; 48531650d64SMarek Vasut 48631650d64SMarek Vasut list_for_each_safe(p, q, &pchan->active) { 48731650d64SMarek Vasut if ((pchan->active_num) <= sem) 48831650d64SMarek Vasut break; 48931650d64SMarek Vasut 49031650d64SMarek Vasut pdesc = list_entry(p, struct mxs_dma_desc, node); 49131650d64SMarek Vasut pdesc->flags &= ~MXS_DMA_DESC_READY; 49231650d64SMarek Vasut 49331650d64SMarek Vasut if (head) 49431650d64SMarek Vasut list_move_tail(p, head); 49531650d64SMarek Vasut else 49631650d64SMarek Vasut list_move_tail(p, &pchan->done); 49731650d64SMarek Vasut 49831650d64SMarek Vasut if (pdesc->flags & MXS_DMA_DESC_LAST) 49931650d64SMarek Vasut pchan->active_num--; 50031650d64SMarek Vasut } 50131650d64SMarek Vasut 50231650d64SMarek Vasut if (sem == 0) 50331650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_BUSY; 50431650d64SMarek Vasut 50531650d64SMarek Vasut return 0; 50631650d64SMarek Vasut } 50731650d64SMarek Vasut 50831650d64SMarek Vasut /* 50931650d64SMarek Vasut * Wait for DMA channel to complete 51031650d64SMarek Vasut */ 511aa72e43bSMarek Vasut static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) 51231650d64SMarek Vasut { 5139c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 5149c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 51531650d64SMarek Vasut int ret; 51631650d64SMarek Vasut 51731650d64SMarek Vasut ret = mxs_dma_validate_chan(chan); 51831650d64SMarek Vasut if (ret) 51931650d64SMarek Vasut return ret; 52031650d64SMarek Vasut 521fa7a51cbSOtavio Salvador if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, 52231650d64SMarek Vasut 1 << chan, timeout)) { 52331650d64SMarek Vasut ret = -ETIMEDOUT; 52431650d64SMarek Vasut mxs_dma_reset(chan); 52531650d64SMarek Vasut } 52631650d64SMarek Vasut 52712dab4ceSWolfram Sang return ret; 52831650d64SMarek Vasut } 52931650d64SMarek Vasut 53031650d64SMarek Vasut /* 53131650d64SMarek Vasut * Execute the DMA channel 53231650d64SMarek Vasut */ 53331650d64SMarek Vasut int mxs_dma_go(int chan) 53431650d64SMarek Vasut { 5351375f044SMarek Vasut uint32_t timeout = 10000000; 53631650d64SMarek Vasut int ret; 53731650d64SMarek Vasut 53831650d64SMarek Vasut LIST_HEAD(tmp_desc_list); 53931650d64SMarek Vasut 54031650d64SMarek Vasut mxs_dma_enable_irq(chan, 1); 54131650d64SMarek Vasut mxs_dma_enable(chan); 54231650d64SMarek Vasut 54331650d64SMarek Vasut /* Wait for DMA to finish. */ 54431650d64SMarek Vasut ret = mxs_dma_wait_complete(timeout, chan); 54531650d64SMarek Vasut 54631650d64SMarek Vasut /* Clear out the descriptors we just ran. */ 54731650d64SMarek Vasut mxs_dma_finish(chan, &tmp_desc_list); 54831650d64SMarek Vasut 54931650d64SMarek Vasut /* Shut the DMA channel down. */ 55031650d64SMarek Vasut mxs_dma_ack_irq(chan); 55131650d64SMarek Vasut mxs_dma_reset(chan); 55231650d64SMarek Vasut mxs_dma_enable_irq(chan, 0); 55331650d64SMarek Vasut mxs_dma_disable(chan); 55431650d64SMarek Vasut 55531650d64SMarek Vasut return ret; 55631650d64SMarek Vasut } 55731650d64SMarek Vasut 55831650d64SMarek Vasut /* 55931650d64SMarek Vasut * Initialize the DMA hardware 56031650d64SMarek Vasut */ 56196666a39SMarek Vasut void mxs_dma_init(void) 56231650d64SMarek Vasut { 5639c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 5649c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 56531650d64SMarek Vasut 566fa7a51cbSOtavio Salvador mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); 56731650d64SMarek Vasut 56831650d64SMarek Vasut #ifdef CONFIG_APBH_DMA_BURST8 56931650d64SMarek Vasut writel(APBH_CTRL0_AHB_BURST8_EN, 57031650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 57131650d64SMarek Vasut #else 57231650d64SMarek Vasut writel(APBH_CTRL0_AHB_BURST8_EN, 57331650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 57431650d64SMarek Vasut #endif 57531650d64SMarek Vasut 57631650d64SMarek Vasut #ifdef CONFIG_APBH_DMA_BURST 57731650d64SMarek Vasut writel(APBH_CTRL0_APB_BURST_EN, 57831650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 57931650d64SMarek Vasut #else 58031650d64SMarek Vasut writel(APBH_CTRL0_APB_BURST_EN, 58131650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 58231650d64SMarek Vasut #endif 58396666a39SMarek Vasut } 58431650d64SMarek Vasut 58596666a39SMarek Vasut int mxs_dma_init_channel(int channel) 58696666a39SMarek Vasut { 58796666a39SMarek Vasut struct mxs_dma_chan *pchan; 58896666a39SMarek Vasut int ret; 58996666a39SMarek Vasut 59031650d64SMarek Vasut pchan = mxs_dma_channels + channel; 59131650d64SMarek Vasut pchan->flags = MXS_DMA_FLAGS_VALID; 59231650d64SMarek Vasut 59331650d64SMarek Vasut ret = mxs_dma_request(channel); 59431650d64SMarek Vasut 59531650d64SMarek Vasut if (ret) { 59631650d64SMarek Vasut printf("MXS DMA: Can't acquire DMA channel %i\n", 59731650d64SMarek Vasut channel); 59896666a39SMarek Vasut return ret; 59931650d64SMarek Vasut } 60031650d64SMarek Vasut 60131650d64SMarek Vasut mxs_dma_reset(channel); 60231650d64SMarek Vasut mxs_dma_ack_irq(channel); 60331650d64SMarek Vasut 60431650d64SMarek Vasut return 0; 60531650d64SMarek Vasut } 606