From owner-svn-src-projects@FreeBSD.ORG Mon Feb 20 01:20:58 2012 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 33D96106564A; Mon, 20 Feb 2012 01:20:58 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 1E4FD8FC0C; Mon, 20 Feb 2012 01:20:58 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q1K1KwFl024640; Mon, 20 Feb 2012 01:20:58 GMT (envelope-from gonzo@svn.freebsd.org) Received: (from gonzo@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q1K1KvIZ024635; Mon, 20 Feb 2012 01:20:57 GMT (envelope-from gonzo@svn.freebsd.org) Message-Id: <201202200120.q1K1KvIZ024635@svn.freebsd.org> From: Oleksandr Tymoshenko Date: Mon, 20 Feb 2012 01:20:57 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r231925 - in projects/armv6/sys/arm: conf ti ti/omap4 X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 20 Feb 2012 01:20:58 -0000 Author: gonzo Date: Mon Feb 20 01:20:57 2012 New Revision: 231925 URL: http://svn.freebsd.org/changeset/base/231925 Log: Add OMAP sDMA controller driver/API Submitted by: Ben Gray Added: projects/armv6/sys/arm/ti/omap_dma.c projects/armv6/sys/arm/ti/omap_dma.h projects/armv6/sys/arm/ti/omap_dmareg.h Modified: projects/armv6/sys/arm/conf/PANDABOARD projects/armv6/sys/arm/ti/omap4/files.omap4 Modified: projects/armv6/sys/arm/conf/PANDABOARD ============================================================================== --- projects/armv6/sys/arm/conf/PANDABOARD Mon Feb 20 01:18:32 2012 (r231924) +++ projects/armv6/sys/arm/conf/PANDABOARD Mon Feb 20 01:20:57 2012 (r231925) @@ -127,6 +127,10 @@ device miibus # device axe # ASIX Electronics USB Ethernet device smsc # SMSC LAN95xx USB Ethernet + +# OMAP-specific devices +device omap_dma + # Flattened Device Tree options FDT options FDT_DTB_STATIC Modified: projects/armv6/sys/arm/ti/omap4/files.omap4 ============================================================================== --- projects/armv6/sys/arm/ti/omap4/files.omap4 Mon Feb 20 01:18:32 2012 (r231924) +++ projects/armv6/sys/arm/ti/omap4/files.omap4 Mon Feb 20 01:20:57 2012 (r231925) @@ -21,7 +21,8 @@ arm/ti/ti_machdep.c standard arm/ti/omap_gpio.c optional gpio arm/ti/usb/omap_ehci.c optional usb ehci -arm/ti/ti_i2c.c optional ti_i2c +arm/ti/omap_dma.c optional omap_dma +arm/ti/ti_i2c.c optional ti_i2c arm/ti/omap4/omap4_prcm_clks.c standard arm/ti/omap4/omap4_scm_padconf.c standard Added: projects/armv6/sys/arm/ti/omap_dma.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/armv6/sys/arm/ti/omap_dma.c Mon Feb 20 01:20:57 2012 (r231925) @@ -0,0 +1,1246 @@ +/*- + * Copyright (c) 2011 + * Ben Gray . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * Kernel functions for using the DMA controller + * + * + * DMA TRANSFERS: + * A DMA transfer block consists of a number of frames (FN). Each frame + * consists of a number of elements, and each element can have a size of 8, 16, + * or 32 bits. + * + * OMAP44xx and newer chips support linked list (aka scatter gather) transfers, + * where a linked list of source/destination pairs can be placed in memory + * for the H/W to process. Earlier chips only allowed you to chain multiple + * channels together. However currently this linked list feature is not + * supported by the driver. + * + */ + +/** + * Data structure per DMA channel. + * + * + */ +struct omap_dma_channel { + + /* + * The configuration registers for the given channel, these are modified + * by the set functions and only written to the actual registers when a + * transaction is started. + */ + uint32_t reg_csdp; + uint32_t reg_ccr; + uint32_t reg_cicr; + + /* Set when one of the configuration registers above change */ + uint32_t need_reg_write; + + /* Callback function used when an interrupt is tripped on the given channel */ + void (*callback)(unsigned int ch, uint32_t ch_status, void *data); + + /* Callback data passed in the callback ... duh */ + void* callback_data; + +}; + +/** + * DMA driver context, allocated and stored globally, this driver is not + * intetned to ever be unloaded (see omap_dma_sc). + * + */ +struct omap_dma_softc { + device_t sc_dev; + struct resource* sc_irq_res; + struct resource* sc_mem_res; + + /* + * I guess in theory we should have a mutex per DMA channel for register + * modifications. But since we know we are never going to be run on a SMP + * system, we can use just the single lock for all channels. + */ + struct mtx sc_mtx; + + /* Stores the H/W revision read from the registers */ + uint32_t sc_hw_rev; + + /* + * Bits in the sc_active_channels data field indicate if the channel has + * been activated. + */ + uint32_t sc_active_channels; + + struct omap_dma_channel sc_channel[NUM_DMA_CHANNELS]; + +}; + +static struct omap_dma_softc *omap_dma_sc = NULL; + +/** + * Macros for driver mutex locking + */ +#define OMAP_DMA_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define OMAP_DMA_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define OMAP_DMA_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "omap_dma", MTX_SPIN) +#define OMAP_DMA_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define OMAP_DMA_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define OMAP_DMA_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +/** + * Function prototypes + * + */ +static void omap_dma_intr(void *); + +/** + * omap_dma_read_4 - reads a 32-bit value from one of the DMA registers + * @sc: DMA device context + * @off: The offset of a register from the DMA register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline uint32_t +omap_dma_read_4(struct omap_dma_softc *sc, bus_size_t off) +{ + return bus_read_4(sc->sc_mem_res, off); +} + +/** + * omap_dma_write_4 - writes a 32-bit value to one of the DMA registers + * @sc: DMA device context + * @off: The offset of a register from the DMA register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline void +omap_dma_write_4(struct omap_dma_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->sc_mem_res, off, val); +} + +/** + * omap_dma_is_omap3_rev - returns true if H/W is from OMAP3 series + * @sc: DMA device context + * + */ +static inline int +omap_dma_is_omap3_rev(struct omap_dma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP3_REV); +} + +/** + * omap_dma_is_omap4_rev - returns true if H/W is from OMAP4 series + * @sc: DMA device context + * + */ +static inline int +omap_dma_is_omap4_rev(struct omap_dma_softc *sc) +{ + return (sc->sc_hw_rev == DMA4_OMAP4_REV); +} + +/** + * omap_dma_intr - interrupt handler for all 4 DMA IRQs + * @arg: ignored + * + * Called when any of the four DMA IRQs are triggered. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * nothing + */ +static void +omap_dma_intr(void *arg) +{ + struct omap_dma_softc *sc = omap_dma_sc; + uint32_t intr; + uint32_t csr; + unsigned int ch, j; + struct omap_dma_channel* channel; + + OMAP_DMA_LOCK(sc); + + for (j = 0; j < NUM_DMA_IRQS; j++) { + + /* Get the flag interrupts (enabled) */ + intr = omap_dma_read_4(sc, DMA4_IRQSTATUS_L(j)); + intr &= omap_dma_read_4(sc, DMA4_IRQENABLE_L(j)); + if (intr == 0x00000000) + continue; + + /* Loop through checking the status bits */ + for (ch = 0; ch < NUM_DMA_CHANNELS; ch++) { + if (intr & (1 << ch)) { + channel = &sc->sc_channel[ch]; + + /* Read the CSR regsiter and verify we don't have a spurious IRQ */ + csr = omap_dma_read_4(sc, DMA4_CSR(ch)); + if (csr == 0) { + device_printf(sc->sc_dev, "Spurious DMA IRQ for channel " + "%d\n", ch); + continue; + } + + /* Sanity check this channel is active */ + if ((sc->sc_active_channels & (1 << ch)) == 0) { + device_printf(sc->sc_dev, "IRQ %d for a non-activated " + "channel %d\n", j, ch); + continue; + } + + /* Check the status error codes */ + if (csr & DMA4_CSR_DROP) + device_printf(sc->sc_dev, "Synchronization event drop " + "occurred during the transfer on channel %u\n", + ch); + if (csr & DMA4_CSR_SECURE_ERR) + device_printf(sc->sc_dev, "Secure transaction error event " + "on channel %u\n", ch); + if (csr & DMA4_CSR_MISALIGNED_ADRS_ERR) + device_printf(sc->sc_dev, "Misaligned address error event " + "on channel %u\n", ch); + if (csr & DMA4_CSR_TRANS_ERR) { + device_printf(sc->sc_dev, "Transaction error event on " + "channel %u\n", ch); + /* + * Apparently according to linux code, there is an errata + * that says the channel is not disabled upon this error. + * They explicitly disable the channel here .. since I + * haven't seen the errata, I'm going to ignore for now. + */ + } + + /* Clear the status flags for the IRQ */ + omap_dma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + omap_dma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + + /* Call the callback for the given channel */ + if (channel->callback) + channel->callback(ch, csr, channel->callback_data); + } + } + } + + OMAP_DMA_UNLOCK(sc); + + return; +} + +/** + * omap_dma_activate_channel - activates a DMA channel + * @ch: upon return contains the channel allocated + * @callback: a callback function to associate with the channel + * @data: optional data supplied when the callback is called + * + * Simply activates a channel be enabling and writing default values to the + * channel's register set. It doesn't start a transaction, just populates the + * internal data structures and sets defaults. + * + * Note this function doesn't enable interrupts, for that you need to call + * omap_dma_enable_channel_irq(). If not using IRQ to detect the end of the + * transfer, you can use omap_dma_status_poll() to detect a change in the + * status. + * + * A channel must be activated before any of the other DMA functions can be + * called on it. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * 0 on success, otherwise an error code + */ +int +omap_dma_activate_channel(unsigned int *ch, + void (*callback)(unsigned int ch, uint32_t status, void *data), + void *data) +{ + struct omap_dma_softc *sc = omap_dma_sc; + struct omap_dma_channel *channel = NULL; + uint32_t addr; + unsigned int i; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + if (ch == NULL) + return (EINVAL); + + OMAP_DMA_LOCK(sc); + + /* Check to see if all channels are in use */ + if (sc->sc_active_channels == 0xffffffff) { + OMAP_DMA_UNLOCK(sc); + return (ENOMEM); + } + + /* Find the first non-active channel */ + for (i = 0; i < NUM_DMA_CHANNELS; i++) { + if (!(sc->sc_active_channels & (0x1 << i))) { + sc->sc_active_channels |= (0x1 << i); + *ch = i; + break; + } + } + + /* Get the channel struct and populate the fields */ + channel = &sc->sc_channel[*ch]; + + channel->callback = callback; + channel->callback_data = data; + + channel->need_reg_write = 1; + + /* Set the default configuration for the DMA channel */ + channel->reg_csdp = DMA4_CSDP_DATA_TYPE(0x2) + | DMA4_CSDP_SRC_BURST_MODE(0) + | DMA4_CSDP_DST_BURST_MODE(0) + | DMA4_CSDP_SRC_ENDIANISM(0) + | DMA4_CSDP_DST_ENDIANISM(0) + | DMA4_CSDP_WRITE_MODE(0) + | DMA4_CSDP_SRC_PACKED(0) + | DMA4_CSDP_DST_PACKED(0); + + channel->reg_ccr = DMA4_CCR_DST_ADDRESS_MODE(1) + | DMA4_CCR_SRC_ADDRESS_MODE(1) + | DMA4_CCR_READ_PRIORITY(0) + | DMA4_CCR_WRITE_PRIORITY(0) + | DMA4_CCR_SYNC_TRIGGER(0) + | DMA4_CCR_FRAME_SYNC(0) + | DMA4_CCR_BLOCK_SYNC(0); + + channel->reg_cicr = DMA4_CICR_TRANS_ERR_IE + | DMA4_CICR_SECURE_ERR_IE + | DMA4_CICR_SUPERVISOR_ERR_IE + | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; + + /* Clear all the channel registers, this should abort any transaction */ + for (addr = DMA4_CCR(*ch); addr <= DMA4_COLOR(*ch); addr += 4) + omap_dma_write_4(sc, addr, 0x00000000); + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + +/** + * omap_dma_deactivate_channel - deactivates a channel + * @ch: the channel to deactivate + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_deactivate_channel(unsigned int ch) +{ + struct omap_dma_softc *sc = omap_dma_sc; + unsigned int j; + unsigned int addr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + /* First check if the channel is currently active */ + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EBUSY); + } + + /* Mark the channel as inactive */ + sc->sc_active_channels &= ~(1 << ch); + + /* Disable all DMA interrupts for the channel. */ + omap_dma_write_4(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + omap_dma_write_4(sc, DMA4_CCR(ch), 0); + + /* Clear the CSR register and IRQ status register */ + omap_dma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j = 0; j < NUM_DMA_IRQS; j++) { + omap_dma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + } + + /* Clear all the channel registers, this should abort any transaction */ + for (addr = DMA4_CCR(ch); addr <= DMA4_COLOR(ch); addr += 4) + omap_dma_write_4(sc, addr, 0x00000000); + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + +/** + * omap_dma_disable_channel_irq - disables IRQ's on the given channel + * @ch: the channel to disable IRQ's on + * + * Disable interupt generation for the given channel. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_disable_channel_irq(unsigned int ch) +{ + struct omap_dma_softc *sc = omap_dma_sc; + uint32_t irq_enable; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + /* Disable all the individual error conditions */ + sc->sc_channel[ch].reg_cicr = 0x0000; + omap_dma_write_4(sc, DMA4_CICR(ch), 0x0000); + + /* Disable the channel interrupt enable */ + for (j = 0; j < NUM_DMA_IRQS; j++) { + irq_enable = omap_dma_read_4(sc, DMA4_IRQENABLE_L(j)); + irq_enable &= ~(1 << ch); + + omap_dma_write_4(sc, DMA4_IRQENABLE_L(j), irq_enable); + } + + /* Indicate the registers need to be rewritten on the next transaction */ + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_disable_channel_irq - enables IRQ's on the given channel + * @ch: the channel to enable IRQ's on + * @flags: bitmask of interrupt types to enable + * + * Flags can be a bitmask of the following options: + * DMA_IRQ_FLAG_DROP + * DMA_IRQ_FLAG_HALF_FRAME_COMPL + * DMA_IRQ_FLAG_FRAME_COMPL + * DMA_IRQ_FLAG_START_LAST_FRAME + * DMA_IRQ_FLAG_BLOCK_COMPL + * DMA_IRQ_FLAG_ENDOF_PKT + * DMA_IRQ_FLAG_DRAIN + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_enable_channel_irq(unsigned int ch, uint32_t flags) +{ + struct omap_dma_softc *sc = omap_dma_sc; + uint32_t irq_enable; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + /* Always enable the error interrupts if we have interrupts enabled */ + flags |= DMA4_CICR_TRANS_ERR_IE | DMA4_CICR_SECURE_ERR_IE | + DMA4_CICR_SUPERVISOR_ERR_IE | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; + + sc->sc_channel[ch].reg_cicr = flags; + + /* Write the values to the register */ + omap_dma_write_4(sc, DMA4_CICR(ch), flags); + + /* Enable the channel interrupt enable */ + irq_enable = omap_dma_read_4(sc, DMA4_IRQENABLE_L(0)); + irq_enable |= (1 << ch); + + omap_dma_write_4(sc, DMA4_IRQENABLE_L(0), irq_enable); + + /* Indicate the registers need to be rewritten on the next transaction */ + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_get_channel_status - returns the status of a given channel + * @ch: the channel number to get the status of + * @status: upon return will contain the status bitmask, see below for possible + * values. + * + * DMA_STATUS_DROP + * DMA_STATUS_HALF + * DMA_STATUS_FRAME + * DMA_STATUS_LAST + * DMA_STATUS_BLOCK + * DMA_STATUS_SYNC + * DMA_STATUS_PKT + * DMA_STATUS_TRANS_ERR + * DMA_STATUS_SECURE_ERR + * DMA_STATUS_SUPERVISOR_ERR + * DMA_STATUS_MISALIGNED_ADRS_ERR + * DMA_STATUS_DRAIN_END + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_get_channel_status(unsigned int ch, uint32_t *status) +{ + struct omap_dma_softc *sc = omap_dma_sc; + uint32_t csr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + OMAP_DMA_UNLOCK(sc); + + csr = omap_dma_read_4(sc, DMA4_CSR(ch)); + + if (status != NULL) + *status = csr; + + return (0); +} + +/** + * omap_dma_start_xfer - starts a DMA transfer + * @ch: the channel number to set the endianess of + * @src_paddr: the source phsyical address + * @dst_paddr: the destination phsyical address + * @frmcnt: the number of frames per block + * @elmcnt: the number of elements in a frame, an element is either an 8, 16 + * or 32-bit value as defined by omap_dma_set_xfer_burst() + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_start_xfer(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, + unsigned int frmcnt, unsigned int elmcnt) +{ + struct omap_dma_softc *sc = omap_dma_sc; + struct omap_dma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + omap_dma_write_4(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + /* b) Set the number of element per frame CEN[23:0] */ + omap_dma_write_4(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frame per block CFN[15:0] */ + omap_dma_write_4(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + omap_dma_write_4(sc, DMA4_CSSA(ch), src_paddr); + omap_dma_write_4(sc, DMA4_CDSA(ch), dst_paddr); + + /* e) Write the CCR register */ + omap_dma_write_4(sc, DMA4_CCR(ch), channel->reg_ccr); + + /* f) - Set the source element index increment CSEI[15:0] */ + omap_dma_write_4(sc, DMA4_CSE(ch), 0x0001); + + /* - Set the source frame index increment CSFI[15:0] */ + omap_dma_write_4(sc, DMA4_CSF(ch), 0x0001); + + /* - Set the destination element index increment CDEI[15:0]*/ + omap_dma_write_4(sc, DMA4_CDE(ch), 0x0001); + + /* - Set the destination frame index increment CDFI[31:0] */ + omap_dma_write_4(sc, DMA4_CDF(ch), 0x0001); + + /* Clear the status register */ + omap_dma_write_4(sc, DMA4_CSR(ch), 0x1FFE); + + /* Write the start-bit and away we go */ + ccr = omap_dma_read_4(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + omap_dma_write_4(sc, DMA4_CCR(ch), ccr); + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_start_xfer_packet - starts a packet DMA transfer + * @ch: the channel number to use for the transfer + * @src_paddr: the source physical address + * @dst_paddr: the destination physical address + * @frmcnt: the number of frames to transfer + * @elmcnt: the number of elements in a frame, an element is either an 8, 16 + * or 32-bit value as defined by omap_dma_set_xfer_burst() + * @pktsize: the number of elements in each transfer packet + * + * The @frmcnt and @elmcnt define the overall number of bytes to transfer, + * typically @frmcnt is 1 and @elmcnt contains the total number of elements. + * @pktsize is the size of each individual packet, there might be multiple + * packets per transfer. i.e. for the following with element size of 32-bits + * + * frmcnt = 1, elmcnt = 512, pktsize = 128 + * + * Total transfer bytes = 1 * 512 = 512 elements or 2048 bytes + * Packets transfered = 128 / 512 = 4 + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, + unsigned long dst_paddr, unsigned int frmcnt, + unsigned int elmcnt, unsigned int pktsize) +{ + struct omap_dma_softc *sc = omap_dma_sc; + struct omap_dma_channel *channel; + uint32_t ccr; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + channel = &sc->sc_channel[ch]; + + /* a) Write the CSDP register */ + if (channel->need_reg_write) + omap_dma_write_4(sc, DMA4_CSDP(ch), + channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); + + /* b) Set the number of elements to transfer CEN[23:0] */ + omap_dma_write_4(sc, DMA4_CEN(ch), elmcnt); + + /* c) Set the number of frames to transfer CFN[15:0] */ + omap_dma_write_4(sc, DMA4_CFN(ch), frmcnt); + + /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ + omap_dma_write_4(sc, DMA4_CSSA(ch), src_paddr); + omap_dma_write_4(sc, DMA4_CDSA(ch), dst_paddr); + + /* e) Write the CCR register */ + omap_dma_write_4(sc, DMA4_CCR(ch), + channel->reg_ccr | DMA4_CCR_PACKET_TRANS); + + /* f) - Set the source element index increment CSEI[15:0] */ + omap_dma_write_4(sc, DMA4_CSE(ch), 0x0001); + + /* - Set the packet size, this is dependent on the sync source */ + if (channel->reg_ccr & DMA4_CCR_SEL_SRC_DST_SYNC(1)) + omap_dma_write_4(sc, DMA4_CSF(ch), pktsize); + else + omap_dma_write_4(sc, DMA4_CDE(ch), pktsize); + + /* - Set the destination frame index increment CDFI[31:0] */ + omap_dma_write_4(sc, DMA4_CDF(ch), 0x0001); + + /* Clear the status register */ + omap_dma_write_4(sc, DMA4_CSR(ch), 0x1FFE); + + /* Write the start-bit and away we go */ + ccr = omap_dma_read_4(sc, DMA4_CCR(ch)); + ccr |= (1 << 7); + omap_dma_write_4(sc, DMA4_CCR(ch), ccr); + + /* Clear the reg write flag */ + channel->need_reg_write = 0; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_stop_xfer - stops any currently active transfers + * @ch: the channel number to set the endianess of + * + * This function call is effectively a NOP if no transaction is in progress. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_stop_xfer(unsigned int ch) +{ + struct omap_dma_softc *sc = omap_dma_sc; + unsigned int j; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + /* Disable all DMA interrupts for the channel. */ + omap_dma_write_4(sc, DMA4_CICR(ch), 0); + + /* Make sure the DMA transfer is stopped. */ + omap_dma_write_4(sc, DMA4_CCR(ch), 0); + + /* Clear the CSR register and IRQ status register */ + omap_dma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); + for (j = 0; j < NUM_DMA_IRQS; j++) { + omap_dma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); + } + + /* Configuration registers need to be re-written on the next xfer */ + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return (0); +} + +/** + * omap_dma_set_xfer_endianess - sets the endianess of subsequent transfers + * @ch: the channel number to set the endianess of + * @src: the source endianess (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) + * @dst: the destination endianess (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct omap_dma_softc *sc = omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_ENDIANISM(1); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_ENDIANISM(src); + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_ENDIANISM(1); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_ENDIANISM(dst); + + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + +/** + * omap_dma_set_xfer_burst - sets the source and destination element size + * @ch: the channel number to set the burst settings of + * @src: the source endianess (either DMA_BURST_NONE, DMA_BURST_16, DMA_BURST_32 + * or DMA_BURST_64) + * @dst: the destination endianess (either DMA_BURST_NONE, DMA_BURST_16, + * DMA_BURST_32 or DMA_BURST_64) + * + * This function sets the size of the elements for all subsequent transfers. + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst) +{ + struct omap_dma_softc *sc = omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_BURST_MODE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_BURST_MODE(src); + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_BURST_MODE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_BURST_MODE(dst); + + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + +/** + * omap_dma_set_xfer_data_type - driver attach function + * @ch: the channel number to set the endianess of + * @type: the xfer data type (either DMA_DATA_8BITS_SCALAR, DMA_DATA_16BITS_SCALAR + * or DMA_DATA_32BITS_SCALAR) + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +int +omap_dma_set_xfer_data_type(unsigned int ch, unsigned int type) +{ + struct omap_dma_softc *sc = omap_dma_sc; + + /* Sanity check */ + if (sc == NULL) + return (ENOMEM); + + OMAP_DMA_LOCK(sc); + + if ((sc->sc_active_channels & (1 << ch)) == 0) { + OMAP_DMA_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DATA_TYPE(0x3); + sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DATA_TYPE(type); + + sc->sc_channel[ch].need_reg_write = 1; + + OMAP_DMA_UNLOCK(sc); + + return 0; +} + +/** + * omap_dma_set_callback - driver attach function + * @dev: dma device handle + * + * + * + * LOCKING: + * DMA registers protected by internal mutex + * + * RETURNS: *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***