2018-01-26 14:12:23 -06:00
// SPDX-License-Identifier: GPL-2.0
2016-04-28 16:24:48 -06:00
/*
* PCI Express Downstream Port Containment services driver
2016-08-24 16:57:44 -04:00
* Author : Keith Busch < keith . busch @ intel . com >
*
2016-04-28 16:24:48 -06:00
* Copyright ( C ) 2016 Intel Corp .
*/
# include <linux/delay.h>
# include <linux/interrupt.h>
2016-08-24 16:57:44 -04:00
# include <linux/init.h>
2016-04-28 16:24:48 -06:00
# include <linux/pci.h>
2018-02-13 21:52:18 -06:00
2018-03-09 11:42:01 -06:00
# include "portdrv.h"
2017-03-29 22:48:59 -05:00
# include "../pci.h"
2018-01-24 17:03:18 -06:00
# include "aer/aerdrv.h"
2016-04-28 16:24:48 -06:00
struct dpc_dev {
struct pcie_device * dev ;
2016-06-06 16:06:08 +03:00
struct work_struct work ;
2018-01-25 18:06:03 -06:00
u16 cap_pos ;
2018-01-26 15:46:38 -06:00
bool rp_extensions ;
2017-08-19 17:07:20 +08:00
u32 rp_pio_status ;
2018-01-25 12:49:27 -06:00
u8 rp_log_size ;
2017-08-19 17:07:20 +08:00
} ;
static const char * const rp_pio_error_string [ ] = {
" Configuration Request received UR Completion " , /* Bit Position 0 */
" Configuration Request received CA Completion " , /* Bit Position 1 */
" Configuration Request Completion Timeout " , /* Bit Position 2 */
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
" I/O Request received UR Completion " , /* Bit Position 8 */
" I/O Request received CA Completion " , /* Bit Position 9 */
" I/O Request Completion Timeout " , /* Bit Position 10 */
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
" Memory Request received UR Completion " , /* Bit Position 16 */
" Memory Request received CA Completion " , /* Bit Position 17 */
" Memory Request Completion Timeout " , /* Bit Position 18 */
2016-04-28 16:24:48 -06:00
} ;
2017-02-03 16:46:13 -05:00
static int dpc_wait_rp_inactive ( struct dpc_dev * dpc )
{
unsigned long timeout = jiffies + HZ ;
struct pci_dev * pdev = dpc - > dev - > port ;
2017-08-19 17:07:21 +08:00
struct device * dev = & dpc - > dev - > device ;
2018-01-25 18:06:03 -06:00
u16 cap = dpc - > cap_pos , status ;
2017-02-03 16:46:13 -05:00
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_STATUS , & status ) ;
2017-02-03 16:46:13 -05:00
while ( status & PCI_EXP_DPC_RP_BUSY & &
! time_after ( jiffies , timeout ) ) {
msleep ( 10 ) ;
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_STATUS , & status ) ;
2017-02-03 16:46:13 -05:00
}
if ( status & PCI_EXP_DPC_RP_BUSY ) {
2017-08-19 17:07:21 +08:00
dev_warn ( dev , " DPC root port still busy \n " ) ;
2017-02-03 16:46:13 -05:00
return - EBUSY ;
}
return 0 ;
}
2017-08-19 17:07:21 +08:00
static void dpc_wait_link_inactive ( struct dpc_dev * dpc )
2016-04-28 16:24:48 -06:00
{
2017-08-19 17:07:21 +08:00
struct pci_dev * pdev = dpc - > dev - > port ;
2016-04-28 16:24:48 -06:00
2018-05-17 16:44:11 -05:00
pcie_wait_for_link ( pdev , false ) ;
2016-04-28 16:24:48 -06:00
}
2018-05-17 16:44:20 -05:00
static pci_ers_result_t dpc_reset_link ( struct pci_dev * pdev )
2016-04-28 16:24:48 -06:00
{
2018-05-17 16:44:20 -05:00
struct dpc_dev * dpc ;
struct pcie_device * pciedev ;
struct device * devdpc ;
u16 cap , ctl ;
/*
* DPC disables the Link automatically in hardware , so it has
* already been reset by the time we get here .
*/
devdpc = pcie_port_find_device ( pdev , PCIE_PORT_SERVICE_DPC ) ;
pciedev = to_pcie_device ( devdpc ) ;
dpc = get_service_data ( pciedev ) ;
cap = dpc - > cap_pos ;
/*
* Wait until the Link is inactive , then clear DPC Trigger Status
* to allow the Port to leave DPC .
*/
2017-08-19 17:07:21 +08:00
dpc_wait_link_inactive ( dpc ) ;
2018-05-17 16:44:20 -05:00
2018-01-26 15:46:38 -06:00
if ( dpc - > rp_extensions & & dpc_wait_rp_inactive ( dpc ) )
2018-05-17 16:44:20 -05:00
return PCI_ERS_RESULT_DISCONNECT ;
2018-01-26 15:46:38 -06:00
if ( dpc - > rp_extensions & & dpc - > rp_pio_status ) {
2018-01-25 18:06:03 -06:00
pci_write_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_STATUS ,
dpc - > rp_pio_status ) ;
2017-08-19 17:07:20 +08:00
dpc - > rp_pio_status = 0 ;
}
2018-01-25 18:06:03 -06:00
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_STATUS ,
2016-04-28 16:24:48 -06:00
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT ) ;
2017-12-14 08:20:18 -07:00
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_CTL , & ctl ) ;
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_CTL ,
2017-12-14 08:20:18 -07:00
ctl | PCI_EXP_DPC_CTL_INT_EN ) ;
2018-05-17 16:44:20 -05:00
return PCI_ERS_RESULT_RECOVERED ;
}
static void dpc_work ( struct work_struct * work )
{
struct dpc_dev * dpc = container_of ( work , struct dpc_dev , work ) ;
struct pci_dev * pdev = dpc - > dev - > port ;
/* We configure DPC so it only triggers on ERR_FATAL */
pcie_do_fatal_recovery ( pdev , PCIE_PORT_SERVICE_DPC ) ;
2016-04-28 16:24:48 -06:00
}
2018-01-30 12:12:53 -06:00
static void dpc_process_rp_pio_error ( struct dpc_dev * dpc )
2017-08-19 17:07:20 +08:00
{
2018-01-30 12:12:38 -06:00
struct device * dev = & dpc - > dev - > device ;
2017-08-19 17:07:20 +08:00
struct pci_dev * pdev = dpc - > dev - > port ;
2018-01-30 12:12:48 -06:00
u16 cap = dpc - > cap_pos , dpc_status , first_error ;
u32 status , mask , sev , syserr , exc , dw0 , dw1 , dw2 , dw3 , log , prefix ;
2017-08-19 17:07:20 +08:00
int i ;
2018-01-30 12:12:48 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_STATUS , & status ) ;
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_MASK , & mask ) ;
2018-01-30 12:12:38 -06:00
dev_err ( dev , " rp_pio_status: %#010x, rp_pio_mask: %#010x \n " ,
2018-01-30 12:12:48 -06:00
status , mask ) ;
2017-08-19 17:07:20 +08:00
2018-01-30 12:12:48 -06:00
dpc - > rp_pio_status = status ;
2018-01-30 12:12:43 -06:00
2018-01-30 12:12:48 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_SEVERITY , & sev ) ;
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_SYSERROR , & syserr ) ;
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_EXCEPTION , & exc ) ;
2018-01-30 12:12:38 -06:00
dev_err ( dev , " RP PIO severity=%#010x, syserror=%#010x, exception=%#010x \n " ,
2018-01-30 12:12:48 -06:00
sev , syserr , exc ) ;
2017-08-19 17:07:20 +08:00
/* Get First Error Pointer */
2018-01-30 12:12:27 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_STATUS , & dpc_status ) ;
2018-01-30 12:12:48 -06:00
first_error = ( dpc_status & 0x1f00 ) > > 8 ;
2017-08-19 17:07:20 +08:00
2018-01-30 12:12:48 -06:00
status & = ~ mask ;
2018-01-30 12:12:38 -06:00
for ( i = 0 ; i < ARRAY_SIZE ( rp_pio_error_string ) ; i + + ) {
if ( status & ( 1 < < i ) )
dev_err ( dev , " [%2d] %s%s \n " , i , rp_pio_error_string [ i ] ,
2018-01-30 12:12:48 -06:00
first_error = = i ? " (First) " : " " ) ;
2018-01-30 12:12:38 -06:00
}
2018-01-25 12:49:27 -06:00
if ( dpc - > rp_log_size < 4 )
2017-08-19 17:07:20 +08:00
return ;
2018-01-25 18:06:03 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG ,
2018-01-30 12:12:48 -06:00
& dw0 ) ;
2018-01-25 18:06:03 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4 ,
2018-01-30 12:12:48 -06:00
& dw1 ) ;
2018-01-25 18:06:03 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8 ,
2018-01-30 12:12:48 -06:00
& dw2 ) ;
2018-01-25 18:06:03 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12 ,
2018-01-30 12:12:48 -06:00
& dw3 ) ;
2018-01-30 12:12:38 -06:00
dev_err ( dev , " TLP Header: %#010x %#010x %#010x %#010x \n " ,
2018-01-30 12:12:48 -06:00
dw0 , dw1 , dw2 , dw3 ) ;
2017-08-19 17:07:20 +08:00
2018-01-30 12:12:33 -06:00
if ( dpc - > rp_log_size < 5 )
return ;
2018-01-30 12:12:48 -06:00
pci_read_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG , & log ) ;
dev_err ( dev , " RP PIO ImpSpec Log %#010x \n " , log ) ;
2018-01-30 12:12:33 -06:00
2018-01-30 12:12:38 -06:00
for ( i = 0 ; i < dpc - > rp_log_size - 5 ; i + + ) {
2017-08-19 17:07:20 +08:00
pci_read_config_dword ( pdev ,
2018-01-30 12:12:48 -06:00
cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG , & prefix ) ;
dev_err ( dev , " TLP Prefix Header: dw%d, %#010x \n " , i , prefix ) ;
2018-01-30 12:12:38 -06:00
}
2017-08-19 17:07:20 +08:00
}
2016-04-28 16:24:48 -06:00
static irqreturn_t dpc_irq ( int irq , void * context )
{
struct dpc_dev * dpc = ( struct dpc_dev * ) context ;
struct pci_dev * pdev = dpc - > dev - > port ;
2017-08-19 17:07:21 +08:00
struct device * dev = & dpc - > dev - > device ;
2018-01-25 18:06:03 -06:00
u16 cap = dpc - > cap_pos , ctl , status , source , reason , ext_reason ;
2017-12-14 08:20:18 -07:00
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_CTL , & ctl ) ;
2017-12-14 08:20:18 -07:00
if ( ! ( ctl & PCI_EXP_DPC_CTL_INT_EN ) | | ctl = = ( u16 ) ( ~ 0 ) )
return IRQ_NONE ;
2016-04-28 16:24:48 -06:00
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_STATUS , & status ) ;
2017-12-14 08:20:18 -07:00
if ( ! ( status & PCI_EXP_DPC_STATUS_INTERRUPT ) )
return IRQ_NONE ;
if ( ! ( status & PCI_EXP_DPC_STATUS_TRIGGER ) ) {
2018-01-25 18:06:03 -06:00
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_STATUS ,
2017-12-14 08:20:18 -07:00
PCI_EXP_DPC_STATUS_INTERRUPT ) ;
return IRQ_HANDLED ;
}
2018-01-25 18:06:03 -06:00
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_CTL ,
2017-12-14 08:20:18 -07:00
ctl & ~ PCI_EXP_DPC_CTL_INT_EN ) ;
2018-01-25 18:06:03 -06:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_SOURCE_ID ,
2016-04-28 16:24:48 -06:00
& source ) ;
2017-08-19 17:07:21 +08:00
dev_info ( dev , " DPC containment event, status:%#06x source:%#06x \n " ,
2016-04-28 16:24:48 -06:00
status , source ) ;
2018-01-16 17:37:50 -06:00
reason = ( status & PCI_EXP_DPC_STATUS_TRIGGER_RSN ) > > 1 ;
ext_reason = ( status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT ) > > 5 ;
2017-12-14 08:20:18 -07:00
dev_warn ( dev , " DPC %s detected, remove downstream devices \n " ,
( reason = = 0 ) ? " unmasked uncorrectable error " :
( reason = = 1 ) ? " ERR_NONFATAL " :
( reason = = 2 ) ? " ERR_FATAL " :
( ext_reason = = 0 ) ? " RP PIO error " :
( ext_reason = = 1 ) ? " software trigger " :
" reserved error " ) ;
/* show RP PIO error detail information */
2018-01-26 07:45:18 -06:00
if ( dpc - > rp_extensions & & reason = = 3 & & ext_reason = = 0 )
2017-12-14 08:20:18 -07:00
dpc_process_rp_pio_error ( dpc ) ;
schedule_work ( & dpc - > work ) ;
2016-04-28 16:24:48 -06:00
return IRQ_HANDLED ;
}
# define FLAG(x, y) (((x) & (y)) ? '+' : '-')
static int dpc_probe ( struct pcie_device * dev )
{
struct dpc_dev * dpc ;
struct pci_dev * pdev = dev - > port ;
2017-08-19 17:07:21 +08:00
struct device * device = & dev - > device ;
2016-04-28 16:24:48 -06:00
int status ;
u16 ctl , cap ;
2018-01-24 17:03:18 -06:00
if ( pcie_aer_get_firmware_first ( pdev ) )
return - ENOTSUPP ;
2017-08-19 17:07:21 +08:00
dpc = devm_kzalloc ( device , sizeof ( * dpc ) , GFP_KERNEL ) ;
2016-04-28 16:24:48 -06:00
if ( ! dpc )
return - ENOMEM ;
dpc - > cap_pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_DPC ) ;
dpc - > dev = dev ;
2018-01-26 07:18:56 -06:00
INIT_WORK ( & dpc - > work , dpc_work ) ;
2016-04-28 16:24:48 -06:00
set_service_data ( dev , dpc ) ;
2017-08-19 17:07:21 +08:00
status = devm_request_irq ( device , dev - > irq , dpc_irq , IRQF_SHARED ,
2016-06-06 16:06:07 +03:00
" pcie-dpc " , dpc ) ;
2016-04-28 16:24:48 -06:00
if ( status ) {
2017-08-19 17:07:21 +08:00
dev_warn ( device , " request IRQ%d failed: %d \n " , dev - > irq ,
2016-04-28 16:24:48 -06:00
status ) ;
2016-06-06 16:06:07 +03:00
return status ;
2016-04-28 16:24:48 -06:00
}
pci_read_config_word ( pdev , dpc - > cap_pos + PCI_EXP_DPC_CAP , & cap ) ;
pci_read_config_word ( pdev , dpc - > cap_pos + PCI_EXP_DPC_CTL , & ctl ) ;
2018-01-26 15:46:38 -06:00
dpc - > rp_extensions = ( cap & PCI_EXP_DPC_CAP_RP_EXT ) ;
2018-01-25 12:49:27 -06:00
if ( dpc - > rp_extensions ) {
dpc - > rp_log_size = ( cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE ) > > 8 ;
if ( dpc - > rp_log_size < 4 | | dpc - > rp_log_size > 9 ) {
dev_err ( device , " RP PIO log size %u is invalid \n " ,
dpc - > rp_log_size ) ;
dpc - > rp_log_size = 0 ;
}
}
2017-02-03 16:46:13 -05:00
2018-05-17 16:44:18 -05:00
ctl = ( ctl & 0xfff4 ) | PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN ;
2016-04-28 16:24:48 -06:00
pci_write_config_word ( pdev , dpc - > cap_pos + PCI_EXP_DPC_CTL , ctl ) ;
2017-08-19 17:07:21 +08:00
dev_info ( device , " DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c \n " ,
2018-01-16 22:22:05 -07:00
cap & PCI_EXP_DPC_IRQ , FLAG ( cap , PCI_EXP_DPC_CAP_RP_EXT ) ,
2016-04-28 16:24:48 -06:00
FLAG ( cap , PCI_EXP_DPC_CAP_POISONED_TLP ) ,
2018-01-25 12:49:27 -06:00
FLAG ( cap , PCI_EXP_DPC_CAP_SW_TRIGGER ) , dpc - > rp_log_size ,
2016-04-28 16:24:48 -06:00
FLAG ( cap , PCI_EXP_DPC_CAP_DL_ACTIVE ) ) ;
return status ;
}
static void dpc_remove ( struct pcie_device * dev )
{
struct dpc_dev * dpc = get_service_data ( dev ) ;
struct pci_dev * pdev = dev - > port ;
u16 ctl ;
pci_read_config_word ( pdev , dpc - > cap_pos + PCI_EXP_DPC_CTL , & ctl ) ;
2018-05-17 16:44:18 -05:00
ctl & = ~ ( PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN ) ;
2016-04-28 16:24:48 -06:00
pci_write_config_word ( pdev , dpc - > cap_pos + PCI_EXP_DPC_CTL , ctl ) ;
}
static struct pcie_port_service_driver dpcdriver = {
. name = " dpc " ,
2016-07-06 10:06:00 -06:00
. port_type = PCIE_ANY_PORT ,
2016-04-28 16:24:48 -06:00
. service = PCIE_PORT_SERVICE_DPC ,
. probe = dpc_probe ,
. remove = dpc_remove ,
2018-05-17 16:44:20 -05:00
. reset_link = dpc_reset_link ,
2016-04-28 16:24:48 -06:00
} ;
static int __init dpc_service_init ( void )
{
return pcie_port_service_register ( & dpcdriver ) ;
}
2016-08-24 16:57:44 -04:00
device_initcall ( dpc_service_init ) ;