Path: blob/master/drivers/infiniband/hw/qib/qib_intr.c
15112 views
/*1* Copyright (c) 2006, 2007, 2008, 2009, 2010 QLogic Corporation.2* All rights reserved.3* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.4*5* This software is available to you under a choice of one of two6* licenses. You may choose to be licensed under the terms of the GNU7* General Public License (GPL) Version 2, available from the file8* COPYING in the main directory of this source tree, or the9* OpenIB.org BSD license below:10*11* Redistribution and use in source and binary forms, with or12* without modification, are permitted provided that the following13* conditions are met:14*15* - Redistributions of source code must retain the above16* copyright notice, this list of conditions and the following17* disclaimer.18*19* - Redistributions in binary form must reproduce the above20* copyright notice, this list of conditions and the following21* disclaimer in the documentation and/or other materials22* provided with the distribution.23*24* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,25* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF26* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND27* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS28* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN29* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN30* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE31* SOFTWARE.32*/3334#include <linux/pci.h>35#include <linux/delay.h>3637#include "qib.h"38#include "qib_common.h"3940/**41* qib_format_hwmsg - format a single hwerror message42* @msg message buffer43* @msgl length of message buffer44* @hwmsg message to add to message buffer45*/46static void qib_format_hwmsg(char *msg, size_t msgl, const char *hwmsg)47{48strlcat(msg, "[", msgl);49strlcat(msg, hwmsg, msgl);50strlcat(msg, "]", msgl);51}5253/**54* qib_format_hwerrors - format hardware error messages for display55* @hwerrs hardware errors bit vector56* @hwerrmsgs hardware error descriptions57* @nhwerrmsgs number of hwerrmsgs58* @msg message buffer59* @msgl message buffer length60*/61void qib_format_hwerrors(u64 hwerrs, const struct qib_hwerror_msgs *hwerrmsgs,62size_t nhwerrmsgs, char *msg, size_t msgl)63{64int i;6566for (i = 0; i < nhwerrmsgs; i++)67if (hwerrs & hwerrmsgs[i].mask)68qib_format_hwmsg(msg, msgl, hwerrmsgs[i].msg);69}7071static void signal_ib_event(struct qib_pportdata *ppd, enum ib_event_type ev)72{73struct ib_event event;74struct qib_devdata *dd = ppd->dd;7576event.device = &dd->verbs_dev.ibdev;77event.element.port_num = ppd->port;78event.event = ev;79ib_dispatch_event(&event);80}8182void qib_handle_e_ibstatuschanged(struct qib_pportdata *ppd, u64 ibcs)83{84struct qib_devdata *dd = ppd->dd;85unsigned long flags;86u32 lstate;87u8 ltstate;88enum ib_event_type ev = 0;8990lstate = dd->f_iblink_state(ibcs); /* linkstate */91ltstate = dd->f_ibphys_portstate(ibcs);9293/*94* If linkstate transitions into INIT from any of the various down95* states, or if it transitions from any of the up (INIT or better)96* states into any of the down states (except link recovery), then97* call the chip-specific code to take appropriate actions.98*99* ppd->lflags could be 0 if this is the first time the interrupt100* handlers has been called but the link is already up.101*/102if (lstate >= IB_PORT_INIT &&103(!ppd->lflags || (ppd->lflags & QIBL_LINKDOWN)) &&104ltstate == IB_PHYSPORTSTATE_LINKUP) {105/* transitioned to UP */106if (dd->f_ib_updown(ppd, 1, ibcs))107goto skip_ibchange; /* chip-code handled */108} else if (ppd->lflags & (QIBL_LINKINIT | QIBL_LINKARMED |109QIBL_LINKACTIVE | QIBL_IB_FORCE_NOTIFY)) {110if (ltstate != IB_PHYSPORTSTATE_LINKUP &&111ltstate <= IB_PHYSPORTSTATE_CFG_TRAIN &&112dd->f_ib_updown(ppd, 0, ibcs))113goto skip_ibchange; /* chip-code handled */114qib_set_uevent_bits(ppd, _QIB_EVENT_LINKDOWN_BIT);115}116117if (lstate != IB_PORT_DOWN) {118/* lstate is INIT, ARMED, or ACTIVE */119if (lstate != IB_PORT_ACTIVE) {120*ppd->statusp &= ~QIB_STATUS_IB_READY;121if (ppd->lflags & QIBL_LINKACTIVE)122ev = IB_EVENT_PORT_ERR;123spin_lock_irqsave(&ppd->lflags_lock, flags);124if (lstate == IB_PORT_ARMED) {125ppd->lflags |= QIBL_LINKARMED | QIBL_LINKV;126ppd->lflags &= ~(QIBL_LINKINIT |127QIBL_LINKDOWN | QIBL_LINKACTIVE);128} else {129ppd->lflags |= QIBL_LINKINIT | QIBL_LINKV;130ppd->lflags &= ~(QIBL_LINKARMED |131QIBL_LINKDOWN | QIBL_LINKACTIVE);132}133spin_unlock_irqrestore(&ppd->lflags_lock, flags);134/* start a 75msec timer to clear symbol errors */135mod_timer(&ppd->symerr_clear_timer,136msecs_to_jiffies(75));137} else if (ltstate == IB_PHYSPORTSTATE_LINKUP &&138!(ppd->lflags & QIBL_LINKACTIVE)) {139/* active, but not active defered */140qib_hol_up(ppd); /* useful only for 6120 now */141*ppd->statusp |=142QIB_STATUS_IB_READY | QIB_STATUS_IB_CONF;143qib_clear_symerror_on_linkup((unsigned long)ppd);144spin_lock_irqsave(&ppd->lflags_lock, flags);145ppd->lflags |= QIBL_LINKACTIVE | QIBL_LINKV;146ppd->lflags &= ~(QIBL_LINKINIT |147QIBL_LINKDOWN | QIBL_LINKARMED);148spin_unlock_irqrestore(&ppd->lflags_lock, flags);149if (dd->flags & QIB_HAS_SEND_DMA)150qib_sdma_process_event(ppd,151qib_sdma_event_e30_go_running);152ev = IB_EVENT_PORT_ACTIVE;153dd->f_setextled(ppd, 1);154}155} else { /* down */156if (ppd->lflags & QIBL_LINKACTIVE)157ev = IB_EVENT_PORT_ERR;158spin_lock_irqsave(&ppd->lflags_lock, flags);159ppd->lflags |= QIBL_LINKDOWN | QIBL_LINKV;160ppd->lflags &= ~(QIBL_LINKINIT |161QIBL_LINKACTIVE | QIBL_LINKARMED);162spin_unlock_irqrestore(&ppd->lflags_lock, flags);163*ppd->statusp &= ~QIB_STATUS_IB_READY;164}165166skip_ibchange:167ppd->lastibcstat = ibcs;168if (ev)169signal_ib_event(ppd, ev);170return;171}172173void qib_clear_symerror_on_linkup(unsigned long opaque)174{175struct qib_pportdata *ppd = (struct qib_pportdata *)opaque;176177if (ppd->lflags & QIBL_LINKACTIVE)178return;179180ppd->ibport_data.z_symbol_error_counter =181ppd->dd->f_portcntr(ppd, QIBPORTCNTR_IBSYMBOLERR);182}183184/*185* Handle receive interrupts for user ctxts; this means a user186* process was waiting for a packet to arrive, and didn't want187* to poll.188*/189void qib_handle_urcv(struct qib_devdata *dd, u64 ctxtr)190{191struct qib_ctxtdata *rcd;192unsigned long flags;193int i;194195spin_lock_irqsave(&dd->uctxt_lock, flags);196for (i = dd->first_user_ctxt; dd->rcd && i < dd->cfgctxts; i++) {197if (!(ctxtr & (1ULL << i)))198continue;199rcd = dd->rcd[i];200if (!rcd || !rcd->cnt)201continue;202203if (test_and_clear_bit(QIB_CTXT_WAITING_RCV, &rcd->flag)) {204wake_up_interruptible(&rcd->wait);205dd->f_rcvctrl(rcd->ppd, QIB_RCVCTRL_INTRAVAIL_DIS,206rcd->ctxt);207} else if (test_and_clear_bit(QIB_CTXT_WAITING_URG,208&rcd->flag)) {209rcd->urgent++;210wake_up_interruptible(&rcd->wait);211}212}213spin_unlock_irqrestore(&dd->uctxt_lock, flags);214}215216void qib_bad_intrstatus(struct qib_devdata *dd)217{218static int allbits;219220/* separate routine, for better optimization of qib_intr() */221222/*223* We print the message and disable interrupts, in hope of224* having a better chance of debugging the problem.225*/226qib_dev_err(dd, "Read of chip interrupt status failed"227" disabling interrupts\n");228if (allbits++) {229/* disable interrupt delivery, something is very wrong */230if (allbits == 2)231dd->f_set_intr_state(dd, 0);232if (allbits == 3) {233qib_dev_err(dd, "2nd bad interrupt status, "234"unregistering interrupts\n");235dd->flags |= QIB_BADINTR;236dd->flags &= ~QIB_INITTED;237dd->f_free_irq(dd);238}239}240}241242243