256 lines
7.3 KiB
C
256 lines
7.3 KiB
C
|
/*
|
||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||
|
* License. See the file "COPYING" in the main directory of this archive
|
||
|
* for more details.
|
||
|
*
|
||
|
* Copyright (c) 2000-2007 Silicon Graphics, Inc. All Rights Reserved.
|
||
|
*/
|
||
|
|
||
|
#include <linux/types.h>
|
||
|
#include <asm/sn/sn_sal.h>
|
||
|
#include "ioerror.h"
|
||
|
#include <asm/sn/addrs.h>
|
||
|
#include <asm/sn/shubio.h>
|
||
|
#include <asm/sn/geo.h>
|
||
|
#include "xtalk/xwidgetdev.h"
|
||
|
#include "xtalk/hubdev.h"
|
||
|
#include <asm/sn/bte.h>
|
||
|
#include <asm/param.h>
|
||
|
|
||
|
/*
|
||
|
* Bte error handling is done in two parts. The first captures
|
||
|
* any crb related errors. Since there can be multiple crbs per
|
||
|
* interface and multiple interfaces active, we need to wait until
|
||
|
* all active crbs are completed. This is the first job of the
|
||
|
* second part error handler. When all bte related CRBs are cleanly
|
||
|
* completed, it resets the interfaces and gets them ready for new
|
||
|
* transfers to be queued.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Wait until all BTE related CRBs are completed
|
||
|
* and then reset the interfaces.
|
||
|
*/
|
||
|
static int shub1_bte_error_handler(struct nodepda_s *err_nodepda)
|
||
|
{
|
||
|
struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer;
|
||
|
nasid_t nasid;
|
||
|
int i;
|
||
|
int valid_crbs;
|
||
|
ii_imem_u_t imem; /* II IMEM Register */
|
||
|
ii_icrb0_d_u_t icrbd; /* II CRB Register D */
|
||
|
ii_ibcr_u_t ibcr;
|
||
|
ii_icmr_u_t icmr;
|
||
|
ii_ieclr_u_t ieclr;
|
||
|
|
||
|
BTE_PRINTK(("shub1_bte_error_handler(%p) - %d\n", err_nodepda,
|
||
|
smp_processor_id()));
|
||
|
|
||
|
if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) &&
|
||
|
(err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) {
|
||
|
BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda,
|
||
|
smp_processor_id()));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Determine information about our hub */
|
||
|
nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode);
|
||
|
|
||
|
/*
|
||
|
* A BTE transfer can use multiple CRBs. We need to make sure
|
||
|
* that all the BTE CRBs are complete (or timed out) before
|
||
|
* attempting to clean up the error. Resetting the BTE while
|
||
|
* there are still BTE CRBs active will hang the BTE.
|
||
|
* We should look at all the CRBs to see if they are allocated
|
||
|
* to the BTE and see if they are still active. When none
|
||
|
* are active, we can continue with the cleanup.
|
||
|
*
|
||
|
* We also want to make sure that the local NI port is up.
|
||
|
* When a router resets the NI port can go down, while it
|
||
|
* goes through the LLP handshake, but then comes back up.
|
||
|
*/
|
||
|
icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR);
|
||
|
if (icmr.ii_icmr_fld_s.i_crb_mark != 0) {
|
||
|
/*
|
||
|
* There are errors which still need to be cleaned up by
|
||
|
* hubiio_crb_error_handler
|
||
|
*/
|
||
|
mod_timer(recovery_timer, jiffies + (HZ * 5));
|
||
|
BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda,
|
||
|
smp_processor_id()));
|
||
|
return 1;
|
||
|
}
|
||
|
if (icmr.ii_icmr_fld_s.i_crb_vld != 0) {
|
||
|
|
||
|
valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld;
|
||
|
|
||
|
for (i = 0; i < IIO_NUM_CRBS; i++) {
|
||
|
if (!((1 << i) & valid_crbs)) {
|
||
|
/* This crb was not marked as valid, ignore */
|
||
|
continue;
|
||
|
}
|
||
|
icrbd.ii_icrb0_d_regval =
|
||
|
REMOTE_HUB_L(nasid, IIO_ICRB_D(i));
|
||
|
if (icrbd.d_bteop) {
|
||
|
mod_timer(recovery_timer, jiffies + (HZ * 5));
|
||
|
BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n",
|
||
|
err_nodepda, smp_processor_id(),
|
||
|
i));
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id()));
|
||
|
/* Re-enable both bte interfaces */
|
||
|
imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM);
|
||
|
imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1;
|
||
|
REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval);
|
||
|
|
||
|
/* Clear BTE0/1 error bits */
|
||
|
ieclr.ii_ieclr_regval = 0;
|
||
|
if (err_nodepda->bte_if[0].bh_error != BTE_SUCCESS)
|
||
|
ieclr.ii_ieclr_fld_s.i_e_bte_0 = 1;
|
||
|
if (err_nodepda->bte_if[1].bh_error != BTE_SUCCESS)
|
||
|
ieclr.ii_ieclr_fld_s.i_e_bte_1 = 1;
|
||
|
REMOTE_HUB_S(nasid, IIO_IECLR, ieclr.ii_ieclr_regval);
|
||
|
|
||
|
/* Reinitialize both BTE state machines. */
|
||
|
ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR);
|
||
|
ibcr.ii_ibcr_fld_s.i_soft_reset = 1;
|
||
|
REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval);
|
||
|
|
||
|
del_timer(recovery_timer);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wait until all BTE related CRBs are completed
|
||
|
* and then reset the interfaces.
|
||
|
*/
|
||
|
static int shub2_bte_error_handler(struct nodepda_s *err_nodepda)
|
||
|
{
|
||
|
struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer;
|
||
|
struct bteinfo_s *bte;
|
||
|
nasid_t nasid;
|
||
|
u64 status;
|
||
|
int i;
|
||
|
|
||
|
nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode);
|
||
|
|
||
|
/*
|
||
|
* Verify that all the BTEs are complete
|
||
|
*/
|
||
|
for (i = 0; i < BTES_PER_NODE; i++) {
|
||
|
bte = &err_nodepda->bte_if[i];
|
||
|
status = BTE_LNSTAT_LOAD(bte);
|
||
|
if (status & IBLS_ERROR) {
|
||
|
bte->bh_error = BTE_SHUB2_ERROR(status);
|
||
|
continue;
|
||
|
}
|
||
|
if (!(status & IBLS_BUSY))
|
||
|
continue;
|
||
|
mod_timer(recovery_timer, jiffies + (HZ * 5));
|
||
|
BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda,
|
||
|
smp_processor_id()));
|
||
|
return 1;
|
||
|
}
|
||
|
if (ia64_sn_bte_recovery(nasid))
|
||
|
panic("bte_error_handler(): Fatal BTE Error");
|
||
|
|
||
|
del_timer(recovery_timer);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wait until all BTE related CRBs are completed
|
||
|
* and then reset the interfaces.
|
||
|
*/
|
||
|
void bte_error_handler(struct nodepda_s *err_nodepda)
|
||
|
{
|
||
|
spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock;
|
||
|
int i;
|
||
|
unsigned long irq_flags;
|
||
|
volatile u64 *notify;
|
||
|
bte_result_t bh_error;
|
||
|
|
||
|
BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda,
|
||
|
smp_processor_id()));
|
||
|
|
||
|
spin_lock_irqsave(recovery_lock, irq_flags);
|
||
|
|
||
|
/*
|
||
|
* Lock all interfaces on this node to prevent new transfers
|
||
|
* from being queued.
|
||
|
*/
|
||
|
for (i = 0; i < BTES_PER_NODE; i++) {
|
||
|
if (err_nodepda->bte_if[i].cleanup_active) {
|
||
|
continue;
|
||
|
}
|
||
|
spin_lock(&err_nodepda->bte_if[i].spinlock);
|
||
|
BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda,
|
||
|
smp_processor_id(), i));
|
||
|
err_nodepda->bte_if[i].cleanup_active = 1;
|
||
|
}
|
||
|
|
||
|
if (is_shub1()) {
|
||
|
if (shub1_bte_error_handler(err_nodepda)) {
|
||
|
spin_unlock_irqrestore(recovery_lock, irq_flags);
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
if (shub2_bte_error_handler(err_nodepda)) {
|
||
|
spin_unlock_irqrestore(recovery_lock, irq_flags);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < BTES_PER_NODE; i++) {
|
||
|
bh_error = err_nodepda->bte_if[i].bh_error;
|
||
|
if (bh_error != BTE_SUCCESS) {
|
||
|
/* There is an error which needs to be notified */
|
||
|
notify = err_nodepda->bte_if[i].most_rcnt_na;
|
||
|
BTE_PRINTK(("cnode %d bte %d error=0x%lx\n",
|
||
|
err_nodepda->bte_if[i].bte_cnode,
|
||
|
err_nodepda->bte_if[i].bte_num,
|
||
|
IBLS_ERROR | (u64) bh_error));
|
||
|
*notify = IBLS_ERROR | bh_error;
|
||
|
err_nodepda->bte_if[i].bh_error = BTE_SUCCESS;
|
||
|
}
|
||
|
|
||
|
err_nodepda->bte_if[i].cleanup_active = 0;
|
||
|
BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda,
|
||
|
smp_processor_id(), i));
|
||
|
spin_unlock(&err_nodepda->bte_if[i].spinlock);
|
||
|
}
|
||
|
|
||
|
spin_unlock_irqrestore(recovery_lock, irq_flags);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* First part error handler. This is called whenever any error CRB interrupt
|
||
|
* is generated by the II.
|
||
|
*/
|
||
|
void
|
||
|
bte_crb_error_handler(cnodeid_t cnode, int btenum,
|
||
|
int crbnum, ioerror_t * ioe, int bteop)
|
||
|
{
|
||
|
struct bteinfo_s *bte;
|
||
|
|
||
|
|
||
|
bte = &(NODEPDA(cnode)->bte_if[btenum]);
|
||
|
|
||
|
/*
|
||
|
* The caller has already figured out the error type, we save that
|
||
|
* in the bte handle structure for the thread exercising the
|
||
|
* interface to consume.
|
||
|
*/
|
||
|
bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET;
|
||
|
bte->bte_error_count++;
|
||
|
|
||
|
BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n",
|
||
|
bte->bte_cnode, bte->bte_num, ioe->ie_errortype));
|
||
|
bte_error_handler(NODEPDA(cnode));
|
||
|
}
|
||
|
|