2005-10-27 11:03:38 -07:00
/*
* Copyright ( c ) 2005 Cisco Systems . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
2005-11-07 00:59:43 -08:00
# include <linux/jiffies.h>
2011-05-27 15:35:46 -04:00
# include <linux/module.h>
2005-11-07 00:59:43 -08:00
# include <linux/timer.h>
2006-08-15 21:11:18 +03:00
# include <linux/workqueue.h>
2005-11-07 00:59:43 -08:00
2005-10-27 11:03:38 -07:00
# include "mthca_dev.h"
enum {
MTHCA_CATAS_POLL_INTERVAL = 5 * HZ ,
MTHCA_CATAS_TYPE_INTERNAL = 0 ,
MTHCA_CATAS_TYPE_UPLINK = 3 ,
MTHCA_CATAS_TYPE_DDR = 4 ,
MTHCA_CATAS_TYPE_PARITY = 5 ,
} ;
static DEFINE_SPINLOCK ( catas_lock ) ;
2006-08-15 21:11:18 +03:00
static LIST_HEAD ( catas_list ) ;
static struct workqueue_struct * catas_wq ;
static struct work_struct catas_work ;
static int catas_reset_disable ;
module_param_named ( catas_reset_disable , catas_reset_disable , int , 0644 ) ;
MODULE_PARM_DESC ( catas_reset_disable , " disable reset on catastrophic event if nonzero " ) ;
2006-11-22 14:57:56 +00:00
static void catas_reset ( struct work_struct * work )
2006-08-15 21:11:18 +03:00
{
struct mthca_dev * dev , * tmpdev ;
LIST_HEAD ( tlist ) ;
int ret ;
mutex_lock ( & mthca_device_mutex ) ;
spin_lock_irq ( & catas_lock ) ;
list_splice_init ( & catas_list , & tlist ) ;
spin_unlock_irq ( & catas_lock ) ;
list_for_each_entry_safe ( dev , tmpdev , & tlist , catas_err . list ) {
2009-09-24 11:55:41 -07:00
struct pci_dev * pdev = dev - > pdev ;
2006-08-15 21:11:18 +03:00
ret = __mthca_restart_one ( dev - > pdev ) ;
2009-09-24 11:55:41 -07:00
/* 'dev' now is not valid */
2006-08-15 21:11:18 +03:00
if ( ret )
2009-09-24 11:55:41 -07:00
printk ( KERN_ERR " mthca %s: Reset failed (%d) \n " ,
pci_name ( pdev ) , ret ) ;
else {
struct mthca_dev * d = pci_get_drvdata ( pdev ) ;
mthca_dbg ( d , " Reset succeeded \n " ) ;
}
2006-08-15 21:11:18 +03:00
}
mutex_unlock ( & mthca_device_mutex ) ;
}
2005-10-27 11:03:38 -07:00
static void handle_catas ( struct mthca_dev * dev )
{
struct ib_event event ;
2006-08-15 21:11:18 +03:00
unsigned long flags ;
2005-10-27 11:03:38 -07:00
const char * type ;
int i ;
event . device = & dev - > ib_dev ;
event . event = IB_EVENT_DEVICE_FATAL ;
event . element . port_num = 0 ;
2009-09-05 20:36:16 -07:00
dev - > active = false ;
2005-10-27 11:03:38 -07:00
ib_dispatch_event ( & event ) ;
switch ( swab32 ( readl ( dev - > catas_err . map ) ) > > 24 ) {
case MTHCA_CATAS_TYPE_INTERNAL :
type = " internal error " ;
break ;
case MTHCA_CATAS_TYPE_UPLINK :
type = " uplink bus error " ;
break ;
case MTHCA_CATAS_TYPE_DDR :
type = " DDR data error " ;
break ;
case MTHCA_CATAS_TYPE_PARITY :
type = " internal parity error " ;
break ;
default :
type = " unknown error " ;
break ;
}
mthca_err ( dev , " Catastrophic error detected: %s \n " , type ) ;
for ( i = 0 ; i < dev - > catas_err . size ; + + i )
mthca_err ( dev , " buf[%02x]: %08x \n " ,
i , swab32 ( readl ( dev - > catas_err . map + i ) ) ) ;
2006-08-15 21:11:18 +03:00
if ( catas_reset_disable )
return ;
spin_lock_irqsave ( & catas_lock , flags ) ;
list_add ( & dev - > catas_err . list , & catas_list ) ;
queue_work ( catas_wq , & catas_work ) ;
spin_unlock_irqrestore ( & catas_lock , flags ) ;
2005-10-27 11:03:38 -07:00
}
static void poll_catas ( unsigned long dev_ptr )
{
struct mthca_dev * dev = ( struct mthca_dev * ) dev_ptr ;
int i ;
for ( i = 0 ; i < dev - > catas_err . size ; + + i )
if ( readl ( dev - > catas_err . map + i ) ) {
handle_catas ( dev ) ;
return ;
}
2008-07-14 23:48:52 -07:00
mod_timer ( & dev - > catas_err . timer ,
2008-07-14 23:48:52 -07:00
round_jiffies ( jiffies + MTHCA_CATAS_POLL_INTERVAL ) ) ;
2005-10-27 11:03:38 -07:00
}
void mthca_start_catas_poll ( struct mthca_dev * dev )
{
2011-01-11 20:39:46 -08:00
phys_addr_t addr ;
2005-10-27 11:03:38 -07:00
init_timer ( & dev - > catas_err . timer ) ;
dev - > catas_err . map = NULL ;
addr = pci_resource_start ( dev - > pdev , 0 ) +
( ( pci_resource_len ( dev - > pdev , 0 ) - 1 ) &
dev - > catas_err . addr ) ;
dev - > catas_err . map = ioremap ( addr , dev - > catas_err . size * 4 ) ;
if ( ! dev - > catas_err . map ) {
mthca_warn ( dev , " couldn't map catastrophic error region "
2011-01-11 20:39:46 -08:00
" at 0x%llx/0x%x \n " , ( unsigned long long ) addr ,
dev - > catas_err . size * 4 ) ;
2005-10-27 11:03:38 -07:00
return ;
}
dev - > catas_err . timer . data = ( unsigned long ) dev ;
dev - > catas_err . timer . function = poll_catas ;
dev - > catas_err . timer . expires = jiffies + MTHCA_CATAS_POLL_INTERVAL ;
2006-08-15 21:11:18 +03:00
INIT_LIST_HEAD ( & dev - > catas_err . list ) ;
2005-10-27 11:03:38 -07:00
add_timer ( & dev - > catas_err . timer ) ;
}
void mthca_stop_catas_poll ( struct mthca_dev * dev )
{
del_timer_sync ( & dev - > catas_err . timer ) ;
2008-09-29 21:37:33 -07:00
if ( dev - > catas_err . map )
2005-10-27 11:03:38 -07:00
iounmap ( dev - > catas_err . map ) ;
2006-08-15 21:11:18 +03:00
spin_lock_irq ( & catas_lock ) ;
list_del ( & dev - > catas_err . list ) ;
spin_unlock_irq ( & catas_lock ) ;
}
int __init mthca_catas_init ( void )
{
2006-11-22 14:57:56 +00:00
INIT_WORK ( & catas_work , catas_reset ) ;
2006-08-15 21:11:18 +03:00
catas_wq = create_singlethread_workqueue ( " mthca_catas " ) ;
if ( ! catas_wq )
return - ENOMEM ;
return 0 ;
}
void mthca_catas_cleanup ( void )
{
destroy_workqueue ( catas_wq ) ;
2005-10-27 11:03:38 -07:00
}