Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c679b599 authored by Vijaya Mohan Guvva's avatar Vijaya Mohan Guvva Committed by James Bottomley
Browse files

[SCSI] bfa: kdump fix on 815 and 825 adapters



Root cause: When kernel crashes, On brocade 815/825 adapters,
 bfa IOC state machine and FW doesn't get a notification and
hence are not cleanly shutdown. So registers holding driver/IOC
state information are not reset back to valid disabled/parking
values. This causes subsequent driver initialization to fail
during kdump kernel boot.

Fix description: during the initialization of first PCI function, reset
corresponding register when unclean shutown is detect by reading chip
registers. This will make sure that ioc/fw gets clean re-initialization.

Signed-off-by: default avatarVijaya Mohan Guvva <vmohan@brocade.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent f2a0cc3f
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -67,6 +67,14 @@ BFA_TRC_FILE(CNA, IOC);
			((__ioc)->ioc_hwif->ioc_sync_ack(__ioc))
#define bfa_ioc_sync_complete(__ioc)            \
			((__ioc)->ioc_hwif->ioc_sync_complete(__ioc))
#define bfa_ioc_set_cur_ioc_fwstate(__ioc, __fwstate)		\
			((__ioc)->ioc_hwif->ioc_set_fwstate(__ioc, __fwstate))
#define bfa_ioc_get_cur_ioc_fwstate(__ioc)		\
			((__ioc)->ioc_hwif->ioc_get_fwstate(__ioc))
#define bfa_ioc_set_alt_ioc_fwstate(__ioc, __fwstate)		\
		((__ioc)->ioc_hwif->ioc_set_alt_fwstate(__ioc, __fwstate))
#define bfa_ioc_get_alt_ioc_fwstate(__ioc)		\
			((__ioc)->ioc_hwif->ioc_get_alt_fwstate(__ioc))

#define bfa_ioc_mbox_cmd_pending(__ioc)		\
			(!list_empty(&((__ioc)->mbox_mod.cmd_q)) || \
@@ -698,7 +706,7 @@ bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf)
	}

	/* h/w sem init */
	fwstate = readl(iocpf->ioc->ioc_regs.ioc_fwstate);
	fwstate = bfa_ioc_get_cur_ioc_fwstate(iocpf->ioc);
	if (fwstate == BFI_IOC_UNINIT) {
		writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
		goto sem_get;
@@ -725,8 +733,8 @@ bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf)

	bfa_trc(iocpf->ioc, fwstate);
	bfa_trc(iocpf->ioc, swab32(fwhdr.exec));
	writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.ioc_fwstate);
	writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.alt_ioc_fwstate);
	bfa_ioc_set_cur_ioc_fwstate(iocpf->ioc, BFI_IOC_UNINIT);
	bfa_ioc_set_alt_ioc_fwstate(iocpf->ioc, BFI_IOC_UNINIT);

	/*
	 * Unlock the hw semaphore. Should be here only once per boot.
@@ -1037,7 +1045,7 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
		 */

	case IOCPF_E_TIMEOUT:
		writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
		bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
		bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
		break;

@@ -1138,7 +1146,7 @@ bfa_iocpf_sm_initfail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
	case IOCPF_E_SEMLOCKED:
		bfa_ioc_notify_fail(ioc);
		bfa_ioc_sync_leave(ioc);
		writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
		bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
		writel(1, ioc->ioc_regs.ioc_sem_reg);
		bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail);
		break;
@@ -1227,7 +1235,7 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
		bfa_ioc_notify_fail(ioc);
		if (!iocpf->auto_recover) {
			bfa_ioc_sync_leave(ioc);
			writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
			bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
			writel(1, ioc->ioc_regs.ioc_sem_reg);
			bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
		} else {
@@ -1519,7 +1527,7 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
	u32 boot_type;
	u32 boot_env;

	ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
	ioc_fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);

	if (force)
		ioc_fwstate = BFI_IOC_UNINIT;
@@ -2006,11 +2014,11 @@ bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_env)
	 * Initialize IOC state of all functions on a chip reset.
	 */
	if (boot_type == BFI_FWBOOT_TYPE_MEMTEST) {
		writel(BFI_IOC_MEMTEST, ioc->ioc_regs.ioc_fwstate);
		writel(BFI_IOC_MEMTEST, ioc->ioc_regs.alt_ioc_fwstate);
		bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_MEMTEST);
		bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_MEMTEST);
	} else {
		writel(BFI_IOC_INITING, ioc->ioc_regs.ioc_fwstate);
		writel(BFI_IOC_INITING, ioc->ioc_regs.alt_ioc_fwstate);
		bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_INITING);
		bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_INITING);
	}

	bfa_ioc_msgflush(ioc);
@@ -2038,7 +2046,7 @@ bfa_ioc_is_operational(struct bfa_ioc_s *ioc)
bfa_boolean_t
bfa_ioc_is_initialized(struct bfa_ioc_s *ioc)
{
	u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
	u32 r32 = bfa_ioc_get_cur_ioc_fwstate(ioc);

	return ((r32 != BFI_IOC_UNINIT) &&
		(r32 != BFI_IOC_INITING) &&
@@ -2430,12 +2438,12 @@ bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc)
	if (!bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled))
		return BFA_FALSE;

	ioc_state = readl(ioc->ioc_regs.ioc_fwstate);
	ioc_state = bfa_ioc_get_cur_ioc_fwstate(ioc);
	if (!bfa_ioc_state_disabled(ioc_state))
		return BFA_FALSE;

	if (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_FC_8G1P) {
		ioc_state = readl(ioc->ioc_regs.alt_ioc_fwstate);
		ioc_state = bfa_ioc_get_cur_ioc_fwstate(ioc);
		if (!bfa_ioc_state_disabled(ioc_state))
			return BFA_FALSE;
	}
@@ -2449,8 +2457,8 @@ bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc)
void
bfa_ioc_reset_fwstate(struct bfa_ioc_s *ioc)
{
	writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
	writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
	bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_UNINIT);
	bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_UNINIT);
}

#define BFA_MFG_NAME "Brocade"
@@ -2917,7 +2925,7 @@ bfa_iocpf_sem_timeout(void *ioc_arg)
static void
bfa_ioc_poll_fwinit(struct bfa_ioc_s *ioc)
{
	u32 fwstate = readl(ioc->ioc_regs.ioc_fwstate);
	u32 fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);

	bfa_trc(ioc, fwstate);

+6 −0
Original line number Diff line number Diff line
@@ -346,6 +346,12 @@ struct bfa_ioc_hwif_s {
	void		(*ioc_sync_ack)		(struct bfa_ioc_s *ioc);
	bfa_boolean_t	(*ioc_sync_complete)	(struct bfa_ioc_s *ioc);
	bfa_boolean_t	(*ioc_lpu_read_stat)	(struct bfa_ioc_s *ioc);
	void		(*ioc_set_fwstate)	(struct bfa_ioc_s *ioc,
					enum bfi_ioc_state fwstate);
	enum bfi_ioc_state	(*ioc_get_fwstate)	(struct bfa_ioc_s *ioc);
	void		(*ioc_set_alt_fwstate)	(struct bfa_ioc_s *ioc,
					enum bfi_ioc_state fwstate);
	enum bfi_ioc_state	(*ioc_get_alt_fwstate)	(struct bfa_ioc_s *ioc);
};

/*
+79 −7
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@

BFA_TRC_FILE(CNA, IOC_CB);

#define bfa_ioc_cb_join_pos(__ioc) ((u32) (1 << BFA_IOC_CB_JOIN_SH))

/*
 * forward declarations
 */
@@ -37,6 +39,12 @@ static void bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc);
static bfa_boolean_t bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_set_cur_ioc_fwstate(
			struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
static enum bfi_ioc_state bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_set_alt_ioc_fwstate(
			struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
static enum bfi_ioc_state bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc);

static struct bfa_ioc_hwif_s hwif_cb;

@@ -59,6 +67,10 @@ bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc)
	hwif_cb.ioc_sync_leave = bfa_ioc_cb_sync_leave;
	hwif_cb.ioc_sync_ack = bfa_ioc_cb_sync_ack;
	hwif_cb.ioc_sync_complete = bfa_ioc_cb_sync_complete;
	hwif_cb.ioc_set_fwstate = bfa_ioc_cb_set_cur_ioc_fwstate;
	hwif_cb.ioc_get_fwstate = bfa_ioc_cb_get_cur_ioc_fwstate;
	hwif_cb.ioc_set_alt_fwstate = bfa_ioc_cb_set_alt_ioc_fwstate;
	hwif_cb.ioc_get_alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate;

	ioc->ioc_hwif = &hwif_cb;
}
@@ -187,6 +199,20 @@ bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix)
static bfa_boolean_t
bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc)
{
	u32 ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);

	/**
	 * Driver load time.  If the join bit is set,
	 * it is due to an unclean exit by the driver for this
	 * PCI fn in the previous incarnation. Whoever comes here first
	 * should clean it up, no matter which PCI fn.
	 */
	if (ioc_fwstate & BFA_IOC_CB_JOIN_MASK) {
		writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
		writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
		return BFA_TRUE;
	}

	return bfa_ioc_cb_sync_complete(ioc);
}

@@ -212,24 +238,66 @@ bfa_ioc_cb_ownership_reset(struct bfa_ioc_s *ioc)
static void
bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc)
{
	u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
	u32 join_pos = bfa_ioc_cb_join_pos(ioc);

	writel((r32 | join_pos), ioc->ioc_regs.ioc_fwstate);
}

static void
bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc)
{
	u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
	u32 join_pos = bfa_ioc_cb_join_pos(ioc);

	writel((r32 & ~join_pos), ioc->ioc_regs.ioc_fwstate);
}

static void
bfa_ioc_cb_set_cur_ioc_fwstate(struct bfa_ioc_s *ioc,
			enum bfi_ioc_state fwstate)
{
	u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);

	writel((fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)),
				ioc->ioc_regs.ioc_fwstate);
}

static enum bfi_ioc_state
bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc)
{
	return (enum bfi_ioc_state)(readl(ioc->ioc_regs.ioc_fwstate) &
			BFA_IOC_CB_FWSTATE_MASK);
}

static void
bfa_ioc_cb_set_alt_ioc_fwstate(struct bfa_ioc_s *ioc,
			enum bfi_ioc_state fwstate)
{
	u32 r32 = readl(ioc->ioc_regs.alt_ioc_fwstate);

	writel((fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)),
				ioc->ioc_regs.alt_ioc_fwstate);
}

static enum bfi_ioc_state
bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc)
{
	return (enum bfi_ioc_state)(readl(ioc->ioc_regs.alt_ioc_fwstate) &
			BFA_IOC_CB_FWSTATE_MASK);
}

static void
bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc)
{
	writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
	bfa_ioc_cb_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
}

static bfa_boolean_t
bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
{
	uint32_t fwstate, alt_fwstate;
	fwstate = readl(ioc->ioc_regs.ioc_fwstate);
	u32 fwstate, alt_fwstate;
	fwstate = bfa_ioc_cb_get_cur_ioc_fwstate(ioc);

	/*
	 * At this point, this IOC is hoding the hw sem in the
@@ -257,7 +325,7 @@ bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
		fwstate == BFI_IOC_OP)
		return BFA_TRUE;
	else {
		alt_fwstate = readl(ioc->ioc_regs.alt_ioc_fwstate);
		alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate(ioc);
		if (alt_fwstate == BFI_IOC_FAIL ||
			alt_fwstate == BFI_IOC_DISABLED ||
			alt_fwstate == BFI_IOC_UNINIT ||
@@ -272,7 +340,7 @@ bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
bfa_status_t
bfa_ioc_cb_pll_init(void __iomem *rb, enum bfi_asic_mode fcmode)
{
	u32	pll_sclk, pll_fclk;
	u32	pll_sclk, pll_fclk, join_bits;

	pll_sclk = __APP_PLL_SCLK_ENABLE | __APP_PLL_SCLK_LRESETN |
		__APP_PLL_SCLK_P0_1(3U) |
@@ -282,8 +350,12 @@ bfa_ioc_cb_pll_init(void __iomem *rb, enum bfi_asic_mode fcmode)
		__APP_PLL_LCLK_RSEL200500 | __APP_PLL_LCLK_P0_1(3U) |
		__APP_PLL_LCLK_JITLMT0_1(3U) |
		__APP_PLL_LCLK_CNTLMT0_1(3U);
	writel(BFI_IOC_UNINIT, (rb + BFA_IOC0_STATE_REG));
	writel(BFI_IOC_UNINIT, (rb + BFA_IOC1_STATE_REG));
	join_bits = readl(rb + BFA_IOC0_STATE_REG) &
			BFA_IOC_CB_JOIN_MASK;
	writel((BFI_IOC_UNINIT | join_bits), (rb + BFA_IOC0_STATE_REG));
	join_bits = readl(rb + BFA_IOC1_STATE_REG) &
			BFA_IOC_CB_JOIN_MASK;
	writel((BFI_IOC_UNINIT | join_bits), (rb + BFA_IOC1_STATE_REG));
	writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
	writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
	writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
+36 −0
Original line number Diff line number Diff line
@@ -43,6 +43,12 @@ static void bfa_ioc_ct_sync_join(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_sync_leave(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_sync_ack(struct bfa_ioc_s *ioc);
static bfa_boolean_t bfa_ioc_ct_sync_complete(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_set_cur_ioc_fwstate(
			struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
static enum bfi_ioc_state bfa_ioc_ct_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_set_alt_ioc_fwstate(
			struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
static enum bfi_ioc_state bfa_ioc_ct_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc);

static struct bfa_ioc_hwif_s hwif_ct;
static struct bfa_ioc_hwif_s hwif_ct2;
@@ -512,6 +518,10 @@ bfa_ioc_set_ctx_hwif(struct bfa_ioc_s *ioc, struct bfa_ioc_hwif_s *hwif)
	hwif->ioc_sync_leave = bfa_ioc_ct_sync_leave;
	hwif->ioc_sync_ack = bfa_ioc_ct_sync_ack;
	hwif->ioc_sync_complete = bfa_ioc_ct_sync_complete;
	hwif->ioc_set_fwstate = bfa_ioc_ct_set_cur_ioc_fwstate;
	hwif->ioc_get_fwstate = bfa_ioc_ct_get_cur_ioc_fwstate;
	hwif->ioc_set_alt_fwstate = bfa_ioc_ct_set_alt_ioc_fwstate;
	hwif->ioc_get_alt_fwstate = bfa_ioc_ct_get_alt_ioc_fwstate;
}

/**
@@ -959,3 +969,29 @@ bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)

	return BFA_STATUS_OK;
}

static void
bfa_ioc_ct_set_cur_ioc_fwstate(struct bfa_ioc_s *ioc,
		enum bfi_ioc_state fwstate)
{
	writel(fwstate, ioc->ioc_regs.ioc_fwstate);
}

static enum bfi_ioc_state
bfa_ioc_ct_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc)
{
	return (enum bfi_ioc_state)readl(ioc->ioc_regs.ioc_fwstate);
}

static void
bfa_ioc_ct_set_alt_ioc_fwstate(struct bfa_ioc_s *ioc,
		enum bfi_ioc_state fwstate)
{
	writel(fwstate, ioc->ioc_regs.alt_ioc_fwstate);
}

static enum bfi_ioc_state
bfa_ioc_ct_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc)
{
	return (enum bfi_ioc_state) readl(ioc->ioc_regs.alt_ioc_fwstate);
}
+4 −0
Original line number Diff line number Diff line
@@ -374,6 +374,10 @@ enum bfi_ioc_state {
	BFI_IOC_MEMTEST		= 9,	/*  IOC is doing memtest	     */
};

#define BFA_IOC_CB_JOIN_SH	16
#define BFA_IOC_CB_FWSTATE_MASK	0x0000ffff
#define BFA_IOC_CB_JOIN_MASK	0xffff0000

#define BFI_IOC_ENDIAN_SIG  0x12345678

enum {