2017-03-27 12:45:14 +03:00
/**
* Host side test driver to test endpoint functionality
*
* Copyright ( C ) 2017 Texas Instruments
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/crc32.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/io.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/miscdevice.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/random.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/pci_regs.h>
# include <uapi/linux/pcitest.h>
# define DRV_MODULE_NAME "pci-endpoint-test"
# define PCI_ENDPOINT_TEST_MAGIC 0x0
# define PCI_ENDPOINT_TEST_COMMAND 0x4
# define COMMAND_RAISE_LEGACY_IRQ BIT(0)
# define COMMAND_RAISE_MSI_IRQ BIT(1)
# define MSI_NUMBER_SHIFT 2
/* 6 bits for MSI number */
# define COMMAND_READ BIT(8)
# define COMMAND_WRITE BIT(9)
# define COMMAND_COPY BIT(10)
# define PCI_ENDPOINT_TEST_STATUS 0x8
# define STATUS_READ_SUCCESS BIT(0)
# define STATUS_READ_FAIL BIT(1)
# define STATUS_WRITE_SUCCESS BIT(2)
# define STATUS_WRITE_FAIL BIT(3)
# define STATUS_COPY_SUCCESS BIT(4)
# define STATUS_COPY_FAIL BIT(5)
# define STATUS_IRQ_RAISED BIT(6)
# define STATUS_SRC_ADDR_INVALID BIT(7)
# define STATUS_DST_ADDR_INVALID BIT(8)
# define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc
# define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
# define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14
# define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18
# define PCI_ENDPOINT_TEST_SIZE 0x1c
# define PCI_ENDPOINT_TEST_CHECKSUM 0x20
static DEFINE_IDA ( pci_endpoint_test_ida ) ;
# define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
miscdev )
2017-08-18 17:58:09 +03:00
static bool no_msi ;
module_param ( no_msi , bool , 0444 ) ;
MODULE_PARM_DESC ( no_msi , " Disable MSI interrupt in pci_endpoint_test " ) ;
2017-03-27 12:45:14 +03:00
enum pci_barno {
BAR_0 ,
BAR_1 ,
BAR_2 ,
BAR_3 ,
BAR_4 ,
BAR_5 ,
} ;
struct pci_endpoint_test {
struct pci_dev * pdev ;
void __iomem * base ;
void __iomem * bar [ 6 ] ;
struct completion irq_raised ;
int last_irq ;
2017-10-11 11:44:38 +03:00
int num_irqs ;
2017-03-27 12:45:14 +03:00
/* mutex to protect the ioctls */
struct mutex mutex ;
struct miscdevice miscdev ;
2017-08-18 17:58:05 +03:00
enum pci_barno test_reg_bar ;
2017-08-18 17:58:06 +03:00
size_t alignment ;
2017-03-27 12:45:14 +03:00
} ;
2017-08-18 17:58:05 +03:00
struct pci_endpoint_test_data {
enum pci_barno test_reg_bar ;
2017-08-18 17:58:06 +03:00
size_t alignment ;
2017-08-18 17:58:07 +03:00
bool no_msi ;
2017-08-18 17:58:05 +03:00
} ;
2017-03-27 12:45:14 +03:00
static inline u32 pci_endpoint_test_readl ( struct pci_endpoint_test * test ,
u32 offset )
{
return readl ( test - > base + offset ) ;
}
static inline void pci_endpoint_test_writel ( struct pci_endpoint_test * test ,
u32 offset , u32 value )
{
writel ( value , test - > base + offset ) ;
}
static inline u32 pci_endpoint_test_bar_readl ( struct pci_endpoint_test * test ,
int bar , int offset )
{
return readl ( test - > bar [ bar ] + offset ) ;
}
static inline void pci_endpoint_test_bar_writel ( struct pci_endpoint_test * test ,
int bar , u32 offset , u32 value )
{
writel ( value , test - > bar [ bar ] + offset ) ;
}
static irqreturn_t pci_endpoint_test_irqhandler ( int irq , void * dev_id )
{
struct pci_endpoint_test * test = dev_id ;
u32 reg ;
reg = pci_endpoint_test_readl ( test , PCI_ENDPOINT_TEST_STATUS ) ;
if ( reg & STATUS_IRQ_RAISED ) {
test - > last_irq = irq ;
complete ( & test - > irq_raised ) ;
reg & = ~ STATUS_IRQ_RAISED ;
}
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_STATUS ,
reg ) ;
return IRQ_HANDLED ;
}
static bool pci_endpoint_test_bar ( struct pci_endpoint_test * test ,
enum pci_barno barno )
{
int j ;
u32 val ;
int size ;
2017-08-18 17:58:08 +03:00
struct pci_dev * pdev = test - > pdev ;
2017-03-27 12:45:14 +03:00
if ( ! test - > bar [ barno ] )
return false ;
2017-08-18 17:58:08 +03:00
size = pci_resource_len ( pdev , barno ) ;
2017-03-27 12:45:14 +03:00
2017-08-18 17:58:05 +03:00
if ( barno = = test - > test_reg_bar )
size = 0x4 ;
2017-03-27 12:45:14 +03:00
for ( j = 0 ; j < size ; j + = 4 )
pci_endpoint_test_bar_writel ( test , barno , j , 0xA0A0A0A0 ) ;
for ( j = 0 ; j < size ; j + = 4 ) {
val = pci_endpoint_test_bar_readl ( test , barno , j ) ;
if ( val ! = 0xA0A0A0A0 )
return false ;
}
return true ;
}
static bool pci_endpoint_test_legacy_irq ( struct pci_endpoint_test * test )
{
u32 val ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_COMMAND ,
COMMAND_RAISE_LEGACY_IRQ ) ;
val = wait_for_completion_timeout ( & test - > irq_raised ,
msecs_to_jiffies ( 1000 ) ) ;
if ( ! val )
return false ;
return true ;
}
static bool pci_endpoint_test_msi_irq ( struct pci_endpoint_test * test ,
u8 msi_num )
{
u32 val ;
struct pci_dev * pdev = test - > pdev ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_COMMAND ,
msi_num < < MSI_NUMBER_SHIFT |
COMMAND_RAISE_MSI_IRQ ) ;
val = wait_for_completion_timeout ( & test - > irq_raised ,
msecs_to_jiffies ( 1000 ) ) ;
if ( ! val )
return false ;
2018-05-14 20:27:48 +03:00
if ( pci_irq_vector ( pdev , msi_num - 1 ) = = test - > last_irq )
2017-03-27 12:45:14 +03:00
return true ;
return false ;
}
static bool pci_endpoint_test_copy ( struct pci_endpoint_test * test , size_t size )
{
bool ret = false ;
void * src_addr ;
void * dst_addr ;
dma_addr_t src_phys_addr ;
dma_addr_t dst_phys_addr ;
struct pci_dev * pdev = test - > pdev ;
struct device * dev = & pdev - > dev ;
2017-08-18 17:58:06 +03:00
void * orig_src_addr ;
dma_addr_t orig_src_phys_addr ;
void * orig_dst_addr ;
dma_addr_t orig_dst_phys_addr ;
size_t offset ;
size_t alignment = test - > alignment ;
2017-03-27 12:45:14 +03:00
u32 src_crc32 ;
u32 dst_crc32 ;
2017-09-30 11:15:52 +03:00
if ( size > SIZE_MAX - alignment )
goto err ;
2017-08-18 17:58:06 +03:00
orig_src_addr = dma_alloc_coherent ( dev , size + alignment ,
& orig_src_phys_addr , GFP_KERNEL ) ;
if ( ! orig_src_addr ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to allocate source buffer \n " ) ;
2017-03-27 12:45:14 +03:00
ret = false ;
goto err ;
}
2017-08-18 17:58:06 +03:00
if ( alignment & & ! IS_ALIGNED ( orig_src_phys_addr , alignment ) ) {
src_phys_addr = PTR_ALIGN ( orig_src_phys_addr , alignment ) ;
offset = src_phys_addr - orig_src_phys_addr ;
src_addr = orig_src_addr + offset ;
} else {
src_phys_addr = orig_src_phys_addr ;
src_addr = orig_src_addr ;
}
2017-03-27 12:45:14 +03:00
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_LOWER_SRC_ADDR ,
lower_32_bits ( src_phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_UPPER_SRC_ADDR ,
upper_32_bits ( src_phys_addr ) ) ;
get_random_bytes ( src_addr , size ) ;
src_crc32 = crc32_le ( ~ 0 , src_addr , size ) ;
2017-08-18 17:58:06 +03:00
orig_dst_addr = dma_alloc_coherent ( dev , size + alignment ,
& orig_dst_phys_addr , GFP_KERNEL ) ;
if ( ! orig_dst_addr ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to allocate destination address \n " ) ;
2017-03-27 12:45:14 +03:00
ret = false ;
2017-08-18 17:58:06 +03:00
goto err_orig_src_addr ;
}
if ( alignment & & ! IS_ALIGNED ( orig_dst_phys_addr , alignment ) ) {
dst_phys_addr = PTR_ALIGN ( orig_dst_phys_addr , alignment ) ;
offset = dst_phys_addr - orig_dst_phys_addr ;
dst_addr = orig_dst_addr + offset ;
} else {
dst_phys_addr = orig_dst_phys_addr ;
dst_addr = orig_dst_addr ;
2017-03-27 12:45:14 +03:00
}
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_LOWER_DST_ADDR ,
lower_32_bits ( dst_phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_UPPER_DST_ADDR ,
upper_32_bits ( dst_phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_SIZE ,
size ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_COMMAND ,
1 < < MSI_NUMBER_SHIFT | COMMAND_COPY ) ;
wait_for_completion ( & test - > irq_raised ) ;
dst_crc32 = crc32_le ( ~ 0 , dst_addr , size ) ;
if ( dst_crc32 = = src_crc32 )
ret = true ;
2017-08-18 17:58:06 +03:00
dma_free_coherent ( dev , size + alignment , orig_dst_addr ,
orig_dst_phys_addr ) ;
2017-03-27 12:45:14 +03:00
2017-08-18 17:58:06 +03:00
err_orig_src_addr :
dma_free_coherent ( dev , size + alignment , orig_src_addr ,
orig_src_phys_addr ) ;
2017-03-27 12:45:14 +03:00
err :
return ret ;
}
static bool pci_endpoint_test_write ( struct pci_endpoint_test * test , size_t size )
{
bool ret = false ;
u32 reg ;
void * addr ;
dma_addr_t phys_addr ;
struct pci_dev * pdev = test - > pdev ;
struct device * dev = & pdev - > dev ;
2017-08-18 17:58:06 +03:00
void * orig_addr ;
dma_addr_t orig_phys_addr ;
size_t offset ;
size_t alignment = test - > alignment ;
2017-03-27 12:45:14 +03:00
u32 crc32 ;
2017-09-30 11:15:52 +03:00
if ( size > SIZE_MAX - alignment )
goto err ;
2017-08-18 17:58:06 +03:00
orig_addr = dma_alloc_coherent ( dev , size + alignment , & orig_phys_addr ,
GFP_KERNEL ) ;
if ( ! orig_addr ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to allocate address \n " ) ;
2017-03-27 12:45:14 +03:00
ret = false ;
goto err ;
}
2017-08-18 17:58:06 +03:00
if ( alignment & & ! IS_ALIGNED ( orig_phys_addr , alignment ) ) {
phys_addr = PTR_ALIGN ( orig_phys_addr , alignment ) ;
offset = phys_addr - orig_phys_addr ;
addr = orig_addr + offset ;
} else {
phys_addr = orig_phys_addr ;
addr = orig_addr ;
}
2017-03-27 12:45:14 +03:00
get_random_bytes ( addr , size ) ;
crc32 = crc32_le ( ~ 0 , addr , size ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_CHECKSUM ,
crc32 ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_LOWER_SRC_ADDR ,
lower_32_bits ( phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_UPPER_SRC_ADDR ,
upper_32_bits ( phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_SIZE , size ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_COMMAND ,
1 < < MSI_NUMBER_SHIFT | COMMAND_READ ) ;
wait_for_completion ( & test - > irq_raised ) ;
reg = pci_endpoint_test_readl ( test , PCI_ENDPOINT_TEST_STATUS ) ;
if ( reg & STATUS_READ_SUCCESS )
ret = true ;
2017-08-18 17:58:06 +03:00
dma_free_coherent ( dev , size + alignment , orig_addr , orig_phys_addr ) ;
2017-03-27 12:45:14 +03:00
err :
return ret ;
}
static bool pci_endpoint_test_read ( struct pci_endpoint_test * test , size_t size )
{
bool ret = false ;
void * addr ;
dma_addr_t phys_addr ;
struct pci_dev * pdev = test - > pdev ;
struct device * dev = & pdev - > dev ;
2017-08-18 17:58:06 +03:00
void * orig_addr ;
dma_addr_t orig_phys_addr ;
size_t offset ;
size_t alignment = test - > alignment ;
2017-03-27 12:45:14 +03:00
u32 crc32 ;
2017-09-30 11:15:52 +03:00
if ( size > SIZE_MAX - alignment )
goto err ;
2017-08-18 17:58:06 +03:00
orig_addr = dma_alloc_coherent ( dev , size + alignment , & orig_phys_addr ,
GFP_KERNEL ) ;
if ( ! orig_addr ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to allocate destination address \n " ) ;
2017-03-27 12:45:14 +03:00
ret = false ;
goto err ;
}
2017-08-18 17:58:06 +03:00
if ( alignment & & ! IS_ALIGNED ( orig_phys_addr , alignment ) ) {
phys_addr = PTR_ALIGN ( orig_phys_addr , alignment ) ;
offset = phys_addr - orig_phys_addr ;
addr = orig_addr + offset ;
} else {
phys_addr = orig_phys_addr ;
addr = orig_addr ;
}
2017-03-27 12:45:14 +03:00
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_LOWER_DST_ADDR ,
lower_32_bits ( phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_UPPER_DST_ADDR ,
upper_32_bits ( phys_addr ) ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_SIZE , size ) ;
pci_endpoint_test_writel ( test , PCI_ENDPOINT_TEST_COMMAND ,
1 < < MSI_NUMBER_SHIFT | COMMAND_WRITE ) ;
wait_for_completion ( & test - > irq_raised ) ;
crc32 = crc32_le ( ~ 0 , addr , size ) ;
if ( crc32 = = pci_endpoint_test_readl ( test , PCI_ENDPOINT_TEST_CHECKSUM ) )
ret = true ;
2017-08-18 17:58:06 +03:00
dma_free_coherent ( dev , size + alignment , orig_addr , orig_phys_addr ) ;
2017-03-27 12:45:14 +03:00
err :
return ret ;
}
static long pci_endpoint_test_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
int ret = - EINVAL ;
enum pci_barno bar ;
struct pci_endpoint_test * test = to_endpoint_test ( file - > private_data ) ;
mutex_lock ( & test - > mutex ) ;
switch ( cmd ) {
case PCITEST_BAR :
bar = arg ;
if ( bar < 0 | | bar > 5 )
goto ret ;
ret = pci_endpoint_test_bar ( test , bar ) ;
break ;
case PCITEST_LEGACY_IRQ :
ret = pci_endpoint_test_legacy_irq ( test ) ;
break ;
case PCITEST_MSI :
ret = pci_endpoint_test_msi_irq ( test , arg ) ;
break ;
case PCITEST_WRITE :
ret = pci_endpoint_test_write ( test , arg ) ;
break ;
case PCITEST_READ :
ret = pci_endpoint_test_read ( test , arg ) ;
break ;
case PCITEST_COPY :
ret = pci_endpoint_test_copy ( test , arg ) ;
break ;
}
ret :
mutex_unlock ( & test - > mutex ) ;
return ret ;
}
static const struct file_operations pci_endpoint_test_fops = {
. owner = THIS_MODULE ,
. unlocked_ioctl = pci_endpoint_test_ioctl ,
} ;
static int pci_endpoint_test_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
int i ;
int err ;
2017-08-18 17:58:07 +03:00
int irq = 0 ;
2017-03-27 12:45:14 +03:00
int id ;
char name [ 20 ] ;
enum pci_barno bar ;
void __iomem * base ;
struct device * dev = & pdev - > dev ;
struct pci_endpoint_test * test ;
2017-08-18 17:58:05 +03:00
struct pci_endpoint_test_data * data ;
enum pci_barno test_reg_bar = BAR_0 ;
2017-03-27 12:45:14 +03:00
struct miscdevice * misc_device ;
if ( pci_is_bridge ( pdev ) )
return - ENODEV ;
test = devm_kzalloc ( dev , sizeof ( * test ) , GFP_KERNEL ) ;
if ( ! test )
return - ENOMEM ;
2017-08-18 17:58:05 +03:00
test - > test_reg_bar = 0 ;
2017-08-18 17:58:06 +03:00
test - > alignment = 0 ;
2017-03-27 12:45:14 +03:00
test - > pdev = pdev ;
2017-08-18 17:58:05 +03:00
data = ( struct pci_endpoint_test_data * ) ent - > driver_data ;
2017-08-18 17:58:06 +03:00
if ( data ) {
2017-08-18 17:58:05 +03:00
test_reg_bar = data - > test_reg_bar ;
2017-08-18 17:58:06 +03:00
test - > alignment = data - > alignment ;
2017-08-18 17:58:07 +03:00
no_msi = data - > no_msi ;
2017-08-18 17:58:06 +03:00
}
2017-08-18 17:58:05 +03:00
2017-03-27 12:45:14 +03:00
init_completion ( & test - > irq_raised ) ;
mutex_init ( & test - > mutex ) ;
err = pci_enable_device ( pdev ) ;
if ( err ) {
dev_err ( dev , " Cannot enable PCI device \n " ) ;
return err ;
}
err = pci_request_regions ( pdev , DRV_MODULE_NAME ) ;
if ( err ) {
dev_err ( dev , " Cannot obtain PCI resources \n " ) ;
goto err_disable_pdev ;
}
pci_set_master ( pdev ) ;
2017-08-18 17:58:07 +03:00
if ( ! no_msi ) {
irq = pci_alloc_irq_vectors ( pdev , 1 , 32 , PCI_IRQ_MSI ) ;
if ( irq < 0 )
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to get MSI interrupts \n " ) ;
2017-10-11 11:44:38 +03:00
test - > num_irqs = irq ;
2017-08-18 17:58:07 +03:00
}
2017-03-27 12:45:14 +03:00
err = devm_request_irq ( dev , pdev - > irq , pci_endpoint_test_irqhandler ,
IRQF_SHARED , DRV_MODULE_NAME , test ) ;
if ( err ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to request IRQ %d \n " , pdev - > irq ) ;
2017-03-27 12:45:14 +03:00
goto err_disable_msi ;
}
for ( i = 1 ; i < irq ; i + + ) {
2018-05-14 20:27:48 +03:00
err = devm_request_irq ( dev , pci_irq_vector ( pdev , i ) ,
2017-03-27 12:45:14 +03:00
pci_endpoint_test_irqhandler ,
IRQF_SHARED , DRV_MODULE_NAME , test ) ;
if ( err )
dev_err ( dev , " failed to request IRQ %d for MSI %d \n " ,
2018-05-14 20:27:48 +03:00
pci_irq_vector ( pdev , i ) , i + 1 ) ;
2017-03-27 12:45:14 +03:00
}
for ( bar = BAR_0 ; bar < = BAR_5 ; bar + + ) {
2018-03-28 14:50:17 +03:00
if ( pci_resource_flags ( pdev , bar ) & IORESOURCE_MEM ) {
base = pci_ioremap_bar ( pdev , bar ) ;
if ( ! base ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to read BAR%d \n " , bar ) ;
2018-03-28 14:50:17 +03:00
WARN_ON ( bar = = test_reg_bar ) ;
}
test - > bar [ bar ] = base ;
2017-03-27 12:45:14 +03:00
}
}
2017-08-18 17:58:05 +03:00
test - > base = test - > bar [ test_reg_bar ] ;
2017-03-27 12:45:14 +03:00
if ( ! test - > base ) {
2017-10-11 11:44:36 +03:00
err = - ENOMEM ;
2017-08-18 17:58:05 +03:00
dev_err ( dev , " Cannot perform PCI test without BAR%d \n " ,
test_reg_bar ) ;
2017-03-27 12:45:14 +03:00
goto err_iounmap ;
}
pci_set_drvdata ( pdev , test ) ;
id = ida_simple_get ( & pci_endpoint_test_ida , 0 , 0 , GFP_KERNEL ) ;
if ( id < 0 ) {
2017-10-11 11:44:36 +03:00
err = id ;
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Unable to get id \n " ) ;
2017-03-27 12:45:14 +03:00
goto err_iounmap ;
}
snprintf ( name , sizeof ( name ) , DRV_MODULE_NAME " .%d " , id ) ;
misc_device = & test - > miscdev ;
misc_device - > minor = MISC_DYNAMIC_MINOR ;
2017-10-11 11:44:37 +03:00
misc_device - > name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! misc_device - > name ) {
err = - ENOMEM ;
goto err_ida_remove ;
}
2017-03-27 12:45:14 +03:00
misc_device - > fops = & pci_endpoint_test_fops ,
err = misc_register ( misc_device ) ;
if ( err ) {
2018-05-14 19:56:23 +03:00
dev_err ( dev , " Failed to register device \n " ) ;
2017-10-11 11:44:37 +03:00
goto err_kfree_name ;
2017-03-27 12:45:14 +03:00
}
return 0 ;
2017-10-11 11:44:37 +03:00
err_kfree_name :
kfree ( misc_device - > name ) ;
2017-03-27 12:45:14 +03:00
err_ida_remove :
ida_simple_remove ( & pci_endpoint_test_ida , id ) ;
err_iounmap :
for ( bar = BAR_0 ; bar < = BAR_5 ; bar + + ) {
if ( test - > bar [ bar ] )
pci_iounmap ( pdev , test - > bar [ bar ] ) ;
}
2017-10-11 11:44:38 +03:00
for ( i = 0 ; i < irq ; i + + )
2018-05-14 20:27:48 +03:00
devm_free_irq ( & pdev - > dev , pci_irq_vector ( pdev , i ) , test ) ;
2017-10-11 11:44:38 +03:00
2017-03-27 12:45:14 +03:00
err_disable_msi :
pci_disable_msi ( pdev ) ;
pci_release_regions ( pdev ) ;
err_disable_pdev :
pci_disable_device ( pdev ) ;
return err ;
}
static void pci_endpoint_test_remove ( struct pci_dev * pdev )
{
int id ;
2017-10-11 11:44:38 +03:00
int i ;
2017-03-27 12:45:14 +03:00
enum pci_barno bar ;
struct pci_endpoint_test * test = pci_get_drvdata ( pdev ) ;
struct miscdevice * misc_device = & test - > miscdev ;
if ( sscanf ( misc_device - > name , DRV_MODULE_NAME " .%d " , & id ) ! = 1 )
return ;
2017-09-30 11:16:51 +03:00
if ( id < 0 )
return ;
2017-03-27 12:45:14 +03:00
misc_deregister ( & test - > miscdev ) ;
2017-10-11 11:44:37 +03:00
kfree ( misc_device - > name ) ;
2017-03-27 12:45:14 +03:00
ida_simple_remove ( & pci_endpoint_test_ida , id ) ;
for ( bar = BAR_0 ; bar < = BAR_5 ; bar + + ) {
if ( test - > bar [ bar ] )
pci_iounmap ( pdev , test - > bar [ bar ] ) ;
}
2017-10-11 11:44:38 +03:00
for ( i = 0 ; i < test - > num_irqs ; i + + )
2018-05-14 20:27:48 +03:00
devm_free_irq ( & pdev - > dev , pci_irq_vector ( pdev , i ) , test ) ;
2017-03-27 12:45:14 +03:00
pci_disable_msi ( pdev ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
}
static const struct pci_device_id pci_endpoint_test_tbl [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_TI , PCI_DEVICE_ID_TI_DRA74x ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_TI , PCI_DEVICE_ID_TI_DRA72x ) } ,
2018-05-15 17:41:44 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_SYNOPSYS , 0xedda ) } ,
2017-03-27 12:45:14 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( pci , pci_endpoint_test_tbl ) ;
static struct pci_driver pci_endpoint_test_driver = {
. name = DRV_MODULE_NAME ,
. id_table = pci_endpoint_test_tbl ,
. probe = pci_endpoint_test_probe ,
. remove = pci_endpoint_test_remove ,
} ;
module_pci_driver ( pci_endpoint_test_driver ) ;
MODULE_DESCRIPTION ( " PCI ENDPOINT TEST HOST DRIVER " ) ;
MODULE_AUTHOR ( " Kishon Vijay Abraham I <kishon@ti.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;