/tools/perf/scripts/python/Perf-Trace-Util/lib/

ion/atom+xml'/>
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/dma/ste_dma40.c
blob: 94ee15dd3aed7ca0c0dc55c0a06f4e12e7834097 (plain) (tree)
1
2
3
4
5
6
  

                                         
                                                                 
                                                                    
                                                            







                                  
                      















                                                                    

                                            




                                                



                                                                       



                                         
                                    
                           





















                                                                        
                                    





                                                                               
                      
                                 
                                                              
                                                               









                                                                          
                                                  
                                            
                                                 


                                                                         
                                                               
                    


                                                                   






                                                  
                                                 

                                                     



                                              
                                                           
                                                




                                                      




                                                                    
                                                     
                                                                 


                              
                                 

                                        
                              
                                    











                                                                          
                     



















                                                                               

                                                                              





                                                                             
                                                          
                                                          





















                                                                   
                                                 
                                                    




                                                     
                                              


                                                          









                                                                            
                                   











                                                                              











                                                                              
                                     





                                                        
                                                



















                                                           
                                                    




























                                                                         




                                                     









                                                   





                                                       





                                                      
                                                                           
                                          
 
                                            












                                                                           
                                                          








                                                                        
                                                           
                                                                

                                            
                
                                                           
                                                                












                                                                             




                 
                                                                           
 



                                                                          






                                   

 




































                                                                             
                                   






















                                                                                                






                                                           
                                     

                                         


                                    

                                                                    
                                                           
                                                   

                                                               
                                      
                         
         







                                                                            



                                                                       
 
                                      
                                      
                                                     






                                                                         
















                                                                          
                                                                             
 



                                                            
                                   
                                

                           
 










                                                                               
                                                           










                                                                               
 

                                              
 






                                                              



                          



                                                                       
                                       




                                                                   



                                                                    
 











                                                                       


                                                              
                                                         







                                                                       
                                                                      




                                      
    

                                        
 

                                                                       
                                     
                                             
                                                  

                                                

 






























                                                                        

























































                                                                        
 
                                            



                                                                

                   


                                 
                  
















                                                                          


                                                                       




















                                                                            


                                                                                     













                                                                



                                                     





                                                             


                                          




                             


                                                                      
                                                   






























                                                                               

                                                                       

                            






                                                                          

                                                              
         
 


                                                                          

                                                              




                                                             
                                                     
 
                                                 
                
 

                                                    
 
                   

 


















                                                              
                                   




                                                       
                                                   


                      



                                                       
                                                   



                                                                       
                                                                      


                                                                       
                                    



                                                                              
                                                    

                                                                         
 
                                       

                                                            
         

 



                                             
                                  

                                                                               





                                                                      






                                                                  
                                  

                                                                               


                                                                     


                       
                                           
 


                            


                         



                                                                     
                                            











                                                                               
                                            
 


                            


                         


                                              
                                            







                                                                               
                                          









                                                                     















                                                                      









                                                                       






                                             








                                                   


                                   
                                            






                                                                               
                                  
                                                 
 
                                                              


















                                                              

                                          
 

                                      
 

                                    









                                                





                                              












                                                                              
 




                                                               
 









                                                        








                                                         
                              






                                              
                                          
                         

                         

                                                   










                                                                          


                                                  


                                                      
                                              







                                                                          









                                                   
                                                               




                                         
                                                             





















                                                                        

























                                                                               
                                                               









                                                                            

                                                                              








                                                             





                                                                    
                                                          
 
                         
                                                       






                                                                     

                                                                   





                                                                     

                                                                   



                                                  
                                                         
                                                


                              
                                                  
                                                         
                                                




                                                                   
                                                  




                                                     
                                                        







                                                                     
                                                                   


                              








                                                                  
                                                                               


                              



                                                                    
                                                               


                                             
                      




















































                                                                              



































                                                               
                                                                  























                                                                         

                                                                        





                                                                            



                                                                        






















                                                                               
                                                                           




                                                                        
                                                                           



















                                                                         













                                                                           
                                              










                                              
                  

                                                 


                            



                                                       


                                                                      
                                                   
                                           


                                               
                          
                                                




                                                   
                                                         


                               


                                                                      


                                                                      

                              
                                                      


                               

                                                                     
                                                   


                           
                                    
                                                                        
 
                                                  











                                                                               

                                                                           




                                                   


                                                           



                                                              
                                                           


                              
                                 


                                                      

 

                                                
                                                 




                                 


                                              
                                     













                                                                            
                                                          
                                                       
                                                                      
                                                              
                                                                 
                                                                      
                                                              
                
                                                      

                           
 




                                                        






                                                   













                                                          


                                                                       

                                                             



                                                                     
                
 














                                                      




                                                                       

                                                             
 


                                                                     
                                

                


                                                   



                                                                
                                                           




                                                                
                                                           







                                                                            





                                                            
                








                                                                             

                         
 



                                                            

         
 






                                                              



                                  

 

                                                                          
 




















                                                                           

                                    
                              
                            
                
 


                                                                       

         
 
                                              
 

                                                              

                         


                                                   







                                                                        


                                                                 
                                                                          

                                                                 
                                                                          




                                                                     

         


                                                   
 
    


                                                   

                    














                                                          


                                        



                               































                                                                            






                                                          
                         



                                              

                                                                               
                                              
                          
                                                                               

                                  
         
                                               


                                         
                                                               
                          

         

                                                       
                                                               
 

                                    
                                    
















                                                                            

                                       
     
                                                   
                   








                                                          
                                     
                                                                    



                       




                                              
                                                           






                                                                             
                                                                               
 

                                  
 

                                  
 

                                      
 

                                   
 
                                                                           

 
                                       



                                                                      



                                   
                                                                                 

 



                                                                                           
                                                                                 
 


                                                                       
                                                                         

 





























                                                                          








                                                                          
                                     
                                                                              


                               


                                        



                                                                              
 

                                                           








                                                                          
                                     
                                                              


                       








                                                                      















































































                                                                            
                                    














                                                      

                                                     


                                                     



                                              
                                         


                                                        
                                         

                                                        
                                     
                                  




                                                                             











                                                                     

                                                                    
 

                                                                          
                                     
                                                              


                               

                               
                                               
                       
                                       
                        
                                        





                                                         





















                                                                               



                                             



                                              







                                                         

















                                                                       


                                                                    







                                                                    









                                                              
                                                          
 
                                             



                                                          
                                                                          







                                                                        


                                                       



                                                           

                                                                     








                                                             
                                                     
                                                          

                                            


                                                         

                                                                                      




































                                                                        


                                                                       




                                                                  

         



























                                                                                 
                                        
                                 

                                       
















                                                            
                
                



                                        
                                                                 





















                                                                         

                                                                                         






                                                                      
                                               
                                                   
 

                                                       

                                                                           
                                         


                             


                                                    



                                                                        
                                  
















                                                                                
                                                       


                             
                        
































                                                                             



                                                                        



                                                        





                                                                              


                    
                           
























































































                                                                            

                                                          
                                                      
























                                                                             

                                                                            
















                                                                                



                                                                              















                                                                                








                                                                    






                                                  
























                                                                         
                                                                     







                                                               


                                                                    














                                                                   
                                                                       

                             
 

                                      
                                                                      




                                              


                                                                              
                  
                                                        













                                                           

                                                            

                                                





                                                                             


                                                                            


                                                      

















                                                            
                                              









                                            
                                     



                                                             
/*
 * Copyright (C) Ericsson AB 2007-2008
 * Copyright (C) ST-Ericsson SA 2008-2010
 * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
 * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
 * License terms: GNU General Public License (GPL) version 2
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>

#include <plat/ste_dma40.h>

#include "ste_dma40_ll.h"

#define D40_NAME "dma40"

#define D40_PHY_CHAN -1

/* For masking out/in 2 bit channel positions */
#define D40_CHAN_POS(chan)  (2 * (chan / 2))
#define D40_CHAN_POS_MASK(chan) (0x3 << D40_CHAN_POS(chan))

/* Maximum iterations taken before giving up suspending a channel */
#define D40_SUSPEND_MAX_IT 500

/* Hardware requirement on LCLA alignment */
#define LCLA_ALIGNMENT 0x40000

/* Max number of links per event group */
#define D40_LCLA_LINK_PER_EVENT_GRP 128
#define D40_LCLA_END D40_LCLA_LINK_PER_EVENT_GRP

/* Attempts before giving up to trying to get pages that are aligned */
#define MAX_LCLA_ALLOC_ATTEMPTS 256

/* Bit markings for allocation map */
#define D40_ALLOC_FREE		(1 << 31)
#define D40_ALLOC_PHY		(1 << 30)
#define D40_ALLOC_LOG_FREE	0

/* Hardware designer of the block */
#define D40_HW_DESIGNER 0x8

/**
 * enum 40_command - The different commands and/or statuses.
 *
 * @D40_DMA_STOP: DMA channel command STOP or status STOPPED,
 * @D40_DMA_RUN: The DMA channel is RUNNING of the command RUN.
 * @D40_DMA_SUSPEND_REQ: Request the DMA to SUSPEND as soon as possible.
 * @D40_DMA_SUSPENDED: The DMA channel is SUSPENDED.
 */
enum d40_command {
	D40_DMA_STOP		= 0,
	D40_DMA_RUN		= 1,
	D40_DMA_SUSPEND_REQ	= 2,
	D40_DMA_SUSPENDED	= 3
};

/**
 * struct d40_lli_pool - Structure for keeping LLIs in memory
 *
 * @base: Pointer to memory area when the pre_alloc_lli's are not large
 * enough, IE bigger than the most common case, 1 dst and 1 src. NULL if
 * pre_alloc_lli is used.
 * @dma_addr: DMA address, if mapped
 * @size: The size in bytes of the memory at base or the size of pre_alloc_lli.
 * @pre_alloc_lli: Pre allocated area for the most common case of transfers,
 * one buffer to one buffer.
 */
struct d40_lli_pool {
	void	*base;
	int	 size;
	dma_addr_t	dma_addr;
	/* Space for dst and src, plus an extra for padding */
	u8	 pre_alloc_lli[3 * sizeof(struct d40_phy_lli)];
};

/**
 * struct d40_desc - A descriptor is one DMA job.
 *
 * @lli_phy: LLI settings for physical channel. Both src and dst=
 * points into the lli_pool, to base if lli_len > 1 or to pre_alloc_lli if
 * lli_len equals one.
 * @lli_log: Same as above but for logical channels.
 * @lli_pool: The pool with two entries pre-allocated.
 * @lli_len: Number of llis of current descriptor.
 * @lli_current: Number of transferred llis.
 * @lcla_alloc: Number of LCLA entries allocated.
 * @txd: DMA engine struct. Used for among other things for communication
 * during a transfer.
 * @node: List entry.
 * @is_in_client_list: true if the client owns this descriptor.
 * the previous one.
 *
 * This descriptor is used for both logical and physical transfers.
 */
struct d40_desc {
	/* LLI physical */
	struct d40_phy_lli_bidir	 lli_phy;
	/* LLI logical */
	struct d40_log_lli_bidir	 lli_log;

	struct d40_lli_pool		 lli_pool;
	int				 lli_len;
	int				 lli_current;
	int				 lcla_alloc;

	struct dma_async_tx_descriptor	 txd;
	struct list_head		 node;

	bool				 is_in_client_list;
	bool				 cyclic;
};

/**
 * struct d40_lcla_pool - LCLA pool settings and data.
 *
 * @base: The virtual address of LCLA. 18 bit aligned.
 * @base_unaligned: The orignal kmalloc pointer, if kmalloc is used.
 * This pointer is only there for clean-up on error.
 * @pages: The number of pages needed for all physical channels.
 * Only used later for clean-up on error
 * @lock: Lock to protect the content in this struct.
 * @alloc_map: big map over which LCLA entry is own by which job.
 */
struct d40_lcla_pool {
	void		*base;
	dma_addr_t	dma_addr;
	void		*base_unaligned;
	int		 pages;
	spinlock_t	 lock;
	struct d40_desc	**alloc_map;
};

/**
 * struct d40_phy_res - struct for handling eventlines mapped to physical
 * channels.
 *
 * @lock: A lock protection this entity.
 * @num: The physical channel number of this entity.
 * @allocated_src: Bit mapped to show which src event line's are mapped to
 * this physical channel. Can also be free or physically allocated.
 * @allocated_dst: Same as for src but is dst.
 * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as
 * event line number.
 */
struct d40_phy_res {
	spinlock_t lock;
	int	   num;
	u32	   allocated_src;
	u32	   allocated_dst;
};

struct d40_base;

/**
 * struct d40_chan - Struct that describes a channel.
 *
 * @lock: A spinlock to protect this struct.
 * @log_num: The logical number, if any of this channel.
 * @completed: Starts with 1, after first interrupt it is set to dma engine's
 * current cookie.
 * @pending_tx: The number of pending transfers. Used between interrupt handler
 * and tasklet.
 * @busy: Set to true when transfer is ongoing on this channel.
 * @phy_chan: Pointer to physical channel which this instance runs on. If this
 * point is NULL, then the channel is not allocated.
 * @chan: DMA engine handle.
 * @tasklet: Tasklet that gets scheduled from interrupt context to complete a
 * transfer and call client callback.
 * @client: Cliented owned descriptor list.
 * @active: Active descriptor.
 * @queue: Queued jobs.
 * @dma_cfg: The client configuration of this dma channel.
 * @configured: whether the dma_cfg configuration is valid
 * @base: Pointer to the device instance struct.
 * @src_def_cfg: Default cfg register setting for src.
 * @dst_def_cfg: Default cfg register setting for dst.
 * @log_def: Default logical channel settings.
 * @lcla: Space for one dst src pair for logical channel transfers.
 * @lcpa: Pointer to dst and src lcpa settings.
 *
 * This struct can either "be" a logical or a physical channel.
 */
struct d40_chan {
	spinlock_t			 lock;
	int				 log_num;
	/* ID of the most recent completed transfer */
	int				 completed;
	int				 pending_tx;
	bool				 busy;
	struct d40_phy_res		*phy_chan;
	struct dma_chan			 chan;
	struct tasklet_struct		 tasklet;
	struct list_head		 client;
	struct list_head		 active;
	struct list_head		 queue;
	struct stedma40_chan_cfg	 dma_cfg;
	bool				 configured;
	struct d40_base			*base;
	/* Default register configurations */
	u32				 src_def_cfg;
	u32				 dst_def_cfg;
	struct d40_def_lcsp		 log_def;
	struct d40_log_lli_full		*lcpa;
	/* Runtime reconfiguration */
	dma_addr_t			runtime_addr;
	enum dma_data_direction		runtime_direction;
};

/**
 * struct d40_base - The big global struct, one for each probe'd instance.
 *
 * @interrupt_lock: Lock used to make sure one interrupt is handle a time.
 * @execmd_lock: Lock for execute command usage since several channels share
 * the same physical register.
 * @dev: The device structure.
 * @virtbase: The virtual base address of the DMA's register.
 * @rev: silicon revision detected.
 * @clk: Pointer to the DMA clock structure.
 * @phy_start: Physical memory start of the DMA registers.
 * @phy_size: Size of the DMA register map.
 * @irq: The IRQ number.
 * @num_phy_chans: The number of physical channels. Read from HW. This
 * is the number of available channels for this driver, not counting "Secure
 * mode" allocated physical channels.
 * @num_log_chans: The number of logical channels. Calculated from
 * num_phy_chans.
 * @dma_both: dma_device channels that can do both memcpy and slave transfers.
 * @dma_slave: dma_device channels that can do only do slave transfers.
 * @dma_memcpy: dma_device channels that can do only do memcpy transfers.
 * @log_chans: Room for all possible logical channels in system.
 * @lookup_log_chans: Used to map interrupt number to logical channel. Points
 * to log_chans entries.
 * @lookup_phy_chans: Used to map interrupt number to physical channel. Points
 * to phy_chans entries.
 * @plat_data: Pointer to provided platform_data which is the driver
 * configuration.
 * @phy_res: Vector containing all physical channels.
 * @lcla_pool: lcla pool settings and data.
 * @lcpa_base: The virtual mapped address of LCPA.
 * @phy_lcpa: The physical address of the LCPA.
 * @lcpa_size: The size of the LCPA area.
 * @desc_slab: cache for descriptors.
 */
struct d40_base {
	spinlock_t			 interrupt_lock;
	spinlock_t			 execmd_lock;
	struct device			 *dev;
	void __iomem			 *virtbase;
	u8				  rev:4;
	struct clk			 *clk;
	phys_addr_t			  phy_start;
	resource_size_t			  phy_size;
	int				  irq;
	int				  num_phy_chans;
	int				  num_log_chans;
	struct dma_device		  dma_both;
	struct dma_device		  dma_slave;
	struct dma_device		  dma_memcpy;
	struct d40_chan			 *phy_chans;
	struct d40_chan			 *log_chans;
	struct d40_chan			**lookup_log_chans;
	struct d40_chan			**lookup_phy_chans;
	struct stedma40_platform_data	 *plat_data;
	/* Physical half channels */
	struct d40_phy_res		 *phy_res;
	struct d40_lcla_pool		  lcla_pool;
	void				 *lcpa_base;
	dma_addr_t			  phy_lcpa;
	resource_size_t			  lcpa_size;
	struct kmem_cache		 *desc_slab;
};

/**
 * struct d40_interrupt_lookup - lookup table for interrupt handler
 *
 * @src: Interrupt mask register.
 * @clr: Interrupt clear register.
 * @is_error: true if this is an error interrupt.
 * @offset: start delta in the lookup_log_chans in d40_base. If equals to
 * D40_PHY_CHAN, the lookup_phy_chans shall be used instead.
 */
struct d40_interrupt_lookup {
	u32 src;
	u32 clr;
	bool is_error;
	int offset;
};

/**
 * struct d40_reg_val - simple lookup struct
 *
 * @reg: The register.
 * @val: The value that belongs to the register in reg.
 */
struct d40_reg_val {
	unsigned int reg;
	unsigned int val;
};

static struct device *chan2dev(struct d40_chan *d40c)
{
	return &d40c->chan.dev->device;
}

static bool chan_is_physical(struct d40_chan *chan)
{
	return chan->log_num == D40_PHY_CHAN;
}

static bool chan_is_logical(struct d40_chan *chan)
{
	return !chan_is_physical(chan);
}

static void __iomem *chan_base(struct d40_chan *chan)
{
	return chan->base->virtbase + D40_DREG_PCBASE +
	       chan->phy_chan->num * D40_DREG_PCDELTA;
}

#define d40_err(dev, format, arg...)		\
	dev_err(dev, "[%s] " format, __func__, ## arg)

#define chan_err(d40c, format, arg...)		\
	d40_err(chan2dev(d40c), format, ## arg)

static int d40_pool_lli_alloc(struct d40_chan *d40c, struct d40_desc *d40d,
			      int lli_len)
{
	bool is_log = chan_is_logical(d40c);
	u32 align;
	void *base;

	if (is_log)
		align = sizeof(struct d40_log_lli);
	else
		align = sizeof(struct d40_phy_lli);

	if (lli_len == 1) {
		base = d40d->lli_pool.pre_alloc_lli;
		d40d->lli_pool.size = sizeof(d40d->lli_pool.pre_alloc_lli);
		d40d->lli_pool.base = NULL;
	} else {
		d40d->lli_pool.size = lli_len * 2 * align;

		base = kmalloc(d40d->lli_pool.size + align, GFP_NOWAIT);
		d40d->lli_pool.base = base;

		if (d40d->lli_pool.base == NULL)
			return -ENOMEM;
	}

	if (is_log) {
		d40d->lli_log.src = PTR_ALIGN(base, align);
		d40d->lli_log.dst = d40d->lli_log.src + lli_len;

		d40d->lli_pool.dma_addr = 0;
	} else {
		d40d->lli_phy.src = PTR_ALIGN(base, align);
		d40d->lli_phy.dst = d40d->lli_phy.src + lli_len;

		d40d->lli_pool.dma_addr = dma_map_single(d40c->base->dev,
							 d40d->lli_phy.src,
							 d40d->lli_pool.size,
							 DMA_TO_DEVICE);

		if (dma_mapping_error(d40c->base->dev,
				      d40d->lli_pool.dma_addr)) {
			kfree(d40d->lli_pool.base);
			d40d->lli_pool.base = NULL;
			d40d->lli_pool.dma_addr = 0;
			return -ENOMEM;
		}
	}

	return 0;
}

static void d40_pool_lli_free(struct d40_chan *d40c, struct d40_desc *d40d)
{
	if (d40d->lli_pool.dma_addr)
		dma_unmap_single(d40c->base->dev, d40d->lli_pool.dma_addr,
				 d40d->lli_pool.size, DMA_TO_DEVICE);

	kfree(d40d->lli_pool.base);
	d40d->lli_pool.base = NULL;
	d40d->lli_pool.size = 0;
	d40d->lli_log.src = NULL;
	d40d->lli_log.dst = NULL;
	d40d->lli_phy.src = NULL;
	d40d->lli_phy.dst = NULL;
}

static int d40_lcla_alloc_one(struct d40_chan *d40c,
			      struct d40_desc *d40d)
{
	unsigned long flags;
	int i;
	int ret = -EINVAL;
	int p;

	spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);

	p = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP;

	/*
	 * Allocate both src and dst at the same time, therefore the half
	 * start on 1 since 0 can't be used since zero is used as end marker.
	 */
	for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
		if (!d40c->base->lcla_pool.alloc_map[p + i]) {
			d40c->base->lcla_pool.alloc_map[p + i] = d40d;
			d40d->lcla_alloc++;
			ret = i;
			break;
		}
	}

	spin_unlock_irqrestore(&d40c->base->lcla_pool.lock, flags);

	return ret;
}

static int d40_lcla_free_all(struct d40_chan *d40c,
			     struct d40_desc *d40d)
{
	unsigned long flags;
	int i;
	int ret = -EINVAL;

	if (chan_is_physical(d40c))
		return 0;

	spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);

	for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
		if (d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num *
						    D40_LCLA_LINK_PER_EVENT_GRP + i] == d40d) {
			d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num *
							D40_LCLA_LINK_PER_EVENT_GRP + i] = NULL;
			d40d->lcla_alloc--;
			if (d40d->lcla_alloc == 0) {
				ret = 0;
				break;
			}
		}
	}

	spin_unlock_irqrestore(&d40c->base->lcla_pool.lock, flags);

	return ret;

}

static void d40_desc_remove(struct d40_desc *d40d)
{
	list_del(&d40d->node);
}

static struct d40_desc *d40_desc_get(struct d40_chan *d40c)
{
	struct d40_desc *desc = NULL;

	if (!list_empty(&d40c->client)) {
		struct d40_desc *d;
		struct d40_desc *_d;

		list_for_each_entry_safe(d, _d, &d40c->client, node)
			if (async_tx_test_ack(&d->txd)) {
				d40_pool_lli_free(d40c, d);
				d40_desc_remove(d);
				desc = d;
				memset(desc, 0, sizeof(*desc));
				break;
			}
	}

	if (!desc)
		desc = kmem_cache_zalloc(d40c->base->desc_slab, GFP_NOWAIT);

	if (desc)
		INIT_LIST_HEAD(&desc->node);

	return desc;
}

static void d40_desc_free(struct d40_chan *d40c, struct d40_desc *d40d)
{

	d40_pool_lli_free(d40c, d40d);
	d40_lcla_free_all(d40c, d40d);
	kmem_cache_free(d40c->base->desc_slab, d40d);
}

static void d40_desc_submit(struct d40_chan *d40c, struct d40_desc *desc)
{
	list_add_tail(&desc->node, &d40c->active);
}

static void d40_phy_lli_load(struct d40_chan *chan, struct d40_desc *desc)
{
	struct d40_phy_lli *lli_dst = desc->lli_phy.dst;
	struct d40_phy_lli *lli_src = desc->lli_phy.src;
	void __iomem *base = chan_base(chan);

	writel(lli_src->reg_cfg, base + D40_CHAN_REG_SSCFG);
	writel(lli_src->reg_elt, base + D40_CHAN_REG_SSELT);
	writel(lli_src->reg_ptr, base + D40_CHAN_REG_SSPTR);
	writel(lli_src->reg_lnk, base + D40_CHAN_REG_SSLNK);

	writel(lli_dst->reg_cfg, base + D40_CHAN_REG_SDCFG);
	writel(lli_dst->reg_elt, base + D40_CHAN_REG_SDELT);
	writel(lli_dst->reg_ptr, base + D40_CHAN_REG_SDPTR);
	writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK);
}

static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc)
{
	struct d40_lcla_pool *pool = &chan->base->lcla_pool;
	struct d40_log_lli_bidir *lli = &desc->lli_log;
	int lli_current = desc->lli_current;
	int lli_len = desc->lli_len;
	bool cyclic = desc->cyclic;
	int curr_lcla = -EINVAL;
	int first_lcla = 0;
	bool linkback;

	/*
	 * We may have partially running cyclic transfers, in case we did't get
	 * enough LCLA entries.
	 */
	linkback = cyclic && lli_current == 0;

	/*
	 * For linkback, we need one LCLA even with only one link, because we
	 * can't link back to the one in LCPA space
	 */
	if (linkback || (lli_len - lli_current > 1)) {
		curr_lcla = d40_lcla_alloc_one(chan, desc);
		first_lcla = curr_lcla;
	}

	/*
	 * For linkback, we normally load the LCPA in the loop since we need to
	 * link it to the second LCLA and not the first.  However, if we
	 * couldn't even get a first LCLA, then we have to run in LCPA and
	 * reload manually.
	 */
	if (!linkback || curr_lcla == -EINVAL) {
		unsigned int flags = 0;

		if (curr_lcla == -EINVAL)
			flags |= LLI_TERM_INT;

		d40_log_lli_lcpa_write(chan->lcpa,
				       &lli->dst[lli_current],
				       &lli->src[lli_current],
				       curr_lcla,
				       flags);
		lli_current++;
	}

	if (curr_lcla < 0)
		goto out;

	for (; lli_current < lli_len; lli_current++) {
		unsigned int lcla_offset = chan->phy_chan->num * 1024 +
					   8 * curr_lcla * 2;
		struct d40_log_lli *lcla = pool->base + lcla_offset;
		unsigned int flags = 0;
		int next_lcla;

		if (lli_current + 1 < lli_len)
			next_lcla = d40_lcla_alloc_one(chan, desc);
		else
			next_lcla = linkback ? first_lcla : -EINVAL;

		if (cyclic || next_lcla == -EINVAL)
			flags |= LLI_TERM_INT;

		if (linkback && curr_lcla == first_lcla) {
			/* First link goes in both LCPA and LCLA */
			d40_log_lli_lcpa_write(chan->lcpa,
					       &lli->dst[lli_current],
					       &lli->src[lli_current],
					       next_lcla, flags);
		}

		/*
		 * One unused LCLA in the cyclic case if the very first
		 * next_lcla fails...
		 */
		d40_log_lli_lcla_write(lcla,
				       &lli->dst[lli_current],
				       &lli->src[lli_current],
				       next_lcla, flags);

		dma_sync_single_range_for_device(chan->base->dev,
					pool->dma_addr, lcla_offset,
					2 * sizeof(struct d40_log_lli),
					DMA_TO_DEVICE);

		curr_lcla = next_lcla;

		if (curr_lcla == -EINVAL || curr_lcla == first_lcla) {
			lli_current++;
			break;
		}
	}

out:
	desc->lli_current = lli_current;
}

static void d40_desc_load(struct d40_chan *d40c, struct d40_desc *d40d)
{
	if (chan_is_physical(d40c)) {
		d40_phy_lli_load(d40c, d40d);
		d40d->lli_current = d40d->lli_len;
	} else
		d40_log_lli_to_lcxa(d40c, d40d);
}

static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
{
	struct d40_desc *d;

	if (list_empty(&d40c->active))
		return NULL;

	d = list_first_entry(&d40c->active,
			     struct d40_desc,
			     node);
	return d;
}

static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
{
	list_add_tail(&desc->node, &d40c->queue);
}

static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
{
	struct d40_desc *d;

	if (list_empty(&d40c->queue))
		return NULL;

	d = list_first_entry(&d40c->queue,
			     struct d40_desc,
			     node);
	return d;
}

static int d40_psize_2_burst_size(bool is_log, int psize)
{
	if (is_log) {
		if (psize == STEDMA40_PSIZE_LOG_1)
			return 1;
	} else {
		if (psize == STEDMA40_PSIZE_PHY_1)
			return 1;
	}

	return 2 << psize;
}

/*
 * The dma only supports transmitting packages up to
 * STEDMA40_MAX_SEG_SIZE << data_width. Calculate the total number of
 * dma elements required to send the entire sg list
 */
static int d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2)
{
	int dmalen;
	u32 max_w = max(data_width1, data_width2);
	u32 min_w = min(data_width1, data_width2);
	u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);

	if (seg_max > STEDMA40_MAX_SEG_SIZE)
		seg_max -= (1 << max_w);

	if (!IS_ALIGNED(size, 1 << max_w))
		return -EINVAL;

	if (size <= seg_max)
		dmalen = 1;
	else {
		dmalen = size / seg_max;
		if (dmalen * seg_max < size)
			dmalen++;
	}
	return dmalen;
}

static int d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len,
			   u32 data_width1, u32 data_width2)
{
	struct scatterlist *sg;
	int i;
	int len = 0;
	int ret;

	for_each_sg(sgl, sg, sg_len, i) {
		ret = d40_size_2_dmalen(sg_dma_len(sg),
					data_width1, data_width2);
		if (ret < 0)
			return ret;
		len += ret;
	}
	return len;
}

/* Support functions for logical channels */

static int d40_channel_execute_command(struct d40_chan *d40c,
				       enum d40_command command)
{
	u32 status;
	int i;
	void __iomem *active_reg;
	int ret = 0;
	unsigned long flags;
	u32 wmask;

	spin_lock_irqsave(&d40c->base->execmd_lock, flags);

	if (d40c->phy_chan->num % 2 == 0)
		active_reg = d40c->base->virtbase + D40_DREG_ACTIVE;
	else
		active_reg = d40c->base->virtbase + D40_DREG_ACTIVO;

	if (command == D40_DMA_SUSPEND_REQ) {
		status = (readl(active_reg) &
			  D40_CHAN_POS_MASK(d40c->phy_chan->num)) >>
			D40_CHAN_POS(d40c->phy_chan->num);

		if (status == D40_DMA_SUSPENDED || status == D40_DMA_STOP)
			goto done;
	}

	wmask = 0xffffffff & ~(D40_CHAN_POS_MASK(d40c->phy_chan->num));
	writel(wmask | (command << D40_CHAN_POS(d40c->phy_chan->num)),
	       active_reg);

	if (command == D40_DMA_SUSPEND_REQ) {

		for (i = 0 ; i < D40_SUSPEND_MAX_IT; i++) {
			status = (readl(active_reg) &
				  D40_CHAN_POS_MASK(d40c->phy_chan->num)) >>
				D40_CHAN_POS(d40c->phy_chan->num);

			cpu_relax();
			/*
			 * Reduce the number of bus accesses while
			 * waiting for the DMA to suspend.
			 */
			udelay(3);

			if (status == D40_DMA_STOP ||
			    status == D40_DMA_SUSPENDED)
				break;
		}

		if (i == D40_SUSPEND_MAX_IT) {
			chan_err(d40c,
				"unable to suspend the chl %d (log: %d) status %x\n",
				d40c->phy_chan->num, d40c->log_num,
				status);
			dump_stack();
			ret = -EBUSY;
		}

	}
done:
	spin_unlock_irqrestore(&d40c->base->execmd_lock, flags);
	return ret;
}

static void d40_term_all(struct d40_chan *d40c)
{
	struct d40_desc *d40d;

	/* Release active descriptors */
	while ((d40d = d40_first_active_get(d40c))) {
		d40_desc_remove(d40d);
		d40_desc_free(d40c, d40d);
	}

	/* Release queued descriptors waiting for transfer */
	while ((d40d = d40_first_queued(d40c))) {
		d40_desc_remove(d40d);
		d40_desc_free(d40c, d40d);
	}


	d40c->pending_tx = 0;
	d40c->busy = false;
}

static void __d40_config_set_event(struct d40_chan *d40c, bool enable,
				   u32 event, int reg)
{
	void __iomem *addr = chan_base(d40c) + reg;
	int tries;

	if (!enable) {
		writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
		       | ~D40_EVENTLINE_MASK(event), addr);
		return;
	}

	/*
	 * The hardware sometimes doesn't register the enable when src and dst
	 * event lines are active on the same logical channel.  Retry to ensure
	 * it does.  Usually only one retry is sufficient.
	 */
	tries = 100;
	while (--tries) {
		writel((D40_ACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
		       | ~D40_EVENTLINE_MASK(event), addr);

		if (readl(addr) & D40_EVENTLINE_MASK(event))
			break;
	}

	if (tries != 99)
		dev_dbg(chan2dev(d40c),
			"[%s] workaround enable S%cLNK (%d tries)\n",
			__func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D',
			100 - tries);

	WARN_ON(!tries);
}

static void d40_config_set_event(struct d40_chan *d40c, bool do_enable)
{
	unsigned long flags;

	spin_lock_irqsave(&d40c->phy_chan->lock, flags);

	/* Enable event line connected to device (or memcpy) */
	if ((d40c->dma_cfg.dir ==  STEDMA40_PERIPH_TO_MEM) ||
	    (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) {
		u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);

		__d40_config_set_event(d40c, do_enable, event,
				       D40_CHAN_REG_SSLNK);
	}

	if (d40c->dma_cfg.dir !=  STEDMA40_PERIPH_TO_MEM) {
		u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);

		__d40_config_set_event(d40c, do_enable, event,
				       D40_CHAN_REG_SDLNK);
	}

	spin_unlock_irqrestore(&d40c->phy_chan->lock, flags);
}

static u32 d40_chan_has_events(struct d40_chan *d40c)
{
	void __iomem *chanbase = chan_base(d40c);
	u32 val;

	val = readl(chanbase + D40_CHAN_REG_SSLNK);
	val |= readl(chanbase + D40_CHAN_REG_SDLNK);

	return val;
}

static u32 d40_get_prmo(struct d40_chan *d40c)
{
	static const unsigned int phy_map[] = {
		[STEDMA40_PCHAN_BASIC_MODE]
			= D40_DREG_PRMO_PCHAN_BASIC,
		[STEDMA40_PCHAN_MODULO_MODE]
			= D40_DREG_PRMO_PCHAN_MODULO,
		[STEDMA40_PCHAN_DOUBLE_DST_MODE]
			= D40_DREG_PRMO_PCHAN_DOUBLE_DST,
	};
	static const unsigned int log_map[] = {
		[STEDMA40_LCHAN_SRC_PHY_DST_LOG]
			= D40_DREG_PRMO_LCHAN_SRC_PHY_DST_LOG,
		[STEDMA40_LCHAN_SRC_LOG_DST_PHY]
			= D40_DREG_PRMO_LCHAN_SRC_LOG_DST_PHY,
		[STEDMA40_LCHAN_SRC_LOG_DST_LOG]
			= D40_DREG_PRMO_LCHAN_SRC_LOG_DST_LOG,
	};

	if (chan_is_physical(d40c))
		return phy_map[d40c->dma_cfg.mode_opt];
	else
		return log_map[d40c->dma_cfg.mode_opt];
}

static void d40_config_write(struct d40_chan *d40c)
{
	u32 addr_base;
	u32 var;

	/* Odd addresses are even addresses + 4 */
	addr_base = (d40c->phy_chan->num % 2) * 4;
	/* Setup channel mode to logical or physical */
	var = ((u32)(chan_is_logical(d40c)) + 1) <<
		D40_CHAN_POS(d40c->phy_chan->num);
	writel(var, d40c->base->virtbase + D40_DREG_PRMSE + addr_base);

	/* Setup operational mode option register */
	var = d40_get_prmo(d40c) << D40_CHAN_POS(d40c->phy_chan->num);

	writel(var, d40c->base->virtbase + D40_DREG_PRMOE + addr_base);

	if (chan_is_logical(d40c)) {
		int lidx = (d40c->phy_chan->num << D40_SREG_ELEM_LOG_LIDX_POS)
			   & D40_SREG_ELEM_LOG_LIDX_MASK;
		void __iomem *chanbase = chan_base(d40c);

		/* Set default config for CFG reg */
		writel(d40c->src_def_cfg, chanbase + D40_CHAN_REG_SSCFG);
		writel(d40c->dst_def_cfg, chanbase + D40_CHAN_REG_SDCFG);

		/* Set LIDX for lcla */
		writel(lidx, chanbase + D40_CHAN_REG_SSELT);
		writel(lidx, chanbase + D40_CHAN_REG_SDELT);
	}
}

static u32 d40_residue(struct d40_chan *d40c)
{
	u32 num_elt;

	if (chan_is_logical(d40c))
		num_elt = (readl(&d40c->lcpa->lcsp2) & D40_MEM_LCSP2_ECNT_MASK)