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 * 101a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 1131650d64SMarek Vasut */ 1231650d64SMarek Vasut 1331650d64SMarek Vasut #include <linux/list.h> 1431650d64SMarek Vasut 1531650d64SMarek Vasut #include <common.h> 1631650d64SMarek Vasut #include <malloc.h> 17*1221ce45SMasahiro Yamada #include <linux/errno.h> 1831650d64SMarek Vasut #include <asm/io.h> 1931650d64SMarek Vasut #include <asm/arch/clock.h> 2031650d64SMarek Vasut #include <asm/arch/imx-regs.h> 2131650d64SMarek Vasut #include <asm/arch/sys_proto.h> 220499218dSStefan Roese #include <asm/imx-common/dma.h> 230499218dSStefan Roese #include <asm/imx-common/regs-apbh.h> 2431650d64SMarek Vasut 2531650d64SMarek Vasut static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; 2631650d64SMarek Vasut 2731650d64SMarek Vasut /* 2831650d64SMarek Vasut * Test is the DMA channel is valid channel 2931650d64SMarek Vasut */ 3031650d64SMarek Vasut int mxs_dma_validate_chan(int channel) 3131650d64SMarek Vasut { 3231650d64SMarek Vasut struct mxs_dma_chan *pchan; 3331650d64SMarek Vasut 3431650d64SMarek Vasut if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) 3531650d64SMarek Vasut return -EINVAL; 3631650d64SMarek Vasut 3731650d64SMarek Vasut pchan = mxs_dma_channels + channel; 3831650d64SMarek Vasut if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED)) 3931650d64SMarek Vasut return -EINVAL; 4031650d64SMarek Vasut 4131650d64SMarek Vasut return 0; 4231650d64SMarek Vasut } 4331650d64SMarek Vasut 4431650d64SMarek Vasut /* 45aa72e43bSMarek Vasut * Return the address of the command within a descriptor. 46aa72e43bSMarek Vasut */ 47aa72e43bSMarek Vasut static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) 48aa72e43bSMarek Vasut { 49aa72e43bSMarek Vasut return desc->address + offsetof(struct mxs_dma_desc, cmd); 50aa72e43bSMarek Vasut } 51aa72e43bSMarek Vasut 52aa72e43bSMarek Vasut /* 53aa72e43bSMarek Vasut * Read a DMA channel's hardware semaphore. 54aa72e43bSMarek Vasut * 55aa72e43bSMarek Vasut * As used by the MXS platform's DMA software, the DMA channel's hardware 56aa72e43bSMarek Vasut * semaphore reflects the number of DMA commands the hardware will process, but 57aa72e43bSMarek Vasut * has not yet finished. This is a volatile value read directly from hardware, 58aa72e43bSMarek Vasut * so it must be be viewed as immediately stale. 59aa72e43bSMarek Vasut * 60aa72e43bSMarek Vasut * If the channel is not marked busy, or has finished processing all its 61aa72e43bSMarek Vasut * commands, this value should be zero. 62aa72e43bSMarek Vasut * 63aa72e43bSMarek Vasut * See mxs_dma_append() for details on how DMA command blocks must be configured 64aa72e43bSMarek Vasut * to maintain the expected behavior of the semaphore's value. 65aa72e43bSMarek Vasut */ 66aa72e43bSMarek Vasut static int mxs_dma_read_semaphore(int channel) 67aa72e43bSMarek Vasut { 689c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 699c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 70aa72e43bSMarek Vasut uint32_t tmp; 71aa72e43bSMarek Vasut int ret; 72aa72e43bSMarek Vasut 73aa72e43bSMarek Vasut ret = mxs_dma_validate_chan(channel); 74aa72e43bSMarek Vasut if (ret) 75aa72e43bSMarek Vasut return ret; 76aa72e43bSMarek Vasut 77aa72e43bSMarek Vasut tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); 78aa72e43bSMarek Vasut 79aa72e43bSMarek Vasut tmp &= APBH_CHn_SEMA_PHORE_MASK; 80aa72e43bSMarek Vasut tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; 81aa72e43bSMarek Vasut 82aa72e43bSMarek Vasut return tmp; 83aa72e43bSMarek Vasut } 84aa72e43bSMarek Vasut 85c3dfe707SMarek Vasut #ifndef CONFIG_SYS_DCACHE_OFF 86c3dfe707SMarek Vasut void mxs_dma_flush_desc(struct mxs_dma_desc *desc) 87c3dfe707SMarek Vasut { 88c3dfe707SMarek Vasut uint32_t addr; 89c3dfe707SMarek Vasut uint32_t size; 90c3dfe707SMarek Vasut 91c3dfe707SMarek Vasut addr = (uint32_t)desc; 92c3dfe707SMarek Vasut size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); 93c3dfe707SMarek Vasut 94c3dfe707SMarek Vasut flush_dcache_range(addr, addr + size); 95c3dfe707SMarek Vasut } 96c3dfe707SMarek Vasut #else 97c3dfe707SMarek Vasut inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {} 98c3dfe707SMarek Vasut #endif 99c3dfe707SMarek Vasut 100aa72e43bSMarek Vasut /* 10131650d64SMarek Vasut * Enable a DMA channel. 10231650d64SMarek Vasut * 10331650d64SMarek Vasut * If the given channel has any DMA descriptors on its active list, this 10431650d64SMarek Vasut * function causes the DMA hardware to begin processing them. 10531650d64SMarek Vasut * 10631650d64SMarek Vasut * This function marks the DMA channel as "busy," whether or not there are any 10731650d64SMarek Vasut * descriptors to process. 10831650d64SMarek Vasut */ 109aa72e43bSMarek Vasut static int mxs_dma_enable(int channel) 11031650d64SMarek Vasut { 1119c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 1129c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 11331650d64SMarek Vasut unsigned int sem; 11431650d64SMarek Vasut struct mxs_dma_chan *pchan; 11531650d64SMarek Vasut struct mxs_dma_desc *pdesc; 11631650d64SMarek Vasut int ret; 11731650d64SMarek Vasut 11831650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 11931650d64SMarek Vasut if (ret) 12031650d64SMarek Vasut return ret; 12131650d64SMarek Vasut 12231650d64SMarek Vasut pchan = mxs_dma_channels + channel; 12331650d64SMarek Vasut 12431650d64SMarek Vasut if (pchan->pending_num == 0) { 12531650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_BUSY; 12631650d64SMarek Vasut return 0; 12731650d64SMarek Vasut } 12831650d64SMarek Vasut 12931650d64SMarek Vasut pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); 13031650d64SMarek Vasut if (pdesc == NULL) 13131650d64SMarek Vasut return -EFAULT; 13231650d64SMarek Vasut 13331650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_BUSY) { 13431650d64SMarek Vasut if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) 13531650d64SMarek Vasut return 0; 13631650d64SMarek Vasut 13731650d64SMarek Vasut sem = mxs_dma_read_semaphore(channel); 13831650d64SMarek Vasut if (sem == 0) 13931650d64SMarek Vasut return 0; 14031650d64SMarek Vasut 14131650d64SMarek Vasut if (sem == 1) { 14231650d64SMarek Vasut pdesc = list_entry(pdesc->node.next, 14331650d64SMarek Vasut struct mxs_dma_desc, node); 14431650d64SMarek Vasut writel(mxs_dma_cmd_address(pdesc), 14531650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); 14631650d64SMarek Vasut } 14731650d64SMarek Vasut writel(pchan->pending_num, 14831650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_sema); 14931650d64SMarek Vasut pchan->active_num += pchan->pending_num; 15031650d64SMarek Vasut pchan->pending_num = 0; 15131650d64SMarek Vasut } else { 15231650d64SMarek Vasut pchan->active_num += pchan->pending_num; 15331650d64SMarek Vasut pchan->pending_num = 0; 15431650d64SMarek Vasut writel(mxs_dma_cmd_address(pdesc), 15531650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); 15631650d64SMarek Vasut writel(pchan->active_num, 15731650d64SMarek Vasut &apbh_regs->ch[channel].hw_apbh_ch_sema); 15831650d64SMarek Vasut writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), 15931650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 16031650d64SMarek Vasut } 16131650d64SMarek Vasut 16231650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_BUSY; 16331650d64SMarek Vasut return 0; 16431650d64SMarek Vasut } 16531650d64SMarek Vasut 16631650d64SMarek Vasut /* 16731650d64SMarek Vasut * Disable a DMA channel. 16831650d64SMarek Vasut * 16931650d64SMarek Vasut * This function shuts down a DMA channel and marks it as "not busy." Any 17031650d64SMarek Vasut * descriptors on the active list are immediately moved to the head of the 17131650d64SMarek Vasut * "done" list, whether or not they have actually been processed by the 17231650d64SMarek Vasut * hardware. The "ready" flags of these descriptors are NOT cleared, so they 17331650d64SMarek Vasut * still appear to be active. 17431650d64SMarek Vasut * 17531650d64SMarek Vasut * This function immediately shuts down a DMA channel's hardware, aborting any 17631650d64SMarek Vasut * I/O that may be in progress, potentially leaving I/O hardware in an undefined 17731650d64SMarek Vasut * state. It is unwise to call this function if there is ANY chance the hardware 17831650d64SMarek Vasut * is still processing a command. 17931650d64SMarek Vasut */ 180aa72e43bSMarek Vasut static int mxs_dma_disable(int channel) 18131650d64SMarek Vasut { 18231650d64SMarek Vasut struct mxs_dma_chan *pchan; 1839c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 1849c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 18531650d64SMarek Vasut int ret; 18631650d64SMarek Vasut 18731650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 18831650d64SMarek Vasut if (ret) 18931650d64SMarek Vasut return ret; 19031650d64SMarek Vasut 19131650d64SMarek Vasut pchan = mxs_dma_channels + channel; 19231650d64SMarek Vasut 19331650d64SMarek Vasut if (!(pchan->flags & MXS_DMA_FLAGS_BUSY)) 19431650d64SMarek Vasut return -EINVAL; 19531650d64SMarek Vasut 19631650d64SMarek Vasut writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), 19731650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 19831650d64SMarek Vasut 19931650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_BUSY; 20031650d64SMarek Vasut pchan->active_num = 0; 20131650d64SMarek Vasut pchan->pending_num = 0; 20231650d64SMarek Vasut list_splice_init(&pchan->active, &pchan->done); 20331650d64SMarek Vasut 20431650d64SMarek Vasut return 0; 20531650d64SMarek Vasut } 20631650d64SMarek Vasut 20731650d64SMarek Vasut /* 20831650d64SMarek Vasut * Resets the DMA channel hardware. 20931650d64SMarek Vasut */ 210aa72e43bSMarek Vasut static int mxs_dma_reset(int channel) 21131650d64SMarek Vasut { 2129c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2139c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 21431650d64SMarek Vasut int ret; 2150e5c05efSMarek Vasut #if defined(CONFIG_MX23) 2160e5c05efSMarek Vasut uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set); 2170e5c05efSMarek Vasut uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET; 2181fc4f804SPeng Fan #elif (defined(CONFIG_MX28) || defined(CONFIG_MX6) || defined(CONFIG_MX7)) 2190e5c05efSMarek Vasut uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set); 2200e5c05efSMarek Vasut uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET; 2210e5c05efSMarek Vasut #endif 22231650d64SMarek Vasut 22331650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 22431650d64SMarek Vasut if (ret) 22531650d64SMarek Vasut return ret; 22631650d64SMarek Vasut 2270e5c05efSMarek Vasut writel(1 << (channel + offset), setreg); 22831650d64SMarek Vasut 22931650d64SMarek Vasut return 0; 23031650d64SMarek Vasut } 23131650d64SMarek Vasut 23231650d64SMarek Vasut /* 23331650d64SMarek Vasut * Enable or disable DMA interrupt. 23431650d64SMarek Vasut * 23531650d64SMarek Vasut * This function enables the given DMA channel to interrupt the CPU. 23631650d64SMarek Vasut */ 237aa72e43bSMarek Vasut static int mxs_dma_enable_irq(int channel, int enable) 23831650d64SMarek Vasut { 2399c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2409c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 24131650d64SMarek Vasut int ret; 24231650d64SMarek Vasut 24331650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 24431650d64SMarek Vasut if (ret) 24531650d64SMarek Vasut return ret; 24631650d64SMarek Vasut 24731650d64SMarek Vasut if (enable) 24831650d64SMarek Vasut writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), 24931650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl1_set); 25031650d64SMarek Vasut else 25131650d64SMarek Vasut writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), 25231650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl1_clr); 25331650d64SMarek Vasut 25431650d64SMarek Vasut return 0; 25531650d64SMarek Vasut } 25631650d64SMarek Vasut 25731650d64SMarek Vasut /* 25831650d64SMarek Vasut * Clear DMA interrupt. 25931650d64SMarek Vasut * 26031650d64SMarek Vasut * The software that is using the DMA channel must register to receive its 26131650d64SMarek Vasut * interrupts and, when they arrive, must call this function to clear them. 26231650d64SMarek Vasut */ 263aa72e43bSMarek Vasut static int mxs_dma_ack_irq(int channel) 26431650d64SMarek Vasut { 2659c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 2669c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 26731650d64SMarek Vasut int ret; 26831650d64SMarek Vasut 26931650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 27031650d64SMarek Vasut if (ret) 27131650d64SMarek Vasut return ret; 27231650d64SMarek Vasut 27331650d64SMarek Vasut writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr); 27431650d64SMarek Vasut writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr); 27531650d64SMarek Vasut 27631650d64SMarek Vasut return 0; 27731650d64SMarek Vasut } 27831650d64SMarek Vasut 27931650d64SMarek Vasut /* 28031650d64SMarek Vasut * Request to reserve a DMA channel 28131650d64SMarek Vasut */ 282aa72e43bSMarek Vasut static int mxs_dma_request(int channel) 28331650d64SMarek Vasut { 28431650d64SMarek Vasut struct mxs_dma_chan *pchan; 28531650d64SMarek Vasut 28631650d64SMarek Vasut if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) 28731650d64SMarek Vasut return -EINVAL; 28831650d64SMarek Vasut 28931650d64SMarek Vasut pchan = mxs_dma_channels + channel; 29031650d64SMarek Vasut if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID) 29131650d64SMarek Vasut return -ENODEV; 29231650d64SMarek Vasut 29331650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED) 29431650d64SMarek Vasut return -EBUSY; 29531650d64SMarek Vasut 29631650d64SMarek Vasut pchan->flags |= MXS_DMA_FLAGS_ALLOCATED; 29731650d64SMarek Vasut pchan->active_num = 0; 29831650d64SMarek Vasut pchan->pending_num = 0; 29931650d64SMarek Vasut 30031650d64SMarek Vasut INIT_LIST_HEAD(&pchan->active); 30131650d64SMarek Vasut INIT_LIST_HEAD(&pchan->done); 30231650d64SMarek Vasut 30331650d64SMarek Vasut return 0; 30431650d64SMarek Vasut } 30531650d64SMarek Vasut 30631650d64SMarek Vasut /* 30731650d64SMarek Vasut * Release a DMA channel. 30831650d64SMarek Vasut * 30931650d64SMarek Vasut * This function releases a DMA channel from its current owner. 31031650d64SMarek Vasut * 31131650d64SMarek Vasut * The channel will NOT be released if it's marked "busy" (see 31231650d64SMarek Vasut * mxs_dma_enable()). 31331650d64SMarek Vasut */ 31496666a39SMarek Vasut int mxs_dma_release(int channel) 31531650d64SMarek Vasut { 31631650d64SMarek Vasut struct mxs_dma_chan *pchan; 31731650d64SMarek Vasut int ret; 31831650d64SMarek Vasut 31931650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 32031650d64SMarek Vasut if (ret) 32131650d64SMarek Vasut return ret; 32231650d64SMarek Vasut 32331650d64SMarek Vasut pchan = mxs_dma_channels + channel; 32431650d64SMarek Vasut 32531650d64SMarek Vasut if (pchan->flags & MXS_DMA_FLAGS_BUSY) 32631650d64SMarek Vasut return -EBUSY; 32731650d64SMarek Vasut 32831650d64SMarek Vasut pchan->dev = 0; 32931650d64SMarek Vasut pchan->active_num = 0; 33031650d64SMarek Vasut pchan->pending_num = 0; 33131650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED; 33231650d64SMarek Vasut 33331650d64SMarek Vasut return 0; 33431650d64SMarek Vasut } 33531650d64SMarek Vasut 33631650d64SMarek Vasut /* 33731650d64SMarek Vasut * Allocate DMA descriptor 33831650d64SMarek Vasut */ 33931650d64SMarek Vasut struct mxs_dma_desc *mxs_dma_desc_alloc(void) 34031650d64SMarek Vasut { 34131650d64SMarek Vasut struct mxs_dma_desc *pdesc; 342c3dfe707SMarek Vasut uint32_t size; 34331650d64SMarek Vasut 344c3dfe707SMarek Vasut size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); 345c3dfe707SMarek Vasut pdesc = memalign(MXS_DMA_ALIGNMENT, size); 34631650d64SMarek Vasut 34731650d64SMarek Vasut if (pdesc == NULL) 34831650d64SMarek Vasut return NULL; 34931650d64SMarek Vasut 35031650d64SMarek Vasut memset(pdesc, 0, sizeof(*pdesc)); 35131650d64SMarek Vasut pdesc->address = (dma_addr_t)pdesc; 35231650d64SMarek Vasut 35331650d64SMarek Vasut return pdesc; 35431650d64SMarek Vasut }; 35531650d64SMarek Vasut 35631650d64SMarek Vasut /* 35731650d64SMarek Vasut * Free DMA descriptor 35831650d64SMarek Vasut */ 35931650d64SMarek Vasut void mxs_dma_desc_free(struct mxs_dma_desc *pdesc) 36031650d64SMarek Vasut { 36131650d64SMarek Vasut if (pdesc == NULL) 36231650d64SMarek Vasut return; 36331650d64SMarek Vasut 36431650d64SMarek Vasut free(pdesc); 36531650d64SMarek Vasut } 36631650d64SMarek Vasut 36731650d64SMarek Vasut /* 36831650d64SMarek Vasut * Add a DMA descriptor to a channel. 36931650d64SMarek Vasut * 37031650d64SMarek Vasut * If the descriptor list for this channel is not empty, this function sets the 37131650d64SMarek Vasut * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so 37231650d64SMarek Vasut * it will chain to the new descriptor's command. 37331650d64SMarek Vasut * 37431650d64SMarek Vasut * Then, this function marks the new descriptor as "ready," adds it to the end 37531650d64SMarek Vasut * of the active descriptor list, and increments the count of pending 37631650d64SMarek Vasut * descriptors. 37731650d64SMarek Vasut * 37831650d64SMarek Vasut * The MXS platform DMA software imposes some rules on DMA commands to maintain 37931650d64SMarek Vasut * important invariants. These rules are NOT checked, but they must be carefully 38031650d64SMarek Vasut * applied by software that uses MXS DMA channels. 38131650d64SMarek Vasut * 38231650d64SMarek Vasut * Invariant: 38331650d64SMarek Vasut * The DMA channel's hardware semaphore must reflect the number of DMA 38431650d64SMarek Vasut * commands the hardware will process, but has not yet finished. 38531650d64SMarek Vasut * 38631650d64SMarek Vasut * Explanation: 38731650d64SMarek Vasut * A DMA channel begins processing commands when its hardware semaphore is 38831650d64SMarek Vasut * written with a value greater than zero, and it stops processing commands 38931650d64SMarek Vasut * when the semaphore returns to zero. 39031650d64SMarek Vasut * 39131650d64SMarek Vasut * When a channel finishes a DMA command, it will decrement its semaphore if 39231650d64SMarek Vasut * the DECREMENT_SEMAPHORE bit is set in that command's flags bits. 39331650d64SMarek Vasut * 39431650d64SMarek Vasut * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set, 39531650d64SMarek Vasut * unless it suits the purposes of the software. For example, one could 39631650d64SMarek Vasut * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE 39731650d64SMarek Vasut * bit set only in the last one. Then, setting the DMA channel's hardware 39831650d64SMarek Vasut * semaphore to one would cause the entire series of five commands to be 39931650d64SMarek Vasut * processed. However, this example would violate the invariant given above. 40031650d64SMarek Vasut * 40131650d64SMarek Vasut * Rule: 40231650d64SMarek Vasut * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA 40331650d64SMarek Vasut * channel's hardware semaphore will be decremented EVERY time a command is 40431650d64SMarek Vasut * processed. 40531650d64SMarek Vasut */ 40631650d64SMarek Vasut int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc) 40731650d64SMarek Vasut { 40831650d64SMarek Vasut struct mxs_dma_chan *pchan; 40931650d64SMarek Vasut struct mxs_dma_desc *last; 41031650d64SMarek Vasut int ret; 41131650d64SMarek Vasut 41231650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 41331650d64SMarek Vasut if (ret) 41431650d64SMarek Vasut return ret; 41531650d64SMarek Vasut 41631650d64SMarek Vasut pchan = mxs_dma_channels + channel; 41731650d64SMarek Vasut 41831650d64SMarek Vasut pdesc->cmd.next = mxs_dma_cmd_address(pdesc); 41931650d64SMarek Vasut pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST; 42031650d64SMarek Vasut 42131650d64SMarek Vasut if (!list_empty(&pchan->active)) { 42231650d64SMarek Vasut last = list_entry(pchan->active.prev, struct mxs_dma_desc, 42331650d64SMarek Vasut node); 42431650d64SMarek Vasut 42531650d64SMarek Vasut pdesc->flags &= ~MXS_DMA_DESC_FIRST; 42631650d64SMarek Vasut last->flags &= ~MXS_DMA_DESC_LAST; 42731650d64SMarek Vasut 42831650d64SMarek Vasut last->cmd.next = mxs_dma_cmd_address(pdesc); 42931650d64SMarek Vasut last->cmd.data |= MXS_DMA_DESC_CHAIN; 430c3dfe707SMarek Vasut 431c3dfe707SMarek Vasut mxs_dma_flush_desc(last); 43231650d64SMarek Vasut } 43331650d64SMarek Vasut pdesc->flags |= MXS_DMA_DESC_READY; 43431650d64SMarek Vasut if (pdesc->flags & MXS_DMA_DESC_FIRST) 43531650d64SMarek Vasut pchan->pending_num++; 43631650d64SMarek Vasut list_add_tail(&pdesc->node, &pchan->active); 43731650d64SMarek Vasut 438c3dfe707SMarek Vasut mxs_dma_flush_desc(pdesc); 439c3dfe707SMarek Vasut 44031650d64SMarek Vasut return ret; 44131650d64SMarek Vasut } 44231650d64SMarek Vasut 44331650d64SMarek Vasut /* 44431650d64SMarek Vasut * Clean up processed DMA descriptors. 44531650d64SMarek Vasut * 44631650d64SMarek Vasut * This function removes processed DMA descriptors from the "active" list. Pass 44731650d64SMarek Vasut * in a non-NULL list head to get the descriptors moved to your list. Pass NULL 44831650d64SMarek Vasut * to get the descriptors moved to the channel's "done" list. Descriptors on 44931650d64SMarek Vasut * the "done" list can be retrieved with mxs_dma_get_finished(). 45031650d64SMarek Vasut * 45131650d64SMarek Vasut * This function marks the DMA channel as "not busy" if no unprocessed 45231650d64SMarek Vasut * descriptors remain on the "active" list. 45331650d64SMarek Vasut */ 454aa72e43bSMarek Vasut static int mxs_dma_finish(int channel, struct list_head *head) 45531650d64SMarek Vasut { 45631650d64SMarek Vasut int sem; 45731650d64SMarek Vasut struct mxs_dma_chan *pchan; 45831650d64SMarek Vasut struct list_head *p, *q; 45931650d64SMarek Vasut struct mxs_dma_desc *pdesc; 46031650d64SMarek Vasut int ret; 46131650d64SMarek Vasut 46231650d64SMarek Vasut ret = mxs_dma_validate_chan(channel); 46331650d64SMarek Vasut if (ret) 46431650d64SMarek Vasut return ret; 46531650d64SMarek Vasut 46631650d64SMarek Vasut pchan = mxs_dma_channels + channel; 46731650d64SMarek Vasut 46831650d64SMarek Vasut sem = mxs_dma_read_semaphore(channel); 46931650d64SMarek Vasut if (sem < 0) 47031650d64SMarek Vasut return sem; 47131650d64SMarek Vasut 47231650d64SMarek Vasut if (sem == pchan->active_num) 47331650d64SMarek Vasut return 0; 47431650d64SMarek Vasut 47531650d64SMarek Vasut list_for_each_safe(p, q, &pchan->active) { 47631650d64SMarek Vasut if ((pchan->active_num) <= sem) 47731650d64SMarek Vasut break; 47831650d64SMarek Vasut 47931650d64SMarek Vasut pdesc = list_entry(p, struct mxs_dma_desc, node); 48031650d64SMarek Vasut pdesc->flags &= ~MXS_DMA_DESC_READY; 48131650d64SMarek Vasut 48231650d64SMarek Vasut if (head) 48331650d64SMarek Vasut list_move_tail(p, head); 48431650d64SMarek Vasut else 48531650d64SMarek Vasut list_move_tail(p, &pchan->done); 48631650d64SMarek Vasut 48731650d64SMarek Vasut if (pdesc->flags & MXS_DMA_DESC_LAST) 48831650d64SMarek Vasut pchan->active_num--; 48931650d64SMarek Vasut } 49031650d64SMarek Vasut 49131650d64SMarek Vasut if (sem == 0) 49231650d64SMarek Vasut pchan->flags &= ~MXS_DMA_FLAGS_BUSY; 49331650d64SMarek Vasut 49431650d64SMarek Vasut return 0; 49531650d64SMarek Vasut } 49631650d64SMarek Vasut 49731650d64SMarek Vasut /* 49831650d64SMarek Vasut * Wait for DMA channel to complete 49931650d64SMarek Vasut */ 500aa72e43bSMarek Vasut static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) 50131650d64SMarek Vasut { 5029c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 5039c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 50431650d64SMarek Vasut int ret; 50531650d64SMarek Vasut 50631650d64SMarek Vasut ret = mxs_dma_validate_chan(chan); 50731650d64SMarek Vasut if (ret) 50831650d64SMarek Vasut return ret; 50931650d64SMarek Vasut 510fa7a51cbSOtavio Salvador if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, 51131650d64SMarek Vasut 1 << chan, timeout)) { 51231650d64SMarek Vasut ret = -ETIMEDOUT; 51331650d64SMarek Vasut mxs_dma_reset(chan); 51431650d64SMarek Vasut } 51531650d64SMarek Vasut 51612dab4ceSWolfram Sang return ret; 51731650d64SMarek Vasut } 51831650d64SMarek Vasut 51931650d64SMarek Vasut /* 52031650d64SMarek Vasut * Execute the DMA channel 52131650d64SMarek Vasut */ 52231650d64SMarek Vasut int mxs_dma_go(int chan) 52331650d64SMarek Vasut { 5241375f044SMarek Vasut uint32_t timeout = 10000000; 52531650d64SMarek Vasut int ret; 52631650d64SMarek Vasut 52731650d64SMarek Vasut LIST_HEAD(tmp_desc_list); 52831650d64SMarek Vasut 52931650d64SMarek Vasut mxs_dma_enable_irq(chan, 1); 53031650d64SMarek Vasut mxs_dma_enable(chan); 53131650d64SMarek Vasut 53231650d64SMarek Vasut /* Wait for DMA to finish. */ 53331650d64SMarek Vasut ret = mxs_dma_wait_complete(timeout, chan); 53431650d64SMarek Vasut 53531650d64SMarek Vasut /* Clear out the descriptors we just ran. */ 53631650d64SMarek Vasut mxs_dma_finish(chan, &tmp_desc_list); 53731650d64SMarek Vasut 53831650d64SMarek Vasut /* Shut the DMA channel down. */ 53931650d64SMarek Vasut mxs_dma_ack_irq(chan); 54031650d64SMarek Vasut mxs_dma_reset(chan); 54131650d64SMarek Vasut mxs_dma_enable_irq(chan, 0); 54231650d64SMarek Vasut mxs_dma_disable(chan); 54331650d64SMarek Vasut 54431650d64SMarek Vasut return ret; 54531650d64SMarek Vasut } 54631650d64SMarek Vasut 54731650d64SMarek Vasut /* 54869f7345cSMarek Vasut * Execute a continuously running circular DMA descriptor. 54969f7345cSMarek Vasut * NOTE: This is not intended for general use, but rather 55069f7345cSMarek Vasut * for the LCD driver in Smart-LCD mode. It allows 55169f7345cSMarek Vasut * continuous triggering of the RUN bit there. 55269f7345cSMarek Vasut */ 55369f7345cSMarek Vasut void mxs_dma_circ_start(int chan, struct mxs_dma_desc *pdesc) 55469f7345cSMarek Vasut { 55569f7345cSMarek Vasut struct mxs_apbh_regs *apbh_regs = 55669f7345cSMarek Vasut (struct mxs_apbh_regs *)MXS_APBH_BASE; 55769f7345cSMarek Vasut 55869f7345cSMarek Vasut mxs_dma_flush_desc(pdesc); 55969f7345cSMarek Vasut 56069f7345cSMarek Vasut mxs_dma_enable_irq(chan, 1); 56169f7345cSMarek Vasut 56269f7345cSMarek Vasut writel(mxs_dma_cmd_address(pdesc), 56369f7345cSMarek Vasut &apbh_regs->ch[chan].hw_apbh_ch_nxtcmdar); 56469f7345cSMarek Vasut writel(1, &apbh_regs->ch[chan].hw_apbh_ch_sema); 56569f7345cSMarek Vasut writel(1 << (chan + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), 56669f7345cSMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 56769f7345cSMarek Vasut } 56869f7345cSMarek Vasut 56969f7345cSMarek Vasut /* 57031650d64SMarek Vasut * Initialize the DMA hardware 57131650d64SMarek Vasut */ 57296666a39SMarek Vasut void mxs_dma_init(void) 57331650d64SMarek Vasut { 5749c471142SOtavio Salvador struct mxs_apbh_regs *apbh_regs = 5759c471142SOtavio Salvador (struct mxs_apbh_regs *)MXS_APBH_BASE; 57631650d64SMarek Vasut 577fa7a51cbSOtavio Salvador mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); 57831650d64SMarek Vasut 57931650d64SMarek Vasut #ifdef CONFIG_APBH_DMA_BURST8 58031650d64SMarek Vasut writel(APBH_CTRL0_AHB_BURST8_EN, 58131650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 58231650d64SMarek Vasut #else 58331650d64SMarek Vasut writel(APBH_CTRL0_AHB_BURST8_EN, 58431650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 58531650d64SMarek Vasut #endif 58631650d64SMarek Vasut 58731650d64SMarek Vasut #ifdef CONFIG_APBH_DMA_BURST 58831650d64SMarek Vasut writel(APBH_CTRL0_APB_BURST_EN, 58931650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_set); 59031650d64SMarek Vasut #else 59131650d64SMarek Vasut writel(APBH_CTRL0_APB_BURST_EN, 59231650d64SMarek Vasut &apbh_regs->hw_apbh_ctrl0_clr); 59331650d64SMarek Vasut #endif 59496666a39SMarek Vasut } 59531650d64SMarek Vasut 59696666a39SMarek Vasut int mxs_dma_init_channel(int channel) 59796666a39SMarek Vasut { 59896666a39SMarek Vasut struct mxs_dma_chan *pchan; 59996666a39SMarek Vasut int ret; 60096666a39SMarek Vasut 60131650d64SMarek Vasut pchan = mxs_dma_channels + channel; 60231650d64SMarek Vasut pchan->flags = MXS_DMA_FLAGS_VALID; 60331650d64SMarek Vasut 60431650d64SMarek Vasut ret = mxs_dma_request(channel); 60531650d64SMarek Vasut 60631650d64SMarek Vasut if (ret) { 60731650d64SMarek Vasut printf("MXS DMA: Can't acquire DMA channel %i\n", 60831650d64SMarek Vasut channel); 60996666a39SMarek Vasut return ret; 61031650d64SMarek Vasut } 61131650d64SMarek Vasut 61231650d64SMarek Vasut mxs_dma_reset(channel); 61331650d64SMarek Vasut mxs_dma_ack_irq(channel); 61431650d64SMarek Vasut 61531650d64SMarek Vasut return 0; 61631650d64SMarek Vasut } 617