2017-01-23 22:09:15 -08:00
/*
* aQuantia Corporation Network Driver
* Copyright ( C ) 2014 - 2017 aQuantia Corporation . All rights reserved
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*/
/* File aq_pci_func.c: Definition of PCI functions. */
2018-01-15 16:41:18 +03:00
# include <linux/interrupt.h>
# include <linux/module.h>
2018-01-19 17:03:21 +03:00
# include "aq_main.h"
2017-01-23 22:09:15 -08:00
# include "aq_nic.h"
# include "aq_vec.h"
# include "aq_hw.h"
2018-01-19 17:03:21 +03:00
# include "aq_pci_func.h"
2018-01-15 16:41:18 +03:00
# include "hw_atl/hw_atl_a0.h"
# include "hw_atl/hw_atl_b0.h"
2018-11-12 15:46:00 +00:00
# include "aq_filters.h"
2017-01-23 22:09:15 -08:00
2018-01-15 16:41:18 +03:00
static const struct pci_device_id aq_pci_tbl [ ] = {
2018-01-19 17:03:18 +03:00
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_0001 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_D100 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_D107 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_D108 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_D109 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC100 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC107 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC108 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC109 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC111 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC112 ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC100S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC107S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC108S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC109S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC111S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC112S ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC111E ) , } ,
{ PCI_VDEVICE ( AQUANTIA , AQ_DEVICE_ID_AQC112E ) , } ,
2018-01-15 16:41:18 +03:00
{ }
} ;
2018-01-23 02:10:38 +00:00
static const struct aq_board_revision_s hw_atl_boards [ ] = {
2018-01-19 17:03:19 +03:00
{ AQ_DEVICE_ID_0001 , AQ_HWREV_1 , & hw_atl_ops_a0 , & hw_atl_a0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_D100 , AQ_HWREV_1 , & hw_atl_ops_a0 , & hw_atl_a0_caps_aqc100 , } ,
{ AQ_DEVICE_ID_D107 , AQ_HWREV_1 , & hw_atl_ops_a0 , & hw_atl_a0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_D108 , AQ_HWREV_1 , & hw_atl_ops_a0 , & hw_atl_a0_caps_aqc108 , } ,
{ AQ_DEVICE_ID_D109 , AQ_HWREV_1 , & hw_atl_ops_a0 , & hw_atl_a0_caps_aqc109 , } ,
{ AQ_DEVICE_ID_0001 , AQ_HWREV_2 , & hw_atl_ops_b0 , & hw_atl_b0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_D100 , AQ_HWREV_2 , & hw_atl_ops_b0 , & hw_atl_b0_caps_aqc100 , } ,
{ AQ_DEVICE_ID_D107 , AQ_HWREV_2 , & hw_atl_ops_b0 , & hw_atl_b0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_D108 , AQ_HWREV_2 , & hw_atl_ops_b0 , & hw_atl_b0_caps_aqc108 , } ,
{ AQ_DEVICE_ID_D109 , AQ_HWREV_2 , & hw_atl_ops_b0 , & hw_atl_b0_caps_aqc109 , } ,
{ AQ_DEVICE_ID_AQC100 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_AQC107 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc107 , } ,
{ AQ_DEVICE_ID_AQC108 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc108 , } ,
{ AQ_DEVICE_ID_AQC109 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc109 , } ,
{ AQ_DEVICE_ID_AQC111 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc111 , } ,
{ AQ_DEVICE_ID_AQC112 , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc112 , } ,
{ AQ_DEVICE_ID_AQC100S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc100s , } ,
{ AQ_DEVICE_ID_AQC107S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc107s , } ,
{ AQ_DEVICE_ID_AQC108S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc108s , } ,
{ AQ_DEVICE_ID_AQC109S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc109s , } ,
{ AQ_DEVICE_ID_AQC111S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc111s , } ,
{ AQ_DEVICE_ID_AQC112S , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc112s , } ,
{ AQ_DEVICE_ID_AQC111E , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc111e , } ,
{ AQ_DEVICE_ID_AQC112E , AQ_HWREV_ANY , & hw_atl_ops_b1 , & hw_atl_b0_caps_aqc112e , } ,
} ;
2018-01-15 16:41:18 +03:00
MODULE_DEVICE_TABLE ( pci , aq_pci_tbl ) ;
2018-01-19 17:03:19 +03:00
static int aq_pci_probe_get_hw_by_id ( struct pci_dev * pdev ,
const struct aq_hw_ops * * ops ,
const struct aq_hw_caps_s * * caps )
2018-01-15 16:41:18 +03:00
{
2018-10-08 14:35:58 +01:00
int i ;
2018-01-19 17:03:19 +03:00
if ( pdev - > vendor ! = PCI_VENDOR_ID_AQUANTIA )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( hw_atl_boards ) ; i + + ) {
if ( hw_atl_boards [ i ] . devid = = pdev - > device & &
( hw_atl_boards [ i ] . revision = = AQ_HWREV_ANY | |
hw_atl_boards [ i ] . revision = = pdev - > revision ) ) {
* ops = hw_atl_boards [ i ] . ops ;
* caps = hw_atl_boards [ i ] . caps ;
break ;
}
}
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:19 +03:00
if ( i = = ARRAY_SIZE ( hw_atl_boards ) )
return - EINVAL ;
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:19 +03:00
return 0 ;
2018-01-15 16:41:18 +03:00
}
2018-01-19 17:03:21 +03:00
int aq_pci_func_init ( struct pci_dev * pdev )
2017-01-23 22:09:15 -08:00
{
2018-10-08 14:35:58 +01:00
int err ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
err = pci_set_dma_mask ( pdev , DMA_BIT_MASK ( 64 ) ) ;
2017-01-23 22:09:15 -08:00
if ( ! err ) {
2018-01-19 17:03:21 +03:00
err = pci_set_consistent_dma_mask ( pdev , DMA_BIT_MASK ( 64 ) ) ;
2017-01-23 22:09:15 -08:00
}
if ( err ) {
2018-01-19 17:03:21 +03:00
err = pci_set_dma_mask ( pdev , DMA_BIT_MASK ( 32 ) ) ;
2017-01-23 22:09:15 -08:00
if ( ! err )
2018-01-19 17:03:21 +03:00
err = pci_set_consistent_dma_mask ( pdev ,
2017-01-23 22:09:15 -08:00
DMA_BIT_MASK ( 32 ) ) ;
}
if ( err ! = 0 ) {
err = - ENOSR ;
goto err_exit ;
}
2018-01-19 17:03:21 +03:00
err = pci_request_regions ( pdev , AQ_CFG_DRV_NAME " _mmio " ) ;
2017-01-23 22:09:15 -08:00
if ( err < 0 )
goto err_exit ;
2018-01-19 17:03:21 +03:00
pci_set_master ( pdev ) ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
return 0 ;
2017-01-23 22:09:15 -08:00
err_exit :
return err ;
}
2018-01-19 17:03:21 +03:00
int aq_pci_func_alloc_irq ( struct aq_nic_s * self , unsigned int i ,
2017-01-23 22:09:15 -08:00
char * name , void * aq_vec , cpumask_t * affinity_mask )
{
2017-02-15 08:38:47 +01:00
struct pci_dev * pdev = self - > pdev ;
2018-10-08 14:35:58 +01:00
int err ;
2017-01-23 22:09:15 -08:00
2017-02-15 08:38:47 +01:00
if ( pdev - > msix_enabled | | pdev - > msi_enabled )
err = request_irq ( pci_irq_vector ( pdev , i ) , aq_vec_isr , 0 ,
2017-01-23 22:09:15 -08:00
name , aq_vec ) ;
2017-02-15 08:38:47 +01:00
else
err = request_irq ( pci_irq_vector ( pdev , i ) , aq_vec_isr_legacy ,
2017-01-23 22:09:15 -08:00
IRQF_SHARED , name , aq_vec ) ;
if ( err > = 0 ) {
self - > msix_entry_mask | = ( 1 < < i ) ;
self - > aq_vec [ i ] = aq_vec ;
2017-02-15 08:38:47 +01:00
if ( pdev - > msix_enabled )
irq_set_affinity_hint ( pci_irq_vector ( pdev , i ) ,
2017-01-23 22:09:15 -08:00
affinity_mask ) ;
}
return err ;
}
2018-01-19 17:03:21 +03:00
void aq_pci_func_free_irqs ( struct aq_nic_s * self )
2017-01-23 22:09:15 -08:00
{
2017-02-15 08:38:47 +01:00
struct pci_dev * pdev = self - > pdev ;
2018-10-08 14:35:58 +01:00
unsigned int i ;
2017-01-23 22:09:15 -08:00
for ( i = 32U ; i - - ; ) {
if ( ! ( ( 1U < < i ) & self - > msix_entry_mask ) )
continue ;
2019-02-27 12:10:07 +00:00
if ( i > = AQ_CFG_VECS_MAX )
continue ;
2017-01-23 22:09:15 -08:00
2017-02-15 08:38:47 +01:00
if ( pdev - > msix_enabled )
irq_set_affinity_hint ( pci_irq_vector ( pdev , i ) , NULL ) ;
2017-03-09 13:28:33 -05:00
free_irq ( pci_irq_vector ( pdev , i ) , self - > aq_vec [ i ] ) ;
2017-01-23 22:09:15 -08:00
self - > msix_entry_mask & = ~ ( 1U < < i ) ;
}
}
2018-01-19 17:03:21 +03:00
unsigned int aq_pci_func_get_irq_type ( struct aq_nic_s * self )
2017-01-23 22:09:15 -08:00
{
2017-02-15 08:38:47 +01:00
if ( self - > pdev - > msix_enabled )
return AQ_HW_IRQ_MSIX ;
if ( self - > pdev - > msi_enabled )
return AQ_HW_IRQ_MSIX ;
return AQ_HW_IRQ_LEGACY ;
2017-01-23 22:09:15 -08:00
}
2018-01-19 17:03:21 +03:00
static void aq_pci_free_irq_vectors ( struct aq_nic_s * self )
2017-01-23 22:09:15 -08:00
{
2017-02-15 08:38:47 +01:00
pci_free_irq_vectors ( self - > pdev ) ;
2018-01-19 17:03:21 +03:00
}
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
static int aq_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * pci_id )
{
2018-10-08 14:35:58 +01:00
struct aq_nic_s * self ;
int err ;
2018-01-19 17:03:21 +03:00
struct net_device * ndev ;
resource_size_t mmio_pa ;
u32 bar ;
u32 numvecs ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
err = pci_enable_device ( pdev ) ;
if ( err )
return err ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
err = aq_pci_func_init ( pdev ) ;
if ( err )
goto err_pci_func ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
ndev = aq_ndev_alloc ( ) ;
2018-01-23 02:10:46 +00:00
if ( ! ndev ) {
err = - ENOMEM ;
2018-01-19 17:03:21 +03:00
goto err_ndev ;
2018-01-23 02:10:46 +00:00
}
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
self = netdev_priv ( ndev ) ;
self - > pdev = pdev ;
SET_NETDEV_DEV ( ndev , & pdev - > dev ) ;
pci_set_drvdata ( pdev , self ) ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
err = aq_pci_probe_get_hw_by_id ( pdev , & self - > aq_hw_ops ,
& aq_nic_get_cfg ( self ) - > aq_hw_caps ) ;
if ( err )
goto err_ioremap ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
self - > aq_hw = kzalloc ( sizeof ( * self - > aq_hw ) , GFP_KERNEL ) ;
2018-02-22 12:11:55 +03:00
if ( ! self - > aq_hw ) {
err = - ENOMEM ;
goto err_ioremap ;
}
2018-01-19 17:03:21 +03:00
self - > aq_hw - > aq_nic_cfg = aq_nic_get_cfg ( self ) ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
for ( bar = 0 ; bar < 4 ; + + bar ) {
if ( IORESOURCE_MEM & pci_resource_flags ( pdev , bar ) ) {
resource_size_t reg_sz ;
2017-10-19 18:23:57 +03:00
2018-01-19 17:03:21 +03:00
mmio_pa = pci_resource_start ( pdev , bar ) ;
if ( mmio_pa = = 0U ) {
err = - EIO ;
2018-02-22 12:11:55 +03:00
goto err_free_aq_hw ;
2018-01-19 17:03:21 +03:00
}
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
reg_sz = pci_resource_len ( pdev , bar ) ;
if ( ( reg_sz < = 24 /*ATL_REGS_SIZE*/ ) ) {
err = - EIO ;
2018-02-22 12:11:55 +03:00
goto err_free_aq_hw ;
2018-01-19 17:03:21 +03:00
}
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
self - > aq_hw - > mmio = ioremap_nocache ( mmio_pa , reg_sz ) ;
if ( ! self - > aq_hw - > mmio ) {
err = - EIO ;
2018-02-22 12:11:55 +03:00
goto err_free_aq_hw ;
2018-01-19 17:03:21 +03:00
}
break ;
}
}
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
if ( bar = = 4 ) {
err = - EIO ;
2018-02-22 12:11:55 +03:00
goto err_free_aq_hw ;
2017-01-23 22:09:15 -08:00
}
2018-01-19 17:03:21 +03:00
numvecs = min ( ( u8 ) AQ_CFG_VECS_DEF ,
aq_nic_get_cfg ( self ) - > aq_hw_caps - > msix_irqs ) ;
numvecs = min ( numvecs , num_online_cpus ( ) ) ;
/*enable interrupts */
# if !AQ_CFG_FORCE_LEGACY_INT
2018-06-07 17:54:37 -04:00
err = pci_alloc_irq_vectors ( self - > pdev , 1 , numvecs ,
PCI_IRQ_MSIX | PCI_IRQ_MSI |
PCI_IRQ_LEGACY ) ;
2018-05-07 16:10:39 +03:00
2018-06-07 17:54:37 -04:00
if ( err < 0 )
2018-05-07 16:10:39 +03:00
goto err_hwinit ;
2018-06-07 17:54:37 -04:00
numvecs = err ;
2018-01-19 17:03:21 +03:00
# endif
2018-05-07 16:10:39 +03:00
self - > irqvecs = numvecs ;
2017-01-23 22:09:15 -08:00
2018-01-19 17:03:21 +03:00
/* net device init */
aq_nic_cfg_start ( self ) ;
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:21 +03:00
aq_nic_ndev_init ( self ) ;
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:21 +03:00
err = aq_nic_ndev_register ( self ) ;
2018-01-15 16:41:18 +03:00
if ( err < 0 )
2018-01-19 17:03:21 +03:00
goto err_register ;
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:21 +03:00
return 0 ;
err_register :
aq_nic_free_vectors ( self ) ;
aq_pci_free_irq_vectors ( self ) ;
err_hwinit :
iounmap ( self - > aq_hw - > mmio ) ;
2018-02-22 12:11:55 +03:00
err_free_aq_hw :
kfree ( self - > aq_hw ) ;
2018-01-19 17:03:21 +03:00
err_ioremap :
free_netdev ( ndev ) ;
err_ndev :
2018-05-07 16:10:39 +03:00
pci_release_regions ( pdev ) ;
err_pci_func :
2018-01-19 17:03:21 +03:00
pci_disable_device ( pdev ) ;
2018-01-15 16:41:18 +03:00
return err ;
}
static void aq_pci_remove ( struct pci_dev * pdev )
{
2018-01-19 17:03:21 +03:00
struct aq_nic_s * self = pci_get_drvdata ( pdev ) ;
if ( self - > ndev ) {
2018-11-12 15:46:00 +00:00
aq_clear_rxnfc_all_rules ( self ) ;
2018-01-19 17:03:21 +03:00
if ( self - > ndev - > reg_state = = NETREG_REGISTERED )
unregister_netdev ( self - > ndev ) ;
aq_nic_free_vectors ( self ) ;
aq_pci_free_irq_vectors ( self ) ;
iounmap ( self - > aq_hw - > mmio ) ;
kfree ( self - > aq_hw ) ;
pci_release_regions ( pdev ) ;
free_netdev ( self - > ndev ) ;
}
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:21 +03:00
pci_disable_device ( pdev ) ;
2018-01-15 16:41:18 +03:00
}
2018-03-20 14:40:36 +03:00
static void aq_pci_shutdown ( struct pci_dev * pdev )
{
struct aq_nic_s * self = pci_get_drvdata ( pdev ) ;
aq_nic_shutdown ( self ) ;
pci_disable_device ( pdev ) ;
if ( system_state = = SYSTEM_POWER_OFF ) {
pci_wake_from_d3 ( pdev , false ) ;
pci_set_power_state ( pdev , PCI_D3hot ) ;
}
}
2018-01-15 16:41:18 +03:00
static int aq_pci_suspend ( struct pci_dev * pdev , pm_message_t pm_msg )
{
2018-01-19 17:03:21 +03:00
struct aq_nic_s * self = pci_get_drvdata ( pdev ) ;
2018-01-15 16:41:18 +03:00
2018-01-19 17:03:21 +03:00
return aq_nic_change_pm_state ( self , & pm_msg ) ;
2018-01-15 16:41:18 +03:00
}
static int aq_pci_resume ( struct pci_dev * pdev )
{
2018-01-19 17:03:21 +03:00
struct aq_nic_s * self = pci_get_drvdata ( pdev ) ;
2018-01-15 16:41:18 +03:00
pm_message_t pm_msg = PMSG_RESTORE ;
2018-01-19 17:03:21 +03:00
return aq_nic_change_pm_state ( self , & pm_msg ) ;
2018-01-15 16:41:18 +03:00
}
static struct pci_driver aq_pci_ops = {
. name = AQ_CFG_DRV_NAME ,
. id_table = aq_pci_tbl ,
. probe = aq_pci_probe ,
. remove = aq_pci_remove ,
. suspend = aq_pci_suspend ,
. resume = aq_pci_resume ,
2018-03-20 14:40:36 +03:00
. shutdown = aq_pci_shutdown ,
2018-01-15 16:41:18 +03:00
} ;
module_pci_driver ( aq_pci_ops ) ;