2019-01-07 13:07:38 +02:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2007-2008 Atmel Corporation
// Copyright (C) 2010-2011 ST Microelectronics
// Copyright (C) 2013,2018 Intel Corporation
# include <linux/bitops.h>
2019-01-07 13:07:40 +02:00
# include <linux/dmaengine.h>
2019-01-07 13:07:38 +02:00
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/types.h>
# include "internal.h"
static void dw_dma_initialize_chan ( struct dw_dma_chan * dwc )
{
struct dw_dma * dw = to_dw_dma ( dwc - > chan . device ) ;
2020-07-31 23:08:23 +03:00
u32 cfghi = is_slave_direction ( dwc - > direction ) ? 0 : DWC_CFGH_FIFO_MODE ;
2019-01-07 13:07:38 +02:00
u32 cfglo = DWC_CFGL_CH_PRIOR ( dwc - > priority ) ;
bool hs_polarity = dwc - > dws . hs_polarity ;
cfghi | = DWC_CFGH_DST_PER ( dwc - > dws . dst_id ) ;
cfghi | = DWC_CFGH_SRC_PER ( dwc - > dws . src_id ) ;
cfghi | = DWC_CFGH_PROTCTL ( dw - > pdata - > protctl ) ;
/* Set polarity of handshake interface */
cfglo | = hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0 ;
channel_writel ( dwc , CFG_LO , cfglo ) ;
channel_writel ( dwc , CFG_HI , cfghi ) ;
}
static void dw_dma_suspend_chan ( struct dw_dma_chan * dwc , bool drain )
{
u32 cfglo = channel_readl ( dwc , CFG_LO ) ;
channel_writel ( dwc , CFG_LO , cfglo | DWC_CFGL_CH_SUSP ) ;
}
2019-01-07 13:07:39 +02:00
static void dw_dma_resume_chan ( struct dw_dma_chan * dwc , bool drain )
{
u32 cfglo = channel_readl ( dwc , CFG_LO ) ;
channel_writel ( dwc , CFG_LO , cfglo & ~ DWC_CFGL_CH_SUSP ) ;
}
2019-01-07 13:07:38 +02:00
static u32 dw_dma_bytes2block ( struct dw_dma_chan * dwc ,
size_t bytes , unsigned int width , size_t * len )
{
u32 block ;
if ( ( bytes > > width ) > dwc - > block_size ) {
block = dwc - > block_size ;
* len = dwc - > block_size < < width ;
} else {
block = bytes > > width ;
* len = bytes ;
}
return block ;
}
static size_t dw_dma_block2bytes ( struct dw_dma_chan * dwc , u32 block , u32 width )
{
return DWC_CTLH_BLOCK_TS ( block ) < < width ;
}
2019-01-07 13:07:40 +02:00
static u32 dw_dma_prepare_ctllo ( struct dw_dma_chan * dwc )
{
struct dma_slave_config * sconfig = & dwc - > dma_sconfig ;
2020-07-31 23:08:25 +03:00
u8 smsize = ( dwc - > direction = = DMA_DEV_TO_MEM ) ? sconfig - > src_maxburst : 0 ;
u8 dmsize = ( dwc - > direction = = DMA_MEM_TO_DEV ) ? sconfig - > dst_maxburst : 0 ;
2019-01-07 13:07:40 +02:00
u8 p_master = dwc - > dws . p_master ;
u8 m_master = dwc - > dws . m_master ;
u8 dms = ( dwc - > direction = = DMA_MEM_TO_DEV ) ? p_master : m_master ;
u8 sms = ( dwc - > direction = = DMA_DEV_TO_MEM ) ? p_master : m_master ;
return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN |
DWC_CTLL_DST_MSIZE ( dmsize ) | DWC_CTLL_SRC_MSIZE ( smsize ) |
DWC_CTLL_DMS ( dms ) | DWC_CTLL_SMS ( sms ) ;
}
2019-01-07 13:07:38 +02:00
static void dw_dma_encode_maxburst ( struct dw_dma_chan * dwc , u32 * maxburst )
{
/*
* Fix burst size according to dw_dmac . We need to convert them as :
* 1 - > 0 , 4 - > 1 , 8 - > 2 , 16 - > 3.
*/
* maxburst = * maxburst > 1 ? fls ( * maxburst ) - 2 : 0 ;
}
static void dw_dma_set_device_name ( struct dw_dma * dw , int id )
{
snprintf ( dw - > name , sizeof ( dw - > name ) , " dw:dmac%d " , id ) ;
}
static void dw_dma_disable ( struct dw_dma * dw )
{
do_dw_dma_off ( dw ) ;
}
static void dw_dma_enable ( struct dw_dma * dw )
{
do_dw_dma_on ( dw ) ;
}
int dw_dma_probe ( struct dw_dma_chip * chip )
{
struct dw_dma * dw ;
dw = devm_kzalloc ( chip - > dev , sizeof ( * dw ) , GFP_KERNEL ) ;
if ( ! dw )
return - ENOMEM ;
/* Channel operations */
dw - > initialize_chan = dw_dma_initialize_chan ;
dw - > suspend_chan = dw_dma_suspend_chan ;
2019-01-07 13:07:39 +02:00
dw - > resume_chan = dw_dma_resume_chan ;
2019-01-07 13:07:40 +02:00
dw - > prepare_ctllo = dw_dma_prepare_ctllo ;
2019-01-07 13:07:38 +02:00
dw - > encode_maxburst = dw_dma_encode_maxburst ;
dw - > bytes2block = dw_dma_bytes2block ;
dw - > block2bytes = dw_dma_block2bytes ;
/* Device operations */
dw - > set_device_name = dw_dma_set_device_name ;
dw - > disable = dw_dma_disable ;
dw - > enable = dw_dma_enable ;
chip - > dw = dw ;
return do_dma_probe ( chip ) ;
}
EXPORT_SYMBOL_GPL ( dw_dma_probe ) ;
int dw_dma_remove ( struct dw_dma_chip * chip )
{
return do_dma_remove ( chip ) ;
}
EXPORT_SYMBOL_GPL ( dw_dma_remove ) ;