2013-06-24 18:55:47 +03:00
/*
* linux / drivers / misc / xillybus_pcie . c
*
* Copyright 2011 Xillybus Ltd , http : //xillybus.com
*
* Driver for the Xillybus FPGA / host framework using PCI Express .
*
* This program is free software ; you can redistribute it and / or modify
* it under the smems of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*/
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/pci-aspm.h>
# include <linux/slab.h>
# include "xillybus.h"
MODULE_DESCRIPTION ( " Xillybus driver for PCIe " ) ;
MODULE_AUTHOR ( " Eli Billauer, Xillybus Ltd. " ) ;
MODULE_VERSION ( " 1.06 " ) ;
MODULE_ALIAS ( " xillybus_pcie " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
# define PCI_DEVICE_ID_XILLYBUS 0xebeb
# define PCI_VENDOR_ID_ALTERA 0x1172
# define PCI_VENDOR_ID_ACTEL 0x11aa
# define PCI_VENDOR_ID_LATTICE 0x1204
2013-07-27 00:24:00 +03:00
static const char xillyname [ ] = " xillybus_pcie " ;
2013-12-03 08:26:00 +09:00
static const struct pci_device_id xillyids [ ] = {
2013-06-24 18:55:47 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_XILINX , PCI_DEVICE_ID_XILLYBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ALTERA , PCI_DEVICE_ID_XILLYBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ACTEL , PCI_DEVICE_ID_XILLYBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_LATTICE , PCI_DEVICE_ID_XILLYBUS ) } ,
{ /* End: all zeroes */ }
} ;
static int xilly_pci_direction ( int direction )
{
switch ( direction ) {
case DMA_TO_DEVICE :
return PCI_DMA_TODEVICE ;
case DMA_FROM_DEVICE :
return PCI_DMA_FROMDEVICE ;
default :
return PCI_DMA_BIDIRECTIONAL ;
}
}
static void xilly_dma_sync_single_for_cpu_pci ( struct xilly_endpoint * ep ,
dma_addr_t dma_handle ,
size_t size ,
int direction )
{
pci_dma_sync_single_for_cpu ( ep - > pdev ,
dma_handle ,
size ,
xilly_pci_direction ( direction ) ) ;
}
static void xilly_dma_sync_single_for_device_pci ( struct xilly_endpoint * ep ,
dma_addr_t dma_handle ,
size_t size ,
int direction )
{
pci_dma_sync_single_for_device ( ep - > pdev ,
dma_handle ,
size ,
xilly_pci_direction ( direction ) ) ;
}
2014-06-21 14:07:12 +03:00
static void xilly_pci_unmap ( void * ptr )
{
struct xilly_mapping * data = ptr ;
pci_unmap_single ( data - > device , data - > dma_addr ,
data - > size , data - > direction ) ;
kfree ( ptr ) ;
}
2013-06-24 18:55:47 +03:00
/*
* Map either through the PCI DMA mapper or the non_PCI one . Behind the
* scenes exactly the same functions are called with the same parameters ,
* but that can change .
*/
2014-06-21 14:07:12 +03:00
static int xilly_map_single_pci ( struct xilly_endpoint * ep ,
void * ptr ,
size_t size ,
int direction ,
dma_addr_t * ret_dma_handle
2013-06-24 18:55:47 +03:00
)
{
int pci_direction ;
2014-06-21 14:07:12 +03:00
dma_addr_t addr ;
struct xilly_mapping * this ;
2013-06-24 18:55:47 +03:00
2014-06-21 14:07:12 +03:00
this = kzalloc ( sizeof ( * this ) , GFP_KERNEL ) ;
2013-06-24 18:55:47 +03:00
if ( ! this )
2014-06-21 14:07:12 +03:00
return - ENOMEM ;
2013-06-24 18:55:47 +03:00
pci_direction = xilly_pci_direction ( direction ) ;
2014-06-21 14:07:12 +03:00
2013-06-24 18:55:47 +03:00
addr = pci_map_single ( ep - > pdev , ptr , size , pci_direction ) ;
if ( pci_dma_mapping_error ( ep - > pdev , addr ) ) {
kfree ( this ) ;
2014-06-21 14:07:12 +03:00
return - ENODEV ;
2013-06-24 18:55:47 +03:00
}
2014-06-21 14:07:12 +03:00
this - > device = ep - > pdev ;
2013-06-24 18:55:47 +03:00
this - > dma_addr = addr ;
this - > size = size ;
2014-06-21 14:07:12 +03:00
this - > direction = pci_direction ;
2013-06-24 18:55:47 +03:00
2014-06-21 14:07:12 +03:00
* ret_dma_handle = addr ;
2013-06-24 18:55:47 +03:00
2016-04-30 17:13:20 +01:00
return devm_add_action_or_reset ( ep - > dev , xilly_pci_unmap , this ) ;
2013-06-24 18:55:47 +03:00
}
static struct xilly_endpoint_hardware pci_hw = {
. owner = THIS_MODULE ,
2013-07-31 11:22:43 +03:00
. hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci ,
. hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci ,
2013-06-24 18:55:47 +03:00
. map_single = xilly_map_single_pci ,
} ;
static int xilly_probe ( struct pci_dev * pdev ,
2014-09-04 17:47:56 +03:00
const struct pci_device_id * ent )
2013-06-24 18:55:47 +03:00
{
struct xilly_endpoint * endpoint ;
2014-09-04 17:47:48 +03:00
int rc ;
2013-06-24 18:55:47 +03:00
2013-10-19 01:02:25 +03:00
endpoint = xillybus_init_endpoint ( pdev , & pdev - > dev , & pci_hw ) ;
2013-06-24 18:55:47 +03:00
if ( ! endpoint )
return - ENOMEM ;
pci_set_drvdata ( pdev , endpoint ) ;
2014-05-11 19:53:46 +03:00
rc = pcim_enable_device ( pdev ) ;
2013-06-24 18:55:47 +03:00
if ( rc ) {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev ,
2014-05-11 19:53:46 +03:00
" pcim_enable_device() failed. Aborting. \n " ) ;
return rc ;
2013-06-24 18:55:47 +03:00
}
2014-05-11 19:53:46 +03:00
/* L0s has caused packet drops. No power saving, thank you. */
pci_disable_link_state ( pdev , PCIE_LINK_STATE_L0S ) ;
2013-06-24 18:55:47 +03:00
if ( ! ( pci_resource_flags ( pdev , 0 ) & IORESOURCE_MEM ) ) {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev ,
" Incorrect BAR configuration. Aborting. \n " ) ;
2014-05-11 19:53:46 +03:00
return - ENODEV ;
2013-06-24 18:55:47 +03:00
}
2014-05-11 19:53:46 +03:00
rc = pcim_iomap_regions ( pdev , 0x01 , xillyname ) ;
2013-06-24 18:55:47 +03:00
if ( rc ) {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev ,
2014-05-11 19:53:46 +03:00
" pcim_iomap_regions() failed. Aborting. \n " ) ;
return rc ;
2013-06-24 18:55:47 +03:00
}
2014-05-11 19:53:46 +03:00
endpoint - > registers = pcim_iomap_table ( pdev ) [ 0 ] ;
2013-06-24 18:55:47 +03:00
pci_set_master ( pdev ) ;
/* Set up a single MSI interrupt */
if ( pci_enable_msi ( pdev ) ) {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev ,
" Failed to enable MSI interrupts. Aborting. \n " ) ;
2014-05-11 19:53:46 +03:00
return - ENODEV ;
2013-06-24 18:55:47 +03:00
}
2014-05-11 19:53:46 +03:00
rc = devm_request_irq ( & pdev - > dev , pdev - > irq , xillybus_isr , 0 ,
xillyname , endpoint ) ;
2013-06-24 18:55:47 +03:00
if ( rc ) {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev ,
" Failed to register MSI handler. Aborting. \n " ) ;
2014-05-11 19:53:46 +03:00
return - ENODEV ;
2013-06-24 18:55:47 +03:00
}
/*
2015-08-05 13:03:26 +03:00
* Some ( old and buggy ? ) hardware drops 64 - bit addressed PCIe packets ,
* even when the PCIe driver claims that a 64 - bit mask is OK . On the
* other hand , on some architectures , 64 - bit addressing is mandatory .
* So go for the 64 - bit mask only when failing is the other option .
2013-06-24 18:55:47 +03:00
*/
2014-09-04 17:47:51 +03:00
if ( ! pci_set_dma_mask ( pdev , DMA_BIT_MASK ( 32 ) ) ) {
2013-06-24 18:55:47 +03:00
endpoint - > dma_using_dac = 0 ;
2015-08-05 13:03:26 +03:00
} else if ( ! pci_set_dma_mask ( pdev , DMA_BIT_MASK ( 64 ) ) ) {
endpoint - > dma_using_dac = 1 ;
2014-09-04 17:47:51 +03:00
} else {
2013-10-19 01:02:26 +03:00
dev_err ( endpoint - > dev , " Failed to set DMA mask. Aborting. \n " ) ;
2014-05-11 19:53:46 +03:00
return - ENODEV ;
2013-06-24 18:55:47 +03:00
}
2014-06-21 14:07:12 +03:00
return xillybus_endpoint_discovery ( endpoint ) ;
2013-06-24 18:55:47 +03:00
}
static void xilly_remove ( struct pci_dev * pdev )
{
struct xilly_endpoint * endpoint = pci_get_drvdata ( pdev ) ;
xillybus_endpoint_remove ( endpoint ) ;
}
MODULE_DEVICE_TABLE ( pci , xillyids ) ;
static struct pci_driver xillybus_driver = {
. name = xillyname ,
. id_table = xillyids ,
. probe = xilly_probe ,
. remove = xilly_remove ,
} ;
module_pci_driver ( xillybus_driver ) ;