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 .
*/
2018-07-16 17:05:05 -05:00
# include <linux/aer.h>
2016-04-28 16:24:48 -06:00
# 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"
2016-04-28 16:24:48 -06:00
struct dpc_dev {
struct pcie_device * dev ;
2018-01-25 18:06:03 -06:00
u16 cap_pos ;
2018-01-26 15:46:38 -06:00
bool rp_extensions ;
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
} ;
2018-09-20 10:27:08 -06:00
static struct dpc_dev * to_dpc_dev ( struct pci_dev * dev )
{
struct device * device ;
device = pcie_port_find_device ( dev , PCIE_PORT_SERVICE_DPC ) ;
if ( ! device )
return NULL ;
return get_service_data ( to_pcie_device ( device ) ) ;
}
void pci_save_dpc_state ( struct pci_dev * dev )
{
struct dpc_dev * dpc ;
struct pci_cap_saved_state * save_state ;
u16 * cap ;
if ( ! pci_is_pcie ( dev ) )
return ;
dpc = to_dpc_dev ( dev ) ;
if ( ! dpc )
return ;
save_state = pci_find_saved_ext_cap ( dev , PCI_EXT_CAP_ID_DPC ) ;
if ( ! save_state )
return ;
cap = ( u16 * ) & save_state - > cap . data [ 0 ] ;
pci_read_config_word ( dev , dpc - > cap_pos + PCI_EXP_DPC_CTL , cap ) ;
}
void pci_restore_dpc_state ( struct pci_dev * dev )
{
struct dpc_dev * dpc ;
struct pci_cap_saved_state * save_state ;
u16 * cap ;
if ( ! pci_is_pcie ( dev ) )
return ;
dpc = to_dpc_dev ( dev ) ;
if ( ! dpc )
return ;
save_state = pci_find_saved_ext_cap ( dev , PCI_EXT_CAP_ID_DPC ) ;
if ( ! save_state )
return ;
cap = ( u16 * ) & save_state - > cap . data [ 0 ] ;
pci_write_config_word ( dev , dpc - > cap_pos + PCI_EXP_DPC_CTL , * cap ) ;
}
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 ;
}
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 ;
2018-06-20 15:38:27 -06:00
u16 cap ;
2018-05-17 16:44:20 -05:00
/*
* DPC disables the Link automatically in hardware , so it has
* already been reset by the time we get here .
*/
2018-09-20 10:27:08 -06:00
dpc = to_dpc_dev ( pdev ) ;
2018-05-17 16:44:20 -05:00
cap = dpc - > cap_pos ;
/*
* Wait until the Link is inactive , then clear DPC Trigger Status
* to allow the Port to leave DPC .
*/
2018-07-16 17:05:07 -05:00
pcie_wait_for_link ( pdev , false ) ;
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 ;
2017-08-19 17:07:20 +08:00
2018-01-25 18:06:03 -06:00
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_STATUS ,
2018-05-16 15:59:35 -05:00
PCI_EXP_DPC_STATUS_TRIGGER ) ;
2017-12-14 08:20:18 -07:00
2018-09-20 10:27:17 -06:00
if ( ! pcie_wait_for_link ( pdev , true ) )
return PCI_ERS_RESULT_DISCONNECT ;
2018-05-17 16:44:20 -05:00
return PCI_ERS_RESULT_RECOVERED ;
}
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
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:38 -06:00
for ( i = 0 ; i < ARRAY_SIZE ( rp_pio_error_string ) ; i + + ) {
2018-07-16 17:05:03 -05:00
if ( ( status & ~ mask ) & ( 1 < < i ) )
2018-01-30 12:12:38 -06:00
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 )
2018-07-16 17:05:03 -05:00
goto clear_status ;
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 )
2018-07-16 17:05:03 -05:00
goto clear_status ;
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
}
2018-07-16 17:05:03 -05:00
clear_status :
pci_write_config_dword ( pdev , cap + PCI_EXP_DPC_RP_PIO_STATUS , status ) ;
2017-08-19 17:07:20 +08:00
}
2019-02-11 15:02:59 +08:00
static int dpc_get_aer_uncorrect_severity ( struct pci_dev * dev ,
struct aer_err_info * info )
{
int pos = dev - > aer_cap ;
u32 status , mask , sev ;
pci_read_config_dword ( dev , pos + PCI_ERR_UNCOR_STATUS , & status ) ;
pci_read_config_dword ( dev , pos + PCI_ERR_UNCOR_MASK , & mask ) ;
status & = ~ mask ;
if ( ! status )
return 0 ;
pci_read_config_dword ( dev , pos + PCI_ERR_UNCOR_SEVER , & sev ) ;
status & = sev ;
if ( status )
info - > severity = AER_FATAL ;
else
info - > severity = AER_NONFATAL ;
return 1 ;
}
2018-07-16 17:05:06 -05:00
static irqreturn_t dpc_handler ( int irq , void * context )
2016-04-28 16:24:48 -06:00
{
2018-07-16 17:05:05 -05:00
struct aer_err_info info ;
2018-07-16 17:05:06 -05:00
struct dpc_dev * dpc = context ;
2016-04-28 16:24:48 -06:00
struct pci_dev * pdev = dpc - > dev - > port ;
2017-08-19 17:07:21 +08:00
struct device * dev = & dpc - > dev - > device ;
2018-06-20 15:38:27 -06:00
u16 cap = dpc - > cap_pos , status , source , reason , ext_reason ;
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 ) ;
2018-07-16 17:05:02 -05:00
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_SOURCE_ID , & source ) ;
2016-04-28 16:24:48 -06:00
2017-08-19 17:07:21 +08:00
dev_info ( dev , " DPC containment event, status:%#06x source:%#06x \n " ,
2018-07-16 17:05:02 -05:00
status , source ) ;
2016-04-28 16:24:48 -06:00
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 ;
2018-09-20 10:27:12 -06:00
dev_warn ( dev , " DPC %s detected \n " ,
2017-12-14 08:20:18 -07:00
( 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 " ) ;
2018-07-16 17:05:02 -05:00
2017-12-14 08:20:18 -07:00
/* 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 ) ;
2019-02-11 15:02:59 +08:00
else if ( reason = = 0 & &
dpc_get_aer_uncorrect_severity ( pdev , & info ) & &
aer_get_device_error_info ( pdev , & info ) ) {
2018-07-16 17:05:05 -05:00
aer_print_error ( pdev , & info ) ;
pci_cleanup_aer_uncorrect_error_status ( pdev ) ;
2019-02-11 15:02:59 +08:00
pci_aer_clear_fatal_status ( pdev ) ;
2018-07-16 17:05:05 -05:00
}
2017-12-14 08:20:18 -07:00
2018-07-16 17:05:02 -05:00
/* We configure DPC so it only triggers on ERR_FATAL */
2018-09-20 10:27:12 -06:00
pcie_do_recovery ( pdev , pci_channel_io_frozen , PCIE_PORT_SERVICE_DPC ) ;
2018-07-16 17:05:06 -05:00
return IRQ_HANDLED ;
2018-07-16 17:05:02 -05: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 ;
u16 cap = dpc - > cap_pos , status ;
pci_read_config_word ( pdev , cap + PCI_EXP_DPC_STATUS , & status ) ;
if ( ! ( status & PCI_EXP_DPC_STATUS_INTERRUPT ) | | status = = ( u16 ) ( ~ 0 ) )
return IRQ_NONE ;
2018-05-16 15:59:35 -05:00
pci_write_config_word ( pdev , cap + PCI_EXP_DPC_STATUS ,
PCI_EXP_DPC_STATUS_INTERRUPT ) ;
2018-06-20 15:38:27 -06:00
if ( status & PCI_EXP_DPC_STATUS_TRIGGER )
2018-07-16 17:05:06 -05:00
return IRQ_WAKE_THREAD ;
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 ;
set_service_data ( dev , dpc ) ;
2018-07-16 17:05:06 -05:00
status = devm_request_threaded_irq ( device , dev - > irq , dpc_irq ,
dpc_handler , IRQF_SHARED ,
" 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 ) ) ;
2018-09-20 10:27:08 -06:00
pci_add_ext_cap_save_buffer ( pdev , PCI_EXT_CAP_ID_DPC , sizeof ( u16 ) ) ;
2016-04-28 16:24:48 -06:00
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
} ;
2018-09-20 10:27:06 -06:00
int __init pcie_dpc_init ( void )
2016-04-28 16:24:48 -06:00
{
return pcie_port_service_register ( & dpcdriver ) ;
}