2007-07-19 12:49:47 +04:00
/*
2007-07-19 12:49:33 +04:00
* ( C ) 2005 , 2006 Linux Networx ( http : //lnxi.com)
* This file may be distributed under the terms of the
* GNU General Public License .
*
* Written Doug Thompson < norsk5 @ xmission . com >
*
*/
# include <linux/module.h>
# include <linux/sysdev.h>
# include <linux/ctype.h>
2007-07-19 12:49:47 +04:00
# include "edac_core.h"
2007-07-19 12:49:33 +04:00
# include "edac_module.h"
2007-07-26 21:41:15 +04:00
/* Turn off this whole feature if PCI is not configured */
2007-07-19 12:49:33 +04:00
# ifdef CONFIG_PCI
2007-07-19 12:49:52 +04:00
# define EDAC_PCI_SYMLINK "device"
2007-07-26 21:41:15 +04:00
/* data variables exported via sysfs */
static int check_pci_errors ; /* default NO check PCI parity */
static int edac_pci_panic_on_pe ; /* default NO panic on PCI Parity */
static int edac_pci_log_pe = 1 ; /* log PCI parity errors */
2007-07-19 12:49:54 +04:00
static int edac_pci_log_npe = 1 ; /* log PCI non-parity error errors */
2007-07-26 21:41:15 +04:00
static int edac_pci_poll_msec = 1000 ; /* one second workq period */
2007-07-19 12:49:33 +04:00
static atomic_t pci_parity_count = ATOMIC_INIT ( 0 ) ;
2007-07-19 12:49:52 +04:00
static atomic_t pci_nonparity_count = ATOMIC_INIT ( 0 ) ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
static struct kobject edac_pci_top_main_kobj ;
2007-07-19 12:49:52 +04:00
static atomic_t edac_pci_sysfs_refcount = ATOMIC_INIT ( 0 ) ;
2007-07-26 21:41:15 +04:00
/* getter functions for the data variables */
2007-07-19 12:49:54 +04:00
int edac_pci_get_check_errors ( void )
{
return check_pci_errors ;
}
int edac_pci_get_log_pe ( void )
{
return edac_pci_log_pe ;
}
int edac_pci_get_log_npe ( void )
{
return edac_pci_log_npe ;
}
int edac_pci_get_panic_on_pe ( void )
{
return edac_pci_panic_on_pe ;
}
int edac_pci_get_poll_msec ( void )
{
return edac_pci_poll_msec ;
}
2007-07-19 12:49:52 +04:00
/**************************** EDAC PCI sysfs instance *******************/
static ssize_t instance_pe_count_show ( struct edac_pci_ctl_info * pci , char * data )
{
2007-07-19 12:49:58 +04:00
return sprintf ( data , " %u \n " , atomic_read ( & pci - > counters . pe_count ) ) ;
2007-07-19 12:49:52 +04:00
}
static ssize_t instance_npe_count_show ( struct edac_pci_ctl_info * pci ,
2007-07-19 12:50:13 +04:00
char * data )
2007-07-19 12:49:52 +04:00
{
2007-07-19 12:49:58 +04:00
return sprintf ( data , " %u \n " , atomic_read ( & pci - > counters . npe_count ) ) ;
2007-07-19 12:49:52 +04:00
}
# define to_instance(k) container_of(k, struct edac_pci_ctl_info, kobj)
# define to_instance_attr(a) container_of(a, struct instance_attribute, attr)
/* DEVICE instance kobject release() function */
static void edac_pci_instance_release ( struct kobject * kobj )
{
struct edac_pci_ctl_info * pci ;
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() \n " , __func__ ) ;
2007-07-19 12:49:52 +04:00
2007-07-26 21:41:15 +04:00
/* Form pointer to containing struct, the pci control struct */
2007-07-19 12:49:52 +04:00
pci = to_instance ( kobj ) ;
2007-07-26 21:41:15 +04:00
/* decrement reference count on top main kobj */
kobject_put ( & edac_pci_top_main_kobj ) ;
kfree ( pci ) ; /* Free the control struct */
2007-07-19 12:49:52 +04:00
}
/* instance specific attribute structure */
struct instance_attribute {
2007-07-19 12:49:58 +04:00
struct attribute attr ;
2007-07-26 21:41:15 +04:00
ssize_t ( * show ) ( struct edac_pci_ctl_info * , char * ) ;
ssize_t ( * store ) ( struct edac_pci_ctl_info * , const char * , size_t ) ;
2007-07-19 12:49:52 +04:00
} ;
/* Function to 'show' fields from the edac_pci 'instance' structure */
static ssize_t edac_pci_instance_show ( struct kobject * kobj ,
2007-07-19 12:50:13 +04:00
struct attribute * attr , char * buffer )
2007-07-19 12:49:52 +04:00
{
2007-07-19 12:49:58 +04:00
struct edac_pci_ctl_info * pci = to_instance ( kobj ) ;
struct instance_attribute * instance_attr = to_instance_attr ( attr ) ;
2007-07-19 12:49:52 +04:00
2007-07-19 12:49:58 +04:00
if ( instance_attr - > show )
return instance_attr - > show ( pci , buffer ) ;
return - EIO ;
2007-07-19 12:49:52 +04:00
}
/* Function to 'store' fields into the edac_pci 'instance' structure */
static ssize_t edac_pci_instance_store ( struct kobject * kobj ,
2007-07-19 12:50:13 +04:00
struct attribute * attr ,
const char * buffer , size_t count )
2007-07-19 12:49:52 +04:00
{
2007-07-19 12:49:58 +04:00
struct edac_pci_ctl_info * pci = to_instance ( kobj ) ;
struct instance_attribute * instance_attr = to_instance_attr ( attr ) ;
2007-07-19 12:49:52 +04:00
2007-07-19 12:49:58 +04:00
if ( instance_attr - > store )
return instance_attr - > store ( pci , buffer , count ) ;
return - EIO ;
2007-07-19 12:49:52 +04:00
}
2007-07-26 21:41:15 +04:00
/* fs_ops table */
2007-07-19 12:49:52 +04:00
static struct sysfs_ops pci_instance_ops = {
. show = edac_pci_instance_show ,
. store = edac_pci_instance_store
} ;
# define INSTANCE_ATTR(_name, _mode, _show, _store) \
static struct instance_attribute attr_instance_ # # _name = { \
. attr = { . name = __stringify ( _name ) , . mode = _mode } , \
. show = _show , \
. store = _store , \
} ;
INSTANCE_ATTR ( pe_count , S_IRUGO , instance_pe_count_show , NULL ) ;
INSTANCE_ATTR ( npe_count , S_IRUGO , instance_npe_count_show , NULL ) ;
/* pci instance attributes */
static struct instance_attribute * pci_instance_attr [ ] = {
& attr_instance_pe_count ,
& attr_instance_npe_count ,
NULL
} ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* the ktype for a pci instance */
2007-07-19 12:49:52 +04:00
static struct kobj_type ktype_pci_instance = {
. release = edac_pci_instance_release ,
. sysfs_ops = & pci_instance_ops ,
. default_attrs = ( struct attribute * * ) pci_instance_attr ,
} ;
2007-07-26 21:41:15 +04:00
/*
* edac_pci_create_instance_kobj
*
* construct one EDAC PCI instance ' s kobject for use
*/
2007-07-19 12:49:52 +04:00
static int edac_pci_create_instance_kobj ( struct edac_pci_ctl_info * pci , int idx )
{
2007-07-26 21:41:15 +04:00
struct kobject * main_kobj ;
2007-07-19 12:49:52 +04:00
int err ;
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() \n " , __func__ ) ;
/* First bump the ref count on the top main kobj, which will
* track the number of PCI instances we have , and thus nest
* properly on keeping the module loaded
*/
main_kobj = kobject_get ( & edac_pci_top_main_kobj ) ;
if ( ! main_kobj ) {
err = - ENODEV ;
goto error_out ;
}
/* And now register this new kobject under the main kobj */
2007-12-17 22:54:39 +03:00
err = kobject_init_and_add ( & pci - > kobj , & ktype_pci_instance ,
& edac_pci_top_main_kobj , " pci%d " , idx ) ;
2007-07-19 12:49:52 +04:00
if ( err ! = 0 ) {
debugf2 ( " %s() failed to register instance pci%d \n " ,
2007-07-19 12:49:58 +04:00
__func__ , idx ) ;
2007-07-26 21:41:15 +04:00
kobject_put ( & edac_pci_top_main_kobj ) ;
goto error_out ;
2007-07-19 12:49:52 +04:00
}
2007-12-17 22:54:39 +03:00
kobject_uevent ( & pci - > kobj , KOBJ_ADD ) ;
2007-07-19 12:49:52 +04:00
debugf1 ( " %s() Register instance 'pci%d' kobject \n " , __func__ , idx ) ;
return 0 ;
2007-07-26 21:41:15 +04:00
/* Error unwind statck */
error_out :
return err ;
2007-07-19 12:49:52 +04:00
}
2007-07-26 21:41:15 +04:00
/*
* edac_pci_unregister_sysfs_instance_kobj
*
* unregister the kobj for the EDAC PCI instance
*/
void edac_pci_unregister_sysfs_instance_kobj ( struct edac_pci_ctl_info * pci )
2007-07-19 12:49:52 +04:00
{
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() \n " , __func__ ) ;
/* Unregister the instance kobject and allow its release
* function release the main reference count and then
* kfree the memory
*/
2007-12-20 19:13:05 +03:00
kobject_put ( & pci - > kobj ) ;
2007-07-19 12:49:52 +04:00
}
/***************************** EDAC PCI sysfs root **********************/
# define to_edacpci(k) container_of(k, struct edac_pci_ctl_info, kobj)
# define to_edacpci_attr(a) container_of(a, struct edac_pci_attr, attr)
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* simple show/store functions for attributes */
2007-07-19 12:49:33 +04:00
static ssize_t edac_pci_int_show ( void * ptr , char * buffer )
{
int * value = ptr ;
2007-07-19 12:49:58 +04:00
return sprintf ( buffer , " %d \n " , * value ) ;
2007-07-19 12:49:33 +04:00
}
static ssize_t edac_pci_int_store ( void * ptr , const char * buffer , size_t count )
{
int * value = ptr ;
if ( isdigit ( * buffer ) )
2007-07-19 12:49:58 +04:00
* value = simple_strtoul ( buffer , NULL , 0 ) ;
2007-07-19 12:49:33 +04:00
return count ;
}
struct edac_pci_dev_attribute {
struct attribute attr ;
void * value ;
2007-07-19 12:49:58 +04:00
ssize_t ( * show ) ( void * , char * ) ;
ssize_t ( * store ) ( void * , const char * , size_t ) ;
2007-07-19 12:49:33 +04:00
} ;
/* Set of show/store abstract level functions for PCI Parity object */
static ssize_t edac_pci_dev_show ( struct kobject * kobj , struct attribute * attr ,
2007-07-19 12:49:58 +04:00
char * buffer )
2007-07-19 12:49:33 +04:00
{
struct edac_pci_dev_attribute * edac_pci_dev ;
2007-07-19 12:49:58 +04:00
edac_pci_dev = ( struct edac_pci_dev_attribute * ) attr ;
2007-07-19 12:49:33 +04:00
if ( edac_pci_dev - > show )
return edac_pci_dev - > show ( edac_pci_dev - > value , buffer ) ;
return - EIO ;
}
static ssize_t edac_pci_dev_store ( struct kobject * kobj ,
2007-07-19 12:50:13 +04:00
struct attribute * attr , const char * buffer ,
size_t count )
2007-07-19 12:49:33 +04:00
{
struct edac_pci_dev_attribute * edac_pci_dev ;
2007-07-19 12:49:58 +04:00
edac_pci_dev = ( struct edac_pci_dev_attribute * ) attr ;
2007-07-19 12:49:33 +04:00
if ( edac_pci_dev - > show )
return edac_pci_dev - > store ( edac_pci_dev - > value , buffer , count ) ;
return - EIO ;
}
static struct sysfs_ops edac_pci_sysfs_ops = {
2007-07-19 12:49:58 +04:00
. show = edac_pci_dev_show ,
. store = edac_pci_dev_store
2007-07-19 12:49:33 +04:00
} ;
# define EDAC_PCI_ATTR(_name,_mode,_show,_store) \
static struct edac_pci_dev_attribute edac_pci_attr_ # # _name = { \
. attr = { . name = __stringify ( _name ) , . mode = _mode } , \
. value = & _name , \
. show = _show , \
. store = _store , \
} ;
# define EDAC_PCI_STRING_ATTR(_name,_data,_mode,_show,_store) \
static struct edac_pci_dev_attribute edac_pci_attr_ # # _name = { \
. attr = { . name = __stringify ( _name ) , . mode = _mode } , \
. value = _data , \
. show = _show , \
. store = _store , \
} ;
/* PCI Parity control files */
2007-07-19 12:49:58 +04:00
EDAC_PCI_ATTR ( check_pci_errors , S_IRUGO | S_IWUSR , edac_pci_int_show ,
2007-07-19 12:50:13 +04:00
edac_pci_int_store ) ;
2007-07-19 12:49:58 +04:00
EDAC_PCI_ATTR ( edac_pci_log_pe , S_IRUGO | S_IWUSR , edac_pci_int_show ,
2007-07-19 12:50:13 +04:00
edac_pci_int_store ) ;
2007-07-19 12:49:58 +04:00
EDAC_PCI_ATTR ( edac_pci_log_npe , S_IRUGO | S_IWUSR , edac_pci_int_show ,
2007-07-19 12:50:13 +04:00
edac_pci_int_store ) ;
2007-07-19 12:49:58 +04:00
EDAC_PCI_ATTR ( edac_pci_panic_on_pe , S_IRUGO | S_IWUSR , edac_pci_int_show ,
2007-07-19 12:50:13 +04:00
edac_pci_int_store ) ;
2007-07-19 12:49:33 +04:00
EDAC_PCI_ATTR ( pci_parity_count , S_IRUGO , edac_pci_int_show , NULL ) ;
2007-07-19 12:49:52 +04:00
EDAC_PCI_ATTR ( pci_nonparity_count , S_IRUGO , edac_pci_int_show , NULL ) ;
2007-07-19 12:49:33 +04:00
/* Base Attributes of the memory ECC object */
static struct edac_pci_dev_attribute * edac_pci_attr [ ] = {
2007-07-19 12:49:52 +04:00
& edac_pci_attr_check_pci_errors ,
2007-07-19 12:49:54 +04:00
& edac_pci_attr_edac_pci_log_pe ,
& edac_pci_attr_edac_pci_log_npe ,
& edac_pci_attr_edac_pci_panic_on_pe ,
2007-07-19 12:49:33 +04:00
& edac_pci_attr_pci_parity_count ,
2007-07-19 12:49:52 +04:00
& edac_pci_attr_pci_nonparity_count ,
2007-07-19 12:49:33 +04:00
NULL ,
} ;
2007-07-26 21:41:15 +04:00
/*
* edac_pci_release_main_kobj
*
* This release function is called when the reference count to the
* passed kobj goes to zero .
*
* This kobj is the ' main ' kobject that EDAC PCI instances
* link to , and thus provide for proper nesting counts
*/
static void edac_pci_release_main_kobj ( struct kobject * kobj )
2007-07-19 12:49:33 +04:00
{
2007-07-19 12:49:52 +04:00
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() here to module_put(THIS_MODULE) \n " , __func__ ) ;
2007-07-19 12:49:52 +04:00
2007-07-26 21:41:15 +04:00
/* last reference to top EDAC PCI kobject has been removed,
* NOW release our ref count on the core module
*/
module_put ( THIS_MODULE ) ;
2007-07-19 12:49:33 +04:00
}
2007-07-26 21:41:15 +04:00
/* ktype struct for the EDAC PCI main kobj */
static struct kobj_type ktype_edac_pci_main_kobj = {
. release = edac_pci_release_main_kobj ,
2007-07-19 12:49:33 +04:00
. sysfs_ops = & edac_pci_sysfs_ops ,
2007-07-19 12:49:58 +04:00
. default_attrs = ( struct attribute * * ) edac_pci_attr ,
2007-07-19 12:49:33 +04:00
} ;
/**
2007-07-26 21:41:15 +04:00
* edac_pci_main_kobj_setup ( )
2007-07-19 12:49:33 +04:00
*
* setup the sysfs for EDAC PCI attributes
* assumes edac_class has already been initialized
*/
2007-07-26 21:41:15 +04:00
int edac_pci_main_kobj_setup ( void )
2007-07-19 12:49:33 +04:00
{
int err ;
struct sysdev_class * edac_class ;
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() \n " , __func__ ) ;
/* check and count if we have already created the main kobject */
if ( atomic_inc_return ( & edac_pci_sysfs_refcount ) ! = 1 )
return 0 ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* First time, so create the main kobject and its
* controls and atributes
*/
2007-07-19 12:49:33 +04:00
edac_class = edac_get_edac_class ( ) ;
2007-07-19 12:49:52 +04:00
if ( edac_class = = NULL ) {
debugf1 ( " %s() no edac_class \n " , __func__ ) ;
2007-07-26 21:41:15 +04:00
err = - ENODEV ;
goto decrement_count_fail ;
2007-07-19 12:49:52 +04:00
}
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* Bump the reference count on this module to ensure the
* modules isn ' t unloaded until we deconstruct the top
* level main kobj for EDAC PCI
*/
if ( ! try_module_get ( THIS_MODULE ) ) {
debugf1 ( " %s() try_module_get() failed \n " , __func__ ) ;
err = - ENODEV ;
goto decrement_count_fail ;
}
2007-07-19 12:49:33 +04:00
2007-07-19 12:49:52 +04:00
/* Instanstiate the pci object */
2007-12-17 22:54:39 +03:00
err = kobject_init_and_add ( & edac_pci_top_main_kobj , & ktype_edac_pci_main_kobj ,
& edac_class - > kset . kobj , " pci " ) ;
2007-07-19 12:49:52 +04:00
if ( err ) {
debugf1 ( " Failed to register '.../edac/pci' \n " ) ;
2007-12-17 22:54:39 +03:00
goto kobject_init_and_add_fail ;
2007-07-19 12:49:33 +04:00
}
2007-07-26 21:41:15 +04:00
/* At this point, to 'release' the top level kobject
* for EDAC PCI , then edac_pci_main_kobj_teardown ( )
* must be used , for resources to be cleaned up properly
*/
2007-12-17 22:54:39 +03:00
kobject_uevent ( & edac_pci_top_main_kobj , KOBJ_ADD ) ;
2007-07-19 12:49:52 +04:00
debugf1 ( " Registered '.../edac/pci' kobject \n " ) ;
return 0 ;
2007-07-26 21:41:15 +04:00
/* Error unwind statck */
2007-12-17 22:54:39 +03:00
kobject_init_and_add_fail :
2007-07-26 21:41:15 +04:00
module_put ( THIS_MODULE ) ;
decrement_count_fail :
/* if are on this error exit, nothing to tear down */
atomic_dec ( & edac_pci_sysfs_refcount ) ;
return err ;
2007-07-19 12:49:33 +04:00
}
/*
2007-07-26 21:41:15 +04:00
* edac_pci_main_kobj_teardown ( )
2007-07-19 12:49:33 +04:00
*
2007-07-26 21:41:15 +04:00
* if no longer linked ( needed ) remove the top level EDAC PCI
* kobject with its controls and attributes
2007-07-19 12:49:33 +04:00
*/
2007-07-26 21:41:15 +04:00
static void edac_pci_main_kobj_teardown ( void )
2007-07-19 12:49:33 +04:00
{
debugf0 ( " %s() \n " , __func__ ) ;
2007-07-26 21:41:15 +04:00
/* Decrement the count and only if no more controller instances
* are connected perform the unregisteration of the top level
* main kobj
*/
if ( atomic_dec_return ( & edac_pci_sysfs_refcount ) = = 0 ) {
2007-12-20 19:13:05 +03:00
debugf0 ( " %s() called kobject_put on main kobj \n " ,
2007-07-26 21:41:15 +04:00
__func__ ) ;
2007-12-20 19:13:05 +03:00
kobject_put ( & edac_pci_top_main_kobj ) ;
2007-07-26 21:41:15 +04:00
}
2007-07-19 12:49:33 +04:00
}
2007-07-26 21:41:15 +04:00
/*
*
* edac_pci_create_sysfs
*
* Create the controls / attributes for the specified EDAC PCI device
*/
2007-07-19 12:49:52 +04:00
int edac_pci_create_sysfs ( struct edac_pci_ctl_info * pci )
{
int err ;
struct kobject * edac_kobj = & pci - > kobj ;
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() idx=%d \n " , __func__ , pci - > pci_idx ) ;
2007-07-19 12:49:52 +04:00
2007-07-26 21:41:15 +04:00
/* create the top main EDAC PCI kobject, IF needed */
err = edac_pci_main_kobj_setup ( ) ;
if ( err )
return err ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* Create this instance's kobject under the MAIN kobject */
err = edac_pci_create_instance_kobj ( pci , pci - > pci_idx ) ;
if ( err )
goto unregister_cleanup ;
2007-07-19 12:49:52 +04:00
2007-07-19 12:49:58 +04:00
err = sysfs_create_link ( edac_kobj , & pci - > dev - > kobj , EDAC_PCI_SYMLINK ) ;
2007-07-19 12:49:52 +04:00
if ( err ) {
debugf0 ( " %s() sysfs_create_link() returned err= %d \n " ,
2007-07-19 12:49:58 +04:00
__func__ , err ) ;
2007-07-26 21:41:15 +04:00
goto symlink_fail ;
2007-07-19 12:49:52 +04:00
}
return 0 ;
2007-07-26 21:41:15 +04:00
/* Error unwind stack */
symlink_fail :
edac_pci_unregister_sysfs_instance_kobj ( pci ) ;
unregister_cleanup :
edac_pci_main_kobj_teardown ( ) ;
return err ;
2007-07-19 12:49:52 +04:00
}
2007-07-26 21:41:15 +04:00
/*
* edac_pci_remove_sysfs
*
* remove the controls and attributes for this EDAC PCI device
*/
2007-07-19 12:49:52 +04:00
void edac_pci_remove_sysfs ( struct edac_pci_ctl_info * pci )
{
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() index=%d \n " , __func__ , pci - > pci_idx ) ;
2007-07-19 12:49:52 +04:00
2007-07-26 21:41:15 +04:00
/* Remove the symlink */
2007-07-19 12:49:52 +04:00
sysfs_remove_link ( & pci - > kobj , EDAC_PCI_SYMLINK ) ;
2007-07-26 21:41:15 +04:00
/* remove this PCI instance's sysfs entries */
edac_pci_unregister_sysfs_instance_kobj ( pci ) ;
/* Call the main unregister function, which will determine
* if this ' pci ' is the last instance .
* If it is , the main kobject will be unregistered as a result
*/
debugf0 ( " %s() calling edac_pci_main_kobj_teardown() \n " , __func__ ) ;
edac_pci_main_kobj_teardown ( ) ;
2007-07-19 12:49:52 +04:00
}
/************************ PCI error handling *************************/
2007-07-19 12:49:33 +04:00
static u16 get_pci_parity_status ( struct pci_dev * dev , int secondary )
{
int where ;
u16 status ;
where = secondary ? PCI_SEC_STATUS : PCI_STATUS ;
pci_read_config_word ( dev , where , & status ) ;
/* If we get back 0xFFFF then we must suspect that the card has been
* pulled but the Linux PCI layer has not yet finished cleaning up .
* We don ' t want to report on such devices
*/
if ( status = = 0xFFFF ) {
u32 sanity ;
pci_read_config_dword ( dev , 0 , & sanity ) ;
if ( sanity = = 0xFFFFFFFF )
return 0 ;
}
status & = PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR |
2007-07-19 12:50:13 +04:00
PCI_STATUS_PARITY ;
2007-07-19 12:49:33 +04:00
if ( status )
/* reset only the bits we are interested in */
pci_write_config_word ( dev , where , status ) ;
return status ;
}
/* Clear any PCI parity errors logged by this device. */
static void edac_pci_dev_parity_clear ( struct pci_dev * dev )
{
u8 header_type ;
2007-07-26 21:41:15 +04:00
debugf0 ( " %s() \n " , __func__ ) ;
2007-07-19 12:49:33 +04:00
get_pci_parity_status ( dev , 0 ) ;
/* read the device TYPE, looking for bridges */
pci_read_config_byte ( dev , PCI_HEADER_TYPE , & header_type ) ;
if ( ( header_type & 0x7F ) = = PCI_HEADER_TYPE_BRIDGE )
get_pci_parity_status ( dev , 1 ) ;
}
/*
* PCI Parity polling
*
2007-07-26 21:41:15 +04:00
* Fucntion to retrieve the current parity status
* and decode it
*
2007-07-19 12:49:33 +04:00
*/
static void edac_pci_dev_parity_test ( struct pci_dev * dev )
{
2007-07-26 21:41:15 +04:00
unsigned long flags ;
2007-07-19 12:49:33 +04:00
u16 status ;
2007-07-19 12:49:58 +04:00
u8 header_type ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/* stop any interrupts until we can acquire the status */
local_irq_save ( flags ) ;
/* read the STATUS register on this device */
2007-07-19 12:49:33 +04:00
status = get_pci_parity_status ( dev , 0 ) ;
2007-07-26 21:41:15 +04:00
/* read the device TYPE, looking for bridges */
pci_read_config_byte ( dev , PCI_HEADER_TYPE , & header_type ) ;
local_irq_restore ( flags ) ;
debugf4 ( " PCI STATUS= 0x%04x %s \n " , status , dev - > dev . bus_id ) ;
2007-07-19 12:49:33 +04:00
2008-02-07 11:14:58 +03:00
/* check the status reg for errors on boards NOT marked as broken
* if broken , we cannot trust any of the status bits
*/
if ( status & & ! dev - > broken_parity_status ) {
2007-07-19 12:49:52 +04:00
if ( status & ( PCI_STATUS_SIG_SYSTEM_ERROR ) ) {
2007-07-19 12:49:33 +04:00
edac_printk ( KERN_CRIT , EDAC_PCI ,
2007-07-19 12:50:13 +04:00
" Signaled System Error on %s \n " ,
pci_name ( dev ) ) ;
2007-07-19 12:49:52 +04:00
atomic_inc ( & pci_nonparity_count ) ;
}
2007-07-19 12:49:33 +04:00
if ( status & ( PCI_STATUS_PARITY ) ) {
edac_printk ( KERN_CRIT , EDAC_PCI ,
2007-07-19 12:50:13 +04:00
" Master Data Parity Error on %s \n " ,
pci_name ( dev ) ) ;
2007-07-19 12:49:33 +04:00
atomic_inc ( & pci_parity_count ) ;
}
if ( status & ( PCI_STATUS_DETECTED_PARITY ) ) {
edac_printk ( KERN_CRIT , EDAC_PCI ,
2007-07-19 12:50:13 +04:00
" Detected Parity Error on %s \n " ,
pci_name ( dev ) ) ;
2007-07-19 12:49:33 +04:00
atomic_inc ( & pci_parity_count ) ;
}
}
2007-07-26 21:41:15 +04:00
debugf4 ( " PCI HEADER TYPE= 0x%02x %s \n " , header_type , dev - > dev . bus_id ) ;
2007-07-19 12:49:33 +04:00
if ( ( header_type & 0x7F ) = = PCI_HEADER_TYPE_BRIDGE ) {
/* On bridges, need to examine secondary status register */
status = get_pci_parity_status ( dev , 1 ) ;
2007-07-26 21:41:15 +04:00
debugf4 ( " PCI SEC_STATUS= 0x%04x %s \n " , status , dev - > dev . bus_id ) ;
2007-07-19 12:49:33 +04:00
2008-02-07 11:14:58 +03:00
/* check the secondary status reg for errors,
* on NOT broken boards
*/
if ( status & & ! dev - > broken_parity_status ) {
2007-07-19 12:49:52 +04:00
if ( status & ( PCI_STATUS_SIG_SYSTEM_ERROR ) ) {
2007-07-19 12:49:33 +04:00
edac_printk ( KERN_CRIT , EDAC_PCI , " Bridge "
2007-07-19 12:50:13 +04:00
" Signaled System Error on %s \n " ,
pci_name ( dev ) ) ;
2007-07-19 12:49:52 +04:00
atomic_inc ( & pci_nonparity_count ) ;
}
2007-07-19 12:49:33 +04:00
if ( status & ( PCI_STATUS_PARITY ) ) {
edac_printk ( KERN_CRIT , EDAC_PCI , " Bridge "
2007-07-19 12:50:13 +04:00
" Master Data Parity Error on "
" %s \n " , pci_name ( dev ) ) ;
2007-07-19 12:49:33 +04:00
atomic_inc ( & pci_parity_count ) ;
}
if ( status & ( PCI_STATUS_DETECTED_PARITY ) ) {
edac_printk ( KERN_CRIT , EDAC_PCI , " Bridge "
2007-07-19 12:50:13 +04:00
" Detected Parity Error on %s \n " ,
pci_name ( dev ) ) ;
2007-07-19 12:49:33 +04:00
atomic_inc ( & pci_parity_count ) ;
}
}
}
}
2007-07-26 21:41:15 +04:00
/* reduce some complexity in definition of the iterator */
typedef void ( * pci_parity_check_fn_t ) ( struct pci_dev * dev ) ;
2007-07-19 12:49:33 +04:00
/*
* pci_dev parity list iterator
2007-07-26 21:41:15 +04:00
* Scan the PCI device list for one pass , looking for SERRORs
2007-07-19 12:49:33 +04:00
* Master Parity ERRORS or Parity ERRORs on primary or secondary devices
*/
static inline void edac_pci_dev_parity_iterator ( pci_parity_check_fn_t fn )
{
struct pci_dev * dev = NULL ;
/* request for kernel access to the next PCI device, if any,
* and while we are looking at it have its reference count
* bumped until we are done with it
*/
2007-07-19 12:49:58 +04:00
while ( ( dev = pci_get_device ( PCI_ANY_ID , PCI_ANY_ID , dev ) ) ! = NULL ) {
2007-07-19 12:49:33 +04:00
fn ( dev ) ;
}
}
/*
* edac_pci_do_parity_check
*
* performs the actual PCI parity check operation
*/
void edac_pci_do_parity_check ( void )
{
int before_count ;
debugf3 ( " %s() \n " , __func__ ) ;
2007-07-26 21:41:15 +04:00
/* if policy has PCI check off, leave now */
2007-07-19 12:49:52 +04:00
if ( ! check_pci_errors )
2007-07-19 12:49:33 +04:00
return ;
before_count = atomic_read ( & pci_parity_count ) ;
/* scan all PCI devices looking for a Parity Error on devices and
2007-07-26 21:41:15 +04:00
* bridges .
* The iterator calls pci_get_device ( ) which might sleep , thus
* we cannot disable interrupts in this scan .
2007-07-19 12:49:33 +04:00
*/
edac_pci_dev_parity_iterator ( edac_pci_dev_parity_test ) ;
/* Only if operator has selected panic on PCI Error */
2007-07-19 12:49:54 +04:00
if ( edac_pci_get_panic_on_pe ( ) ) {
2007-07-19 12:49:33 +04:00
/* If the count is different 'after' from 'before' */
if ( before_count ! = atomic_read ( & pci_parity_count ) )
panic ( " EDAC: PCI Parity Error " ) ;
}
}
2007-07-26 21:41:15 +04:00
/*
* edac_pci_clear_parity_errors
*
* function to perform an iteration over the PCI devices
* and clearn their current status
*/
2007-07-19 12:49:33 +04:00
void edac_pci_clear_parity_errors ( void )
{
/* Clear any PCI bus parity errors that devices initially have logged
* in their registers .
*/
edac_pci_dev_parity_iterator ( edac_pci_dev_parity_clear ) ;
}
2007-07-26 21:41:15 +04:00
/*
* edac_pci_handle_pe
*
* Called to handle a PARITY ERROR event
*/
2007-07-19 12:49:52 +04:00
void edac_pci_handle_pe ( struct edac_pci_ctl_info * pci , const char * msg )
{
/* global PE counter incremented by edac_pci_do_parity_check() */
atomic_inc ( & pci - > counters . pe_count ) ;
2007-07-19 12:49:54 +04:00
if ( edac_pci_get_log_pe ( ) )
2007-07-19 12:49:52 +04:00
edac_pci_printk ( pci , KERN_WARNING ,
" Parity Error ctl: %s %d: %s \n " ,
pci - > ctl_name , pci - > pci_idx , msg ) ;
/*
* poke all PCI devices and see which one is the troublemaker
* panic ( ) is called if set
*/
edac_pci_do_parity_check ( ) ;
}
EXPORT_SYMBOL_GPL ( edac_pci_handle_pe ) ;
2007-07-19 12:49:33 +04:00
2007-07-26 21:41:15 +04:00
/*
* edac_pci_handle_npe
*
* Called to handle a NON - PARITY ERROR event
*/
2007-07-19 12:49:52 +04:00
void edac_pci_handle_npe ( struct edac_pci_ctl_info * pci , const char * msg )
{
/* global NPE counter incremented by edac_pci_do_parity_check() */
atomic_inc ( & pci - > counters . npe_count ) ;
2007-07-19 12:49:54 +04:00
if ( edac_pci_get_log_npe ( ) )
2007-07-19 12:49:52 +04:00
edac_pci_printk ( pci , KERN_WARNING ,
" Non-Parity Error ctl: %s %d: %s \n " ,
pci - > ctl_name , pci - > pci_idx , msg ) ;
/*
* poke all PCI devices and see which one is the troublemaker
* panic ( ) is called if set
*/
edac_pci_do_parity_check ( ) ;
}
EXPORT_SYMBOL_GPL ( edac_pci_handle_npe ) ;
2007-07-19 12:49:33 +04:00
/*
* Define the PCI parameter to the module
*/
2007-07-19 12:49:52 +04:00
module_param ( check_pci_errors , int , 0644 ) ;
2007-07-19 12:49:54 +04:00
MODULE_PARM_DESC ( check_pci_errors ,
2007-07-19 12:49:58 +04:00
" Check for PCI bus parity errors: 0=off 1=on " ) ;
2007-07-19 12:49:54 +04:00
module_param ( edac_pci_panic_on_pe , int , 0644 ) ;
MODULE_PARM_DESC ( edac_pci_panic_on_pe ,
2007-07-19 12:49:58 +04:00
" Panic on PCI Bus Parity error: 0=off 1=on " ) ;
2007-07-19 12:49:33 +04:00
2007-07-19 12:49:58 +04:00
# endif /* CONFIG_PCI */