diff options
Diffstat (limited to 'drivers/net/ethernet/brocade/bna')
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_cee.c | 35 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_cee.h | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_defs.h | 98 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_ioc.c | 493 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_ioc.h | 54 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bfi.h | 97 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bna_enet.c | 13 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bna_types.h | 7 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad.c | 70 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad.h | 29 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad_debugfs.c | 623 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad_ethtool.c | 157 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/cna.h | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/brocade/bna/cna_fwimg.c | 1 | 
15 files changed, 1623 insertions, 63 deletions
diff --git a/drivers/net/ethernet/brocade/bna/Makefile b/drivers/net/ethernet/brocade/bna/Makefile index 74d3abca196..6027302ae73 100644 --- a/drivers/net/ethernet/brocade/bna/Makefile +++ b/drivers/net/ethernet/brocade/bna/Makefile @@ -5,7 +5,7 @@  obj-$(CONFIG_BNA) += bna.o -bna-objs := bnad.o bnad_ethtool.o bna_enet.o bna_tx_rx.o +bna-objs := bnad.o bnad_ethtool.o bnad_debugfs.o bna_enet.o bna_tx_rx.o  bna-objs += bfa_msgq.o bfa_ioc.o bfa_ioc_ct.o bfa_cee.o  bna-objs += cna_fwimg.o diff --git a/drivers/net/ethernet/brocade/bna/bfa_cee.c b/drivers/net/ethernet/brocade/bna/bfa_cee.c index 8e627186507..29f284f79e0 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_cee.c +++ b/drivers/net/ethernet/brocade/bna/bfa_cee.c @@ -185,6 +185,41 @@ bfa_nw_cee_mem_claim(struct bfa_cee *cee, u8 *dma_kva, u64 dma_pa)  }  /** + * bfa_cee_get_attr() + * + * @brief	Send the request to the f/w to fetch CEE attributes. + * + * @param[in]	Pointer to the CEE module data structure. + * + * @return	Status + */ +enum bfa_status +bfa_nw_cee_get_attr(struct bfa_cee *cee, struct bfa_cee_attr *attr, +		    bfa_cee_get_attr_cbfn_t cbfn, void *cbarg) +{ +	struct bfi_cee_get_req *cmd; + +	BUG_ON(!((cee != NULL) && (cee->ioc != NULL))); +	if (!bfa_nw_ioc_is_operational(cee->ioc)) +		return BFA_STATUS_IOC_FAILURE; + +	if (cee->get_attr_pending == true) +		return  BFA_STATUS_DEVBUSY; + +	cee->get_attr_pending = true; +	cmd = (struct bfi_cee_get_req *) cee->get_cfg_mb.msg; +	cee->attr = attr; +	cee->cbfn.get_attr_cbfn = cbfn; +	cee->cbfn.get_attr_cbarg = cbarg; +	bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ, +		    bfa_ioc_portid(cee->ioc)); +	bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa); +	bfa_nw_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb, NULL, NULL); + +	return BFA_STATUS_OK; +} + +/**   * bfa_cee_isrs()   *   * @brief Handles Mail-box interrupts for CEE module. diff --git a/drivers/net/ethernet/brocade/bna/bfa_cee.h b/drivers/net/ethernet/brocade/bna/bfa_cee.h index 58d54e98d59..93fde633d6f 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_cee.h +++ b/drivers/net/ethernet/brocade/bna/bfa_cee.h @@ -59,5 +59,7 @@ u32 bfa_nw_cee_meminfo(void);  void bfa_nw_cee_mem_claim(struct bfa_cee *cee, u8 *dma_kva,  	u64 dma_pa);  void bfa_nw_cee_attach(struct bfa_cee *cee, struct bfa_ioc *ioc, void *dev); - +enum bfa_status bfa_nw_cee_get_attr(struct bfa_cee *cee, +				struct bfa_cee_attr *attr, +				bfa_cee_get_attr_cbfn_t cbfn, void *cbarg);  #endif /* __BFA_CEE_H__ */ diff --git a/drivers/net/ethernet/brocade/bna/bfa_defs.h b/drivers/net/ethernet/brocade/bna/bfa_defs.h index 2f12d68021d..871c6309334 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_defs.h +++ b/drivers/net/ethernet/brocade/bna/bfa_defs.h @@ -219,41 +219,39 @@ enum {   * All numerical fields are in big-endian format.   */  struct bfa_mfg_block { -	u8		version;	/*!< manufacturing block version */ -	u8		mfg_sig[3];	/*!< characters 'M', 'F', 'G' */ -	u16	mfgsize;	/*!< mfg block size */ -	u16	u16_chksum;	/*!< old u16 checksum */ -	char		brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)]; -	char		brcd_partnum[STRSZ(BFA_MFG_PARTNUM_SIZE)]; -	u8		mfg_day;	/*!< manufacturing day */ -	u8		mfg_month;	/*!< manufacturing month */ -	u16	mfg_year;	/*!< manufacturing year */ -	u64		mfg_wwn;	/*!< wwn base for this adapter */ -	u8		num_wwn;	/*!< number of wwns assigned */ -	u8		mfg_speeds;	/*!< speeds allowed for this adapter */ -	u8		rsv[2]; -	char		supplier_id[STRSZ(BFA_MFG_SUPPLIER_ID_SIZE)]; -	char		supplier_partnum[STRSZ(BFA_MFG_SUPPLIER_PARTNUM_SIZE)]; -	char -		supplier_serialnum[STRSZ(BFA_MFG_SUPPLIER_SERIALNUM_SIZE)]; -	char -		supplier_revision[STRSZ(BFA_MFG_SUPPLIER_REVISION_SIZE)]; -	mac_t		mfg_mac;	/*!< mac address */ -	u8		num_mac;	/*!< number of mac addresses */ -	u8		rsv2; -	u32		card_type;	/*!< card type */ -	char		cap_nic;	/*!< capability nic */ -	char		cap_cna;	/*!< capability cna */ -	char		cap_hba;	/*!< capability hba */ -	char		cap_fc16g;	/*!< capability fc 16g */ -	char		cap_sriov;	/*!< capability sriov */ -	char		cap_mezz;	/*!< capability mezz */ -	u8		rsv3; -	u8		mfg_nports;	/*!< number of ports */ -	char		media[8];	/*!< xfi/xaui */ -	char		initial_mode[8];/*!< initial mode: hba/cna/nic */ -	u8		rsv4[84]; -	u8		md5_chksum[BFA_MFG_CHKSUM_SIZE]; /*!< md5 checksum */ +	u8	version;	/* manufacturing block version */ +	u8	mfg_sig[3];	/* characters 'M', 'F', 'G' */ +	u16	mfgsize;	/* mfg block size */ +	u16	u16_chksum;	/* old u16 checksum */ +	char	brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)]; +	char	brcd_partnum[STRSZ(BFA_MFG_PARTNUM_SIZE)]; +	u8	mfg_day;	/* manufacturing day */ +	u8	mfg_month;	/* manufacturing month */ +	u16	mfg_year;	/* manufacturing year */ +	u64	mfg_wwn;	/* wwn base for this adapter */ +	u8	num_wwn;	/* number of wwns assigned */ +	u8	mfg_speeds;	/* speeds allowed for this adapter */ +	u8	rsv[2]; +	char	supplier_id[STRSZ(BFA_MFG_SUPPLIER_ID_SIZE)]; +	char	supplier_partnum[STRSZ(BFA_MFG_SUPPLIER_PARTNUM_SIZE)]; +	char	supplier_serialnum[STRSZ(BFA_MFG_SUPPLIER_SERIALNUM_SIZE)]; +	char	supplier_revision[STRSZ(BFA_MFG_SUPPLIER_REVISION_SIZE)]; +	mac_t	mfg_mac;	/* base mac address */ +	u8	num_mac;	/* number of mac addresses */ +	u8	rsv2; +	u32	card_type;	/* card type          */ +	char	cap_nic;	/* capability nic     */ +	char	cap_cna;	/* capability cna     */ +	char	cap_hba;	/* capability hba     */ +	char	cap_fc16g;	/* capability fc 16g      */ +	char	cap_sriov;	/* capability sriov       */ +	char	cap_mezz;	/* capability mezz        */ +	u8	rsv3; +	u8	mfg_nports;	/* number of ports        */ +	char	media[8];	/* xfi/xaui           */ +	char	initial_mode[8]; /* initial mode: hba/cna/nic */ +	u8	rsv4[84]; +	u8	md5_chksum[BFA_MFG_CHKSUM_SIZE]; /* md5 checksum */  };  #pragma pack() @@ -293,4 +291,34 @@ enum bfa_mode {  	BFA_MODE_NIC		= 3  }; +/* + *	Flash module specific + */ +#define BFA_FLASH_PART_ENTRY_SIZE	32	/* partition entry size */ +#define BFA_FLASH_PART_MAX		32	/* maximal # of partitions */ +#define BFA_TOTAL_FLASH_SIZE		0x400000 +#define BFA_FLASH_PART_MFG		7 + +/* + * flash partition attributes + */ +struct bfa_flash_part_attr { +	u32	part_type;	/* partition type */ +	u32	part_instance;	/* partition instance */ +	u32	part_off;	/* partition offset */ +	u32	part_size;	/* partition size */ +	u32	part_len;	/* partition content length */ +	u32	part_status;	/* partition status */ +	char	rsv[BFA_FLASH_PART_ENTRY_SIZE - 24]; +}; + +/* + * flash attributes + */ +struct bfa_flash_attr { +	u32	status;	/* flash overall status */ +	u32	npart;  /* num of partitions */ +	struct bfa_flash_part_attr part[BFA_FLASH_PART_MAX]; +}; +  #endif /* __BFA_DEFS_H__ */ diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index b0307a00a10..abfad275b5f 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -74,6 +74,7 @@ static void bfa_ioc_check_attr_wwns(struct bfa_ioc *ioc);  static void bfa_ioc_event_notify(struct bfa_ioc *, enum bfa_ioc_event);  static void bfa_ioc_disable_comp(struct bfa_ioc *ioc);  static void bfa_ioc_lpu_stop(struct bfa_ioc *ioc); +static void bfa_nw_ioc_debug_save_ftrc(struct bfa_ioc *ioc);  static void bfa_ioc_fail_notify(struct bfa_ioc *ioc);  static void bfa_ioc_pf_enabled(struct bfa_ioc *ioc);  static void bfa_ioc_pf_disabled(struct bfa_ioc *ioc); @@ -997,6 +998,7 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf *iocpf, enum iocpf_event event)  static void  bfa_iocpf_sm_initfail_sync_entry(struct bfa_iocpf *iocpf)  { +	bfa_nw_ioc_debug_save_ftrc(iocpf->ioc);  	bfa_ioc_hw_sem_get(iocpf->ioc);  } @@ -1743,6 +1745,114 @@ bfa_ioc_mbox_flush(struct bfa_ioc *ioc)  		bfa_q_deq(&mod->cmd_q, &cmd);  } +/** + * Read data from SMEM to host through PCI memmap + * + * @param[in]  ioc     memory for IOC + * @param[in]  tbuf    app memory to store data from smem + * @param[in]  soff    smem offset + * @param[in]  sz      size of smem in bytes + */ +static int +bfa_nw_ioc_smem_read(struct bfa_ioc *ioc, void *tbuf, u32 soff, u32 sz) +{ +	u32 pgnum, loff, r32; +	int i, len; +	u32 *buf = tbuf; + +	pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, soff); +	loff = PSS_SMEM_PGOFF(soff); + +	/* +	 *  Hold semaphore to serialize pll init and fwtrc. +	*/ +	if (bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_init_sem_reg) == 0) +		return 1; + +	writel(pgnum, ioc->ioc_regs.host_page_num_fn); + +	len = sz/sizeof(u32); +	for (i = 0; i < len; i++) { +		r32 = swab32(readl((loff) + (ioc->ioc_regs.smem_page_start))); +		buf[i] = be32_to_cpu(r32); +		loff += sizeof(u32); + +		/** +		 * handle page offset wrap around +		 */ +		loff = PSS_SMEM_PGOFF(loff); +		if (loff == 0) { +			pgnum++; +			writel(pgnum, ioc->ioc_regs.host_page_num_fn); +		} +	} + +	writel(PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, 0), +	       ioc->ioc_regs.host_page_num_fn); + +	/* +	 * release semaphore +	 */ +	readl(ioc->ioc_regs.ioc_init_sem_reg); +	writel(1, ioc->ioc_regs.ioc_init_sem_reg); +	return 0; +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +int +bfa_nw_ioc_debug_fwtrc(struct bfa_ioc *ioc, void *trcdata, int *trclen) +{ +	u32 loff = BFI_IOC_TRC_OFF + BNA_DBG_FWTRC_LEN * ioc->port_id; +	int tlen, status = 0; + +	tlen = *trclen; +	if (tlen > BNA_DBG_FWTRC_LEN) +		tlen = BNA_DBG_FWTRC_LEN; + +	status = bfa_nw_ioc_smem_read(ioc, trcdata, loff, tlen); +	*trclen = tlen; +	return status; +} + +/** + * Save firmware trace if configured. + */ +static void +bfa_nw_ioc_debug_save_ftrc(struct bfa_ioc *ioc) +{ +	int tlen; + +	if (ioc->dbg_fwsave_once) { +		ioc->dbg_fwsave_once = 0; +		if (ioc->dbg_fwsave_len) { +			tlen = ioc->dbg_fwsave_len; +			bfa_nw_ioc_debug_fwtrc(ioc, ioc->dbg_fwsave, &tlen); +		} +	} +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +int +bfa_nw_ioc_debug_fwsave(struct bfa_ioc *ioc, void *trcdata, int *trclen) +{ +	int tlen; + +	if (ioc->dbg_fwsave_len == 0) +		return BFA_STATUS_ENOFSAVE; + +	tlen = *trclen; +	if (tlen > ioc->dbg_fwsave_len) +		tlen = ioc->dbg_fwsave_len; + +	memcpy(trcdata, ioc->dbg_fwsave, tlen); +	*trclen = tlen; +	return BFA_STATUS_OK; +} +  static void  bfa_ioc_fail_notify(struct bfa_ioc *ioc)  { @@ -1751,6 +1861,7 @@ bfa_ioc_fail_notify(struct bfa_ioc *ioc)  	 */  	ioc->cbfn->hbfail_cbfn(ioc->bfa);  	bfa_ioc_event_notify(ioc, BFA_IOC_E_FAILED); +	bfa_nw_ioc_debug_save_ftrc(ioc);  }  /** @@ -2058,6 +2169,16 @@ bfa_nw_ioc_disable(struct bfa_ioc *ioc)  	bfa_fsm_send_event(ioc, IOC_E_DISABLE);  } +/** + * Initialize memory for saving firmware trace. + */ +void +bfa_nw_ioc_debug_memclaim(struct bfa_ioc *ioc, void *dbg_fwsave) +{ +	ioc->dbg_fwsave = dbg_fwsave; +	ioc->dbg_fwsave_len = ioc->iocpf.auto_recover ? BNA_DBG_FWTRC_LEN : 0; +} +  static u32  bfa_ioc_smem_pgnum(struct bfa_ioc *ioc, u32 fmaddr)  { @@ -2172,6 +2293,15 @@ bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc)  }  /** + * return true if IOC is operational + */ +bool +bfa_nw_ioc_is_operational(struct bfa_ioc *ioc) +{ +	return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op); +} + +/**   * Add to IOC heartbeat failure notification queue. To be used by common   * modules such as cee, port, diag.   */ @@ -2471,3 +2601,366 @@ bfa_ioc_poll_fwinit(struct bfa_ioc *ioc)  			msecs_to_jiffies(BFA_IOC_POLL_TOV));  	}  } + +/* + *	Flash module specific + */ + +/* + * FLASH DMA buffer should be big enough to hold both MFG block and + * asic block(64k) at the same time and also should be 2k aligned to + * avoid write segement to cross sector boundary. + */ +#define BFA_FLASH_SEG_SZ	2048 +#define BFA_FLASH_DMA_BUF_SZ	\ +	roundup(0x010000 + sizeof(struct bfa_mfg_block), BFA_FLASH_SEG_SZ) + +static void +bfa_flash_cb(struct bfa_flash *flash) +{ +	flash->op_busy = 0; +	if (flash->cbfn) +		flash->cbfn(flash->cbarg, flash->status); +} + +static void +bfa_flash_notify(void *cbarg, enum bfa_ioc_event event) +{ +	struct bfa_flash *flash = cbarg; + +	switch (event) { +	case BFA_IOC_E_DISABLED: +	case BFA_IOC_E_FAILED: +		if (flash->op_busy) { +			flash->status = BFA_STATUS_IOC_FAILURE; +			flash->cbfn(flash->cbarg, flash->status); +			flash->op_busy = 0; +		} +		break; +	default: +		break; +	} +} + +/* + * Send flash write request. + * + * @param[in] cbarg - callback argument + */ +static void +bfa_flash_write_send(struct bfa_flash *flash) +{ +	struct bfi_flash_write_req *msg = +			(struct bfi_flash_write_req *) flash->mb.msg; +	u32	len; + +	msg->type = be32_to_cpu(flash->type); +	msg->instance = flash->instance; +	msg->offset = be32_to_cpu(flash->addr_off + flash->offset); +	len = (flash->residue < BFA_FLASH_DMA_BUF_SZ) ? +	       flash->residue : BFA_FLASH_DMA_BUF_SZ; +	msg->length = be32_to_cpu(len); + +	/* indicate if it's the last msg of the whole write operation */ +	msg->last = (len == flash->residue) ? 1 : 0; + +	bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_WRITE_REQ, +		    bfa_ioc_portid(flash->ioc)); +	bfa_alen_set(&msg->alen, len, flash->dbuf_pa); +	memcpy(flash->dbuf_kva, flash->ubuf + flash->offset, len); +	bfa_nw_ioc_mbox_queue(flash->ioc, &flash->mb, NULL, NULL); + +	flash->residue -= len; +	flash->offset += len; +} + +/* + * Send flash read request. + * + * @param[in] cbarg - callback argument + */ +static void +bfa_flash_read_send(void *cbarg) +{ +	struct bfa_flash *flash = cbarg; +	struct bfi_flash_read_req *msg = +			(struct bfi_flash_read_req *) flash->mb.msg; +	u32	len; + +	msg->type = be32_to_cpu(flash->type); +	msg->instance = flash->instance; +	msg->offset = be32_to_cpu(flash->addr_off + flash->offset); +	len = (flash->residue < BFA_FLASH_DMA_BUF_SZ) ? +	       flash->residue : BFA_FLASH_DMA_BUF_SZ; +	msg->length = be32_to_cpu(len); +	bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_READ_REQ, +		    bfa_ioc_portid(flash->ioc)); +	bfa_alen_set(&msg->alen, len, flash->dbuf_pa); +	bfa_nw_ioc_mbox_queue(flash->ioc, &flash->mb, NULL, NULL); +} + +/* + * Process flash response messages upon receiving interrupts. + * + * @param[in] flasharg - flash structure + * @param[in] msg - message structure + */ +static void +bfa_flash_intr(void *flasharg, struct bfi_mbmsg *msg) +{ +	struct bfa_flash *flash = flasharg; +	u32	status; + +	union { +		struct bfi_flash_query_rsp *query; +		struct bfi_flash_write_rsp *write; +		struct bfi_flash_read_rsp *read; +		struct bfi_mbmsg   *msg; +	} m; + +	m.msg = msg; + +	/* receiving response after ioc failure */ +	if (!flash->op_busy && msg->mh.msg_id != BFI_FLASH_I2H_EVENT) +		return; + +	switch (msg->mh.msg_id) { +	case BFI_FLASH_I2H_QUERY_RSP: +		status = be32_to_cpu(m.query->status); +		if (status == BFA_STATUS_OK) { +			u32	i; +			struct bfa_flash_attr *attr, *f; + +			attr = (struct bfa_flash_attr *) flash->ubuf; +			f = (struct bfa_flash_attr *) flash->dbuf_kva; +			attr->status = be32_to_cpu(f->status); +			attr->npart = be32_to_cpu(f->npart); +			for (i = 0; i < attr->npart; i++) { +				attr->part[i].part_type = +					be32_to_cpu(f->part[i].part_type); +				attr->part[i].part_instance = +					be32_to_cpu(f->part[i].part_instance); +				attr->part[i].part_off = +					be32_to_cpu(f->part[i].part_off); +				attr->part[i].part_size = +					be32_to_cpu(f->part[i].part_size); +				attr->part[i].part_len = +					be32_to_cpu(f->part[i].part_len); +				attr->part[i].part_status = +					be32_to_cpu(f->part[i].part_status); +			} +		} +		flash->status = status; +		bfa_flash_cb(flash); +		break; +	case BFI_FLASH_I2H_WRITE_RSP: +		status = be32_to_cpu(m.write->status); +		if (status != BFA_STATUS_OK || flash->residue == 0) { +			flash->status = status; +			bfa_flash_cb(flash); +		} else +			bfa_flash_write_send(flash); +		break; +	case BFI_FLASH_I2H_READ_RSP: +		status = be32_to_cpu(m.read->status); +		if (status != BFA_STATUS_OK) { +			flash->status = status; +			bfa_flash_cb(flash); +		} else { +			u32 len = be32_to_cpu(m.read->length); +			memcpy(flash->ubuf + flash->offset, +			       flash->dbuf_kva, len); +			flash->residue -= len; +			flash->offset += len; +			if (flash->residue == 0) { +				flash->status = status; +				bfa_flash_cb(flash); +			} else +				bfa_flash_read_send(flash); +		} +		break; +	case BFI_FLASH_I2H_BOOT_VER_RSP: +	case BFI_FLASH_I2H_EVENT: +		break; +	default: +		WARN_ON(1); +	} +} + +/* + * Flash memory info API. + */ +u32 +bfa_nw_flash_meminfo(void) +{ +	return roundup(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); +} + +/* + * Flash attach API. + * + * @param[in] flash - flash structure + * @param[in] ioc  - ioc structure + * @param[in] dev  - device structure + */ +void +bfa_nw_flash_attach(struct bfa_flash *flash, struct bfa_ioc *ioc, void *dev) +{ +	flash->ioc = ioc; +	flash->cbfn = NULL; +	flash->cbarg = NULL; +	flash->op_busy = 0; + +	bfa_nw_ioc_mbox_regisr(flash->ioc, BFI_MC_FLASH, bfa_flash_intr, flash); +	bfa_q_qe_init(&flash->ioc_notify); +	bfa_ioc_notify_init(&flash->ioc_notify, bfa_flash_notify, flash); +	list_add_tail(&flash->ioc_notify.qe, &flash->ioc->notify_q); +} + +/* + * Claim memory for flash + * + * @param[in] flash - flash structure + * @param[in] dm_kva - pointer to virtual memory address + * @param[in] dm_pa - physical memory address + */ +void +bfa_nw_flash_memclaim(struct bfa_flash *flash, u8 *dm_kva, u64 dm_pa) +{ +	flash->dbuf_kva = dm_kva; +	flash->dbuf_pa = dm_pa; +	memset(flash->dbuf_kva, 0, BFA_FLASH_DMA_BUF_SZ); +	dm_kva += roundup(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); +	dm_pa += roundup(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); +} + +/* + * Get flash attribute. + * + * @param[in] flash - flash structure + * @param[in] attr - flash attribute structure + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +enum bfa_status +bfa_nw_flash_get_attr(struct bfa_flash *flash, struct bfa_flash_attr *attr, +		      bfa_cb_flash cbfn, void *cbarg) +{ +	struct bfi_flash_query_req *msg = +			(struct bfi_flash_query_req *) flash->mb.msg; + +	if (!bfa_nw_ioc_is_operational(flash->ioc)) +		return BFA_STATUS_IOC_NON_OP; + +	if (flash->op_busy) +		return BFA_STATUS_DEVBUSY; + +	flash->op_busy = 1; +	flash->cbfn = cbfn; +	flash->cbarg = cbarg; +	flash->ubuf = (u8 *) attr; + +	bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_QUERY_REQ, +		    bfa_ioc_portid(flash->ioc)); +	bfa_alen_set(&msg->alen, sizeof(struct bfa_flash_attr), flash->dbuf_pa); +	bfa_nw_ioc_mbox_queue(flash->ioc, &flash->mb, NULL, NULL); + +	return BFA_STATUS_OK; +} + +/* + * Update flash partition. + * + * @param[in] flash - flash structure + * @param[in] type - flash partition type + * @param[in] instance - flash partition instance + * @param[in] buf - update data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to the partition starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +enum bfa_status +bfa_nw_flash_update_part(struct bfa_flash *flash, u32 type, u8 instance, +			 void *buf, u32 len, u32 offset, +			 bfa_cb_flash cbfn, void *cbarg) +{ +	if (!bfa_nw_ioc_is_operational(flash->ioc)) +		return BFA_STATUS_IOC_NON_OP; + +	/* +	 * 'len' must be in word (4-byte) boundary +	 */ +	if (!len || (len & 0x03)) +		return BFA_STATUS_FLASH_BAD_LEN; + +	if (type == BFA_FLASH_PART_MFG) +		return BFA_STATUS_EINVAL; + +	if (flash->op_busy) +		return BFA_STATUS_DEVBUSY; + +	flash->op_busy = 1; +	flash->cbfn = cbfn; +	flash->cbarg = cbarg; +	flash->type = type; +	flash->instance = instance; +	flash->residue = len; +	flash->offset = 0; +	flash->addr_off = offset; +	flash->ubuf = buf; + +	bfa_flash_write_send(flash); + +	return BFA_STATUS_OK; +} + +/* + * Read flash partition. + * + * @param[in] flash - flash structure + * @param[in] type - flash partition type + * @param[in] instance - flash partition instance + * @param[in] buf - read data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to the partition starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +enum bfa_status +bfa_nw_flash_read_part(struct bfa_flash *flash, u32 type, u8 instance, +		       void *buf, u32 len, u32 offset, +		       bfa_cb_flash cbfn, void *cbarg) +{ +	if (!bfa_nw_ioc_is_operational(flash->ioc)) +		return BFA_STATUS_IOC_NON_OP; + +	/* +	 * 'len' must be in word (4-byte) boundary +	 */ +	if (!len || (len & 0x03)) +		return BFA_STATUS_FLASH_BAD_LEN; + +	if (flash->op_busy) +		return BFA_STATUS_DEVBUSY; + +	flash->op_busy = 1; +	flash->cbfn = cbfn; +	flash->cbarg = cbarg; +	flash->type = type; +	flash->instance = instance; +	flash->residue = len; +	flash->offset = 0; +	flash->addr_off = offset; +	flash->ubuf = buf; + +	bfa_flash_read_send(flash); + +	return BFA_STATUS_OK; +} diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h index ca158d1eaef..3b4460fdc14 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.h +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h @@ -27,6 +27,8 @@  #define BFA_IOC_HWSEM_TOV	500	/* msecs */  #define BFA_IOC_HB_TOV		500	/* msecs */  #define BFA_IOC_POLL_TOV	200	/* msecs */ +#define BNA_DBG_FWTRC_LEN      (BFI_IOC_TRC_ENTS * BFI_IOC_TRC_ENT_SZ + \ +				BFI_IOC_TRC_HDR_SZ)  /**   * PCI device information required by IOC @@ -68,6 +70,16 @@ __bfa_dma_be_addr_set(union bfi_addr_u *dma_addr, u64 pa)  	dma_addr->a32.addr_hi = (u32) htonl(upper_32_bits(pa));  } +#define bfa_alen_set(__alen, __len, __pa)	\ +	__bfa_alen_set(__alen, __len, (u64)__pa) + +static inline void +__bfa_alen_set(struct bfi_alen *alen, u32 len, u64 pa) +{ +	alen->al_len = cpu_to_be32(len); +	bfa_dma_be_addr_set(alen->al_addr, pa); +} +  struct bfa_ioc_regs {  	void __iomem *hfn_mbox_cmd;  	void __iomem *hfn_mbox; @@ -296,6 +308,7 @@ void bfa_nw_ioc_disable(struct bfa_ioc *ioc);  void bfa_nw_ioc_error_isr(struct bfa_ioc *ioc);  bool bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc); +bool bfa_nw_ioc_is_operational(struct bfa_ioc *ioc);  void bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr);  void bfa_nw_ioc_notify_register(struct bfa_ioc *ioc,  	struct bfa_ioc_notify *notify); @@ -307,6 +320,9 @@ void bfa_nw_ioc_fwver_get(struct bfa_ioc *ioc,  bool bfa_nw_ioc_fwver_cmp(struct bfa_ioc *ioc,  			struct bfi_ioc_image_hdr *fwhdr);  mac_t bfa_nw_ioc_get_mac(struct bfa_ioc *ioc); +void bfa_nw_ioc_debug_memclaim(struct bfa_ioc *ioc, void *dbg_fwsave); +int bfa_nw_ioc_debug_fwtrc(struct bfa_ioc *ioc, void *trcdata, int *trclen); +int bfa_nw_ioc_debug_fwsave(struct bfa_ioc *ioc, void *trcdata, int *trclen);  /*   * Timeout APIs @@ -322,4 +338,42 @@ void bfa_nw_iocpf_sem_timeout(void *ioc);  u32 *bfa_cb_image_get_chunk(enum bfi_asic_gen asic_gen, u32 off);  u32 bfa_cb_image_get_size(enum bfi_asic_gen asic_gen); +/* + *	Flash module specific + */ +typedef void	(*bfa_cb_flash) (void *cbarg, enum bfa_status status); + +struct bfa_flash { +	struct bfa_ioc *ioc;		/* back pointer to ioc */ +	u32		type;		/* partition type */ +	u8		instance;	/* partition instance */ +	u8		rsv[3]; +	u32		op_busy;	/*  operation busy flag */ +	u32		residue;	/*  residual length */ +	u32		offset;		/*  offset */ +	enum bfa_status	status;		/*  status */ +	u8		*dbuf_kva;	/*  dma buf virtual address */ +	u64		dbuf_pa;	/*  dma buf physical address */ +	bfa_cb_flash	cbfn;		/*  user callback function */ +	void		*cbarg;		/*  user callback arg */ +	u8		*ubuf;		/*  user supplied buffer */ +	u32		addr_off;	/*  partition address offset */ +	struct bfa_mbox_cmd mb;		/*  mailbox */ +	struct bfa_ioc_notify ioc_notify; /*  ioc event notify */ +}; + +enum bfa_status bfa_nw_flash_get_attr(struct bfa_flash *flash, +			struct bfa_flash_attr *attr, +			bfa_cb_flash cbfn, void *cbarg); +enum bfa_status bfa_nw_flash_update_part(struct bfa_flash *flash, +			u32 type, u8 instance, void *buf, u32 len, u32 offset, +			bfa_cb_flash cbfn, void *cbarg); +enum bfa_status bfa_nw_flash_read_part(struct bfa_flash *flash, +			u32 type, u8 instance, void *buf, u32 len, u32 offset, +			bfa_cb_flash cbfn, void *cbarg); +u32	bfa_nw_flash_meminfo(void); +void	bfa_nw_flash_attach(struct bfa_flash *flash, +			    struct bfa_ioc *ioc, void *dev); +void	bfa_nw_flash_memclaim(struct bfa_flash *flash, u8 *dm_kva, u64 dm_pa); +  #endif /* __BFA_IOC_H__ */ diff --git a/drivers/net/ethernet/brocade/bna/bfi.h b/drivers/net/ethernet/brocade/bna/bfi.h index 7a1393aabd4..0d9df695397 100644 --- a/drivers/net/ethernet/brocade/bna/bfi.h +++ b/drivers/net/ethernet/brocade/bna/bfi.h @@ -83,6 +83,14 @@ union bfi_addr_u {  	} a32;  }; +/** + * Generic DMA addr-len pair. + */ +struct bfi_alen { +	union bfi_addr_u	al_addr;	/* DMA addr of buffer	*/ +	u32			al_len;		/* length of buffer */ +}; +  /*   * Large Message structure - 128 Bytes size Msgs   */ @@ -249,6 +257,8 @@ struct bfi_ioc_getattr_reply {   */  #define BFI_IOC_TRC_OFF		(0x4b00)  #define BFI_IOC_TRC_ENTS	256 +#define BFI_IOC_TRC_ENT_SZ	16 +#define BFI_IOC_TRC_HDR_SZ	32  #define BFI_IOC_FW_SIGNATURE	(0xbfadbfad)  #define BFI_IOC_MD5SUM_SZ	4 @@ -476,6 +486,93 @@ struct bfi_msgq_i2h_cmdq_copy_req {  	u16     len;  }; +/* + *      FLASH module specific + */ +enum bfi_flash_h2i_msgs { +	BFI_FLASH_H2I_QUERY_REQ = 1, +	BFI_FLASH_H2I_ERASE_REQ = 2, +	BFI_FLASH_H2I_WRITE_REQ = 3, +	BFI_FLASH_H2I_READ_REQ = 4, +	BFI_FLASH_H2I_BOOT_VER_REQ = 5, +}; + +enum bfi_flash_i2h_msgs { +	BFI_FLASH_I2H_QUERY_RSP = BFA_I2HM(1), +	BFI_FLASH_I2H_ERASE_RSP = BFA_I2HM(2), +	BFI_FLASH_I2H_WRITE_RSP = BFA_I2HM(3), +	BFI_FLASH_I2H_READ_RSP = BFA_I2HM(4), +	BFI_FLASH_I2H_BOOT_VER_RSP = BFA_I2HM(5), +	BFI_FLASH_I2H_EVENT = BFA_I2HM(127), +}; + +/* + * Flash query request + */ +struct bfi_flash_query_req { +	struct bfi_mhdr mh;   /* Common msg header */ +	struct bfi_alen alen; +}; + +/* + * Flash write request + */ +struct bfi_flash_write_req { +	struct bfi_mhdr mh;	/* Common msg header */ +	struct bfi_alen alen; +	u32	type;   /* partition type */ +	u8	instance; /* partition instance */ +	u8	last; +	u8	rsv[2]; +	u32	offset; +	u32	length; +}; + +/* + * Flash read request + */ +struct bfi_flash_read_req { +	struct bfi_mhdr mh;	/* Common msg header */ +	u32	type;		/* partition type */ +	u8	instance;	/* partition instance */ +	u8	rsv[3]; +	u32	offset; +	u32	length; +	struct bfi_alen alen; +}; + +/* + * Flash query response + */ +struct bfi_flash_query_rsp { +	struct bfi_mhdr mh;	/* Common msg header */ +	u32	status; +}; + +/* + * Flash read response + */ +struct bfi_flash_read_rsp { +	struct bfi_mhdr mh;	/* Common msg header */ +	u32	type;		/* partition type */ +	u8	instance;	/* partition instance */ +	u8	rsv[3]; +	u32	status; +	u32	length; +}; + +/* + * Flash write response + */ +struct bfi_flash_write_rsp { +	struct bfi_mhdr mh;	/* Common msg header */ +	u32	type;		/* partition type */ +	u8	instance;	/* partition instance */ +	u8	rsv[3]; +	u32	status; +	u32	length; +}; +  #pragma pack()  #endif /* __BFI_H__ */ diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c index 26f5c5abfd1..9ccc586e376 100644 --- a/drivers/net/ethernet/brocade/bna/bna_enet.c +++ b/drivers/net/ethernet/brocade/bna/bna_enet.c @@ -1727,6 +1727,7 @@ bna_ioceth_init(struct bna_ioceth *ioceth, struct bna *bna,  	bfa_nw_ioc_mem_claim(&ioceth->ioc, kva, dma);  	kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva; +	bfa_nw_ioc_debug_memclaim(&ioceth->ioc, kva);  	/**  	 * Attach common modules (Diag, SFP, CEE, Port) and claim respective @@ -1740,6 +1741,11 @@ bna_ioceth_init(struct bna_ioceth *ioceth, struct bna *bna,  	kva += bfa_nw_cee_meminfo();  	dma += bfa_nw_cee_meminfo(); +	bfa_nw_flash_attach(&bna->flash, &ioceth->ioc, bna); +	bfa_nw_flash_memclaim(&bna->flash, kva, dma); +	kva += bfa_nw_flash_meminfo(); +	dma += bfa_nw_flash_meminfo(); +  	bfa_msgq_attach(&bna->msgq, &ioceth->ioc);  	bfa_msgq_memclaim(&bna->msgq, kva, dma);  	bfa_msgq_regisr(&bna->msgq, BFI_MC_ENET, bna_msgq_rsp_handler, bna); @@ -1892,7 +1898,8 @@ bna_res_req(struct bna_res_info *res_info)  	res_info[BNA_RES_MEM_T_COM].res_u.mem_info.num = 1;  	res_info[BNA_RES_MEM_T_COM].res_u.mem_info.len = ALIGN(  				(bfa_nw_cee_meminfo() + -				bfa_msgq_meminfo()), PAGE_SIZE); +				 bfa_nw_flash_meminfo() + +				 bfa_msgq_meminfo()), PAGE_SIZE);  	/* DMA memory for retrieving IOC attributes */  	res_info[BNA_RES_MEM_T_ATTR].res_type = BNA_RES_T_MEM; @@ -1904,8 +1911,8 @@ bna_res_req(struct bna_res_info *res_info)  	/* Virtual memory for retreiving fw_trc */  	res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM;  	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA; -	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 0; -	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = 0; +	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 1; +	res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = BNA_DBG_FWTRC_LEN;  	/* DMA memory for retreiving stats */  	res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM; diff --git a/drivers/net/ethernet/brocade/bna/bna_types.h b/drivers/net/ethernet/brocade/bna/bna_types.h index d090fbfb12f..e8d3ab7ea6c 100644 --- a/drivers/net/ethernet/brocade/bna/bna_types.h +++ b/drivers/net/ethernet/brocade/bna/bna_types.h @@ -427,7 +427,7 @@ struct bna_ethport {  /* Doorbell structure */  struct bna_ib_dbell { -	void *__iomem doorbell_addr; +	void __iomem   *doorbell_addr;  	u32		doorbell_ack;  }; @@ -463,7 +463,7 @@ struct bna_tcb {  	u32		consumer_index;  	volatile u32	*hw_consumer_index;  	u32		q_depth; -	void *__iomem q_dbell; +	void __iomem   *q_dbell;  	struct bna_ib_dbell *i_dbell;  	int			page_idx;  	int			page_count; @@ -599,7 +599,7 @@ struct bna_rcb {  	u32		producer_index;  	u32		consumer_index;  	u32		q_depth; -	void *__iomem q_dbell; +	void __iomem   *q_dbell;  	int			page_idx;  	int			page_count;  	/* Control path */ @@ -966,6 +966,7 @@ struct bna {  	struct bna_ioceth ioceth;  	struct bfa_cee cee; +	struct bfa_flash flash;  	struct bfa_msgq msgq;  	struct bna_ethport ethport; diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 7f3091e7eb4..be7d91e4b78 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -44,11 +44,18 @@ static uint bnad_ioc_auto_recover = 1;  module_param(bnad_ioc_auto_recover, uint, 0444);  MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery"); +static uint bna_debugfs_enable = 1; +module_param(bna_debugfs_enable, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(bna_debugfs_enable, "Enables debugfs feature, default=1," +		 " Range[false:0|true:1]"); +  /*   * Global variables   */  u32 bnad_rxqs_per_cq = 2; - +static u32 bna_id; +static struct mutex bnad_list_mutex; +static LIST_HEAD(bnad_list);  static const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};  /* @@ -75,6 +82,23 @@ do {								\  #define BNAD_TXRX_SYNC_MDELAY	250	/* 250 msecs */ +static void +bnad_add_to_list(struct bnad *bnad) +{ +	mutex_lock(&bnad_list_mutex); +	list_add_tail(&bnad->list_entry, &bnad_list); +	bnad->id = bna_id++; +	mutex_unlock(&bnad_list_mutex); +} + +static void +bnad_remove_from_list(struct bnad *bnad) +{ +	mutex_lock(&bnad_list_mutex); +	list_del(&bnad->list_entry); +	mutex_unlock(&bnad_list_mutex); +} +  /*   * Reinitialize completions in CQ, once Rx is taken down   */ @@ -723,7 +747,7 @@ void  bnad_cb_ethport_link_status(struct bnad *bnad,  			enum bna_link_status link_status)  { -	bool link_up = 0; +	bool link_up = false;  	link_up = (link_status == BNA_LINK_UP) || (link_status == BNA_CEE_UP); @@ -1084,6 +1108,16 @@ bnad_cb_enet_mtu_set(struct bnad *bnad)  	complete(&bnad->bnad_completions.mtu_comp);  } +void +bnad_cb_completion(void *arg, enum bfa_status status) +{ +	struct bnad_iocmd_comp *iocmd_comp = +			(struct bnad_iocmd_comp *)arg; + +	iocmd_comp->comp_status = (u32) status; +	complete(&iocmd_comp->comp); +} +  /* Resource allocation, free functions */  static void @@ -2968,7 +3002,7 @@ bnad_change_mtu(struct net_device *netdev, int new_mtu)  	return err;  } -static void +static int  bnad_vlan_rx_add_vid(struct net_device *netdev,  				 unsigned short vid)  { @@ -2976,7 +3010,7 @@ bnad_vlan_rx_add_vid(struct net_device *netdev,  	unsigned long flags;  	if (!bnad->rx_info[0].rx) -		return; +		return 0;  	mutex_lock(&bnad->conf_mutex); @@ -2986,9 +3020,11 @@ bnad_vlan_rx_add_vid(struct net_device *netdev,  	spin_unlock_irqrestore(&bnad->bna_lock, flags);  	mutex_unlock(&bnad->conf_mutex); + +	return 0;  } -static void +static int  bnad_vlan_rx_kill_vid(struct net_device *netdev,  				  unsigned short vid)  { @@ -2996,7 +3032,7 @@ bnad_vlan_rx_kill_vid(struct net_device *netdev,  	unsigned long flags;  	if (!bnad->rx_info[0].rx) -		return; +		return 0;  	mutex_lock(&bnad->conf_mutex); @@ -3006,6 +3042,8 @@ bnad_vlan_rx_kill_vid(struct net_device *netdev,  	spin_unlock_irqrestore(&bnad->bna_lock, flags);  	mutex_unlock(&bnad->conf_mutex); + +	return 0;  }  #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3163,12 +3201,14 @@ bnad_lock_init(struct bnad *bnad)  {  	spin_lock_init(&bnad->bna_lock);  	mutex_init(&bnad->conf_mutex); +	mutex_init(&bnad_list_mutex);  }  static void  bnad_lock_uninit(struct bnad *bnad)  {  	mutex_destroy(&bnad->conf_mutex); +	mutex_destroy(&bnad_list_mutex);  }  /* PCI Initialization */ @@ -3186,7 +3226,7 @@ bnad_pci_init(struct bnad *bnad,  		goto disable_device;  	if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) &&  	    !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { -		*using_dac = 1; +		*using_dac = true;  	} else {  		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));  		if (err) { @@ -3195,7 +3235,7 @@ bnad_pci_init(struct bnad *bnad,  			if (err)  				goto release_regions;  		} -		*using_dac = 0; +		*using_dac = false;  	}  	pci_set_master(pdev);  	return 0; @@ -3249,8 +3289,8 @@ bnad_pci_probe(struct pci_dev *pdev,  		return err;  	}  	bnad = netdev_priv(netdev); -  	bnad_lock_init(bnad); +	bnad_add_to_list(bnad);  	mutex_lock(&bnad->conf_mutex);  	/* @@ -3277,6 +3317,10 @@ bnad_pci_probe(struct pci_dev *pdev,  	/* Set link to down state */  	netif_carrier_off(netdev); +	/* Setup the debugfs node for this bfad */ +	if (bna_debugfs_enable) +		bnad_debugfs_init(bnad); +  	/* Get resource requirement form bna */  	spin_lock_irqsave(&bnad->bna_lock, flags);  	bna_res_req(&bnad->res_info[0]); @@ -3398,11 +3442,15 @@ disable_ioceth:  res_free:  	bnad_res_free(bnad, &bnad->res_info[0], BNA_RES_T_MAX);  drv_uninit: +	/* Remove the debugfs node for this bnad */ +	kfree(bnad->regdata); +	bnad_debugfs_uninit(bnad);  	bnad_uninit(bnad);  pci_uninit:  	bnad_pci_uninit(pdev);  unlock_mutex:  	mutex_unlock(&bnad->conf_mutex); +	bnad_remove_from_list(bnad);  	bnad_lock_uninit(bnad);  	free_netdev(netdev);  	return err; @@ -3441,7 +3489,11 @@ bnad_pci_remove(struct pci_dev *pdev)  	bnad_disable_msix(bnad);  	bnad_pci_uninit(pdev);  	mutex_unlock(&bnad->conf_mutex); +	bnad_remove_from_list(bnad);  	bnad_lock_uninit(bnad); +	/* Remove the debugfs node for this bnad */ +	kfree(bnad->regdata); +	bnad_debugfs_uninit(bnad);  	bnad_uninit(bnad);  	free_netdev(netdev);  } diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index 5487ca42d01..55824d92699 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h @@ -124,6 +124,12 @@ enum bnad_link_state {  	BNAD_LS_UP		= 1  }; +struct bnad_iocmd_comp { +	struct bnad		*bnad; +	struct completion	comp; +	int			comp_status; +}; +  struct bnad_completion {  	struct completion	ioc_comp;  	struct completion	ucast_comp; @@ -251,6 +257,8 @@ struct bnad_unmap_q {  struct bnad {  	struct net_device	*netdev; +	u32			id; +	struct list_head	list_entry;  	/* Data path */  	struct bnad_tx_info tx_info[BNAD_MAX_TX]; @@ -320,12 +328,26 @@ struct bnad {  	char			adapter_name[BNAD_NAME_LEN];  	char			port_name[BNAD_NAME_LEN];  	char			mbox_irq_name[BNAD_NAME_LEN]; + +	/* debugfs specific data */ +	char	*regdata; +	u32	reglen; +	struct dentry *bnad_dentry_files[5]; +	struct dentry *port_debugfs_root; +}; + +struct bnad_drvinfo { +	struct bfa_ioc_attr  ioc_attr; +	struct bfa_cee_attr  cee_attr; +	struct bfa_flash_attr flash_attr; +	u32	cee_status; +	u32	flash_status;  };  /*   * EXTERN VARIABLES   */ -extern struct firmware *bfi_fw; +extern const struct firmware *bfi_fw;  extern u32		bnad_rxqs_per_cq;  /* @@ -340,6 +362,7 @@ extern int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr);  extern int bnad_enable_default_bcast(struct bnad *bnad);  extern void bnad_restore_vlans(struct bnad *bnad, u32 rx_id);  extern void bnad_set_ethtool_ops(struct net_device *netdev); +extern void bnad_cb_completion(void *arg, enum bfa_status status);  /* Configuration & setup */  extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad); @@ -359,6 +382,10 @@ extern void bnad_netdev_qstats_fill(struct bnad *bnad,  extern void bnad_netdev_hwstats_fill(struct bnad *bnad,  		struct rtnl_link_stats64 *stats); +/* Debugfs */ +void	bnad_debugfs_init(struct bnad *bnad); +void	bnad_debugfs_uninit(struct bnad *bnad); +  /**   * MACROS   */ diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c new file mode 100644 index 00000000000..592ad3929f5 --- /dev/null +++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c @@ -0,0 +1,623 @@ +/* + * Linux network driver for Brocade Converged Network Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ +/* + * Copyright (c) 2005-2011 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + */ + +#include <linux/debugfs.h> +#include <linux/module.h> +#include "bnad.h" + +/* + * BNA debufs interface + * + * To access the interface, debugfs file system should be mounted + * if not already mounted using: + *	mount -t debugfs none /sys/kernel/debug + * + * BNA Hierarchy: + *	- bna/pci_dev:<pci_name> + * where the pci_name corresponds to the one under /sys/bus/pci/drivers/bna + * + * Debugging service available per pci_dev: + *	fwtrc:  To collect current firmware trace. + *	fwsave: To collect last saved fw trace as a result of firmware crash. + *	regwr:  To write one word to chip register + *	regrd:  To read one or more words from chip register. + */ + +struct bnad_debug_info { +	char *debug_buffer; +	void *i_private; +	int buffer_len; +}; + +static int +bnad_debugfs_open_fwtrc(struct inode *inode, struct file *file) +{ +	struct bnad *bnad = inode->i_private; +	struct bnad_debug_info *fw_debug; +	unsigned long flags; +	int rc; + +	fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); +	if (!fw_debug) +		return -ENOMEM; + +	fw_debug->buffer_len = BNA_DBG_FWTRC_LEN; + +	fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL); +	if (!fw_debug->debug_buffer) { +		kfree(fw_debug); +		fw_debug = NULL; +		pr_warn("bna %s: Failed to allocate fwtrc buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	spin_lock_irqsave(&bnad->bna_lock, flags); +	rc = bfa_nw_ioc_debug_fwtrc(&bnad->bna.ioceth.ioc, +			fw_debug->debug_buffer, +			&fw_debug->buffer_len); +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	if (rc != BFA_STATUS_OK) { +		kfree(fw_debug->debug_buffer); +		fw_debug->debug_buffer = NULL; +		kfree(fw_debug); +		fw_debug = NULL; +		pr_warn("bnad %s: Failed to collect fwtrc\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	file->private_data = fw_debug; + +	return 0; +} + +static int +bnad_debugfs_open_fwsave(struct inode *inode, struct file *file) +{ +	struct bnad *bnad = inode->i_private; +	struct bnad_debug_info *fw_debug; +	unsigned long flags; +	int rc; + +	fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); +	if (!fw_debug) +		return -ENOMEM; + +	fw_debug->buffer_len = BNA_DBG_FWTRC_LEN; + +	fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL); +	if (!fw_debug->debug_buffer) { +		kfree(fw_debug); +		fw_debug = NULL; +		pr_warn("bna %s: Failed to allocate fwsave buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	spin_lock_irqsave(&bnad->bna_lock, flags); +	rc = bfa_nw_ioc_debug_fwsave(&bnad->bna.ioceth.ioc, +			fw_debug->debug_buffer, +			&fw_debug->buffer_len); +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	if (rc != BFA_STATUS_OK && rc != BFA_STATUS_ENOFSAVE) { +		kfree(fw_debug->debug_buffer); +		fw_debug->debug_buffer = NULL; +		kfree(fw_debug); +		fw_debug = NULL; +		pr_warn("bna %s: Failed to collect fwsave\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	file->private_data = fw_debug; + +	return 0; +} + +static int +bnad_debugfs_open_reg(struct inode *inode, struct file *file) +{ +	struct bnad_debug_info *reg_debug; + +	reg_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); +	if (!reg_debug) +		return -ENOMEM; + +	reg_debug->i_private = inode->i_private; + +	file->private_data = reg_debug; + +	return 0; +} + +static int +bnad_get_debug_drvinfo(struct bnad *bnad, void *buffer, u32 len) +{ +	struct bnad_drvinfo *drvinfo = (struct bnad_drvinfo *) buffer; +	struct bnad_iocmd_comp fcomp; +	unsigned long flags = 0; +	int ret = BFA_STATUS_FAILED; + +	/* Get IOC info */ +	spin_lock_irqsave(&bnad->bna_lock, flags); +	bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, &drvinfo->ioc_attr); +	spin_unlock_irqrestore(&bnad->bna_lock, flags); + +	/* Retrieve CEE related info */ +	fcomp.bnad = bnad; +	fcomp.comp_status = 0; +	init_completion(&fcomp.comp); +	spin_lock_irqsave(&bnad->bna_lock, flags); +	ret = bfa_nw_cee_get_attr(&bnad->bna.cee, &drvinfo->cee_attr, +				bnad_cb_completion, &fcomp); +	if (ret != BFA_STATUS_OK) { +		spin_unlock_irqrestore(&bnad->bna_lock, flags); +		goto out; +	} +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	wait_for_completion(&fcomp.comp); +	drvinfo->cee_status = fcomp.comp_status; + +	/* Retrieve flash partition info */ +	fcomp.comp_status = 0; +	init_completion(&fcomp.comp); +	spin_lock_irqsave(&bnad->bna_lock, flags); +	ret = bfa_nw_flash_get_attr(&bnad->bna.flash, &drvinfo->flash_attr, +				bnad_cb_completion, &fcomp); +	if (ret != BFA_STATUS_OK) { +		spin_unlock_irqrestore(&bnad->bna_lock, flags); +		goto out; +	} +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	wait_for_completion(&fcomp.comp); +	drvinfo->flash_status = fcomp.comp_status; +out: +	return ret; +} + +static int +bnad_debugfs_open_drvinfo(struct inode *inode, struct file *file) +{ +	struct bnad *bnad = inode->i_private; +	struct bnad_debug_info *drv_info; +	int rc; + +	drv_info = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); +	if (!drv_info) +		return -ENOMEM; + +	drv_info->buffer_len = sizeof(struct bnad_drvinfo); + +	drv_info->debug_buffer = kzalloc(drv_info->buffer_len, GFP_KERNEL); +	if (!drv_info->debug_buffer) { +		kfree(drv_info); +		drv_info = NULL; +		pr_warn("bna %s: Failed to allocate drv info buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	mutex_lock(&bnad->conf_mutex); +	rc = bnad_get_debug_drvinfo(bnad, drv_info->debug_buffer, +				drv_info->buffer_len); +	mutex_unlock(&bnad->conf_mutex); +	if (rc != BFA_STATUS_OK) { +		kfree(drv_info->debug_buffer); +		drv_info->debug_buffer = NULL; +		kfree(drv_info); +		drv_info = NULL; +		pr_warn("bna %s: Failed to collect drvinfo\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	file->private_data = drv_info; + +	return 0; +} + +/* Changes the current file position */ +static loff_t +bnad_debugfs_lseek(struct file *file, loff_t offset, int orig) +{ +	loff_t pos = file->f_pos; +	struct bnad_debug_info *debug = file->private_data; + +	if (!debug) +		return -EINVAL; + +	switch (orig) { +	case 0: +		file->f_pos = offset; +		break; +	case 1: +		file->f_pos += offset; +		break; +	case 2: +		file->f_pos = debug->buffer_len - offset; +		break; +	default: +		return -EINVAL; +	} + +	if (file->f_pos < 0 || file->f_pos > debug->buffer_len) { +		file->f_pos = pos; +		return -EINVAL; +	} + +	return file->f_pos; +} + +static ssize_t +bnad_debugfs_read(struct file *file, char __user *buf, +		  size_t nbytes, loff_t *pos) +{ +	struct bnad_debug_info *debug = file->private_data; + +	if (!debug || !debug->debug_buffer) +		return 0; + +	return simple_read_from_buffer(buf, nbytes, pos, +				debug->debug_buffer, debug->buffer_len); +} + +#define BFA_REG_CT_ADDRSZ	(0x40000) +#define BFA_REG_CB_ADDRSZ	(0x20000) +#define BFA_REG_ADDRSZ(__ioc)	\ +	((u32)(bfa_asic_id_ctc(bfa_ioc_devid(__ioc)) ?  \ +	 BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ)) +#define BFA_REG_ADDRMSK(__ioc)	(BFA_REG_ADDRSZ(__ioc) - 1) + +/* + * Function to check if the register offset passed is valid. + */ +static int +bna_reg_offset_check(struct bfa_ioc *ioc, u32 offset, u32 len) +{ +	u8 area; + +	/* check [16:15] */ +	area = (offset >> 15) & 0x7; +	if (area == 0) { +		/* PCIe core register */ +		if ((offset + (len<<2)) > 0x8000)	/* 8k dwords or 32KB */ +			return BFA_STATUS_EINVAL; +	} else if (area == 0x1) { +		/* CB 32 KB memory page */ +		if ((offset + (len<<2)) > 0x10000)	/* 8k dwords or 32KB */ +			return BFA_STATUS_EINVAL; +	} else { +		/* CB register space 64KB */ +		if ((offset + (len<<2)) > BFA_REG_ADDRMSK(ioc)) +			return BFA_STATUS_EINVAL; +	} +	return BFA_STATUS_OK; +} + +static ssize_t +bnad_debugfs_read_regrd(struct file *file, char __user *buf, +			size_t nbytes, loff_t *pos) +{ +	struct bnad_debug_info *regrd_debug = file->private_data; +	struct bnad *bnad = (struct bnad *)regrd_debug->i_private; +	ssize_t rc; + +	if (!bnad->regdata) +		return 0; + +	rc = simple_read_from_buffer(buf, nbytes, pos, +			bnad->regdata, bnad->reglen); + +	if ((*pos + nbytes) >= bnad->reglen) { +		kfree(bnad->regdata); +		bnad->regdata = NULL; +		bnad->reglen = 0; +	} + +	return rc; +} + +static ssize_t +bnad_debugfs_write_regrd(struct file *file, const char __user *buf, +		size_t nbytes, loff_t *ppos) +{ +	struct bnad_debug_info *regrd_debug = file->private_data; +	struct bnad *bnad = (struct bnad *)regrd_debug->i_private; +	struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc; +	int addr, len, rc, i; +	u32 *regbuf; +	void __iomem *rb, *reg_addr; +	unsigned long flags; +	void *kern_buf; + +	/* Allocate memory to store the user space buf */ +	kern_buf = kzalloc(nbytes, GFP_KERNEL); +	if (!kern_buf) { +		pr_warn("bna %s: Failed to allocate user buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	if (copy_from_user(kern_buf, (void  __user *)buf, nbytes)) { +		kfree(kern_buf); +		return -ENOMEM; +	} + +	rc = sscanf(kern_buf, "%x:%x", &addr, &len); +	if (rc < 2) { +		pr_warn("bna %s: Failed to read user buffer\n", +			pci_name(bnad->pcidev)); +		kfree(kern_buf); +		return -EINVAL; +	} + +	kfree(kern_buf); +	kfree(bnad->regdata); +	bnad->regdata = NULL; +	bnad->reglen = 0; + +	bnad->regdata = kzalloc(len << 2, GFP_KERNEL); +	if (!bnad->regdata) { +		pr_warn("bna %s: Failed to allocate regrd buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	bnad->reglen = len << 2; +	rb = bfa_ioc_bar0(ioc); +	addr &= BFA_REG_ADDRMSK(ioc); + +	/* offset and len sanity check */ +	rc = bna_reg_offset_check(ioc, addr, len); +	if (rc) { +		pr_warn("bna %s: Failed reg offset check\n", +			pci_name(bnad->pcidev)); +		kfree(bnad->regdata); +		bnad->regdata = NULL; +		bnad->reglen = 0; +		return -EINVAL; +	} + +	reg_addr = rb + addr; +	regbuf =  (u32 *)bnad->regdata; +	spin_lock_irqsave(&bnad->bna_lock, flags); +	for (i = 0; i < len; i++) { +		*regbuf = readl(reg_addr); +		regbuf++; +		reg_addr += sizeof(u32); +	} +	spin_unlock_irqrestore(&bnad->bna_lock, flags); + +	return nbytes; +} + +static ssize_t +bnad_debugfs_write_regwr(struct file *file, const char __user *buf, +		size_t nbytes, loff_t *ppos) +{ +	struct bnad_debug_info *debug = file->private_data; +	struct bnad *bnad = (struct bnad *)debug->i_private; +	struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc; +	int addr, val, rc; +	void __iomem *reg_addr; +	unsigned long flags; +	void *kern_buf; + +	/* Allocate memory to store the user space buf */ +	kern_buf = kzalloc(nbytes, GFP_KERNEL); +	if (!kern_buf) { +		pr_warn("bna %s: Failed to allocate user buffer\n", +			pci_name(bnad->pcidev)); +		return -ENOMEM; +	} + +	if (copy_from_user(kern_buf, (void  __user *)buf, nbytes)) { +		kfree(kern_buf); +		return -ENOMEM; +	} + +	rc = sscanf(kern_buf, "%x:%x", &addr, &val); +	if (rc < 2) { +		pr_warn("bna %s: Failed to read user buffer\n", +			pci_name(bnad->pcidev)); +		kfree(kern_buf); +		return -EINVAL; +	} +	kfree(kern_buf); + +	addr &= BFA_REG_ADDRMSK(ioc); /* offset only 17 bit and word align */ + +	/* offset and len sanity check */ +	rc = bna_reg_offset_check(ioc, addr, 1); +	if (rc) { +		pr_warn("bna %s: Failed reg offset check\n", +			pci_name(bnad->pcidev)); +		return -EINVAL; +	} + +	reg_addr = (bfa_ioc_bar0(ioc)) + addr; +	spin_lock_irqsave(&bnad->bna_lock, flags); +	writel(val, reg_addr); +	spin_unlock_irqrestore(&bnad->bna_lock, flags); + +	return nbytes; +} + +static int +bnad_debugfs_release(struct inode *inode, struct file *file) +{ +	struct bnad_debug_info *debug = file->private_data; + +	if (!debug) +		return 0; + +	file->private_data = NULL; +	kfree(debug); +	return 0; +} + +static int +bnad_debugfs_buffer_release(struct inode *inode, struct file *file) +{ +	struct bnad_debug_info *debug = file->private_data; + +	if (!debug) +		return 0; + +	kfree(debug->debug_buffer); + +	file->private_data = NULL; +	kfree(debug); +	debug = NULL; +	return 0; +} + +static const struct file_operations bnad_debugfs_op_fwtrc = { +	.owner		=	THIS_MODULE, +	.open		=	bnad_debugfs_open_fwtrc, +	.llseek		=	bnad_debugfs_lseek, +	.read		=	bnad_debugfs_read, +	.release	=	bnad_debugfs_buffer_release, +}; + +static const struct file_operations bnad_debugfs_op_fwsave = { +	.owner		=	THIS_MODULE, +	.open		=	bnad_debugfs_open_fwsave, +	.llseek		=	bnad_debugfs_lseek, +	.read		=	bnad_debugfs_read, +	.release	=	bnad_debugfs_buffer_release, +}; + +static const struct file_operations bnad_debugfs_op_regrd = { +	.owner		=       THIS_MODULE, +	.open		=	bnad_debugfs_open_reg, +	.llseek		=	bnad_debugfs_lseek, +	.read		=	bnad_debugfs_read_regrd, +	.write		=	bnad_debugfs_write_regrd, +	.release	=	bnad_debugfs_release, +}; + +static const struct file_operations bnad_debugfs_op_regwr = { +	.owner		=	THIS_MODULE, +	.open		=	bnad_debugfs_open_reg, +	.llseek		=	bnad_debugfs_lseek, +	.write		=	bnad_debugfs_write_regwr, +	.release	=	bnad_debugfs_release, +}; + +static const struct file_operations bnad_debugfs_op_drvinfo = { +	.owner		=	THIS_MODULE, +	.open		=	bnad_debugfs_open_drvinfo, +	.llseek		=	bnad_debugfs_lseek, +	.read		=	bnad_debugfs_read, +	.release	=	bnad_debugfs_buffer_release, +}; + +struct bnad_debugfs_entry { +	const char *name; +	mode_t  mode; +	const struct file_operations *fops; +}; + +static const struct bnad_debugfs_entry bnad_debugfs_files[] = { +	{ "fwtrc",  S_IFREG|S_IRUGO, &bnad_debugfs_op_fwtrc, }, +	{ "fwsave", S_IFREG|S_IRUGO, &bnad_debugfs_op_fwsave, }, +	{ "regrd",  S_IFREG|S_IRUGO|S_IWUSR, &bnad_debugfs_op_regrd, }, +	{ "regwr",  S_IFREG|S_IWUSR, &bnad_debugfs_op_regwr, }, +	{ "drvinfo", S_IFREG|S_IRUGO, &bnad_debugfs_op_drvinfo, }, +}; + +static struct dentry *bna_debugfs_root; +static atomic_t bna_debugfs_port_count; + +/* Initialize debugfs interface for BNA */ +void +bnad_debugfs_init(struct bnad *bnad) +{ +	const struct bnad_debugfs_entry *file; +	char name[64]; +	int i; + +	/* Setup the BNA debugfs root directory*/ +	if (!bna_debugfs_root) { +		bna_debugfs_root = debugfs_create_dir("bna", NULL); +		atomic_set(&bna_debugfs_port_count, 0); +		if (!bna_debugfs_root) { +			pr_warn("BNA: debugfs root dir creation failed\n"); +			return; +		} +	} + +	/* Setup the pci_dev debugfs directory for the port */ +	snprintf(name, sizeof(name), "pci_dev:%s", pci_name(bnad->pcidev)); +	if (!bnad->port_debugfs_root) { +		bnad->port_debugfs_root = +			debugfs_create_dir(name, bna_debugfs_root); +		if (!bnad->port_debugfs_root) { +			pr_warn("bna pci_dev %s: root dir creation failed\n", +				pci_name(bnad->pcidev)); +			return; +		} + +		atomic_inc(&bna_debugfs_port_count); + +		for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) { +			file = &bnad_debugfs_files[i]; +			bnad->bnad_dentry_files[i] = +					debugfs_create_file(file->name, +							file->mode, +							bnad->port_debugfs_root, +							bnad, +							file->fops); +			if (!bnad->bnad_dentry_files[i]) { +				pr_warn( +				     "BNA pci_dev:%s: create %s entry failed\n", +				     pci_name(bnad->pcidev), file->name); +				return; +			} +		} +	} +} + +/* Uninitialize debugfs interface for BNA */ +void +bnad_debugfs_uninit(struct bnad *bnad) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) { +		if (bnad->bnad_dentry_files[i]) { +			debugfs_remove(bnad->bnad_dentry_files[i]); +			bnad->bnad_dentry_files[i] = NULL; +		} +	} + +	/* Remove the pci_dev debugfs directory for the port */ +	if (bnad->port_debugfs_root) { +		debugfs_remove(bnad->port_debugfs_root); +		bnad->port_debugfs_root = NULL; +		atomic_dec(&bna_debugfs_port_count); +	} + +	/* Remove the BNA debugfs root directory */ +	if (atomic_read(&bna_debugfs_port_count) == 0) { +		debugfs_remove(bna_debugfs_root); +		bna_debugfs_root = NULL; +	} +} diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index fd3dcc1e914..9b44ec8096b 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -38,7 +38,7 @@  	sizeof(struct bnad_drv_stats) / sizeof(u64) +		\  	offsetof(struct bfi_enet_stats, rxf_stats[0]) / sizeof(u64)) -static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = { +static const char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {  	"rx_packets",  	"tx_packets",  	"rx_bytes", @@ -296,8 +296,8 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)  	struct bfa_ioc_attr *ioc_attr;  	unsigned long flags; -	strcpy(drvinfo->driver, BNAD_NAME); -	strcpy(drvinfo->version, BNAD_VERSION); +	strlcpy(drvinfo->driver, BNAD_NAME, sizeof(drvinfo->driver)); +	strlcpy(drvinfo->version, BNAD_VERSION, sizeof(drvinfo->version));  	ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);  	if (ioc_attr) { @@ -305,12 +305,13 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)  		bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, ioc_attr);  		spin_unlock_irqrestore(&bnad->bna_lock, flags); -		strncpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver, -			sizeof(drvinfo->fw_version) - 1); +		strlcpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver, +			sizeof(drvinfo->fw_version));  		kfree(ioc_attr);  	} -	strncpy(drvinfo->bus_info, pci_name(bnad->pcidev), ETHTOOL_BUSINFO_LEN); +	strlcpy(drvinfo->bus_info, pci_name(bnad->pcidev), +		sizeof(drvinfo->bus_info));  }  static void @@ -934,7 +935,144 @@ bnad_get_sset_count(struct net_device *netdev, int sset)  	}  } -static struct ethtool_ops bnad_ethtool_ops = { +static u32 +bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, +				u32 *base_offset) +{ +	struct bfa_flash_attr *flash_attr; +	struct bnad_iocmd_comp fcomp; +	u32 i, flash_part = 0, ret; +	unsigned long flags = 0; + +	flash_attr = kzalloc(sizeof(struct bfa_flash_attr), GFP_KERNEL); +	if (!flash_attr) +		return -ENOMEM; + +	fcomp.bnad = bnad; +	fcomp.comp_status = 0; + +	init_completion(&fcomp.comp); +	spin_lock_irqsave(&bnad->bna_lock, flags); +	ret = bfa_nw_flash_get_attr(&bnad->bna.flash, flash_attr, +				bnad_cb_completion, &fcomp); +	if (ret != BFA_STATUS_OK) { +		spin_unlock_irqrestore(&bnad->bna_lock, flags); +		kfree(flash_attr); +		goto out_err; +	} +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	wait_for_completion(&fcomp.comp); +	ret = fcomp.comp_status; + +	/* Check for the flash type & base offset value */ +	if (ret == BFA_STATUS_OK) { +		for (i = 0; i < flash_attr->npart; i++) { +			if (offset >= flash_attr->part[i].part_off && +			    offset < (flash_attr->part[i].part_off + +				      flash_attr->part[i].part_size)) { +				flash_part = flash_attr->part[i].part_type; +				*base_offset = flash_attr->part[i].part_off; +				break; +			} +		} +	} +	kfree(flash_attr); +	return flash_part; +out_err: +	return -EINVAL; +} + +static int +bnad_get_eeprom_len(struct net_device *netdev) +{ +	return BFA_TOTAL_FLASH_SIZE; +} + +static int +bnad_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, +		u8 *bytes) +{ +	struct bnad *bnad = netdev_priv(netdev); +	struct bnad_iocmd_comp fcomp; +	u32 flash_part = 0, base_offset = 0; +	unsigned long flags = 0; +	int ret = 0; + +	/* Check if the flash read request is valid */ +	if (eeprom->magic != (bnad->pcidev->vendor | +			     (bnad->pcidev->device << 16))) +		return -EFAULT; + +	/* Query the flash partition based on the offset */ +	flash_part = bnad_get_flash_partition_by_offset(bnad, +				eeprom->offset, &base_offset); +	if (flash_part <= 0) +		return -EFAULT; + +	fcomp.bnad = bnad; +	fcomp.comp_status = 0; + +	init_completion(&fcomp.comp); +	spin_lock_irqsave(&bnad->bna_lock, flags); +	ret = bfa_nw_flash_read_part(&bnad->bna.flash, flash_part, +				bnad->id, bytes, eeprom->len, +				eeprom->offset - base_offset, +				bnad_cb_completion, &fcomp); +	if (ret != BFA_STATUS_OK) { +		spin_unlock_irqrestore(&bnad->bna_lock, flags); +		goto done; +	} + +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	wait_for_completion(&fcomp.comp); +	ret = fcomp.comp_status; +done: +	return ret; +} + +static int +bnad_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, +		u8 *bytes) +{ +	struct bnad *bnad = netdev_priv(netdev); +	struct bnad_iocmd_comp fcomp; +	u32 flash_part = 0, base_offset = 0; +	unsigned long flags = 0; +	int ret = 0; + +	/* Check if the flash update request is valid */ +	if (eeprom->magic != (bnad->pcidev->vendor | +			     (bnad->pcidev->device << 16))) +		return -EINVAL; + +	/* Query the flash partition based on the offset */ +	flash_part = bnad_get_flash_partition_by_offset(bnad, +				eeprom->offset, &base_offset); +	if (flash_part <= 0) +		return -EFAULT; + +	fcomp.bnad = bnad; +	fcomp.comp_status = 0; + +	init_completion(&fcomp.comp); +	spin_lock_irqsave(&bnad->bna_lock, flags); +	ret = bfa_nw_flash_update_part(&bnad->bna.flash, flash_part, +				bnad->id, bytes, eeprom->len, +				eeprom->offset - base_offset, +				bnad_cb_completion, &fcomp); +	if (ret != BFA_STATUS_OK) { +		spin_unlock_irqrestore(&bnad->bna_lock, flags); +		goto done; +	} + +	spin_unlock_irqrestore(&bnad->bna_lock, flags); +	wait_for_completion(&fcomp.comp); +	ret = fcomp.comp_status; +done: +	return ret; +} + +static const struct ethtool_ops bnad_ethtool_ops = {  	.get_settings = bnad_get_settings,  	.set_settings = bnad_set_settings,  	.get_drvinfo = bnad_get_drvinfo, @@ -948,7 +1086,10 @@ static struct ethtool_ops bnad_ethtool_ops = {  	.set_pauseparam = bnad_set_pauseparam,  	.get_strings = bnad_get_strings,  	.get_ethtool_stats = bnad_get_ethtool_stats, -	.get_sset_count = bnad_get_sset_count +	.get_sset_count = bnad_get_sset_count, +	.get_eeprom_len = bnad_get_eeprom_len, +	.get_eeprom = bnad_get_eeprom, +	.set_eeprom = bnad_set_eeprom,  };  void diff --git a/drivers/net/ethernet/brocade/bna/cna.h b/drivers/net/ethernet/brocade/bna/cna.h index 1b3e90dfbd9..32e8f178ab7 100644 --- a/drivers/net/ethernet/brocade/bna/cna.h +++ b/drivers/net/ethernet/brocade/bna/cna.h @@ -43,8 +43,7 @@ extern char bfa_version[];  #pragma pack(1) -#define MAC_ADDRLEN	(6) -typedef struct mac { u8 mac[MAC_ADDRLEN]; } mac_t; +typedef struct mac { u8 mac[ETH_ALEN]; } mac_t;  #pragma pack() diff --git a/drivers/net/ethernet/brocade/bna/cna_fwimg.c b/drivers/net/ethernet/brocade/bna/cna_fwimg.c index 725b9fff337..cfc22a64157 100644 --- a/drivers/net/ethernet/brocade/bna/cna_fwimg.c +++ b/drivers/net/ethernet/brocade/bna/cna_fwimg.c @@ -16,6 +16,7 @@   * www.brocade.com   */  #include <linux/firmware.h> +#include "bnad.h"  #include "bfi.h"  #include "cna.h"  |