Commit e6826c96 authored by Krishna Gudipati's avatar Krishna Gudipati Committed by James Bottomley
Browse files

[SCSI] bfa: Add support to read/update the FRU data.



- Add FRU sub-module to support FRU read/write/update.
- Add support to read/write from the temp FRU module.

[jejb: fix checkpatch issues]
Signed-off-by: default avatarKrishna Gudipati <kgudipat@brocade.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 4a49b044
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -165,6 +165,16 @@ bfa_com_phy_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)
	bfa_phy_memclaim(phy, phy_dma->kva_curp, phy_dma->dma_curp, mincfg);
}

static void
bfa_com_fru_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)
{
	struct bfa_fru_s	*fru = BFA_FRU(bfa);
	struct bfa_mem_dma_s	*fru_dma = BFA_MEM_FRU_DMA(bfa);

	bfa_fru_attach(fru, &bfa->ioc, bfa, bfa->trcmod, mincfg);
	bfa_fru_memclaim(fru, fru_dma->kva_curp, fru_dma->dma_curp, mincfg);
}

/*
 * BFA IOC FC related definitions
 */
@@ -1752,6 +1762,7 @@ bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
	struct bfa_mem_dma_s *flash_dma = BFA_MEM_FLASH_DMA(bfa);
	struct bfa_mem_dma_s *diag_dma = BFA_MEM_DIAG_DMA(bfa);
	struct bfa_mem_dma_s *phy_dma = BFA_MEM_PHY_DMA(bfa);
	struct bfa_mem_dma_s *fru_dma = BFA_MEM_FRU_DMA(bfa);

	WARN_ON((cfg == NULL) || (meminfo == NULL));

@@ -1776,6 +1787,8 @@ bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
	bfa_mem_dma_setup(meminfo, diag_dma, bfa_diag_meminfo());
	bfa_mem_dma_setup(meminfo, phy_dma,
			  bfa_phy_meminfo(cfg->drvcfg.min_cfg));
	bfa_mem_dma_setup(meminfo, fru_dma,
			  bfa_fru_meminfo(cfg->drvcfg.min_cfg));
}

/*
@@ -1848,6 +1861,7 @@ bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
	bfa_com_flash_attach(bfa, cfg->drvcfg.min_cfg);
	bfa_com_diag_attach(bfa);
	bfa_com_phy_attach(bfa, cfg->drvcfg.min_cfg);
	bfa_com_fru_attach(bfa, cfg->drvcfg.min_cfg);
}

/*
+1 −0
Original line number Diff line number Diff line
@@ -196,6 +196,7 @@ enum bfa_status {
	BFA_STATUS_DPORT_ENABLED = 235, /* D-port mode is already enabled */
	BFA_STATUS_DPORT_DISABLED = 236, /* D-port mode is already disabled */
	BFA_STATUS_CMD_NOTSUPP_MEZZ = 239, /* Cmd not supported for MEZZ card */
	BFA_STATUS_FRU_NOT_PRESENT = 240, /* fru module not present */
	BFA_STATUS_DPORT_ERR = 245,	/* D-port mode is enabled */
	BFA_STATUS_MAX_VAL		/* Unknown error code */
};
+445 −0
Original line number Diff line number Diff line
@@ -5956,3 +5956,448 @@ bfa_dconf_modexit(struct bfa_s *bfa)
	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
	bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
}

/*
 * FRU specific functions
 */

#define BFA_FRU_DMA_BUF_SZ	0x02000		/* 8k dma buffer */
#define BFA_FRU_CHINOOK_MAX_SIZE 0x10000
#define BFA_FRU_LIGHTNING_MAX_SIZE 0x200

static void
bfa_fru_notify(void *cbarg, enum bfa_ioc_event_e event)
{
	struct bfa_fru_s *fru = cbarg;

	bfa_trc(fru, event);

	switch (event) {
	case BFA_IOC_E_DISABLED:
	case BFA_IOC_E_FAILED:
		if (fru->op_busy) {
			fru->status = BFA_STATUS_IOC_FAILURE;
			fru->cbfn(fru->cbarg, fru->status);
			fru->op_busy = 0;
		}
		break;

	default:
		break;
	}
}

/*
 * Send fru write request.
 *
 * @param[in] cbarg - callback argument
 */
static void
bfa_fru_write_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type)
{
	struct bfa_fru_s *fru = cbarg;
	struct bfi_fru_write_req_s *msg =
			(struct bfi_fru_write_req_s *) fru->mb.msg;
	u32 len;

	msg->offset = cpu_to_be32(fru->addr_off + fru->offset);
	len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ?
				fru->residue : BFA_FRU_DMA_BUF_SZ;
	msg->length = cpu_to_be32(len);

	/*
	 * indicate if it's the last msg of the whole write operation
	 */
	msg->last = (len == fru->residue) ? 1 : 0;

	bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc));
	bfa_alen_set(&msg->alen, len, fru->dbuf_pa);

	memcpy(fru->dbuf_kva, fru->ubuf + fru->offset, len);
	bfa_ioc_mbox_queue(fru->ioc, &fru->mb);

	fru->residue -= len;
	fru->offset += len;
}

/*
 * Send fru read request.
 *
 * @param[in] cbarg - callback argument
 */
static void
bfa_fru_read_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type)
{
	struct bfa_fru_s *fru = cbarg;
	struct bfi_fru_read_req_s *msg =
			(struct bfi_fru_read_req_s *) fru->mb.msg;
	u32 len;

	msg->offset = cpu_to_be32(fru->addr_off + fru->offset);
	len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ?
				fru->residue : BFA_FRU_DMA_BUF_SZ;
	msg->length = cpu_to_be32(len);
	bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc));
	bfa_alen_set(&msg->alen, len, fru->dbuf_pa);
	bfa_ioc_mbox_queue(fru->ioc, &fru->mb);
}

/*
 * Flash memory info API.
 *
 * @param[in] mincfg - minimal cfg variable
 */
u32
bfa_fru_meminfo(bfa_boolean_t mincfg)
{
	/* min driver doesn't need fru */
	if (mincfg)
		return 0;

	return BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
}

/*
 * Flash attach API.
 *
 * @param[in] fru - fru structure
 * @param[in] ioc  - ioc structure
 * @param[in] dev  - device structure
 * @param[in] trcmod - trace module
 * @param[in] logmod - log module
 */
void
bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc, void *dev,
	struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg)
{
	fru->ioc = ioc;
	fru->trcmod = trcmod;
	fru->cbfn = NULL;
	fru->cbarg = NULL;
	fru->op_busy = 0;

	bfa_ioc_mbox_regisr(fru->ioc, BFI_MC_FRU, bfa_fru_intr, fru);
	bfa_q_qe_init(&fru->ioc_notify);
	bfa_ioc_notify_init(&fru->ioc_notify, bfa_fru_notify, fru);
	list_add_tail(&fru->ioc_notify.qe, &fru->ioc->notify_q);

	/* min driver doesn't need fru */
	if (mincfg) {
		fru->dbuf_kva = NULL;
		fru->dbuf_pa = 0;
	}
}

/*
 * Claim memory for fru
 *
 * @param[in] fru - fru structure
 * @param[in] dm_kva - pointer to virtual memory address
 * @param[in] dm_pa - frusical memory address
 * @param[in] mincfg - minimal cfg variable
 */
void
bfa_fru_memclaim(struct bfa_fru_s *fru, u8 *dm_kva, u64 dm_pa,
	bfa_boolean_t mincfg)
{
	if (mincfg)
		return;

	fru->dbuf_kva = dm_kva;
	fru->dbuf_pa = dm_pa;
	memset(fru->dbuf_kva, 0, BFA_FRU_DMA_BUF_SZ);
	dm_kva += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
	dm_pa += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
}

/*
 * Update fru vpd image.
 *
 * @param[in] fru - fru structure
 * @param[in] buf - update data buffer
 * @param[in] len - data buffer length
 * @param[in] offset - offset relative to starting address
 * @param[in] cbfn - callback function
 * @param[in] cbarg - callback argument
 *
 * Return status.
 */
bfa_status_t
bfa_fruvpd_update(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
		  bfa_cb_fru_t cbfn, void *cbarg)
{
	bfa_trc(fru, BFI_FRUVPD_H2I_WRITE_REQ);
	bfa_trc(fru, len);
	bfa_trc(fru, offset);

	if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
		return BFA_STATUS_FRU_NOT_PRESENT;

	if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK)
		return BFA_STATUS_CMD_NOTSUPP;

	if (!bfa_ioc_is_operational(fru->ioc))
		return BFA_STATUS_IOC_NON_OP;

	if (fru->op_busy) {
		bfa_trc(fru, fru->op_busy);
		return BFA_STATUS_DEVBUSY;
	}

	fru->op_busy = 1;

	fru->cbfn = cbfn;
	fru->cbarg = cbarg;
	fru->residue = len;
	fru->offset = 0;
	fru->addr_off = offset;
	fru->ubuf = buf;

	bfa_fru_write_send(fru, BFI_FRUVPD_H2I_WRITE_REQ);

	return BFA_STATUS_OK;
}

/*
 * Read fru vpd image.
 *
 * @param[in] fru - fru structure
 * @param[in] buf - read data buffer
 * @param[in] len - data buffer length
 * @param[in] offset - offset relative to starting address
 * @param[in] cbfn - callback function
 * @param[in] cbarg - callback argument
 *
 * Return status.
 */
bfa_status_t
bfa_fruvpd_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
		bfa_cb_fru_t cbfn, void *cbarg)
{
	bfa_trc(fru, BFI_FRUVPD_H2I_READ_REQ);
	bfa_trc(fru, len);
	bfa_trc(fru, offset);

	if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
		return BFA_STATUS_FRU_NOT_PRESENT;

	if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK)
		return BFA_STATUS_CMD_NOTSUPP;

	if (!bfa_ioc_is_operational(fru->ioc))
		return BFA_STATUS_IOC_NON_OP;

	if (fru->op_busy) {
		bfa_trc(fru, fru->op_busy);
		return BFA_STATUS_DEVBUSY;
	}

	fru->op_busy = 1;

	fru->cbfn = cbfn;
	fru->cbarg = cbarg;
	fru->residue = len;
	fru->offset = 0;
	fru->addr_off = offset;
	fru->ubuf = buf;
	bfa_fru_read_send(fru, BFI_FRUVPD_H2I_READ_REQ);

	return BFA_STATUS_OK;
}

/*
 * Get maximum size fru vpd image.
 *
 * @param[in] fru - fru structure
 * @param[out] size - maximum size of fru vpd data
 *
 * Return status.
 */
bfa_status_t
bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size)
{
	if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
		return BFA_STATUS_FRU_NOT_PRESENT;

	if (!bfa_ioc_is_operational(fru->ioc))
		return BFA_STATUS_IOC_NON_OP;

	if (fru->ioc->attr->card_type == BFA_MFG_TYPE_CHINOOK)
		*max_size = BFA_FRU_CHINOOK_MAX_SIZE;
	else
		return BFA_STATUS_CMD_NOTSUPP;
	return BFA_STATUS_OK;
}
/*
 * tfru write.
 *
 * @param[in] fru - fru structure
 * @param[in] buf - update data buffer
 * @param[in] len - data buffer length
 * @param[in] offset - offset relative to starting address
 * @param[in] cbfn - callback function
 * @param[in] cbarg - callback argument
 *
 * Return status.
 */
bfa_status_t
bfa_tfru_write(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
	       bfa_cb_fru_t cbfn, void *cbarg)
{
	bfa_trc(fru, BFI_TFRU_H2I_WRITE_REQ);
	bfa_trc(fru, len);
	bfa_trc(fru, offset);
	bfa_trc(fru, *((u8 *) buf));

	if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
		return BFA_STATUS_FRU_NOT_PRESENT;

	if (!bfa_ioc_is_operational(fru->ioc))
		return BFA_STATUS_IOC_NON_OP;

	if (fru->op_busy) {
		bfa_trc(fru, fru->op_busy);
		return BFA_STATUS_DEVBUSY;
	}

	fru->op_busy = 1;

	fru->cbfn = cbfn;
	fru->cbarg = cbarg;
	fru->residue = len;
	fru->offset = 0;
	fru->addr_off = offset;
	fru->ubuf = buf;

	bfa_fru_write_send(fru, BFI_TFRU_H2I_WRITE_REQ);

	return BFA_STATUS_OK;
}

/*
 * tfru read.
 *
 * @param[in] fru - fru structure
 * @param[in] buf - read data buffer
 * @param[in] len - data buffer length
 * @param[in] offset - offset relative to starting address
 * @param[in] cbfn - callback function
 * @param[in] cbarg - callback argument
 *
 * Return status.
 */
bfa_status_t
bfa_tfru_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
	      bfa_cb_fru_t cbfn, void *cbarg)
{
	bfa_trc(fru, BFI_TFRU_H2I_READ_REQ);
	bfa_trc(fru, len);
	bfa_trc(fru, offset);

	if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
		return BFA_STATUS_FRU_NOT_PRESENT;

	if (!bfa_ioc_is_operational(fru->ioc))
		return BFA_STATUS_IOC_NON_OP;

	if (fru->op_busy) {
		bfa_trc(fru, fru->op_busy);
		return BFA_STATUS_DEVBUSY;
	}

	fru->op_busy = 1;

	fru->cbfn = cbfn;
	fru->cbarg = cbarg;
	fru->residue = len;
	fru->offset = 0;
	fru->addr_off = offset;
	fru->ubuf = buf;
	bfa_fru_read_send(fru, BFI_TFRU_H2I_READ_REQ);

	return BFA_STATUS_OK;
}

/*
 * Process fru response messages upon receiving interrupts.
 *
 * @param[in] fruarg - fru structure
 * @param[in] msg - message structure
 */
void
bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg)
{
	struct bfa_fru_s *fru = fruarg;
	struct bfi_fru_rsp_s *rsp = (struct bfi_fru_rsp_s *)msg;
	u32 status;

	bfa_trc(fru, msg->mh.msg_id);

	if (!fru->op_busy) {
		/*
		 * receiving response after ioc failure
		 */
		bfa_trc(fru, 0x9999);
		return;
	}

	switch (msg->mh.msg_id) {
	case BFI_FRUVPD_I2H_WRITE_RSP:
	case BFI_TFRU_I2H_WRITE_RSP:
		status = be32_to_cpu(rsp->status);
		bfa_trc(fru, status);

		if (status != BFA_STATUS_OK || fru->residue == 0) {
			fru->status = status;
			fru->op_busy = 0;
			if (fru->cbfn)
				fru->cbfn(fru->cbarg, fru->status);
		} else {
			bfa_trc(fru, fru->offset);
			if (msg->mh.msg_id == BFI_FRUVPD_I2H_WRITE_RSP)
				bfa_fru_write_send(fru,
					BFI_FRUVPD_H2I_WRITE_REQ);
			else
				bfa_fru_write_send(fru,
					BFI_TFRU_H2I_WRITE_REQ);
		}
		break;
	case BFI_FRUVPD_I2H_READ_RSP:
	case BFI_TFRU_I2H_READ_RSP:
		status = be32_to_cpu(rsp->status);
		bfa_trc(fru, status);

		if (status != BFA_STATUS_OK) {
			fru->status = status;
			fru->op_busy = 0;
			if (fru->cbfn)
				fru->cbfn(fru->cbarg, fru->status);
		} else {
			u32 len = be32_to_cpu(rsp->length);

			bfa_trc(fru, fru->offset);
			bfa_trc(fru, len);

			memcpy(fru->ubuf + fru->offset, fru->dbuf_kva, len);
			fru->residue -= len;
			fru->offset += len;

			if (fru->residue == 0) {
				fru->status = status;
				fru->op_busy = 0;
				if (fru->cbfn)
					fru->cbfn(fru->cbarg, fru->status);
			} else {
				if (msg->mh.msg_id == BFI_FRUVPD_I2H_READ_RSP)
					bfa_fru_read_send(fru,
						BFI_FRUVPD_H2I_READ_REQ);
				else
					bfa_fru_read_send(fru,
						BFI_TFRU_H2I_READ_REQ);
			}
		}
		break;
	default:
		WARN_ON(1);
	}
}
+49 −0
Original line number Diff line number Diff line
@@ -701,6 +701,55 @@ void bfa_phy_memclaim(struct bfa_phy_s *phy,
		u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg);
void bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg);

/*
 * FRU module specific
 */
typedef void (*bfa_cb_fru_t) (void *cbarg, bfa_status_t status);

struct bfa_fru_s {
	struct bfa_ioc_s *ioc;		/* back pointer to ioc */
	struct bfa_trc_mod_s *trcmod;	/* trace module */
	u8		op_busy;	/* operation busy flag */
	u8		rsv[3];
	u32		residue;	/* residual length */
	u32		offset;		/* offset */
	bfa_status_t	status;		/* status */
	u8		*dbuf_kva;	/* dma buf virtual address */
	u64		dbuf_pa;	/* dma buf physical address */
	struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
	bfa_cb_fru_t	cbfn;		/* user callback function */
	void		*cbarg;		/* user callback arg */
	u8		*ubuf;		/* user supplied buffer */
	struct bfa_cb_qe_s	hcb_qe;	/* comp: BFA callback qelem */
	u32		addr_off;	/* fru address offset */
	struct bfa_mbox_cmd_s mb;	/* mailbox */
	struct bfa_ioc_notify_s ioc_notify; /* ioc event notify */
	struct bfa_mem_dma_s	fru_dma;
};

#define BFA_FRU(__bfa)	(&(__bfa)->modules.fru)
#define BFA_MEM_FRU_DMA(__bfa)	(&(BFA_FRU(__bfa)->fru_dma))

bfa_status_t bfa_fruvpd_update(struct bfa_fru_s *fru,
			void *buf, u32 len, u32 offset,
			bfa_cb_fru_t cbfn, void *cbarg);
bfa_status_t bfa_fruvpd_read(struct bfa_fru_s *fru,
			void *buf, u32 len, u32 offset,
			bfa_cb_fru_t cbfn, void *cbarg);
bfa_status_t bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size);
bfa_status_t bfa_tfru_write(struct bfa_fru_s *fru,
			void *buf, u32 len, u32 offset,
			bfa_cb_fru_t cbfn, void *cbarg);
bfa_status_t bfa_tfru_read(struct bfa_fru_s *fru,
			void *buf, u32 len, u32 offset,
			bfa_cb_fru_t cbfn, void *cbarg);
u32	bfa_fru_meminfo(bfa_boolean_t mincfg);
void bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc,
		void *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg);
void bfa_fru_memclaim(struct bfa_fru_s *fru,
		u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg);
void bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg);

/*
 * Driver Config( dconf) specific
 */
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ struct bfa_modules_s {
	struct bfa_diag_s	diag_mod;	/*  diagnostics module	*/
	struct bfa_phy_s	phy;		/*  phy module		*/
	struct bfa_dconf_mod_s	dconf_mod;	/*  DCONF common module	*/
	struct bfa_fru_s	fru;		/*  fru module		*/
};

/*
Loading