2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-06-17 21:27:26 +04:00
/*
2013-03-25 12:35:17 +04:00
* Copyright ( C ) 2010 - 2013 Bluecherry , LLC < http : //www.bluecherrydvr.com>
*
* Original author :
* Ben Collins < bcollins @ ubuntu . com >
*
* Additional work by :
* John Brooks < john . brooks @ bluecherry . net >
2010-06-17 21:27:26 +04:00
*/
# include <linux/kernel.h>
2013-03-25 12:35:17 +04:00
# include <linux/module.h>
2011-07-29 00:59:37 +04:00
# include <linux/slab.h>
2013-03-25 12:35:17 +04:00
2011-02-11 15:36:27 +03:00
# include "solo6x10.h"
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
static int multi_p2m ;
module_param ( multi_p2m , uint , 0644 ) ;
MODULE_PARM_DESC ( multi_p2m ,
" Use multiple P2M DMA channels (default: no, 6010-only) " ) ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
static int desc_mode ;
module_param ( desc_mode , uint , 0644 ) ;
MODULE_PARM_DESC ( desc_mode ,
" Allow use of descriptor mode DMA (default: no, 6010-only) " ) ;
int solo_p2m_dma ( struct solo_dev * solo_dev , int wr ,
void * sys_addr , u32 ext_addr , u32 size ,
int repeat , u32 ext_size )
2010-06-17 21:27:26 +04:00
{
dma_addr_t dma_addr ;
int ret ;
2013-03-25 12:35:17 +04:00
if ( WARN_ON_ONCE ( ( unsigned long ) sys_addr & 0x03 ) )
return - EINVAL ;
if ( WARN_ON_ONCE ( ! size ) )
2010-06-17 21:27:26 +04:00
return - EINVAL ;
dma_addr = pci_map_single ( solo_dev - > pdev , sys_addr , size ,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE ) ;
2013-03-18 15:21:33 +04:00
if ( pci_dma_mapping_error ( solo_dev - > pdev , dma_addr ) )
return - ENOMEM ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
ret = solo_p2m_dma_t ( solo_dev , wr , dma_addr , ext_addr , size ,
repeat , ext_size ) ;
2010-06-17 21:27:26 +04:00
pci_unmap_single ( solo_dev - > pdev , dma_addr , size ,
wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE ) ;
return ret ;
}
2013-03-25 12:35:17 +04:00
/* Mutex must be held for p2m_id before calling this!! */
int solo_p2m_dma_desc ( struct solo_dev * solo_dev ,
struct solo_p2m_desc * desc , dma_addr_t desc_dma ,
int desc_cnt )
2010-06-17 21:27:26 +04:00
{
struct solo_p2m_dev * p2m_dev ;
2010-11-05 05:51:17 +03:00
unsigned int timeout ;
2013-03-25 12:35:17 +04:00
unsigned int config = 0 ;
2010-11-05 05:51:17 +03:00
int ret = 0 ;
2018-03-23 13:48:07 +03:00
unsigned int p2m_id = 0 ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
/* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */
2018-03-23 13:48:07 +03:00
if ( solo_dev - > type ! = SOLO_DEV_6110 & & multi_p2m )
2013-03-25 12:35:17 +04:00
p2m_id = atomic_inc_return ( & solo_dev - > p2m_count ) % SOLO_NR_P2M ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
p2m_dev = & solo_dev - > p2m_dev [ p2m_id ] ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
if ( mutex_lock_interruptible ( & p2m_dev - > mutex ) )
return - EINTR ;
2010-11-05 17:29:33 +03:00
2013-11-15 02:32:02 +04:00
reinit_completion ( & p2m_dev - > completion ) ;
2010-06-17 21:27:26 +04:00
p2m_dev - > error = 0 ;
2013-03-25 12:35:17 +04:00
if ( desc_cnt > 1 & & solo_dev - > type ! = SOLO_DEV_6110 & & desc_mode ) {
/* For 6010 with more than one desc, we can do a one-shot */
p2m_dev - > desc_count = p2m_dev - > desc_idx = 0 ;
config = solo_reg_read ( solo_dev , SOLO_P2M_CONFIG ( p2m_id ) ) ;
solo_reg_write ( solo_dev , SOLO_P2M_DES_ADR ( p2m_id ) , desc_dma ) ;
solo_reg_write ( solo_dev , SOLO_P2M_DESC_ID ( p2m_id ) , desc_cnt ) ;
solo_reg_write ( solo_dev , SOLO_P2M_CONFIG ( p2m_id ) , config |
SOLO_P2M_DESC_MODE ) ;
} else {
/* For single descriptors and 6110, we need to run each desc */
p2m_dev - > desc_count = desc_cnt ;
p2m_dev - > desc_idx = 1 ;
p2m_dev - > descs = desc ;
solo_reg_write ( solo_dev , SOLO_P2M_TAR_ADR ( p2m_id ) ,
desc [ 1 ] . dma_addr ) ;
solo_reg_write ( solo_dev , SOLO_P2M_EXT_ADR ( p2m_id ) ,
desc [ 1 ] . ext_addr ) ;
solo_reg_write ( solo_dev , SOLO_P2M_EXT_CFG ( p2m_id ) ,
desc [ 1 ] . cfg ) ;
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( p2m_id ) ,
desc [ 1 ] . ctrl ) ;
}
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
timeout = wait_for_completion_timeout ( & p2m_dev - > completion ,
solo_dev - > p2m_jiffies ) ;
2010-11-05 17:29:33 +03:00
2013-03-25 12:35:17 +04:00
if ( WARN_ON_ONCE ( p2m_dev - > error ) )
2010-11-05 05:51:17 +03:00
ret = - EIO ;
2013-03-25 12:35:17 +04:00
else if ( timeout = = 0 ) {
solo_dev - > p2m_timeouts + + ;
2010-11-05 05:51:17 +03:00
ret = - EAGAIN ;
}
2013-03-25 12:35:17 +04:00
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( p2m_id ) , 0 ) ;
2010-11-05 05:51:17 +03:00
2013-03-25 12:35:17 +04:00
/* Don't write here for the no_desc_mode case, because config is 0.
* We can ' t test no_desc_mode again , it might race . */
if ( desc_cnt > 1 & & solo_dev - > type ! = SOLO_DEV_6110 & & config )
solo_reg_write ( solo_dev , SOLO_P2M_CONFIG ( p2m_id ) , config ) ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
mutex_unlock ( & p2m_dev - > mutex ) ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
return ret ;
}
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
void solo_p2m_fill_desc ( struct solo_p2m_desc * desc , int wr ,
dma_addr_t dma_addr , u32 ext_addr , u32 size ,
int repeat , u32 ext_size )
2010-06-17 21:27:26 +04:00
{
2013-03-25 12:35:17 +04:00
WARN_ON_ONCE ( dma_addr & 0x03 ) ;
WARN_ON_ONCE ( ! size ) ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
desc - > cfg = SOLO_P2M_COPY_SIZE ( size > > 2 ) ;
desc - > ctrl = SOLO_P2M_BURST_SIZE ( SOLO_P2M_BURST_256 ) |
( wr ? SOLO_P2M_WRITE : 0 ) | SOLO_P2M_TRANS_ON ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
if ( repeat ) {
desc - > cfg | = SOLO_P2M_EXT_INC ( ext_size > > 2 ) ;
desc - > ctrl | = SOLO_P2M_PCI_INC ( size > > 2 ) |
SOLO_P2M_REPEAT ( repeat ) ;
2010-06-17 21:27:26 +04:00
}
2013-03-25 12:35:17 +04:00
desc - > dma_addr = dma_addr ;
desc - > ext_addr = ext_addr ;
2010-06-17 21:27:26 +04:00
}
2013-03-25 12:35:17 +04:00
int solo_p2m_dma_t ( struct solo_dev * solo_dev , int wr ,
dma_addr_t dma_addr , u32 ext_addr , u32 size ,
int repeat , u32 ext_size )
2010-06-17 21:27:26 +04:00
{
2013-03-25 12:35:17 +04:00
struct solo_p2m_desc desc [ 2 ] ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
solo_p2m_fill_desc ( & desc [ 1 ] , wr , dma_addr , ext_addr , size , repeat ,
ext_size ) ;
2010-06-17 21:27:26 +04:00
2013-03-25 12:35:17 +04:00
/* No need for desc_dma since we know it is a single-shot */
return solo_p2m_dma_desc ( solo_dev , desc , 0 , 1 ) ;
2010-06-17 21:27:26 +04:00
}
2011-02-11 15:38:20 +03:00
void solo_p2m_isr ( struct solo_dev * solo_dev , int id )
2010-06-17 21:27:26 +04:00
{
2010-11-05 05:51:17 +03:00
struct solo_p2m_dev * p2m_dev = & solo_dev - > p2m_dev [ id ] ;
2013-03-25 12:35:17 +04:00
struct solo_p2m_desc * desc ;
if ( p2m_dev - > desc_count < = p2m_dev - > desc_idx ) {
complete ( & p2m_dev - > completion ) ;
return ;
}
2010-11-05 05:51:17 +03:00
2013-03-25 12:35:17 +04:00
/* Setup next descriptor */
p2m_dev - > desc_idx + + ;
desc = & p2m_dev - > descs [ p2m_dev - > desc_idx ] ;
2010-11-05 05:51:17 +03:00
2013-03-25 12:35:17 +04:00
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( id ) , 0 ) ;
solo_reg_write ( solo_dev , SOLO_P2M_TAR_ADR ( id ) , desc - > dma_addr ) ;
solo_reg_write ( solo_dev , SOLO_P2M_EXT_ADR ( id ) , desc - > ext_addr ) ;
solo_reg_write ( solo_dev , SOLO_P2M_EXT_CFG ( id ) , desc - > cfg ) ;
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( id ) , desc - > ctrl ) ;
2010-06-17 21:27:26 +04:00
}
2013-03-25 12:35:17 +04:00
void solo_p2m_error_isr ( struct solo_dev * solo_dev )
2010-06-17 21:27:26 +04:00
{
2013-03-25 12:35:17 +04:00
unsigned int err = solo_reg_read ( solo_dev , SOLO_PCI_ERR ) ;
2010-06-17 21:27:26 +04:00
struct solo_p2m_dev * p2m_dev ;
int i ;
2013-03-15 18:16:56 +04:00
if ( ! ( err & ( SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC ) ) )
2010-06-17 21:27:26 +04:00
return ;
for ( i = 0 ; i < SOLO_NR_P2M ; i + + ) {
p2m_dev = & solo_dev - > p2m_dev [ i ] ;
p2m_dev - > error = 1 ;
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( i ) , 0 ) ;
complete ( & p2m_dev - > completion ) ;
}
}
2011-02-11 15:38:20 +03:00
void solo_p2m_exit ( struct solo_dev * solo_dev )
2010-06-17 21:27:26 +04:00
{
int i ;
for ( i = 0 ; i < SOLO_NR_P2M ; i + + )
2011-02-11 15:38:20 +03:00
solo_irq_off ( solo_dev , SOLO_IRQ_P2M ( i ) ) ;
2010-06-17 21:27:26 +04:00
}
2013-03-25 12:35:17 +04:00
static int solo_p2m_test ( struct solo_dev * solo_dev , int base , int size )
{
u32 * wr_buf ;
u32 * rd_buf ;
int i ;
int ret = - EIO ;
int order = get_order ( size ) ;
wr_buf = ( u32 * ) __get_free_pages ( GFP_KERNEL , order ) ;
if ( wr_buf = = NULL )
return - 1 ;
rd_buf = ( u32 * ) __get_free_pages ( GFP_KERNEL , order ) ;
if ( rd_buf = = NULL ) {
free_pages ( ( unsigned long ) wr_buf , order ) ;
return - 1 ;
}
for ( i = 0 ; i < ( size > > 3 ) ; i + + )
* ( wr_buf + i ) = ( i < < 16 ) | ( i + 1 ) ;
for ( i = ( size > > 3 ) ; i < ( size > > 2 ) ; i + + )
* ( wr_buf + i ) = ~ ( ( i < < 16 ) | ( i + 1 ) ) ;
memset ( rd_buf , 0x55 , size ) ;
if ( solo_p2m_dma ( solo_dev , 1 , wr_buf , base , size , 0 , 0 ) )
goto test_fail ;
if ( solo_p2m_dma ( solo_dev , 0 , rd_buf , base , size , 0 , 0 ) )
goto test_fail ;
for ( i = 0 ; i < ( size > > 2 ) ; i + + ) {
if ( * ( wr_buf + i ) ! = * ( rd_buf + i ) )
goto test_fail ;
}
ret = 0 ;
test_fail :
free_pages ( ( unsigned long ) wr_buf , order ) ;
free_pages ( ( unsigned long ) rd_buf , order ) ;
return ret ;
}
2011-02-11 15:38:20 +03:00
int solo_p2m_init ( struct solo_dev * solo_dev )
2010-06-17 21:27:26 +04:00
{
struct solo_p2m_dev * p2m_dev ;
int i ;
for ( i = 0 ; i < SOLO_NR_P2M ; i + + ) {
p2m_dev = & solo_dev - > p2m_dev [ i ] ;
2010-11-05 05:51:17 +03:00
mutex_init ( & p2m_dev - > mutex ) ;
2010-06-17 21:27:26 +04:00
init_completion ( & p2m_dev - > completion ) ;
solo_reg_write ( solo_dev , SOLO_P2M_CONTROL ( i ) , 0 ) ;
solo_reg_write ( solo_dev , SOLO_P2M_CONFIG ( i ) ,
SOLO_P2M_CSC_16BIT_565 |
2010-11-05 17:29:33 +03:00
SOLO_P2M_DESC_INTR_OPT |
2013-03-25 12:35:17 +04:00
SOLO_P2M_DMA_INTERVAL ( 0 ) |
2010-06-17 21:27:26 +04:00
SOLO_P2M_PCI_MASTER_MODE ) ;
2011-02-11 15:38:20 +03:00
solo_irq_on ( solo_dev , SOLO_IRQ_P2M ( i ) ) ;
2010-06-17 21:27:26 +04:00
}
2013-03-25 12:35:17 +04:00
/* Find correct SDRAM size */
for ( solo_dev - > sdram_size = 0 , i = 2 ; i > = 0 ; i - - ) {
solo_reg_write ( solo_dev , SOLO_DMA_CTRL ,
SOLO_DMA_CTRL_REFRESH_CYCLE ( 1 ) |
SOLO_DMA_CTRL_SDRAM_SIZE ( i ) |
SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
SOLO_DMA_CTRL_READ_CLK_SELECT |
SOLO_DMA_CTRL_LATENCY ( 1 ) ) ;
solo_reg_write ( solo_dev , SOLO_SYS_CFG , solo_dev - > sys_config |
SOLO_SYS_CFG_RESET ) ;
solo_reg_write ( solo_dev , SOLO_SYS_CFG , solo_dev - > sys_config ) ;
switch ( i ) {
case 2 :
if ( solo_p2m_test ( solo_dev , 0x07ff0000 , 0x00010000 ) | |
solo_p2m_test ( solo_dev , 0x05ff0000 , 0x00010000 ) )
continue ;
break ;
case 1 :
if ( solo_p2m_test ( solo_dev , 0x03ff0000 , 0x00010000 ) )
continue ;
break ;
default :
if ( solo_p2m_test ( solo_dev , 0x01ff0000 , 0x00010000 ) )
continue ;
}
solo_dev - > sdram_size = ( 32 < < 20 ) < < i ;
break ;
}
if ( ! solo_dev - > sdram_size ) {
dev_err ( & solo_dev - > pdev - > dev , " Error detecting SDRAM size \n " ) ;
return - EIO ;
}
if ( SOLO_SDRAM_END ( solo_dev ) > solo_dev - > sdram_size ) {
dev_err ( & solo_dev - > pdev - > dev ,
" SDRAM is not large enough (%u < %u) \n " ,
solo_dev - > sdram_size , SOLO_SDRAM_END ( solo_dev ) ) ;
return - EIO ;
}
2010-06-17 21:27:26 +04:00
return 0 ;
}