2020-03-13 17:17:06 +05:30
// SPDX-License-Identifier: GPL-2.0
/* Marvell OcteonTX CPT driver
*
* Copyright ( C ) 2019 Marvell International Ltd .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include "otx_cpt_common.h"
# include "otx_cptpf.h"
# define DRV_NAME "octeontx-cpt"
# define DRV_VERSION "1.0"
static void otx_cpt_disable_mbox_interrupts ( struct otx_cpt_device * cpt )
{
/* Disable mbox(0) interrupts for all VFs */
writeq ( ~ 0ull , cpt - > reg_base + OTX_CPT_PF_MBOX_ENA_W1CX ( 0 ) ) ;
}
static void otx_cpt_enable_mbox_interrupts ( struct otx_cpt_device * cpt )
{
/* Enable mbox(0) interrupts for all VFs */
writeq ( ~ 0ull , cpt - > reg_base + OTX_CPT_PF_MBOX_ENA_W1SX ( 0 ) ) ;
}
static irqreturn_t otx_cpt_mbx0_intr_handler ( int __always_unused irq ,
void * cpt )
{
otx_cpt_mbox_intr_handler ( cpt , 0 ) ;
return IRQ_HANDLED ;
}
static void otx_cpt_reset ( struct otx_cpt_device * cpt )
{
writeq ( 1 , cpt - > reg_base + OTX_CPT_PF_RESET ) ;
}
static void otx_cpt_find_max_enabled_cores ( struct otx_cpt_device * cpt )
{
union otx_cptx_pf_constants pf_cnsts = { 0 } ;
pf_cnsts . u = readq ( cpt - > reg_base + OTX_CPT_PF_CONSTANTS ) ;
cpt - > eng_grps . avail . max_se_cnt = pf_cnsts . s . se ;
cpt - > eng_grps . avail . max_ae_cnt = pf_cnsts . s . ae ;
}
static u32 otx_cpt_check_bist_status ( struct otx_cpt_device * cpt )
{
union otx_cptx_pf_bist_status bist_sts = { 0 } ;
bist_sts . u = readq ( cpt - > reg_base + OTX_CPT_PF_BIST_STATUS ) ;
return bist_sts . u ;
}
static u64 otx_cpt_check_exe_bist_status ( struct otx_cpt_device * cpt )
{
union otx_cptx_pf_exe_bist_status bist_sts = { 0 } ;
bist_sts . u = readq ( cpt - > reg_base + OTX_CPT_PF_EXE_BIST_STATUS ) ;
return bist_sts . u ;
}
static int otx_cpt_device_init ( struct otx_cpt_device * cpt )
{
struct device * dev = & cpt - > pdev - > dev ;
u16 sdevid ;
u64 bist ;
/* Reset the PF when probed first */
otx_cpt_reset ( cpt ) ;
mdelay ( 100 ) ;
pci_read_config_word ( cpt - > pdev , PCI_SUBSYSTEM_ID , & sdevid ) ;
/* Check BIST status */
bist = ( u64 ) otx_cpt_check_bist_status ( cpt ) ;
if ( bist ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " RAM BIST failed with code 0x%llx \n " , bist ) ;
2020-03-13 17:17:06 +05:30
return - ENODEV ;
}
bist = otx_cpt_check_exe_bist_status ( cpt ) ;
if ( bist ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Engine BIST failed with code 0x%llx \n " , bist ) ;
2020-03-13 17:17:06 +05:30
return - ENODEV ;
}
/* Get max enabled cores */
otx_cpt_find_max_enabled_cores ( cpt ) ;
if ( ( sdevid = = OTX_CPT_PCI_PF_SUBSYS_ID ) & &
( cpt - > eng_grps . avail . max_se_cnt = = 0 ) ) {
cpt - > pf_type = OTX_CPT_AE ;
} else if ( ( sdevid = = OTX_CPT_PCI_PF_SUBSYS_ID ) & &
( cpt - > eng_grps . avail . max_ae_cnt = = 0 ) ) {
cpt - > pf_type = OTX_CPT_SE ;
}
/* Get max VQs/VFs supported by the device */
cpt - > max_vfs = pci_sriov_get_totalvfs ( cpt - > pdev ) ;
/* Disable all cores */
otx_cpt_disable_all_cores ( cpt ) ;
return 0 ;
}
static int otx_cpt_register_interrupts ( struct otx_cpt_device * cpt )
{
struct device * dev = & cpt - > pdev - > dev ;
u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT ;
u32 num_vec = OTX_CPT_PF_MSIX_VECTORS ;
int ret ;
/* Enable MSI-X */
ret = pci_alloc_irq_vectors ( cpt - > pdev , num_vec , num_vec , PCI_IRQ_MSIX ) ;
if ( ret < 0 ) {
dev_err ( & cpt - > pdev - > dev ,
" Request for #%d msix vectors failed \n " ,
num_vec ) ;
return ret ;
}
/* Register mailbox interrupt handlers */
ret = request_irq ( pci_irq_vector ( cpt - > pdev ,
OTX_CPT_PF_INT_VEC_E_MBOXX ( mbox_int_idx , 0 ) ) ,
otx_cpt_mbx0_intr_handler , 0 , " CPT Mbox0 " , cpt ) ;
if ( ret ) {
dev_err ( dev , " Request irq failed \n " ) ;
pci_free_irq_vectors ( cpt - > pdev ) ;
return ret ;
}
/* Enable mailbox interrupt */
otx_cpt_enable_mbox_interrupts ( cpt ) ;
return 0 ;
}
static void otx_cpt_unregister_interrupts ( struct otx_cpt_device * cpt )
{
u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT ;
otx_cpt_disable_mbox_interrupts ( cpt ) ;
free_irq ( pci_irq_vector ( cpt - > pdev ,
OTX_CPT_PF_INT_VEC_E_MBOXX ( mbox_int_idx , 0 ) ) ,
cpt ) ;
pci_free_irq_vectors ( cpt - > pdev ) ;
}
static int otx_cpt_sriov_configure ( struct pci_dev * pdev , int numvfs )
{
struct otx_cpt_device * cpt = pci_get_drvdata ( pdev ) ;
int ret = 0 ;
if ( numvfs > cpt - > max_vfs )
numvfs = cpt - > max_vfs ;
if ( numvfs > 0 ) {
ret = otx_cpt_try_create_default_eng_grps ( cpt - > pdev ,
& cpt - > eng_grps ,
cpt - > pf_type ) ;
if ( ret )
return ret ;
cpt - > vfs_enabled = numvfs ;
ret = pci_enable_sriov ( pdev , numvfs ) ;
if ( ret ) {
cpt - > vfs_enabled = 0 ;
return ret ;
}
otx_cpt_set_eng_grps_is_rdonly ( & cpt - > eng_grps , true ) ;
try_module_get ( THIS_MODULE ) ;
ret = numvfs ;
} else {
pci_disable_sriov ( pdev ) ;
otx_cpt_set_eng_grps_is_rdonly ( & cpt - > eng_grps , false ) ;
module_put ( THIS_MODULE ) ;
cpt - > vfs_enabled = 0 ;
}
dev_notice ( & cpt - > pdev - > dev , " VFs enabled: %d \n " , ret ) ;
return ret ;
}
static int otx_cpt_probe ( struct pci_dev * pdev ,
const struct pci_device_id __always_unused * ent )
{
struct device * dev = & pdev - > dev ;
struct otx_cpt_device * cpt ;
int err ;
cpt = devm_kzalloc ( dev , sizeof ( * cpt ) , GFP_KERNEL ) ;
if ( ! cpt )
return - ENOMEM ;
pci_set_drvdata ( pdev , cpt ) ;
cpt - > pdev = pdev ;
err = pci_enable_device ( pdev ) ;
if ( err ) {
dev_err ( dev , " Failed to enable PCI device \n " ) ;
goto err_clear_drvdata ;
}
err = pci_request_regions ( pdev , DRV_NAME ) ;
if ( err ) {
dev_err ( dev , " PCI request regions failed 0x%x \n " , err ) ;
goto err_disable_device ;
}
2020-11-21 08:49:16 +01:00
err = dma_set_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 48 ) ) ;
2020-03-13 17:17:06 +05:30
if ( err ) {
2020-11-21 08:49:16 +01:00
dev_err ( dev , " Unable to get usable 48-bit DMA configuration \n " ) ;
2020-03-13 17:17:06 +05:30
goto err_release_regions ;
}
/* MAP PF's configuration registers */
cpt - > reg_base = pci_iomap ( pdev , OTX_CPT_PF_PCI_CFG_BAR , 0 ) ;
if ( ! cpt - > reg_base ) {
dev_err ( dev , " Cannot map config register space, aborting \n " ) ;
err = - ENOMEM ;
goto err_release_regions ;
}
/* CPT device HW initialization */
err = otx_cpt_device_init ( cpt ) ;
if ( err )
goto err_unmap_region ;
/* Register interrupts */
err = otx_cpt_register_interrupts ( cpt ) ;
if ( err )
goto err_unmap_region ;
/* Initialize engine groups */
err = otx_cpt_init_eng_grps ( pdev , & cpt - > eng_grps , cpt - > pf_type ) ;
if ( err )
goto err_unregister_interrupts ;
return 0 ;
err_unregister_interrupts :
otx_cpt_unregister_interrupts ( cpt ) ;
err_unmap_region :
pci_iounmap ( pdev , cpt - > reg_base ) ;
err_release_regions :
pci_release_regions ( pdev ) ;
err_disable_device :
pci_disable_device ( pdev ) ;
err_clear_drvdata :
pci_set_drvdata ( pdev , NULL ) ;
return err ;
}
static void otx_cpt_remove ( struct pci_dev * pdev )
{
struct otx_cpt_device * cpt = pci_get_drvdata ( pdev ) ;
if ( ! cpt )
return ;
/* Disable VFs */
pci_disable_sriov ( pdev ) ;
/* Cleanup engine groups */
otx_cpt_cleanup_eng_grps ( pdev , & cpt - > eng_grps ) ;
/* Disable CPT PF interrupts */
otx_cpt_unregister_interrupts ( cpt ) ;
/* Disengage SE and AE cores from all groups */
otx_cpt_disable_all_cores ( cpt ) ;
pci_iounmap ( pdev , cpt - > reg_base ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_set_drvdata ( pdev , NULL ) ;
}
/* Supported devices */
static const struct pci_device_id otx_cpt_id_table [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_CAVIUM , OTX_CPT_PCI_PF_DEVICE_ID ) } ,
{ 0 , } /* end of table */
} ;
static struct pci_driver otx_cpt_pci_driver = {
. name = DRV_NAME ,
. id_table = otx_cpt_id_table ,
. probe = otx_cpt_probe ,
. remove = otx_cpt_remove ,
. sriov_configure = otx_cpt_sriov_configure
} ;
module_pci_driver ( otx_cpt_pci_driver ) ;
MODULE_AUTHOR ( " Marvell International Ltd. " ) ;
MODULE_DESCRIPTION ( " Marvell OcteonTX CPT Physical Function Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_DEVICE_TABLE ( pci , otx_cpt_id_table ) ;