X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fdma%2Fapbh_dma.c;h=fea8767d7bae5b65013dc33c3b7506748247f646;hb=2218b32d88f9b4b4484cea9a8b034ddab0be298b;hp=69a10428ef241581a44bf0cd9436acb1987d3e99;hpb=31650d64a8fca2e5acf4dfe2b4f2a9ba9df2147c;p=u-boot diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c index 69a10428ef..fea8767d7b 100644 --- a/drivers/dma/apbh_dma.c +++ b/drivers/dma/apbh_dma.c @@ -7,31 +7,20 @@ * Based on code from LTIB: * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include -#include +#include #include #include #include #include -#include +#include +#include static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; @@ -52,6 +41,62 @@ int mxs_dma_validate_chan(int channel) return 0; } +/* + * Return the address of the command within a descriptor. + */ +static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) +{ + return desc->address + offsetof(struct mxs_dma_desc, cmd); +} + +/* + * Read a DMA channel's hardware semaphore. + * + * As used by the MXS platform's DMA software, the DMA channel's hardware + * semaphore reflects the number of DMA commands the hardware will process, but + * has not yet finished. This is a volatile value read directly from hardware, + * so it must be be viewed as immediately stale. + * + * If the channel is not marked busy, or has finished processing all its + * commands, this value should be zero. + * + * See mxs_dma_append() for details on how DMA command blocks must be configured + * to maintain the expected behavior of the semaphore's value. + */ +static int mxs_dma_read_semaphore(int channel) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + uint32_t tmp; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); + + tmp &= APBH_CHn_SEMA_PHORE_MASK; + tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; + + return tmp; +} + +#ifndef CONFIG_SYS_DCACHE_OFF +void mxs_dma_flush_desc(struct mxs_dma_desc *desc) +{ + uint32_t addr; + uint32_t size; + + addr = (uint32_t)desc; + size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); + + flush_dcache_range(addr, addr + size); +} +#else +inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {} +#endif + /* * Enable a DMA channel. * @@ -61,10 +106,10 @@ int mxs_dma_validate_chan(int channel) * This function marks the DMA channel as "busy," whether or not there are any * descriptors to process. */ -int mxs_dma_enable(int channel) +static int mxs_dma_enable(int channel) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; unsigned int sem; struct mxs_dma_chan *pchan; struct mxs_dma_desc *pdesc; @@ -132,11 +177,11 @@ int mxs_dma_enable(int channel) * state. It is unwise to call this function if there is ANY chance the hardware * is still processing a command. */ -int mxs_dma_disable(int channel) +static int mxs_dma_disable(int channel) { struct mxs_dma_chan *pchan; - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; int ret; ret = mxs_dma_validate_chan(channel); @@ -162,110 +207,37 @@ int mxs_dma_disable(int channel) /* * Resets the DMA channel hardware. */ -int mxs_dma_reset(int channel) -{ - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; - int ret; - - ret = mxs_dma_validate_chan(channel); - if (ret) - return ret; - - writel(1 << (channel + APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET), - &apbh_regs->hw_apbh_channel_ctrl_set); - - return 0; -} - -/* - * Freeze a DMA channel. - * - * This function causes the channel to continuously fail arbitration for bus - * access, which halts all forward progress without losing any state. A call to - * mxs_dma_unfreeze() will cause the channel to continue its current operation - * with no ill effect. - */ -int mxs_dma_freeze(int channel) -{ - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; - int ret; - - ret = mxs_dma_validate_chan(channel); - if (ret) - return ret; - - writel(1 << (channel + APBH_CHANNEL_CTRL_FREEZE_CHANNEL_OFFSET), - &apbh_regs->hw_apbh_channel_ctrl_set); - - return 0; -} - -/* - * Unfreeze a DMA channel. - * - * This function reverses the effect of mxs_dma_freeze(), enabling the DMA - * channel to continue from where it was frozen. - */ -int mxs_dma_unfreeze(int channel) +static int mxs_dma_reset(int channel) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; int ret; +#if defined(CONFIG_MX23) + uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set); + uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET; +#elif (defined(CONFIG_MX28) || defined(CONFIG_MX6) || defined(CONFIG_MX7)) + uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set); + uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET; +#endif ret = mxs_dma_validate_chan(channel); if (ret) return ret; - writel(1 << (channel + APBH_CHANNEL_CTRL_FREEZE_CHANNEL_OFFSET), - &apbh_regs->hw_apbh_channel_ctrl_clr); + writel(1 << (channel + offset), setreg); return 0; } -/* - * Read a DMA channel's hardware semaphore. - * - * As used by the MXS platform's DMA software, the DMA channel's hardware - * semaphore reflects the number of DMA commands the hardware will process, but - * has not yet finished. This is a volatile value read directly from hardware, - * so it must be be viewed as immediately stale. - * - * If the channel is not marked busy, or has finished processing all its - * commands, this value should be zero. - * - * See mxs_dma_append() for details on how DMA command blocks must be configured - * to maintain the expected behavior of the semaphore's value. - */ -int mxs_dma_read_semaphore(int channel) -{ - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; - uint32_t tmp; - int ret; - - ret = mxs_dma_validate_chan(channel); - if (ret) - return ret; - - tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); - - tmp &= APBH_CHn_SEMA_PHORE_MASK; - tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; - - return tmp; -} - /* * Enable or disable DMA interrupt. * * This function enables the given DMA channel to interrupt the CPU. */ -int mxs_dma_enable_irq(int channel, int enable) +static int mxs_dma_enable_irq(int channel, int enable) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; int ret; ret = mxs_dma_validate_chan(channel); @@ -282,36 +254,16 @@ int mxs_dma_enable_irq(int channel, int enable) return 0; } -/* - * Check if a DMA interrupt is pending. - */ -int mxs_dma_irq_is_pending(int channel) -{ - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; - uint32_t tmp; - int ret; - - ret = mxs_dma_validate_chan(channel); - if (ret) - return ret; - - tmp = readl(&apbh_regs->hw_apbh_ctrl1); - tmp |= readl(&apbh_regs->hw_apbh_ctrl2); - - return (tmp >> channel) & 1; -} - /* * Clear DMA interrupt. * * The software that is using the DMA channel must register to receive its * interrupts and, when they arrive, must call this function to clear them. */ -int mxs_dma_ack_irq(int channel) +static int mxs_dma_ack_irq(int channel) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; int ret; ret = mxs_dma_validate_chan(channel); @@ -327,7 +279,7 @@ int mxs_dma_ack_irq(int channel) /* * Request to reserve a DMA channel */ -int mxs_dma_request(int channel) +static int mxs_dma_request(int channel) { struct mxs_dma_chan *pchan; @@ -387,8 +339,10 @@ int mxs_dma_release(int channel) struct mxs_dma_desc *mxs_dma_desc_alloc(void) { struct mxs_dma_desc *pdesc; + uint32_t size; - pdesc = memalign(MXS_DMA_ALIGNMENT, sizeof(struct mxs_dma_desc)); + size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); + pdesc = memalign(MXS_DMA_ALIGNMENT, size); if (pdesc == NULL) return NULL; @@ -410,32 +364,6 @@ void mxs_dma_desc_free(struct mxs_dma_desc *pdesc) free(pdesc); } -/* - * Return the address of the command within a descriptor. - */ -unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) -{ - return desc->address + offsetof(struct mxs_dma_desc, cmd); -} - -/* - * Check if descriptor is on a channel's active list. - * - * This function returns the state of a descriptor's "ready" flag. This flag is - * usually set only if the descriptor appears on a channel's active list. The - * descriptor may or may not have already been processed by the hardware. - * - * The "ready" flag is set when the descriptor is submitted to a channel by a - * call to mxs_dma_append() or mxs_dma_append_list(). The "ready" flag is - * cleared when a processed descriptor is moved off the active list by a call - * to mxs_dma_finish(). The "ready" flag is NOT cleared if the descriptor is - * aborted by a call to mxs_dma_disable(). - */ -int mxs_dma_desc_pending(struct mxs_dma_desc *pdesc) -{ - return pdesc->flags & MXS_DMA_DESC_READY; -} - /* * Add a DMA descriptor to a channel. * @@ -499,38 +427,17 @@ int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc) last->cmd.next = mxs_dma_cmd_address(pdesc); last->cmd.data |= MXS_DMA_DESC_CHAIN; + + mxs_dma_flush_desc(last); } pdesc->flags |= MXS_DMA_DESC_READY; if (pdesc->flags & MXS_DMA_DESC_FIRST) pchan->pending_num++; list_add_tail(&pdesc->node, &pchan->active); - return ret; -} - -/* - * Retrieve processed DMA descriptors. - * - * This function moves all the descriptors from the DMA channel's "done" list to - * the head of the given list. - */ -int mxs_dma_get_finished(int channel, struct list_head *head) -{ - struct mxs_dma_chan *pchan; - int ret; - - ret = mxs_dma_validate_chan(channel); - if (ret) - return ret; - - if (head == NULL) - return 0; - - pchan = mxs_dma_channels + channel; + mxs_dma_flush_desc(pdesc); - list_splice(&pchan->done, head); - - return 0; + return ret; } /* @@ -544,7 +451,7 @@ int mxs_dma_get_finished(int channel, struct list_head *head) * This function marks the DMA channel as "not busy" if no unprocessed * descriptors remain on the "active" list. */ -int mxs_dma_finish(int channel, struct list_head *head) +static int mxs_dma_finish(int channel, struct list_head *head) { int sem; struct mxs_dma_chan *pchan; @@ -590,23 +497,23 @@ int mxs_dma_finish(int channel, struct list_head *head) /* * Wait for DMA channel to complete */ -int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) +static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; int ret; ret = mxs_dma_validate_chan(chan); if (ret) return ret; - if (mx28_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, + if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, 1 << chan, timeout)) { ret = -ETIMEDOUT; mxs_dma_reset(chan); } - return 0; + return ret; } /* @@ -614,7 +521,7 @@ int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) */ int mxs_dma_go(int chan) { - uint32_t timeout = 10000; + uint32_t timeout = 10000000; int ret; LIST_HEAD(tmp_desc_list); @@ -637,17 +544,37 @@ int mxs_dma_go(int chan) return ret; } +/* + * Execute a continuously running circular DMA descriptor. + * NOTE: This is not intended for general use, but rather + * for the LCD driver in Smart-LCD mode. It allows + * continuous triggering of the RUN bit there. + */ +void mxs_dma_circ_start(int chan, struct mxs_dma_desc *pdesc) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + + mxs_dma_flush_desc(pdesc); + + mxs_dma_enable_irq(chan, 1); + + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[chan].hw_apbh_ch_nxtcmdar); + writel(1, &apbh_regs->ch[chan].hw_apbh_ch_sema); + writel(1 << (chan + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_clr); +} + /* * Initialize the DMA hardware */ -int mxs_dma_init(void) +void mxs_dma_init(void) { - struct mx28_apbh_regs *apbh_regs = - (struct mx28_apbh_regs *)MXS_APBH_BASE; - struct mxs_dma_chan *pchan; - int ret, channel; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; - mx28_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); + mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); #ifdef CONFIG_APBH_DMA_BURST8 writel(APBH_CTRL0_AHB_BURST8_EN, @@ -664,28 +591,26 @@ int mxs_dma_init(void) writel(APBH_CTRL0_APB_BURST_EN, &apbh_regs->hw_apbh_ctrl0_clr); #endif +} - for (channel = 0; channel < MXS_MAX_DMA_CHANNELS; channel++) { - pchan = mxs_dma_channels + channel; - pchan->flags = MXS_DMA_FLAGS_VALID; +int mxs_dma_init_channel(int channel) +{ + struct mxs_dma_chan *pchan; + int ret; - ret = mxs_dma_request(channel); + pchan = mxs_dma_channels + channel; + pchan->flags = MXS_DMA_FLAGS_VALID; - if (ret) { - printf("MXS DMA: Can't acquire DMA channel %i\n", - channel); + ret = mxs_dma_request(channel); - goto err; - } - - mxs_dma_reset(channel); - mxs_dma_ack_irq(channel); + if (ret) { + printf("MXS DMA: Can't acquire DMA channel %i\n", + channel); + return ret; } - return 0; + mxs_dma_reset(channel); + mxs_dma_ack_irq(channel); -err: - while (--channel >= 0) - mxs_dma_release(channel); - return ret; + return 0; }