ppc4xx_dma
Ronnie Hedlund
Ronnie.Hedlund at dataductus.se
Fri Dec 21 09:41:11 EST 2007
Hi,
DMA code (for the EBC) is broken for ppc4xx as it is (for all CPUs) in the linux kernel.
This (or some of it) should be implemented in the ppc kernel (since the same broken code has been provided for many years).
This works, and is thread safe. It can use more than one channel in parallel, without causing the "one in a million"-type of errors that WILL happen using the current code.
The old post regarding this matter.
http://ozlabs.org/pipermail/linuxppc-embedded/2005-December/021225.html
It is true that this code does handle the dma-memory in a new way, memory for the sg list is allocated dynamically, which takes a little more CPU time at allocation, but it can still operate on any DMA transfer size (as opposed to the hard coded value (<600Kb) used in current code). Reallocation can be prevented by keeping an allocation for many concurrent transfers.
Hope to help someone, but I'm somewhat shocked that the DMA code in the current 2.6 kernel is in the same state as it was many years ago in the 2.4 kernel, even though patches exists...
Seems everyone that uses the DMA->PLB3 (EBC) needs to patch the kernel code them selves (I'm probably the only one using it/has the hardware to run it, since not much has happened).
[Maybe patches like this are implemented already, then this post was not necessary]
Which you a nice DMA.
/Ronnie Hedlund
-------------- next part --------------
/*
* include/asm-ppc/ppc4xx_dma.h
*
* IBM PPC4xx DMA engine library
*
* Copyright 2000-2004 MontaVista Software Inc.
*
* Cleaned up a bit more, Matt Porter <mporter at kernel.crashing.org>
*
* Original code by Armin Kuster <akuster at mvista.com>
* and Pete Popov <ppopov at mvista.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.
*
* 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.
*/
#ifdef __KERNEL__
#ifndef __ASMPPC_PPC4xx_DMA_H
#define __ASMPPC_PPC4xx_DMA_H
#include <linux/types.h>
#include <asm/mmu.h>
#include <asm/ibm4xx.h>
#undef DEBUG_4xxDMA
#define MAX_PPC4xx_DMA_CHANNELS 4
/*
* Function return status codes
* These values are used to indicate whether or not the function
* call was successful, or a bad/invalid parameter was passed.
*/
#define DMA_STATUS_GOOD 0
#define DMA_STATUS_BAD_CHANNEL 1
#define DMA_STATUS_BAD_HANDLE 2
#define DMA_STATUS_BAD_MODE 3
#define DMA_STATUS_NULL_POINTER 4
#define DMA_STATUS_OUT_OF_MEMORY 5
#define DMA_STATUS_SGL_LIST_EMPTY 6
#define DMA_STATUS_GENERAL_ERROR 7
#define DMA_STATUS_CHANNEL_NOTFREE 8
#define DMA_CHANNEL_BUSY 0x80000000
/*
* These indicate status as returned from the DMA Status Register.
*/
#define DMA_STATUS_NO_ERROR 0
#define DMA_STATUS_CS 1 /* Count Status */
#define DMA_STATUS_TS 2 /* Transfer Status */
#define DMA_STATUS_DMA_ERROR 3 /* DMA Error Occurred */
#define DMA_STATUS_DMA_BUSY 4 /* The channel is busy */
/*
* DMA Channel Control Registers
*/
#if defined(CONFIG_44x) && !defined(CONFIG_440EP)
#define PPC4xx_DMA_64BIT
#define DMA_CR_OFFSET 1
#else
#define DMA_CR_OFFSET 0
#endif
#define DMA_CE_ENABLE (1<<31) /* DMA Channel Enable */
#define SET_DMA_CE_ENABLE(x) (((x)&0x1)<<31)
#define GET_DMA_CE_ENABLE(x) (((x)&DMA_CE_ENABLE)>>31)
#define DMA_CIE_ENABLE (1<<30) /* DMA Channel Interrupt Enable */
#define SET_DMA_CIE_ENABLE(x) (((x)&0x1)<<30)
#define GET_DMA_CIE_ENABLE(x) (((x)&DMA_CIE_ENABLE)>>30)
#define DMA_TD (1<<29)
#define SET_DMA_TD(x) (((x)&0x1)<<29)
#define GET_DMA_TD(x) (((x)&DMA_TD)>>29)
#define DMA_PL (1<<28) /* Peripheral Location */
#define SET_DMA_PL(x) (((x)&0x1)<<28)
#define GET_DMA_PL(x) (((x)&DMA_PL)>>28)
#define EXTERNAL_PERIPHERAL 0
#define INTERNAL_PERIPHERAL 1
#define SET_DMA_PW(x) (((x)&0x3)<<(26-DMA_CR_OFFSET)) /* Peripheral Width */
#define DMA_PW_MASK SET_DMA_PW(3)
#define PW_8 0
#define PW_16 1
#define PW_32 2
#define PW_64 3
/* FIXME: Add PW_128 support for 440GP DMA block */
#define GET_DMA_PW(x) (((x)&DMA_PW_MASK)>>(26-DMA_CR_OFFSET))
#define DMA_DAI (1<<(25-DMA_CR_OFFSET)) /* Destination Address Increment */
#define SET_DMA_DAI(x) (((x)&0x1)<<(25-DMA_CR_OFFSET))
#define DMA_SAI (1<<(24-DMA_CR_OFFSET)) /* Source Address Increment */
#define SET_DMA_SAI(x) (((x)&0x1)<<(24-DMA_CR_OFFSET))
#define DMA_BEN (1<<(23-DMA_CR_OFFSET)) /* Buffer Enable */
#define SET_DMA_BEN(x) (((x)&0x1)<<(23-DMA_CR_OFFSET))
#define SET_DMA_TM(x) (((x)&0x3)<<(21-DMA_CR_OFFSET)) /* Transfer Mode */
#define DMA_TM_MASK SET_DMA_TM(3)
#define TM_PERIPHERAL 0 /* Peripheral */
#define TM_RESERVED 1 /* Reserved */
#define TM_S_MM 2 /* Memory to Memory */
#define TM_D_MM 3 /* Device Paced Memory to Memory */
#define GET_DMA_TM(x) (((x)&DMA_TM_MASK)>>(21-DMA_CR_OFFSET))
#define SET_DMA_PSC(x) (((x)&0x3)<<(19-DMA_CR_OFFSET)) /* Peripheral Setup Cycles */
#define DMA_PSC_MASK SET_DMA_PSC(3)
#define GET_DMA_PSC(x) (((x)&DMA_PSC_MASK)>>(19-DMA_CR_OFFSET))
#define SET_DMA_PWC(x) (((x)&0x3F)<<(13-DMA_CR_OFFSET)) /* Peripheral Wait Cycles */
#define DMA_PWC_MASK SET_DMA_PWC(0x3F)
#define GET_DMA_PWC(x) (((x)&DMA_PWC_MASK)>>(13-DMA_CR_OFFSET))
#define SET_DMA_PHC(x) (((x)&0x7)<<(10-DMA_CR_OFFSET)) /* Peripheral Hold Cycles */
#define DMA_PHC_MASK SET_DMA_PHC(0x7)
#define GET_DMA_PHC(x) (((x)&DMA_PHC_MASK)>>(10-DMA_CR_OFFSET))
#define DMA_ETD_OUTPUT (1<<(9-DMA_CR_OFFSET)) /* EOT pin is a TC output */
#define SET_DMA_ETD(x) (((x)&0x1)<<(9-DMA_CR_OFFSET))
#define DMA_TCE_ENABLE (1<<(8-DMA_CR_OFFSET))
#define SET_DMA_TCE(x) (((x)&0x1)<<(8-DMA_CR_OFFSET))
#define DMA_DEC (1<<(2)) /* Address Decrement */
#define SET_DMA_DEC(x) (((x)&0x1)<<2)
#define GET_DMA_DEC(x) (((x)&DMA_DEC)>>2)
/*
* Transfer Modes
* These modes are defined in a way that makes it possible to
* simply "or" in the value in the control register.
*/
#define DMA_MODE_MM (SET_DMA_TM(TM_S_MM)) /* memory to memory */
/* Device-paced memory to memory, */
/* device is at source address */
#define DMA_MODE_MM_DEVATSRC (DMA_TD | SET_DMA_TM(TM_D_MM))
/* Device-paced memory to memory, */
/* device is at destination address */
#define DMA_MODE_MM_DEVATDST (SET_DMA_TM(TM_D_MM))
/* 405gp/440gp */
#define SET_DMA_PREFETCH(x) (((x)&0x3)<<(4-DMA_CR_OFFSET)) /* Memory Read Prefetch */
#define DMA_PREFETCH_MASK SET_DMA_PREFETCH(3)
#define PREFETCH_1 0 /* Prefetch 1 Double Word */
#define PREFETCH_2 1
#define PREFETCH_4 2
#define GET_DMA_PREFETCH(x) (((x)&DMA_PREFETCH_MASK)>>(4-DMA_CR_OFFSET))
#define DMA_PCE (1<<(3-DMA_CR_OFFSET)) /* Parity Check Enable */
#define SET_DMA_PCE(x) (((x)&0x1)<<(3-DMA_CR_OFFSET))
#define GET_DMA_PCE(x) (((x)&DMA_PCE)>>(3-DMA_CR_OFFSET))
/* stb3x */
#define DMA_ECE_ENABLE (1<<5)
#define SET_DMA_ECE(x) (((x)&0x1)<<5)
#define GET_DMA_ECE(x) (((x)&DMA_ECE_ENABLE)>>5)
#define DMA_TCD_DISABLE (1<<4)
#define SET_DMA_TCD(x) (((x)&0x1)<<4)
#define GET_DMA_TCD(x) (((x)&DMA_TCD_DISABLE)>>4)
typedef uint32_t sgl_handle_t;
#ifdef CONFIG_PPC4xx_EDMA
#ifdef CONFIG_VDR
#define SGL_LIST_SIZE (8*4096)
#else
#define SGL_LIST_SIZE 4096
#endif
#define DMA_PPC4xx_SIZE SGL_LIST_SIZE
#define SET_DMA_PRIORITY(x) (((x)&0x3)<<(6-DMA_CR_OFFSET)) /* DMA Channel Priority */
#define DMA_PRIORITY_MASK SET_DMA_PRIORITY(3)
#define PRIORITY_LOW 0
#define PRIORITY_MID_LOW 1
#define PRIORITY_MID_HIGH 2
#define PRIORITY_HIGH 3
#define GET_DMA_PRIORITY(x) (((x)&DMA_PRIORITY_MASK)>>(6-DMA_CR_OFFSET))
/*
* DMA Polarity Configuration Register
*/
#define DMAReq_ActiveLow(chan) (1<<(31-(chan*3)))
#define DMAAck_ActiveLow(chan) (1<<(30-(chan*3)))
#define EOT_ActiveLow(chan) (1<<(29-(chan*3))) /* End of Transfer */
/*
* DMA Sleep Mode Register
*/
#define SLEEP_MODE_ENABLE (1<<21)
/*
* DMA Status Register
*/
#define DMA_CS0 (1<<31) /* Terminal Count has been reached */
#define DMA_CS1 (1<<30)
#define DMA_CS2 (1<<29)
#define DMA_CS3 (1<<28)
#define DMA_TS0 (1<<27) /* End of Transfer has been requested */
#define DMA_TS1 (1<<26)
#define DMA_TS2 (1<<25)
#define DMA_TS3 (1<<24)
#define DMA_CH0_ERR (1<<23) /* DMA Chanel 0 Error */
#define DMA_CH1_ERR (1<<22)
#define DMA_CH2_ERR (1<<21)
#define DMA_CH3_ERR (1<<20)
#define DMA_IN_DMA_REQ0 (1<<19) /* Internal DMA Request is pending */
#define DMA_IN_DMA_REQ1 (1<<18)
#define DMA_IN_DMA_REQ2 (1<<17)
#define DMA_IN_DMA_REQ3 (1<<16)
#define DMA_EXT_DMA_REQ0 (1<<15) /* External DMA Request is pending */
#define DMA_EXT_DMA_REQ1 (1<<14)
#define DMA_EXT_DMA_REQ2 (1<<13)
#define DMA_EXT_DMA_REQ3 (1<<12)
#define DMA_CH0_BUSY (1<<11) /* DMA Channel 0 Busy */
#define DMA_CH1_BUSY (1<<10)
#define DMA_CH2_BUSY (1<<9)
#define DMA_CH3_BUSY (1<<8)
#define DMA_SG0 (1<<7) /* DMA Channel 0 Scatter/Gather in progress */
#define DMA_SG1 (1<<6)
#define DMA_SG2 (1<<5)
#define DMA_SG3 (1<<4)
/* DMA Channel Count Register */
#define DMA_CTC_BTEN (1<<23) /* Burst Enable/Disable bit */
#define DMA_CTC_BSIZ_MSK (3<<21) /* Mask of the Burst size bits */
#define DMA_CTC_BSIZ_2 (0)
#define DMA_CTC_BSIZ_4 (1<<21)
#define DMA_CTC_BSIZ_8 (2<<21)
#define DMA_CTC_BSIZ_16 (3<<21)
/*
* DMA SG Command Register
*/
#define SSG_ENABLE(chan) (1<<(31-chan)) /* Start Scatter Gather */
#define SSG_MASK_ENABLE(chan) (1<<(15-chan)) /* Enable writing to SSG0 bit */
/*
* DMA Scatter/Gather Descriptor Bit fields
*/
#define SG_LINK (1<<31) /* Link */
#define SG_TCI_ENABLE (1<<29) /* Enable Terminal Count Interrupt */
#define SG_ETI_ENABLE (1<<28) /* Enable End of Transfer Interrupt */
#define SG_ERI_ENABLE (1<<27) /* Enable Error Interrupt */
#define SG_COUNT_MASK 0xFFFF /* Count Field */
#define SET_DMA_CONTROL \
(SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */ \
SET_DMA_BEN(p_init->buffer_enable) | /* buffer enable */\
SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */ \
SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */ \
SET_DMA_PL(p_init->pl) | /* peripheral location */ \
SET_DMA_DAI(p_init->dai) | /* dest addr increment */ \
SET_DMA_SAI(p_init->sai) | /* src addr increment */ \
SET_DMA_PRIORITY(p_init->cp) | /* channel priority */ \
SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */ \
SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */ \
SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */ \
SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */ \
SET_DMA_PREFETCH(p_init->pf) /* read prefetch */)
#define GET_DMA_POLARITY(chan) (DMAReq_ActiveLow(chan) | DMAAck_ActiveLow(chan) | EOT_ActiveLow(chan))
#elif defined(CONFIG_STB03xxx) /* stb03xxx */
#define DMA_PPC4xx_SIZE 4096
/*
* DMA Status Register
*/
#define SET_DMA_PRIORITY(x) (((x)&0x00800001)) /* DMA Channel Priority */
#define DMA_PRIORITY_MASK 0x00800001
#define PRIORITY_LOW 0x00000000
#define PRIORITY_MID_LOW 0x00000001
#define PRIORITY_MID_HIGH 0x00800000
#define PRIORITY_HIGH 0x00800001
#define GET_DMA_PRIORITY(x) (((((x)&DMA_PRIORITY_MASK) &0x00800000) >> 22 ) | (((x)&DMA_PRIORITY_MASK) &0x00000001))
#define DMA_CS0 (1<<31) /* Terminal Count has been reached */
#define DMA_CS1 (1<<30)
#define DMA_CS2 (1<<29)
#define DMA_CS3 (1<<28)
#define DMA_TS0 (1<<27) /* End of Transfer has been requested */
#define DMA_TS1 (1<<26)
#define DMA_TS2 (1<<25)
#define DMA_TS3 (1<<24)
#define DMA_CH0_ERR (1<<23) /* DMA Chanel 0 Error */
#define DMA_CH1_ERR (1<<22)
#define DMA_CH2_ERR (1<<21)
#define DMA_CH3_ERR (1<<20)
#define DMA_CT0 (1<<19) /* Chained transfere */
#define DMA_IN_DMA_REQ0 (1<<18) /* Internal DMA Request is pending */
#define DMA_IN_DMA_REQ1 (1<<17)
#define DMA_IN_DMA_REQ2 (1<<16)
#define DMA_IN_DMA_REQ3 (1<<15)
#define DMA_EXT_DMA_REQ0 (1<<14) /* External DMA Request is pending */
#define DMA_EXT_DMA_REQ1 (1<<13)
#define DMA_EXT_DMA_REQ2 (1<<12)
#define DMA_EXT_DMA_REQ3 (1<<11)
#define DMA_CH0_BUSY (1<<10) /* DMA Channel 0 Busy */
#define DMA_CH1_BUSY (1<<9)
#define DMA_CH2_BUSY (1<<8)
#define DMA_CH3_BUSY (1<<7)
#define DMA_CT1 (1<<6) /* Chained transfere */
#define DMA_CT2 (1<<5)
#define DMA_CT3 (1<<4)
#define DMA_CH_ENABLE (1<<7)
#define SET_DMA_CH(x) (((x)&0x1)<<7)
#define GET_DMA_CH(x) (((x)&DMA_CH_ENABLE)>>7)
/* STBx25xxx dma unique */
/* enable device port on a dma channel
* example ext 0 on dma 1
*/
#define SSP0_RECV 15
#define SSP0_XMIT 14
#define EXT_DMA_0 12
#define SC1_XMIT 11
#define SC1_RECV 10
#define EXT_DMA_2 9
#define EXT_DMA_3 8
#define SERIAL2_XMIT 7
#define SERIAL2_RECV 6
#define SC0_XMIT 5
#define SC0_RECV 4
#define SERIAL1_XMIT 3
#define SERIAL1_RECV 2
#define SERIAL0_XMIT 1
#define SERIAL0_RECV 0
#define DMA_CHAN_0 1
#define DMA_CHAN_1 2
#define DMA_CHAN_2 3
#define DMA_CHAN_3 4
/* end STBx25xx */
/*
* Bit 30 must be one for Redwoods, otherwise transfers may receive errors.
*/
#define DMA_CR_MB0 0x2
#define SET_DMA_CONTROL \
(SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */ \
SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */ \
SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */ \
SET_DMA_PL(p_init->pl) | /* peripheral location */ \
SET_DMA_DAI(p_init->dai) | /* dest addr increment */ \
SET_DMA_SAI(p_init->sai) | /* src addr increment */ \
SET_DMA_PRIORITY(p_init->cp) | /* channel priority */ \
SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */ \
SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */ \
SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */ \
SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */ \
SET_DMA_TCD(p_init->tcd_disable) | /* TC chain mode disable */ \
SET_DMA_ECE(p_init->ece_enable) | /* ECE chanin mode enable */ \
SET_DMA_CH(p_init->ch_enable) | /* Chain enable */ \
DMA_CR_MB0 /* must be one */)
#define GET_DMA_POLARITY(chan) chan
#endif
typedef struct {
unsigned short in_use; /* set when channel is being used, clr when
* available.
*/
/*
* Valid polarity settings:
* DMAReq_ActiveLow(n)
* DMAAck_ActiveLow(n)
* EOT_ActiveLow(n)
*
* n is 0 to max dma chans
*/
unsigned int polarity;
char buffer_enable; /* Boolean: buffer enable */
char tce_enable; /* Boolean: terminal count enable */
char etd_output; /* Boolean: eot pin is a tc output */
char pce; /* Boolean: parity check enable */
/*
* Peripheral location:
* INTERNAL_PERIPHERAL (UART0 on the 405GP)
* EXTERNAL_PERIPHERAL
*/
char pl; /* internal/external peripheral */
/*
* Valid pwidth settings:
* PW_8
* PW_16
* PW_32
* PW_64
*/
unsigned int pwidth;
char dai; /* Boolean: dst address increment */
char sai; /* Boolean: src address increment */
/*
* Valid psc settings: 0-3
*/
unsigned int psc; /* Peripheral Setup Cycles */
/*
* Valid pwc settings:
* 0-63
*/
unsigned int pwc; /* Peripheral Wait Cycles */
/*
* Valid phc settings:
* 0-7
*/
unsigned int phc; /* Peripheral Hold Cycles */
/*
* Valid cp (channel priority) settings:
* PRIORITY_LOW
* PRIORITY_MID_LOW
* PRIORITY_MID_HIGH
* PRIORITY_HIGH
*/
unsigned int cp; /* channel priority */
/*
* Valid pf (memory read prefetch) settings:
*
* PREFETCH_1
* PREFETCH_2
* PREFETCH_4
*/
unsigned int pf; /* memory read prefetch */
/*
* Boolean: channel interrupt enable
* NOTE: for sgl transfers, only the last descriptor will be setup to
* interrupt.
*/
char int_enable;
char shift; /* easy access to byte_count shift, based on */
/* the width of the channel */
uint32_t control; /* channel control word */
/* These variabled are used ONLY in single dma transfers */
unsigned int mode; /* transfer mode */
phys_addr_t addr;
char ce; /* channel enable */
#ifdef CONFIG_STB03xxx
char ch_enable;
char tcd_disable;
char ece_enable;
char td; /* transfer direction */
#endif
char int_on_final_sg;/* for scatter/gather - only interrupt on last sg */
} ppc_dma_ch_t;
/*
* PPC44x DMA implementations have a slightly different
* descriptor layout. Probably moved about due to the
* change to 64-bit addresses and link pointer. I don't
* know why they didn't just leave control_count after
* the dst_addr.
*/
#ifdef PPC4xx_DMA_64BIT
typedef struct {
uint32_t control;
uint32_t control_count;
phys_addr_t src_addr;
phys_addr_t dst_addr;
phys_addr_t next;
} ppc_sgl_t;
#else
typedef struct {
uint32_t control;
uint32_t src_addr;
uint32_t dst_addr;
uint32_t control_count;
uint32_t next;
} ppc_sgl_t;
#endif
typedef struct {
unsigned int dmanr;
uint32_t control; /* channel ctrl word; loaded from each descrptr */
uint32_t sgl_control; /* LK, TCI, ETI, and ERI bits in sgl descriptor */
dma_addr_t dma_addr; /* dma (physical) address of this list */
ppc_sgl_t *phead;
dma_addr_t phead_dma;
ppc_sgl_t *ptail;
dma_addr_t ptail_dma;
} sgl_list_info_t;
typedef struct {
phys_addr_t *src_addr;
phys_addr_t *dst_addr;
phys_addr_t dma_src_addr;
phys_addr_t dma_dst_addr;
} pci_alloc_desc_t;
extern ppc_dma_ch_t dma_channels[];
/*
* The DMA API are in ppc4xx_dma.c and ppc4xx_sgdma.c
*/
extern int ppc4xx_init_dma_channel(unsigned int, ppc_dma_ch_t *);
extern int ppc4xx_get_channel_config(unsigned int, ppc_dma_ch_t *);
extern int ppc4xx_set_channel_priority(unsigned int, unsigned int);
extern unsigned int ppc4xx_get_peripheral_width(unsigned int);
extern void ppc4xx_set_sg_addr(int, phys_addr_t);
extern int ppc4xx_add_dma_sgl(sgl_handle_t, phys_addr_t, phys_addr_t, unsigned int);
extern void ppc4xx_enable_dma_sgl(sgl_handle_t);
extern void ppc4xx_disable_dma_sgl(sgl_handle_t);
extern void ppc4xx_disable_dma_sgl_nr(unsigned int dmanr);
extern int ppc4xx_get_dma_sgl_residue(sgl_handle_t, phys_addr_t *, phys_addr_t *);
extern int ppc4xx_delete_dma_sgl_element(sgl_handle_t, phys_addr_t *, phys_addr_t *);
extern int ppc4xx_alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int);
extern void ppc4xx_free_dma_handle(sgl_handle_t);
extern int ppc4xx_get_dma_status(void);
extern int ppc4xx_enable_burst(unsigned int);
extern int ppc4xx_disable_burst(unsigned int);
extern int ppc4xx_set_burst_size(unsigned int, unsigned int);
extern void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr);
extern void ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr);
extern void ppc4xx_enable_dma(unsigned int dmanr);
extern void ppc4xx_disable_dma(unsigned int dmanr);
extern void ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count);
extern int ppc4xx_get_dma_residue(unsigned int dmanr);
extern void ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
phys_addr_t dst_dma_addr);
extern int ppc4xx_enable_dma_interrupt(unsigned int dmanr);
extern int ppc4xx_disable_dma_interrupt(unsigned int dmanr);
extern int ppc4xx_clr_dma_status(unsigned int dmanr);
extern int ppc4xx_map_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
extern int ppc4xx_disable_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
extern int ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode);
/* These are in kernel/dma.c: */
/* reserve a DMA channel */
extern int request_dma(unsigned int dmanr, const char *device_id);
/* release it again */
extern void free_dma(unsigned int dmanr);
#endif
#endif /* __KERNEL__ */
-------------- next part --------------
/*
* IBM PPC4xx DMA engine core library
*
* Copyright 2000-2004 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter at kernel.crashing.org>
*
* Original code by Armin Kuster <akuster at mvista.com>
* and Pete Popov <ppopov at mvista.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.
*
* 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/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int
ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dst_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}
void
ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
if (p_dma_ch->in_use) {
printk("enable_dma: channel %d in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("enable_dma: bad channel: %d\n", dmanr);
return;
}
if (p_dma_ch->mode == DMA_MODE_READ) {
/* peripheral to memory */
ppc4xx_set_src_addr(dmanr, 0);
ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
/* memory to peripheral */
ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
ppc4xx_set_dst_addr(dmanr, 0);
}
/* for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */
if (p_dma_ch->mode == DMA_MODE_MM) {
/* software initiated memory to memory */
control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
}
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/*
* Clear the CS, TS, RI bits for the channel from DMASR. This
* has been observed to happen correctly only after the mode and
* ETD/DCE bits in DMACRx are set above. Must do this before
* enabling the channel.
*/
mtdcr(DCRN_DMASR, status_bits[dmanr]);
/*
* For device-paced transfers, Terminal Count Enable apparently
* must be on, and this must be turned on after the mode, etc.
* bits are cleared above (at least on Redwood-6).
*/
if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
(p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
control |= DMA_TCE_ENABLE;
/*
* Now enable the channel.
*/
control |= (p_dma_ch->mode | DMA_CE_ENABLE);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 1;
}
void
ppc4xx_disable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (!p_dma_ch->in_use) {
// printk("disable_dma: channel %d not in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("disable_dma: bad channel: %d\n", dmanr);
return;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CE_ENABLE;
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 0;
}
/*
* Sets the dma mode for single DMA transfers only.
* For scatter/gather transfers, the mode is passed to the
* alloc_dma_handle() function as one of the parameters.
*
* The mode is simply saved and used later. This allows
* the driver to call set_dma_mode() and set_dma_addr() in
* any order.
*
* Valid mode values are:
*
* DMA_MODE_READ peripheral to memory
* DMA_MODE_WRITE memory to peripheral
* DMA_MODE_MM memory to memory
* DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
* DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
*/
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dma_mode: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->mode = mode;
return DMA_STATUS_GOOD;
}
/*
* Sets the DMA Count register. Note that 'count' is in bytes.
* However, the DMA Count register counts the number of "transfers",
* where each transfer is equal to the bus width. Thus, count
* MUST be a multiple of the bus width.
*/
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (count & 0x1)
error = 1;
break;
case PW_32:
if (count & 0x3)
error = 1;
break;
case PW_64:
if (count & 0x7)
error = 1;
break;
default:
printk("set_dma_count: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: set_dma_count count 0x%x bus width %d\n",
count, p_dma_ch->pwidth);
}
#endif
count = count >> p_dma_ch->shift;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}
/*
* Returns the number of bytes left to be transfered.
* After a DMA transfer, this should return zero.
* Reading this while a DMA transfer is still in progress will return
* unpredictable results.
*/
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
unsigned int count;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));
return (count << p_dma_ch->shift);
}
/*
* Sets the DMA address for a memory to peripheral or peripheral
* to memory transfer. The address is just saved in the channel
* structure for now and used later in enable_dma().
*/
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if ((unsigned) addr & 0x1)
error = 1;
break;
case PW_32:
if ((unsigned) addr & 0x3)
error = 1;
break;
case PW_64:
if ((unsigned) addr & 0x7)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
addr, p_dma_ch->pwidth);
}
#endif
/* save dma address and program it later after we know the xfer mode */
p_dma_ch->addr = addr;
}
/*
* Sets both DMA addresses for a memory to memory transfer.
* For memory to peripheral or peripheral to memory transfers
* the function set_dma_addr() should be used instead.
*/
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
phys_addr_t dst_dma_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (((unsigned) src_dma_addr & 0x1) ||
((unsigned) dst_dma_addr & 0x1)
)
error = 1;
break;
case PW_32:
if (((unsigned) src_dma_addr & 0x3) ||
((unsigned) dst_dma_addr & 0x3)
)
error = 1;
break;
case PW_64:
if (((unsigned) src_dma_addr & 0x7) ||
((unsigned) dst_dma_addr & 0x7)
)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
}
#endif
ppc4xx_set_src_addr(dmanr, src_dma_addr);
ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}
/*
* Enables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be enabled, if
* they were previously disabled.
*/
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 1;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Disables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be disabled, if
* they were previously enabled.
*/
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 0;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Configures a DMA channel, including the peripheral bus width, if a
* peripheral is attached to the channel, the polarity of the DMAReq and
* DMAAck signals, etc. This information should really be setup by the boot
* code, since most likely the configuration won't change dynamically.
* If the kernel has to call this function, it's recommended that it's
* called from platform specific init code. The driver should not need to
* call this function.
*/
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
unsigned int polarity;
uint32_t control = 0;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */
DMA_MODE_WRITE = 0; /* Memory to Peripheral */
if (!p_init) {
printk("ppc4xx_init_dma_channel: NULL p_init\n");
return DMA_STATUS_NULL_POINTER;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
/* Setup the control register based on the values passed to
* us in p_init. Then, over-write the control register with this
* new value.
*/
control |= SET_DMA_CONTROL;
/* clear all polarity signals and then "or" in new signal levels */
polarity &= ~GET_DMA_POLARITY(dmanr);
polarity |= p_init->polarity;
#if DCRN_POL > 0
mtdcr(DCRN_POL, polarity);
#endif
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/* save these values in our dma channel structure */
memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));
/*
* The peripheral width values written in the control register are:
* PW_8 0
* PW_16 1
* PW_32 2
* PW_64 3
*
* Since the DMA count register takes the number of "transfers",
* we need to divide the count sent to us in certain
* functions by the appropriate number. It so happens that our
* right shift value is equal to the peripheral width value.
*/
p_dma_ch->shift = p_init->pwidth;
/*
* Save the control word for easy access.
*/
p_dma_ch->control = control;
/*
* clear status register for the channel
* only TS, CS and RI needs to be cleared.
*/
mtdcr(DCRN_DMASR, status_bits[dmanr]);
return DMA_STATUS_GOOD;
}
/*
* This function returns the channel configuration.
*/
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
unsigned int polarity;
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
p_dma_ch->cp = GET_DMA_PRIORITY(control);
p_dma_ch->pwidth = GET_DMA_PW(control);
p_dma_ch->psc = GET_DMA_PSC(control);
p_dma_ch->pwc = GET_DMA_PWC(control);
p_dma_ch->phc = GET_DMA_PHC(control);
p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
p_dma_ch->shift = GET_DMA_PW(control);
#ifdef CONFIG_PPC4xx_EDMA
p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
p_dma_ch->ch_enable = GET_DMA_CH(control);
p_dma_ch->ece_enable = GET_DMA_ECE(control);
p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
return DMA_STATUS_GOOD;
}
/*
* Sets the priority for the DMA channel dmanr.
* Since this is setup by the hardware init function, this function
* can be used to dynamically change the priority of a channel.
*
* Acceptable priorities:
*
* PRIORITY_LOW
* PRIORITY_MID_LOW
* PRIORITY_MID_HIGH
* PRIORITY_HIGH
*
*/
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if ((priority != PRIORITY_LOW) &&
(priority != PRIORITY_MID_LOW) &&
(priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= SET_DMA_PRIORITY(priority);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Returns the width of the peripheral attached to this channel. This assumes
* that someone who knows the hardware configuration, boot code or some other
* init code, already set the width.
*
* The return value is one of:
* PW_8
* PW_16
* PW_32
* PW_64
*
* The function returns 0 on error.
*/
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
return (GET_DMA_PW(control));
}
/*
* Clears the channel status bits
*/
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
return DMA_STATUS_GOOD;
}
#ifdef CONFIG_PPC4xx_EDMA
/*
* Enables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_enable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Disables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_disable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Sets the burst size (number of peripheral widths) for the channel
* (BSIZ bits in the control/count register))
* must be one of:
* DMA_CTC_BSIZ_2
* DMA_CTC_BSIZ_4
* DMA_CTC_BSIZ_8
* DMA_CTC_BSIZ_16
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
ctc |= (bsize & DMA_CTC_BSIZ_MSK);
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */
EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);
-------------- next part --------------
/*
* IBM PPC4xx DMA engine scatter/gather library
*
* Copyright 2002-2003 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter at kernel.crashing.org>
*
* Original code by Armin Kuster <akuster at mvista.com>
* and Pete Popov <ppopov at mvista.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.
*
* 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/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/ppc4xx_dma.h>
#ifdef CONFIG_VDR
#include <asm/cacheflush.h>
#endif
void
ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32));
#endif
mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr);
}
/*
* Add a new sgl descriptor to the end of a scatter/gather list
* which was created by alloc_dma_handle().
*
* For a memory to memory transfer, both dma addresses must be
* valid. For a peripheral to memory transfer, one of the addresses
* must be set to NULL, depending on the direction of the transfer:
* memory to peripheral: set dst_addr to NULL,
* peripheral to memory: set src_addr to NULL.
*/
int
ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr,
unsigned int count)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
if (!handle) {
printk("ppc4xx_add_dma_sgl: null handle\n");
return DMA_STATUS_BAD_HANDLE;
}
if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch = &dma_channels[psgl->dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
unsigned int aligned =
(unsigned) src_addr | (unsigned) dst_addr | count;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (aligned & 0x1)
error = 1;
break;
case PW_32:
if (aligned & 0x3)
error = 1;
break;
case PW_64:
if (aligned & 0x7)
error = 1;
break;
default:
printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return DMA_STATUS_GENERAL_ERROR;
}
if (error)
printk
("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n",
src_addr, dst_addr, count, p_dma_ch->pwidth);
}
#endif
#ifdef CONFIG_VDR
/* dynamic alloc each list element */
{
ppc_sgl_t *sgl_el = kmalloc(sizeof(ppc_sgl_t), GFP_KERNEL|GFP_DMA);
if (!sgl_el)
return DMA_STATUS_OUT_OF_MEMORY;
if (!psgl->phead) { /* list was empty */
psgl->phead = sgl_el;
} else { /* not empty, tail exists */
psgl->ptail->next = (uint32_t)virt_to_phys(sgl_el);
dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
}
psgl->ptail = sgl_el;
}
psgl->ptail->control = psgl->control;
psgl->ptail->src_addr = (uint32_t)src_addr;
psgl->ptail->dst_addr = (uint32_t)dst_addr;
psgl->ptail->control_count = (count >> p_dma_ch->shift) |
psgl->sgl_control;
psgl->ptail->next = (uint32_t)virt_to_phys(NULL);
dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t)); /* handled later, skip this one? */
#else
if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) {
printk("sgl handle out of memory \n");
return DMA_STATUS_OUT_OF_MEMORY;
}
if (!psgl->ptail) {
psgl->phead = (ppc_sgl_t *)
((unsigned) psgl + sizeof (sgl_list_info_t));
psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t);
psgl->ptail = psgl->phead;
psgl->ptail_dma = psgl->phead_dma;
} else {
if(p_dma_ch->int_on_final_sg) {
/* mask out all dma interrupts, except error, on tail
before adding new tail. */
psgl->ptail->control_count &=
~(SG_TCI_ENABLE | SG_ETI_ENABLE);
/* PATRIK: Added */
/* Require Terminal Count interrupt on last */
psgl->ptail->control_count |= SG_TCI_ENABLE;
}
psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t);
psgl->ptail++;
psgl->ptail_dma += sizeof(ppc_sgl_t);
}
psgl->ptail->control = psgl->control;
psgl->ptail->src_addr = src_addr;
psgl->ptail->dst_addr = dst_addr;
psgl->ptail->control_count = (count >> p_dma_ch->shift) |
psgl->sgl_control;
psgl->ptail->next = (uint32_t) NULL;
#endif
return DMA_STATUS_GOOD;
}
/*
* Enable (start) the DMA described by the sgl handle.
*/
void
ppc4xx_enable_dma_sgl(sgl_handle_t handle)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
uint32_t sg_command;
if (!handle) {
printk("ppc4xx_enable_dma_sgl: null handle\n");
return;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
psgl->dmanr);
return;
} else if (!psgl->phead) {
printk("ppc4xx_enable_dma_sgl: sg list empty\n");
return;
}
p_dma_ch = &dma_channels[psgl->dmanr];
psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */
if (p_dma_ch->int_enable)
{
/* Require Terminal Count interrupt on last */
psgl->ptail->control_count |= SG_TCI_ENABLE;
}
#ifdef CONFIG_VDR
/* No more changes to tail object allowed */
//dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
dma_cache_wback_inv((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
ppc4xx_set_sg_addr(psgl->dmanr, virt_to_phys(psgl->phead));
#else
ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma);
#endif
sg_command = SSG_ENABLE(psgl->dmanr) | SSG_MASK_ENABLE(psgl->dmanr);
mtdcr(DCRN_ASGC, sg_command); /* start transfer */
}
/*
* Halt an active scatter/gather DMA operation (from handle).
*/
void
ppc4xx_disable_dma_sgl(sgl_handle_t handle)
{
uint32_t sg_command;
if (!handle) {
printk("ppc4xx_disable_dma_sgl: null handle\n");
return;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_disable_dma_sgl: bad channel in handle %d\n",
psgl->dmanr);
return;
}
sg_command = SSG_MASK_ENABLE(psgl->dmanr);
mtdcr(DCRN_ASGC, sg_command); /* stop transfer */
}
/*
* Halt an active scatter/gather DMA operation (from dmanr).
*/
void
ppc4xx_disable_dma_sgl_nr(unsigned int dmanr)
{
uint32_t sg_command;
if (dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_disable_dma_sgl_nr: bad channel %d\n", dmanr);
return;
}
sg_command = SSG_MASK_ENABLE(dmanr);
mtdcr(DCRN_ASGC, sg_command); /* stop transfer */
}
/*
* Returns number of bytes left to be transferred from the entire sgl list.
* *src_addr and *dst_addr get set to the source/destination address of
* the sgl descriptor where the DMA stopped.
*
* An sgl transfer must NOT be active when this function is called.
* Note: Make sure ppc4xx_disable_dma_sgl is called before returning from
* interrupt handler (TSn, CSn will not disable the sgl)!
*/
int
ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr,
phys_addr_t * dst_addr)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
ppc_sgl_t *pnext, *sgl_addr;
uint32_t count_left;
if (!handle) {
printk("ppc4xx_get_dma_sgl_residue: null handle\n");
return DMA_STATUS_BAD_HANDLE;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n",
psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8)));
count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8)) & SG_COUNT_MASK;
#ifdef CONFIG_VDR
if (!sgl_addr) {
/* Last in list */
return count_left;
}
pnext = sgl_addr; /* sgl_addr is next to be loaded */
/*
* Why this interface? Better return nothing or sgl_addr instead...?
*/
*src_addr = pnext->src_addr;
*dst_addr = pnext->dst_addr;
/*
* Now search the remaining descriptors and add their count.
* We already have the remaining count from this descriptor in
* count_left.
*/
while (pnext) {
count_left += pnext->control_count & SG_COUNT_MASK;
pnext = phys_to_virt(pnext->next);
}
/* success */
p_dma_ch = &dma_channels[psgl->dmanr];
return (count_left << p_dma_ch->shift); /* count in bytes */
#else
if (!sgl_addr) {
printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n");
goto error;
}
pnext = psgl->phead;
while (pnext &&
((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) &&
(pnext != sgl_addr))
) {
pnext++;
}
if (pnext == sgl_addr) { /* found the sgl descriptor */
*src_addr = pnext->src_addr;
*dst_addr = pnext->dst_addr;
/*
* Now search the remaining descriptors and add their count.
* We already have the remaining count from this descriptor in
* count_left.
*/
pnext++;
while ((pnext != psgl->ptail) &&
((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE))
) {
count_left += pnext->control_count & SG_COUNT_MASK;
}
if (pnext != psgl->ptail) { /* should never happen */
printk
("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n",
(unsigned int) psgl->ptail, (unsigned int) handle);
goto error;
}
/* success */
p_dma_ch = &dma_channels[psgl->dmanr];
return (count_left << p_dma_ch->shift); /* count in bytes */
} else {
/* this shouldn't happen */
printk
("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n",
(unsigned int) sgl_addr, (unsigned int) handle);
}
error:
*src_addr = (phys_addr_t) NULL;
*dst_addr = (phys_addr_t) NULL;
return 0;
#endif
}
/*
* Returns the address(es) of the buffer(s) contained in the head element of
* the scatter/gather list. The element is removed from the scatter/gather
* list and the next element becomes the head.
*
* This function should only be called when the DMA is not active.
*/
int
ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr,
phys_addr_t * dst_dma_addr)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
if (!handle) {
printk("ppc4xx_delete_sgl_element: null handle\n");
return DMA_STATUS_BAD_HANDLE;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n",
psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if (!psgl->phead) {
/* printk("ppc4xx_delete_sgl_element: sgl list empty\n"); - not an error */
*src_dma_addr = (phys_addr_t) NULL;
*dst_dma_addr = (phys_addr_t) NULL;
return DMA_STATUS_SGL_LIST_EMPTY;
}
*src_dma_addr = (phys_addr_t) psgl->phead->src_addr;
*dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr;
if (psgl->phead == psgl->ptail) {
/* last descriptor on the list */
#ifdef CONFIG_VDR
kfree(psgl->phead);
#endif
psgl->phead = NULL;
psgl->ptail = NULL;
} else {
#ifdef CONFIG_VDR
ppc_sgl_t *next = phys_to_virt(psgl->phead->next);
kfree(psgl->phead);
psgl->phead = next;
#else
psgl->phead++;
psgl->phead_dma += sizeof(ppc_sgl_t);
#endif
}
return DMA_STATUS_GOOD;
}
/*
* Create a scatter/gather list handle. This is simply a structure which
* describes a scatter/gather list.
*
* A handle is returned in "handle" which the driver should save in order to
* be able to access this list later. A chunk of memory will be allocated
* to be used by the API for internal management purposes, including managing
* the sg list and allocating memory for the sgl descriptors. One page should
* be more than enough for that purpose. Perhaps it's a bit wasteful to use
* a whole page for a single sg list, but most likely there will be only one
* sg list per channel.
*
* Interrupt notes:
* Each sgl descriptor has a copy of the DMA control word which the DMA engine
* loads in the control register. The control word has a "global" interrupt
* enable bit for that channel. Interrupts are further qualified by a few bits
* in the sgl descriptor count register. In order to setup an sgl, we have to
* know ahead of time whether or not interrupts will be enabled at the completion
* of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST
* be called before calling alloc_dma_handle(). If the interrupt mode will never
* change after powerup, then enable_dma_interrupt()/disable_dma_interrupt()
* do not have to be called -- interrupts will be enabled or disabled based
* on how the channel was configured after powerup by the hw_init_dma_channel()
* function. Each sgl descriptor will be setup to interrupt if an error occurs;
* however, only the last descriptor will be setup to interrupt. Thus, an
* interrupt will occur (if interrupts are enabled) only after the complete
* sgl transfer is done.
* End of Transfer Interrupt needs to be enabled in all descriptors, since it
* is impossible to know which one will be the last...
*/
int
ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
{
sgl_list_info_t *psgl=NULL;
#ifdef CONFIG_VDR
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#else
dma_addr_t dma_addr;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
uint32_t ctc_settings;
void *ret;
#endif
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if (!phandle) {
printk("ppc4xx_alloc_dma_handle: null handle\n");
return DMA_STATUS_BAD_HANDLE;
}
#ifdef CONFIG_VDR
/* Get memory for the listinfo struct */
psgl = kmalloc(sizeof(sgl_list_info_t), GFP_KERNEL);
if (psgl == NULL) {
*phandle = (sgl_handle_t) NULL;
return DMA_STATUS_OUT_OF_MEMORY;
}
memset(psgl, 0, sizeof(sgl_list_info_t));
/* dma_addr is unused now */
psgl->dmanr = dmanr;
#else
/* Get a page of memory, which is zeroed out by consistent_alloc() */
ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL);
if (ret != NULL) {
memset(ret, 0, DMA_PPC4xx_SIZE);
psgl = (sgl_list_info_t *) ret;
}
if (psgl == NULL) {
*phandle = (sgl_handle_t) NULL;
return DMA_STATUS_OUT_OF_MEMORY;
}
psgl->dma_addr = dma_addr;
psgl->dmanr = dmanr;
#endif
/*
* Modify and save the control word. These words will be
* written to each sgl descriptor. The DMA engine then
* loads this control word into the control register
* every time it reads a new descriptor.
*/
psgl->control = p_dma_ch->control;
/* Clear all mode bits */
psgl->control &= ~(DMA_TM_MASK | DMA_TD);
/* Save control word and mode */
psgl->control |= (mode | DMA_CE_ENABLE);
/* PPC Errata? DMA else ignore count on first in list */
psgl->control |= SET_DMA_TCE(1);
/* In MM mode, we must set ETD/TCE */
if (mode == DMA_MODE_MM)
psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
if (p_dma_ch->int_enable) {
/* Enable channel interrupt */
psgl->control |= DMA_CIE_ENABLE;
} else {
psgl->control &= ~DMA_CIE_ENABLE;
}
/* Enable SGL control access */
psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;
#ifndef CONFIG_VDR
/* keep control count register settings */
ctc_settings = mfdcr(DCRN_DMACT0 + (dmanr * 0x8))
& (DMA_CTC_BSIZ_MSK | DMA_CTC_BTEN); /*burst mode settings*/
psgl->sgl_control |= ctc_settings;
#endif
if (p_dma_ch->int_enable) {
if (p_dma_ch->tce_enable)
psgl->sgl_control |= SG_TCI_ENABLE;
else
psgl->sgl_control |= SG_ETI_ENABLE;
}
*phandle = (sgl_handle_t) psgl;
return DMA_STATUS_GOOD;
}
/*
* Destroy a scatter/gather list handle that was created by alloc_dma_handle().
* The list must be empty (contain no elements).
*/
void
ppc4xx_free_dma_handle(sgl_handle_t handle)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
#ifdef CONFIG_VDR
if (!handle) {
printk("ppc4xx_free_dma_handle: got NULL\n");
return;
} else if (psgl->phead) { /* free list here, why do it externaly? */
phys_addr_t dummy;
while (ppc4xx_delete_dma_sgl_element(handle, &dummy, &dummy) == DMA_STATUS_GOOD)
/* NOOP */;
/* printk("ppc4xx_free_dma_handle: list not empty\n"); */
}
kfree((void *) psgl);
#else
if (!handle) {
printk("ppc4xx_free_dma_handle: got NULL\n");
return;
} else if (psgl->phead) {
printk("ppc4xx_free_dma_handle: list not empty\n");
return;
} else if (!psgl->dma_addr) { /* should never happen */
printk("ppc4xx_free_dma_handle: no dma address\n");
return;
}
dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0);
#endif
}
EXPORT_SYMBOL(ppc4xx_alloc_dma_handle);
EXPORT_SYMBOL(ppc4xx_free_dma_handle);
EXPORT_SYMBOL(ppc4xx_add_dma_sgl);
EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element);
EXPORT_SYMBOL(ppc4xx_enable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_disable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_disable_dma_sgl_nr);
EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue);
More information about the Linuxppc-embedded
mailing list