svn commit: r231925 - in projects/armv6/sys/arm: conf ti ti/omap4

Oleksandr Tymoshenko gonzo at FreeBSD.org
Mon Feb 20 01:20:58 UTC 2012


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 <ben.r.gray at gmail.com>

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 <ben.r.gray at gmail.com>.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/interrupt.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/timetc.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/omap_dma.h>
+#include <arm/ti/omap_dmareg.h>
+
+/**
+ *	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 ***


More information about the svn-src-projects mailing list