Skip to content
Snippets Groups Projects
bna_ctrl.c 79.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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-2010 Brocade Communications Systems, Inc.
     * All rights reserved
     * www.brocade.com
     */
    #include "bna.h"
    #include "bfa_sm.h"
    #include "bfa_wc.h"
    
    
    static void bna_device_cb_port_stopped(void *arg, enum bna_cb_status status);
    
    static void
    bna_port_cb_link_up(struct bna_port *port, struct bfi_ll_aen *aen,
    			int status)
    {
    	int i;
    	u8 prio_map;
    
    	port->llport.link_status = BNA_LINK_UP;
    	if (aen->cee_linkup)
    		port->llport.link_status = BNA_CEE_UP;
    
    	/* Compute the priority */
    	prio_map = aen->prio_map;
    	if (prio_map) {
    		for (i = 0; i < 8; i++) {
    			if ((prio_map >> i) & 0x1)
    				break;
    		}
    		port->priority = i;
    	} else
    		port->priority = 0;
    
    	/* Dispatch events */
    	bna_tx_mod_cee_link_status(&port->bna->tx_mod, aen->cee_linkup);
    	bna_tx_mod_prio_changed(&port->bna->tx_mod, port->priority);
    	port->link_cbfn(port->bna->bnad, port->llport.link_status);
    }
    
    static void
    bna_port_cb_link_down(struct bna_port *port, int status)
    {
    	port->llport.link_status = BNA_LINK_DOWN;
    
    	/* Dispatch events */
    	bna_tx_mod_cee_link_status(&port->bna->tx_mod, BNA_LINK_DOWN);
    	port->link_cbfn(port->bna->bnad, BNA_LINK_DOWN);
    }
    
    
    /**
     * MBOX
     */
    static int
    bna_is_aen(u8 msg_id)
    {
    
    	return msg_id == BFI_LL_I2H_LINK_DOWN_AEN ||
    	       msg_id == BFI_LL_I2H_LINK_UP_AEN;
    
    }
    
    static void
    bna_mbox_aen_callback(struct bna *bna, struct bfi_mbmsg *msg)
    {
    	struct bfi_ll_aen *aen = (struct bfi_ll_aen *)(msg);
    
    	switch (aen->mh.msg_id) {
    	case BFI_LL_I2H_LINK_UP_AEN:
    		bna_port_cb_link_up(&bna->port, aen, aen->reason);
    		break;
    	case BFI_LL_I2H_LINK_DOWN_AEN:
    		bna_port_cb_link_down(&bna->port, aen->reason);
    		break;
    	default:
    		break;
    	}
    }
    
    static void
    bna_ll_isr(void *llarg, struct bfi_mbmsg *msg)
    {
    	struct bna *bna = (struct bna *)(llarg);
    	struct bfi_ll_rsp *mb_rsp = (struct bfi_ll_rsp *)(msg);
    	struct bfi_mhdr *cmd_h, *rsp_h;
    	struct bna_mbox_qe *mb_qe = NULL;
    	int to_post = 0;
    	u8 aen = 0;
    	char message[BNA_MESSAGE_SIZE];
    
    	aen = bna_is_aen(mb_rsp->mh.msg_id);
    
    	if (!aen) {
    		mb_qe = bfa_q_first(&bna->mbox_mod.posted_q);
    		cmd_h = (struct bfi_mhdr *)(&mb_qe->cmd.msg[0]);
    		rsp_h = (struct bfi_mhdr *)(&mb_rsp->mh);
    
    		if ((BFA_I2HM(cmd_h->msg_id) == rsp_h->msg_id) &&
    		    (cmd_h->mtag.i2htok == rsp_h->mtag.i2htok)) {
    			/* Remove the request from posted_q, update state  */
    			list_del(&mb_qe->qe);
    			bna->mbox_mod.msg_pending--;
    			if (list_empty(&bna->mbox_mod.posted_q))
    				bna->mbox_mod.state = BNA_MBOX_FREE;
    			else
    				to_post = 1;
    
    			/* Dispatch the cbfn */
    			if (mb_qe->cbfn)
    				mb_qe->cbfn(mb_qe->cbarg, mb_rsp->error);
    
    			/* Post the next entry, if needed */
    			if (to_post) {
    				mb_qe = bfa_q_first(&bna->mbox_mod.posted_q);
    
    				bfa_nw_ioc_mbox_queue(&bna->device.ioc,
    
    							&mb_qe->cmd);
    			}
    		} else {
    			snprintf(message, BNA_MESSAGE_SIZE,
    				       "No matching rsp for [%d:%d:%d]\n",
    				       mb_rsp->mh.msg_class, mb_rsp->mh.msg_id,
    				       mb_rsp->mh.mtag.i2htok);
    		pr_info("%s", message);
    		}
    
    	} else
    		bna_mbox_aen_callback(bna, msg);
    }
    
    
    static void
    
    bna_err_handler(struct bna *bna, u32 intr_status)
    {
    	u32 init_halt;
    
    	if (intr_status & __HALT_STATUS_BITS) {
    		init_halt = readl(bna->device.ioc.ioc_regs.ll_halt);
    		init_halt &= ~__FW_INIT_HALT_P;
    		writel(init_halt, bna->device.ioc.ioc_regs.ll_halt);
    	}
    
    
    	bfa_nw_ioc_error_isr(&bna->device.ioc);
    
    }
    
    void
    bna_mbox_handler(struct bna *bna, u32 intr_status)
    {
    	if (BNA_IS_ERR_INTR(intr_status)) {
    		bna_err_handler(bna, intr_status);
    		return;
    	}
    	if (BNA_IS_MBOX_INTR(intr_status))
    
    		bfa_nw_ioc_mbox_isr(&bna->device.ioc);
    
    }
    
    void
    bna_mbox_send(struct bna *bna, struct bna_mbox_qe *mbox_qe)
    {
    	struct bfi_mhdr *mh;
    
    	mh = (struct bfi_mhdr *)(&mbox_qe->cmd.msg[0]);
    
    	mh->mtag.i2htok = htons(bna->mbox_mod.msg_ctr);
    	bna->mbox_mod.msg_ctr++;
    	bna->mbox_mod.msg_pending++;
    	if (bna->mbox_mod.state == BNA_MBOX_FREE) {
    		list_add_tail(&mbox_qe->qe, &bna->mbox_mod.posted_q);
    
    		bfa_nw_ioc_mbox_queue(&bna->device.ioc, &mbox_qe->cmd);
    
    		bna->mbox_mod.state = BNA_MBOX_POSTED;
    	} else {
    		list_add_tail(&mbox_qe->qe, &bna->mbox_mod.posted_q);
    	}
    }
    
    
    static void
    
    bna_mbox_flush_q(struct bna *bna, struct list_head *q)
    {
    	struct bna_mbox_qe *mb_qe = NULL;
    	struct bfi_mhdr *cmd_h;
    	struct list_head			*mb_q;
    	void 			(*cbfn)(void *arg, int status);
    	void 			*cbarg;
    
    	mb_q = &bna->mbox_mod.posted_q;
    
    	while (!list_empty(mb_q)) {
    		bfa_q_deq(mb_q, &mb_qe);
    		cbfn = mb_qe->cbfn;
    		cbarg = mb_qe->cbarg;
    		bfa_q_qe_init(mb_qe);
    		bna->mbox_mod.msg_pending--;
    
    		cmd_h = (struct bfi_mhdr *)(&mb_qe->cmd.msg[0]);
    		if (cbfn)
    			cbfn(cbarg, BNA_CB_NOT_EXEC);
    	}
    
    	bna->mbox_mod.state = BNA_MBOX_FREE;
    }
    
    
    static void
    
    bna_mbox_mod_start(struct bna_mbox_mod *mbox_mod)
    {
    }
    
    
    static void
    
    bna_mbox_mod_stop(struct bna_mbox_mod *mbox_mod)
    {
    	bna_mbox_flush_q(mbox_mod->bna, &mbox_mod->posted_q);
    }
    
    
    static void
    
    bna_mbox_mod_init(struct bna_mbox_mod *mbox_mod, struct bna *bna)
    {
    
    	bfa_nw_ioc_mbox_regisr(&bna->device.ioc, BFI_MC_LL, bna_ll_isr, bna);
    
    	mbox_mod->state = BNA_MBOX_FREE;
    	mbox_mod->msg_ctr = mbox_mod->msg_pending = 0;
    	INIT_LIST_HEAD(&mbox_mod->posted_q);
    	mbox_mod->bna = bna;
    }
    
    
    static void
    
    bna_mbox_mod_uninit(struct bna_mbox_mod *mbox_mod)
    {
    	mbox_mod->bna = NULL;
    }
    
    /**
     * LLPORT
     */
    #define call_llport_stop_cbfn(llport, status)\
    do {\
    	if ((llport)->stop_cbfn)\
    		(llport)->stop_cbfn(&(llport)->bna->port, status);\
    	(llport)->stop_cbfn = NULL;\
    } while (0)
    
    static void bna_fw_llport_up(struct bna_llport *llport);
    static void bna_fw_cb_llport_up(void *arg, int status);
    static void bna_fw_llport_down(struct bna_llport *llport);
    static void bna_fw_cb_llport_down(void *arg, int status);
    static void bna_llport_start(struct bna_llport *llport);
    static void bna_llport_stop(struct bna_llport *llport);
    static void bna_llport_fail(struct bna_llport *llport);
    
    enum bna_llport_event {
    	LLPORT_E_START			= 1,
    	LLPORT_E_STOP			= 2,
    	LLPORT_E_FAIL			= 3,
    	LLPORT_E_UP			= 4,
    	LLPORT_E_DOWN			= 5,
    	LLPORT_E_FWRESP_UP		= 6,
    	LLPORT_E_FWRESP_DOWN		= 7
    };
    
    enum bna_llport_state {
    	BNA_LLPORT_STOPPED		= 1,
    	BNA_LLPORT_DOWN			= 2,
    	BNA_LLPORT_UP_RESP_WAIT		= 3,
    	BNA_LLPORT_DOWN_RESP_WAIT	= 4,
    	BNA_LLPORT_UP			= 5,
    	BNA_LLPORT_LAST_RESP_WAIT 	= 6
    };
    
    bfa_fsm_state_decl(bna_llport, stopped, struct bna_llport,
    			enum bna_llport_event);
    bfa_fsm_state_decl(bna_llport, down, struct bna_llport,
    			enum bna_llport_event);
    bfa_fsm_state_decl(bna_llport, up_resp_wait, struct bna_llport,
    			enum bna_llport_event);
    bfa_fsm_state_decl(bna_llport, down_resp_wait, struct bna_llport,
    			enum bna_llport_event);
    bfa_fsm_state_decl(bna_llport, up, struct bna_llport,
    			enum bna_llport_event);
    bfa_fsm_state_decl(bna_llport, last_resp_wait, struct bna_llport,
    			enum bna_llport_event);
    
    static struct bfa_sm_table llport_sm_table[] = {
    	{BFA_SM(bna_llport_sm_stopped), BNA_LLPORT_STOPPED},
    	{BFA_SM(bna_llport_sm_down), BNA_LLPORT_DOWN},
    	{BFA_SM(bna_llport_sm_up_resp_wait), BNA_LLPORT_UP_RESP_WAIT},
    	{BFA_SM(bna_llport_sm_down_resp_wait), BNA_LLPORT_DOWN_RESP_WAIT},
    	{BFA_SM(bna_llport_sm_up), BNA_LLPORT_UP},
    	{BFA_SM(bna_llport_sm_last_resp_wait), BNA_LLPORT_LAST_RESP_WAIT}
    };
    
    static void
    bna_llport_sm_stopped_entry(struct bna_llport *llport)
    {
    	llport->bna->port.link_cbfn((llport)->bna->bnad, BNA_LINK_DOWN);
    	call_llport_stop_cbfn(llport, BNA_CB_SUCCESS);
    }
    
    static void
    bna_llport_sm_stopped(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_START:
    		bfa_fsm_set_state(llport, bna_llport_sm_down);
    		break;
    
    	case LLPORT_E_STOP:
    		call_llport_stop_cbfn(llport, BNA_CB_SUCCESS);
    		break;
    
    	case LLPORT_E_FAIL:
    		break;
    
    	case LLPORT_E_DOWN:
    		/* This event is received due to Rx objects failing */
    		/* No-op */
    		break;
    
    	case LLPORT_E_FWRESP_UP:
    	case LLPORT_E_FWRESP_DOWN:
    		/**
    		 * These events are received due to flushing of mbox when
    		 * device fails
    		 */
    		/* No-op */
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_llport_sm_down_entry(struct bna_llport *llport)
    {
    	bnad_cb_port_link_status((llport)->bna->bnad, BNA_LINK_DOWN);
    }
    
    static void
    bna_llport_sm_down(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_STOP:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_FAIL:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_UP:
    		bfa_fsm_set_state(llport, bna_llport_sm_up_resp_wait);
    		bna_fw_llport_up(llport);
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_llport_sm_up_resp_wait_entry(struct bna_llport *llport)
    {
    	/**
    	 * NOTE: Do not call bna_fw_llport_up() here. That will over step
    	 * mbox due to down_resp_wait -> up_resp_wait transition on event
    	 * LLPORT_E_UP
    	 */
    }
    
    static void
    bna_llport_sm_up_resp_wait(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_STOP:
    		bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
    		break;
    
    	case LLPORT_E_FAIL:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_DOWN:
    		bfa_fsm_set_state(llport, bna_llport_sm_down_resp_wait);
    		break;
    
    	case LLPORT_E_FWRESP_UP:
    		bfa_fsm_set_state(llport, bna_llport_sm_up);
    		break;
    
    	case LLPORT_E_FWRESP_DOWN:
    		/* down_resp_wait -> up_resp_wait transition on LLPORT_E_UP */
    		bna_fw_llport_up(llport);
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_llport_sm_down_resp_wait_entry(struct bna_llport *llport)
    {
    	/**
    	 * NOTE: Do not call bna_fw_llport_down() here. That will over step
    	 * mbox due to up_resp_wait -> down_resp_wait transition on event
    	 * LLPORT_E_DOWN
    	 */
    }
    
    static void
    bna_llport_sm_down_resp_wait(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_STOP:
    		bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
    		break;
    
    	case LLPORT_E_FAIL:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_UP:
    		bfa_fsm_set_state(llport, bna_llport_sm_up_resp_wait);
    		break;
    
    	case LLPORT_E_FWRESP_UP:
    		/* up_resp_wait->down_resp_wait transition on LLPORT_E_DOWN */
    		bna_fw_llport_down(llport);
    		break;
    
    	case LLPORT_E_FWRESP_DOWN:
    		bfa_fsm_set_state(llport, bna_llport_sm_down);
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_llport_sm_up_entry(struct bna_llport *llport)
    {
    }
    
    static void
    bna_llport_sm_up(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_STOP:
    		bfa_fsm_set_state(llport, bna_llport_sm_last_resp_wait);
    		bna_fw_llport_down(llport);
    		break;
    
    	case LLPORT_E_FAIL:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_DOWN:
    		bfa_fsm_set_state(llport, bna_llport_sm_down_resp_wait);
    		bna_fw_llport_down(llport);
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_llport_sm_last_resp_wait_entry(struct bna_llport *llport)
    {
    }
    
    static void
    bna_llport_sm_last_resp_wait(struct bna_llport *llport,
    			enum bna_llport_event event)
    {
    	switch (event) {
    	case LLPORT_E_FAIL:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	case LLPORT_E_DOWN:
    		/**
    		 * This event is received due to Rx objects stopping in
    		 * parallel to llport
    		 */
    		/* No-op */
    		break;
    
    	case LLPORT_E_FWRESP_UP:
    		/* up_resp_wait->last_resp_wait transition on LLPORT_T_STOP */
    		bna_fw_llport_down(llport);
    		break;
    
    	case LLPORT_E_FWRESP_DOWN:
    		bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    		break;
    
    	default:
    		bfa_sm_fault(llport->bna, event);
    	}
    }
    
    static void
    bna_fw_llport_admin_up(struct bna_llport *llport)
    {
    	struct bfi_ll_port_admin_req ll_req;
    
    	memset(&ll_req, 0, sizeof(ll_req));
    	ll_req.mh.msg_class = BFI_MC_LL;
    	ll_req.mh.msg_id = BFI_LL_H2I_PORT_ADMIN_REQ;
    	ll_req.mh.mtag.h2i.lpu_id = 0;
    
    	ll_req.up = BNA_STATUS_T_ENABLED;
    
    	bna_mbox_qe_fill(&llport->mbox_qe, &ll_req, sizeof(ll_req),
    			bna_fw_cb_llport_up, llport);
    
    	bna_mbox_send(llport->bna, &llport->mbox_qe);
    }
    
    static void
    bna_fw_llport_up(struct bna_llport *llport)
    {
    	if (llport->type == BNA_PORT_T_REGULAR)
    		bna_fw_llport_admin_up(llport);
    }
    
    static void
    bna_fw_cb_llport_up(void *arg, int status)
    {
    	struct bna_llport *llport = (struct bna_llport *)arg;
    
    	bfa_q_qe_init(&llport->mbox_qe.qe);
    	bfa_fsm_send_event(llport, LLPORT_E_FWRESP_UP);
    }
    
    static void
    bna_fw_llport_admin_down(struct bna_llport *llport)
    {
    	struct bfi_ll_port_admin_req ll_req;
    
    	memset(&ll_req, 0, sizeof(ll_req));
    	ll_req.mh.msg_class = BFI_MC_LL;
    	ll_req.mh.msg_id = BFI_LL_H2I_PORT_ADMIN_REQ;
    	ll_req.mh.mtag.h2i.lpu_id = 0;
    
    	ll_req.up = BNA_STATUS_T_DISABLED;
    
    	bna_mbox_qe_fill(&llport->mbox_qe, &ll_req, sizeof(ll_req),
    			bna_fw_cb_llport_down, llport);
    
    	bna_mbox_send(llport->bna, &llport->mbox_qe);
    }
    
    static void
    bna_fw_llport_down(struct bna_llport *llport)
    {
    	if (llport->type == BNA_PORT_T_REGULAR)
    		bna_fw_llport_admin_down(llport);
    }
    
    static void
    bna_fw_cb_llport_down(void *arg, int status)
    {
    	struct bna_llport *llport = (struct bna_llport *)arg;
    
    	bfa_q_qe_init(&llport->mbox_qe.qe);
    	bfa_fsm_send_event(llport, LLPORT_E_FWRESP_DOWN);
    }
    
    
    static void
    
    bna_port_cb_llport_stopped(struct bna_port *port,
    				enum bna_cb_status status)
    {
    	bfa_wc_down(&port->chld_stop_wc);
    }
    
    static void
    bna_llport_init(struct bna_llport *llport, struct bna *bna)
    {
    	llport->flags |= BNA_LLPORT_F_ENABLED;
    	llport->type = BNA_PORT_T_REGULAR;
    	llport->bna = bna;
    
    	llport->link_status = BNA_LINK_DOWN;
    
    	llport->admin_up_count = 0;
    
    	llport->stop_cbfn = NULL;
    
    	bfa_q_qe_init(&llport->mbox_qe.qe);
    
    	bfa_fsm_set_state(llport, bna_llport_sm_stopped);
    }
    
    static void
    bna_llport_uninit(struct bna_llport *llport)
    {
    	llport->flags &= ~BNA_LLPORT_F_ENABLED;
    
    	llport->bna = NULL;
    }
    
    static void
    bna_llport_start(struct bna_llport *llport)
    {
    	bfa_fsm_send_event(llport, LLPORT_E_START);
    }
    
    static void
    bna_llport_stop(struct bna_llport *llport)
    {
    	llport->stop_cbfn = bna_port_cb_llport_stopped;
    
    	bfa_fsm_send_event(llport, LLPORT_E_STOP);
    }
    
    static void
    bna_llport_fail(struct bna_llport *llport)
    {
    	bfa_fsm_send_event(llport, LLPORT_E_FAIL);
    }
    
    
    static int
    
    bna_llport_state_get(struct bna_llport *llport)
    {
    	return bfa_sm_to_state(llport_sm_table, llport->fsm);
    }
    
    void
    bna_llport_admin_up(struct bna_llport *llport)
    {
    	llport->admin_up_count++;
    
    	if (llport->admin_up_count == 1) {
    		llport->flags |= BNA_LLPORT_F_RX_ENABLED;
    		if (llport->flags & BNA_LLPORT_F_ENABLED)
    			bfa_fsm_send_event(llport, LLPORT_E_UP);
    	}
    }
    
    void
    bna_llport_admin_down(struct bna_llport *llport)
    {
    	llport->admin_up_count--;
    
    	if (llport->admin_up_count == 0) {
    		llport->flags &= ~BNA_LLPORT_F_RX_ENABLED;
    		if (llport->flags & BNA_LLPORT_F_ENABLED)
    			bfa_fsm_send_event(llport, LLPORT_E_DOWN);
    	}
    }
    
    /**
     * PORT
     */
    #define bna_port_chld_start(port)\
    do {\
    	enum bna_tx_type tx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;\
    	enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
    	bna_llport_start(&(port)->llport);\
    	bna_tx_mod_start(&(port)->bna->tx_mod, tx_type);\
    	bna_rx_mod_start(&(port)->bna->rx_mod, rx_type);\
    } while (0)
    
    #define bna_port_chld_stop(port)\
    do {\
    	enum bna_tx_type tx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_TX_T_REGULAR : BNA_TX_T_LOOPBACK;\
    	enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
    	bfa_wc_up(&(port)->chld_stop_wc);\
    	bfa_wc_up(&(port)->chld_stop_wc);\
    	bfa_wc_up(&(port)->chld_stop_wc);\
    	bna_llport_stop(&(port)->llport);\
    	bna_tx_mod_stop(&(port)->bna->tx_mod, tx_type);\
    	bna_rx_mod_stop(&(port)->bna->rx_mod, rx_type);\
    } while (0)
    
    #define bna_port_chld_fail(port)\
    do {\
    	bna_llport_fail(&(port)->llport);\
    	bna_tx_mod_fail(&(port)->bna->tx_mod);\
    	bna_rx_mod_fail(&(port)->bna->rx_mod);\
    } while (0)
    
    #define bna_port_rx_start(port)\
    do {\
    	enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
    	bna_rx_mod_start(&(port)->bna->rx_mod, rx_type);\
    } while (0)
    
    #define bna_port_rx_stop(port)\
    do {\
    	enum bna_rx_type rx_type = ((port)->type == BNA_PORT_T_REGULAR) ?\
    					BNA_RX_T_REGULAR : BNA_RX_T_LOOPBACK;\
    	bfa_wc_up(&(port)->chld_stop_wc);\
    	bna_rx_mod_stop(&(port)->bna->rx_mod, rx_type);\
    } while (0)
    
    #define call_port_stop_cbfn(port, status)\
    do {\
    	if ((port)->stop_cbfn)\
    		(port)->stop_cbfn((port)->stop_cbarg, status);\
    	(port)->stop_cbfn = NULL;\
    	(port)->stop_cbarg = NULL;\
    } while (0)
    
    #define call_port_pause_cbfn(port, status)\
    do {\
    	if ((port)->pause_cbfn)\
    		(port)->pause_cbfn((port)->bna->bnad, status);\
    	(port)->pause_cbfn = NULL;\
    } while (0)
    
    #define call_port_mtu_cbfn(port, status)\
    do {\
    	if ((port)->mtu_cbfn)\
    		(port)->mtu_cbfn((port)->bna->bnad, status);\
    	(port)->mtu_cbfn = NULL;\
    } while (0)
    
    static void bna_fw_pause_set(struct bna_port *port);
    static void bna_fw_cb_pause_set(void *arg, int status);
    static void bna_fw_mtu_set(struct bna_port *port);
    static void bna_fw_cb_mtu_set(void *arg, int status);
    
    enum bna_port_event {
    	PORT_E_START			= 1,
    	PORT_E_STOP			= 2,
    	PORT_E_FAIL			= 3,
    	PORT_E_PAUSE_CFG		= 4,
    	PORT_E_MTU_CFG			= 5,
    	PORT_E_CHLD_STOPPED		= 6,
    	PORT_E_FWRESP_PAUSE		= 7,
    	PORT_E_FWRESP_MTU		= 8
    };
    
    enum bna_port_state {
    	BNA_PORT_STOPPED		= 1,
    	BNA_PORT_MTU_INIT_WAIT		= 2,
    	BNA_PORT_PAUSE_INIT_WAIT	= 3,
    	BNA_PORT_LAST_RESP_WAIT		= 4,
    	BNA_PORT_STARTED		= 5,
    	BNA_PORT_PAUSE_CFG_WAIT		= 6,
    	BNA_PORT_RX_STOP_WAIT		= 7,
    	BNA_PORT_MTU_CFG_WAIT 		= 8,
    	BNA_PORT_CHLD_STOP_WAIT		= 9
    };
    
    bfa_fsm_state_decl(bna_port, stopped, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, mtu_init_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, pause_init_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, last_resp_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, started, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, pause_cfg_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, rx_stop_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, mtu_cfg_wait, struct bna_port,
    			enum bna_port_event);
    bfa_fsm_state_decl(bna_port, chld_stop_wait, struct bna_port,
    			enum bna_port_event);
    
    static struct bfa_sm_table port_sm_table[] = {
    	{BFA_SM(bna_port_sm_stopped), BNA_PORT_STOPPED},
    	{BFA_SM(bna_port_sm_mtu_init_wait), BNA_PORT_MTU_INIT_WAIT},
    	{BFA_SM(bna_port_sm_pause_init_wait), BNA_PORT_PAUSE_INIT_WAIT},
    	{BFA_SM(bna_port_sm_last_resp_wait), BNA_PORT_LAST_RESP_WAIT},
    	{BFA_SM(bna_port_sm_started), BNA_PORT_STARTED},
    	{BFA_SM(bna_port_sm_pause_cfg_wait), BNA_PORT_PAUSE_CFG_WAIT},
    	{BFA_SM(bna_port_sm_rx_stop_wait), BNA_PORT_RX_STOP_WAIT},
    	{BFA_SM(bna_port_sm_mtu_cfg_wait), BNA_PORT_MTU_CFG_WAIT},
    	{BFA_SM(bna_port_sm_chld_stop_wait), BNA_PORT_CHLD_STOP_WAIT}
    };
    
    static void
    bna_port_sm_stopped_entry(struct bna_port *port)
    {
    	call_port_pause_cbfn(port, BNA_CB_SUCCESS);
    	call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
    	call_port_stop_cbfn(port, BNA_CB_SUCCESS);
    }
    
    static void
    bna_port_sm_stopped(struct bna_port *port, enum bna_port_event event)
    {
    	switch (event) {
    	case PORT_E_START:
    		bfa_fsm_set_state(port, bna_port_sm_mtu_init_wait);
    		break;
    
    	case PORT_E_STOP:
    		call_port_stop_cbfn(port, BNA_CB_SUCCESS);
    		break;
    
    	case PORT_E_FAIL:
    		/* No-op */
    		break;
    
    	case PORT_E_PAUSE_CFG:
    		call_port_pause_cbfn(port, BNA_CB_SUCCESS);
    		break;
    
    	case PORT_E_MTU_CFG:
    		call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
    		break;
    
    	case PORT_E_CHLD_STOPPED:
    		/**
    		 * This event is received due to LLPort, Tx and Rx objects
    		 * failing
    		 */
    		/* No-op */
    		break;
    
    	case PORT_E_FWRESP_PAUSE:
    	case PORT_E_FWRESP_MTU:
    		/**
    		 * These events are received due to flushing of mbox when
    		 * device fails
    		 */
    		/* No-op */
    		break;
    
    	default:
    		bfa_sm_fault(port->bna, event);
    	}
    }
    
    static void
    bna_port_sm_mtu_init_wait_entry(struct bna_port *port)
    {
    	bna_fw_mtu_set(port);
    }
    
    static void
    bna_port_sm_mtu_init_wait(struct bna_port *port, enum bna_port_event event)
    {
    	switch (event) {
    	case PORT_E_STOP:
    		bfa_fsm_set_state(port, bna_port_sm_last_resp_wait);
    		break;
    
    	case PORT_E_FAIL:
    		bfa_fsm_set_state(port, bna_port_sm_stopped);
    		break;
    
    	case PORT_E_PAUSE_CFG:
    		/* No-op */
    		break;
    
    	case PORT_E_MTU_CFG:
    		port->flags |= BNA_PORT_F_MTU_CHANGED;
    		break;
    
    	case PORT_E_FWRESP_MTU:
    		if (port->flags & BNA_PORT_F_MTU_CHANGED) {
    			port->flags &= ~BNA_PORT_F_MTU_CHANGED;
    			bna_fw_mtu_set(port);
    		} else {
    			bfa_fsm_set_state(port, bna_port_sm_pause_init_wait);
    		}
    		break;
    
    	default:
    		bfa_sm_fault(port->bna, event);
    	}
    }
    
    static void
    bna_port_sm_pause_init_wait_entry(struct bna_port *port)
    {
    	bna_fw_pause_set(port);
    }
    
    static void
    bna_port_sm_pause_init_wait(struct bna_port *port,
    				enum bna_port_event event)
    {
    	switch (event) {
    	case PORT_E_STOP:
    		bfa_fsm_set_state(port, bna_port_sm_last_resp_wait);
    		break;
    
    	case PORT_E_FAIL:
    		bfa_fsm_set_state(port, bna_port_sm_stopped);
    		break;
    
    	case PORT_E_PAUSE_CFG:
    		port->flags |= BNA_PORT_F_PAUSE_CHANGED;
    		break;
    
    	case PORT_E_MTU_CFG:
    		port->flags |= BNA_PORT_F_MTU_CHANGED;
    		break;
    
    	case PORT_E_FWRESP_PAUSE:
    		if (port->flags & BNA_PORT_F_PAUSE_CHANGED) {
    			port->flags &= ~BNA_PORT_F_PAUSE_CHANGED;
    			bna_fw_pause_set(port);
    		} else if (port->flags & BNA_PORT_F_MTU_CHANGED) {
    			port->flags &= ~BNA_PORT_F_MTU_CHANGED;
    			bfa_fsm_set_state(port, bna_port_sm_mtu_init_wait);
    		} else {
    			bfa_fsm_set_state(port, bna_port_sm_started);
    			bna_port_chld_start(port);
    		}
    		break;
    
    	default:
    		bfa_sm_fault(port->bna, event);
    	}
    }
    
    static void
    bna_port_sm_last_resp_wait_entry(struct bna_port *port)
    {
    }
    
    static void
    bna_port_sm_last_resp_wait(struct bna_port *port,
    				enum bna_port_event event)
    {
    	switch (event) {
    	case PORT_E_FAIL:
    	case PORT_E_FWRESP_PAUSE:
    	case PORT_E_FWRESP_MTU:
    		bfa_fsm_set_state(port, bna_port_sm_stopped);
    		break;
    
    	default:
    		bfa_sm_fault(port->bna, event);
    	}
    }
    
    static void
    bna_port_sm_started_entry(struct bna_port *port)
    {
    	/**
    	 * NOTE: Do not call bna_port_chld_start() here, since it will be
    	 * inadvertently called during pause_cfg_wait->started transition
    	 * as well
    	 */
    	call_port_pause_cbfn(port, BNA_CB_SUCCESS);
    	call_port_mtu_cbfn(port, BNA_CB_SUCCESS);
    }
    
    static void
    bna_port_sm_started(struct bna_port *port,
    			enum bna_port_event event)
    {
    	switch (event) {
    	case PORT_E_STOP:
    		bfa_fsm_set_state(port, bna_port_sm_chld_stop_wait);
    		break;
    
    	case PORT_E_FAIL:
    		bfa_fsm_set_state(port, bna_port_sm_stopped);
    		bna_port_chld_fail(port);
    		break;
    
    	case PORT_E_PAUSE_CFG:
    		bfa_fsm_set_state(port, bna_port_sm_pause_cfg_wait);
    		break;
    
    	case PORT_E_MTU_CFG:
    		bfa_fsm_set_state(port, bna_port_sm_rx_stop_wait);
    		break;
    
    	default:
    		bfa_sm_fault(port->bna, event);
    	}
    }
    
    static void
    bna_port_sm_pause_cfg_wait_entry(struct bna_port *port)
    {
    	bna_fw_pause_set(port);
    }
    
    static void