blob: 92e39e1f496a67cd8733ea1d0ba36cc759f32b0a [file] [log] [blame]
/*
* sdma2edma.c
*
* SDMA to EDMA3 Wrapper.
*
* NOTE: Since we are invoking EDMA API, comments for all APIs in this file
* are EDMA specific.
*
* Copyright (C) 2010-2011 Texas Instruments.
* Author: Mansoor Ahamed <mansoor.ahamed@ti.com>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <asm/system.h>
#include <mach/hardware.h>
#include <plat/dma.h>
#include <plat/tc.h>
/* some edma specific hacks which might change */
#include <asm/hardware/edma.h>
/**
* omap_request_dma - allocate DMA channel and paired parameter RAM
* @dev_id: specific channel to allocate; negative for "any unmapped channel"
* @callback: optional; to be issued on DMA completion or errors
* @data: passed to callback
* @dma_ch_out: allocated channel number returned in this variable
*
* This allocates a DMA channel and its associated parameter RAM slot.
* The parameter RAM is initialized to hold a dummy transfer.
*
* Normal use is to pass a specific channel number as @channel, to make
* use of hardware events mapped to that channel. When the channel will
* be used only for software triggering or event chaining, channels not
* mapped to hardware events (or mapped to unused events) are preferable.
*
* DMA transfers start from a channel using edma_start(), or by
* chaining. When the transfer described in that channel's parameter RAM
* slot completes, that slot's data may be reloaded through a link.
*
* DMA errors are only reported to the @callback associated with the
* channel driving that transfer, but transfer completion callbacks can
* be sent to another channel under control of the TCC field in
* the option word of the transfer's parameter RAM set. Drivers must not
* use DMA transfer completion callbacks for channels they did not allocate.
* (The same applies to TCC codes used in transfer chaining.)
*
* TODO: -
* . In the edma call, last param i.e TC hard coded to EVENTQ_2
* . The callback's ch_status which should be used in McSPI driver
* to stop/clean EDMA is currently ignored in some driver (eg. McSPI)
*/
int omap_request_dma(int dev_id, const char *dev_name,
void (*callback)(int lch, u16 ch_status, void *data),
void *data, int *dma_ch_out)
{
struct edmacc_param p_ram;
typedef void (*EDMA_CALLBACK)(unsigned, u16, void*);
EDMA_CALLBACK edma_callback = (EDMA_CALLBACK)(callback);
*dma_ch_out = edma_alloc_channel(dev_id, edma_callback, data, EVENTQ_2);
if (*dma_ch_out < 0)
return (-1);
/* enable interrupts */
edma_read_slot(*dma_ch_out, &p_ram);
p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(*dma_ch_out));
edma_write_slot(*dma_ch_out, &p_ram);
return 0;
}
EXPORT_SYMBOL(omap_request_dma);
/**
* omap_free_dma - deallocate DMA channel
* @lch: dma channel returned from edma_alloc_channel()
*
* This deallocates the DMA channel and associated parameter RAM slot
* allocated by omap_request_dma().
*
* Callers are responsible for ensuring the channel is inactive, and
* will not be reactivated by linking, chaining, or software calls to
* omap_start_dma().
*/
void omap_free_dma(int lch)
{
edma_free_channel((unsigned)lch);
}
EXPORT_SYMBOL(omap_free_dma);
/**
* omap_start_dma - start dma on a channel
* @lch: channel being activated
*
* Channels with event associations will be triggered by their hardware
* events, and channels without such associations will be triggered by
* software. (At this writing there is no interface for using software
* triggers except with channels that don't support hardware triggers.)
*
*/
void omap_start_dma(int lch)
{
edma_start((unsigned)lch);
}
EXPORT_SYMBOL(omap_start_dma);
/**
* omap_stop_dma - stops dma on the channel passed
* @lch: channel being deactivated
*
* When @lch is a channel, any active transfer is paused and
* all pending hardware events are cleared. The current transfer
* may not be resumed, and the channel's Parameter RAM should be
* reinitialized before being reused.
*/
void omap_stop_dma(int lch)
{
edma_stop((unsigned)lch);
}
EXPORT_SYMBOL(omap_stop_dma);
/**
* omap_cleanup_dma - Bring back DMA to initial state
* @lch: channel being cleaned up
*
* It cleans ParamEntry and bring back EDMA to initial state if media has
* been removed before EDMA has finished.It is usedful for removable media.
*
*
* FIXME this should not be needed ... edma_stop() should suffice.
*
*/
void omap_cleanup_dma(int lch)
{
edma_clean_channel((unsigned)lch);
}
EXPORT_SYMBOL(omap_cleanup_dma);
/**
* omap_set_dma_transfer_params - configure DMA transfer parameters
* @lch: parameter RAM slot being configured
* @data_type: how many bytes per array (at least one)
* @elem_count: how many arrays per frame (at least one)
* @frame_count: how many frames per block (at least one)
* @sync_mode: ASYNC or ABSYNC
* @dma_trigger: device id (not used)
* @src_or_dst_synch: not used
*
* See the EDMA3 documentation to understand how to configure and link
* transfers using the fields in PaRAM slots. If you are not doing it
* all at once with edma_write_slot(), you will use this routine
* plus two calls each for source and destination, setting the initial
* address and saying how to index that address.
*
* An example of an A-Synchronized transfer is a serial link using a
* single word shift register. In that case, @acnt would be equal to
* that word size; the serial controller issues a DMA synchronization
* event to transfer each word, and memory access by the DMA transfer
* controller will be word-at-a-time.
*
* An example of an AB-Synchronized transfer is a device using a FIFO.
* In that case, @acnt equals the FIFO width and @bcnt equals its depth.
* The controller with the FIFO issues DMA synchronization events when
* the FIFO threshold is reached, and the DMA transfer controller will
* transfer one frame to (or from) the FIFO. It will probably use
* efficient burst modes to access memory.
*
* . dma_trigger and channel number, this is ignored for EDMA
* . Setting bcnt_rld same as bcnt
* TODO
* . what is src_or_dst_synch?
*/
void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
int frame_count, int sync_mode,
int dma_trigger, int src_or_dst_synch)
{
int d_type[3] = {1, 2, 4};
if ((enum sync_dimension)sync_mode > ABSYNC) {
printk("SDMA2EDMA: Line:%d : Param \'sync_mode\' our of range\n",
__LINE__);
return;
}
/* translate data_type */
data_type = d_type[data_type];
edma_set_transfer_params(lch, (u16)data_type, (u16)elem_count,
(u16)frame_count, (u16)elem_count, (enum sync_dimension)sync_mode);
}
EXPORT_SYMBOL(omap_set_dma_transfer_params);
/**
* omap_set_dma_dest_params - Set initial DMA destination addr in param RAM slot
* @lch: parameter RAM slot being configured
* @dest_port: not used
* @dest_amode: INCR, except in very rare cases
* @dest_start: physical address of destination (memory, controller FIFO, etc)
* @dst_ei: byte offset between destination arrays in a frame
* @dst_fi: byte offset between destination frames in a block
*
* Note that the destination address is modified during the DMA transfer
* according to edma_set_dest_index().
*
* TODO
* . Not sure about dst_ei and dst_fi
* . fifo_width for edma is not available in sdma API hence setting it to
* W8BIT
* . dest_port is ignored
*/
void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
unsigned long dest_start,
int dst_ei, int dst_fi)
{
if ((enum address_mode)dest_amode > FIFO) {
printk("SDMA2EDMA: Line:%d : Param \'dest_amode\' our of range\n",
__LINE__);
return;
}
edma_set_dest((unsigned)lch, (dma_addr_t)dest_start,
!dest_amode, W32BIT);
edma_set_dest_index((unsigned)(lch), (s16)dst_ei, (s16)dst_fi);
}
EXPORT_SYMBOL(omap_set_dma_dest_params);
/**
* omap_set_dma_src_params - Set initial DMA source addr in param RAM slot
* @lch: parameter RAM slot being configured
* @src_port: not used
* @src_amode: INCR, except in very rare cases
* @src_start: physical address of destination (memory, controller FIFO, etc)
* @src_ei: byte offset between destination arrays in a frame
* @src_fi: byte offset between destination frames in a block
*
* Note that the source address is modified during the DMA transfer
* according to edma_set_src_index().
*
* TODO
* . Not sure about src_ei and src_fi
* . fifo_width for edma is not available in sdma API hence setting it to
* W8BIT
* . src_port is ignored
*/
void omap_set_dma_src_params(int lch, int src_port, int src_amode,
unsigned long src_start,
int src_ei, int src_fi)
{
if ((enum address_mode)src_amode > FIFO) {
printk("SDMA2EDMA: Line:%d : Param \'src_amode\' our of range\n",
__LINE__);
return;
}
edma_set_src((unsigned)lch, (dma_addr_t)src_start,
!src_amode, W32BIT);
edma_set_src_index((unsigned)(lch), (s16)src_ei, (s16)src_fi);
}
EXPORT_SYMBOL(omap_set_dma_src_params);
void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
{
printk("omap_set_dma_src_burst_mode: un-supported SDMA wrapper\n");
}
EXPORT_SYMBOL(omap_set_dma_src_burst_mode);
void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
{
printk("omap_set_dma_dest_burst_mode: un-supported SDMA wrapper\n");
}
EXPORT_SYMBOL(omap_set_dma_dest_burst_mode);
dma_addr_t omap_get_dma_dst_pos(int lch)
{
printk("omap_get_dma_dst_pos: un-supported in SDMA wrapper\n");
return 0;
}
EXPORT_SYMBOL(omap_get_dma_dst_pos);
int omap_get_dma_active_status(int lch)
{
printk("omap_get_dma_active_status: un-supported in SDMA wrapper\n");
return 0;
}
EXPORT_SYMBOL(omap_get_dma_active_status);