diff options
Diffstat (limited to 'drivers/net/bna/bna_ctrl.c')
-rw-r--r-- | drivers/net/bna/bna_ctrl.c | 3261 |
1 files changed, 3261 insertions, 0 deletions
diff --git a/drivers/net/bna/bna_ctrl.c b/drivers/net/bna/bna_ctrl.c new file mode 100644 index 00000000000..07b26598546 --- /dev/null +++ b/drivers/net/bna/bna_ctrl.c @@ -0,0 +1,3261 @@ +/* + * 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 +bna_port_sm_pause_cfg_wait(struct bna_port *port, + enum bna_port_event event) +{ + switch (event) { + case PORT_E_FAIL: + bfa_fsm_set_state(port, bna_port_sm_stopped); + bna_port_chld_fail(port); + break; + + case PORT_E_FWRESP_PAUSE: + bfa_fsm_set_state(port, bna_port_sm_started); + break; + + default: + bfa_sm_fault(port->bna, event); + } +} + +static void +bna_port_sm_rx_stop_wait_entry(struct bna_port *port) +{ + bna_port_rx_stop(port); +} + +static void +bna_port_sm_rx_stop_wait(struct bna_port *port, + enum bna_port_event event) +{ + switch (event) { + case PORT_E_FAIL: + bfa_fsm_set_state(port, bna_port_sm_stopped); + bna_port_chld_fail(port); + break; + + case PORT_E_CHLD_STOPPED: + bfa_fsm_set_state(port, bna_port_sm_mtu_cfg_wait); + break; + + default: + bfa_sm_fault(port->bna, event); + } +} + +static void +bna_port_sm_mtu_cfg_wait_entry(struct bna_port *port) +{ + bna_fw_mtu_set(port); +} + +static void +bna_port_sm_mtu_cfg_wait(struct bna_port *port, enum bna_port_event event) +{ + switch (event) { + case PORT_E_FAIL: + bfa_fsm_set_state(port, bna_port_sm_stopped); + bna_port_chld_fail(port); + break; + + case PORT_E_FWRESP_MTU: + bfa_fsm_set_state(port, bna_port_sm_started); + bna_port_rx_start(port); + break; + + default: + bfa_sm_fault(port->bna, event); + } +} + +static void +bna_port_sm_chld_stop_wait_entry(struct bna_port *port) +{ + bna_port_chld_stop(port); +} + +static void +bna_port_sm_chld_stop_wait(struct bna_port *port, + enum bna_port_event event) +{ + switch (event) { + case PORT_E_FAIL: + bfa_fsm_set_state(port, bna_port_sm_stopped); + bna_port_chld_fail(port); + break; + + case PORT_E_CHLD_STOPPED: + bfa_fsm_set_state(port, bna_port_sm_stopped); + break; + + default: + bfa_sm_fault(port->bna, event); + } +} + +static void +bna_fw_pause_set(struct bna_port *port) +{ + struct bfi_ll_set_pause_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_SET_PAUSE_REQ; + ll_req.mh.mtag.h2i.lpu_id = 0; + + ll_req.tx_pause = port->pause_config.tx_pause; + ll_req.rx_pause = port->pause_config.rx_pause; + + bna_mbox_qe_fill(&port->mbox_qe, &ll_req, sizeof(ll_req), + bna_fw_cb_pause_set, port); + + bna_mbox_send(port->bna, &port->mbox_qe); +} + +static void +bna_fw_cb_pause_set(void *arg, int status) +{ + struct bna_port *port = (struct bna_port *)arg; + + bfa_q_qe_init(&port->mbox_qe.qe); + bfa_fsm_send_event(port, PORT_E_FWRESP_PAUSE); +} + +void +bna_fw_mtu_set(struct bna_port *port) +{ + struct bfi_ll_mtu_info_req ll_req; + + bfi_h2i_set(ll_req.mh, BFI_MC_LL, BFI_LL_H2I_MTU_INFO_REQ, 0); + ll_req.mtu = htons((u16)port->mtu); + + bna_mbox_qe_fill(&port->mbox_qe, &ll_req, sizeof(ll_req), + bna_fw_cb_mtu_set, port); + bna_mbox_send(port->bna, &port->mbox_qe); +} + +void +bna_fw_cb_mtu_set(void *arg, int status) +{ + struct bna_port *port = (struct bna_port *)arg; + + bfa_q_qe_init(&port->mbox_qe.qe); + bfa_fsm_send_event(port, PORT_E_FWRESP_MTU); +} + +static void +bna_port_cb_chld_stopped(void *arg) +{ + struct bna_port *port = (struct bna_port *)arg; + + bfa_fsm_send_event(port, PORT_E_CHLD_STOPPED); +} + +static void +bna_port_init(struct bna_port *port, struct bna *bna) +{ + port->bna = bna; + port->flags = 0; + port->mtu = 0; + port->type = BNA_PORT_T_REGULAR; + + port->link_cbfn = bnad_cb_port_link_status; + + port->chld_stop_wc.wc_resume = bna_port_cb_chld_stopped; + port->chld_stop_wc.wc_cbarg = port; + port->chld_stop_wc.wc_count = 0; + + port->stop_cbfn = NULL; + port->stop_cbarg = NULL; + + port->pause_cbfn = NULL; + + port->mtu_cbfn = NULL; + + bfa_q_qe_init(&port->mbox_qe.qe); + + bfa_fsm_set_state(port, bna_port_sm_stopped); + + bna_llport_init(&port->llport, bna); +} + +static void +bna_port_uninit(struct bna_port *port) +{ + bna_llport_uninit(&port->llport); + + port->flags = 0; + + port->bna = NULL; +} + +static int +bna_port_state_get(struct bna_port *port) +{ + return bfa_sm_to_state(port_sm_table, port->fsm); +} + +static void +bna_port_start(struct bna_port *port) +{ + port->flags |= BNA_PORT_F_DEVICE_READY; + if (port->flags & BNA_PORT_F_ENABLED) + bfa_fsm_send_event(port, PORT_E_START); +} + +static void +bna_port_stop(struct bna_port *port) +{ + port->stop_cbfn = bna_device_cb_port_stopped; + port->stop_cbarg = &port->bna->device; + + port->flags &= ~BNA_PORT_F_DEVICE_READY; + bfa_fsm_send_event(port, PORT_E_STOP); +} + +static void +bna_port_fail(struct bna_port *port) +{ + port->flags &= ~BNA_PORT_F_DEVICE_READY; + bfa_fsm_send_event(port, PORT_E_FAIL); +} + +void +bna_port_cb_tx_stopped(struct bna_port *port, enum bna_cb_status status) +{ + bfa_wc_down(&port->chld_stop_wc); +} + +void +bna_port_cb_rx_stopped(struct bna_port *port, enum bna_cb_status status) +{ + bfa_wc_down(&port->chld_stop_wc); +} + +int +bna_port_mtu_get(struct bna_port *port) +{ + return port->mtu; +} + +void +bna_port_enable(struct bna_port *port) +{ + if (port->fsm != (bfa_sm_t)bna_port_sm_stopped) + return; + + port->flags |= BNA_PORT_F_ENABLED; + + if (port->flags & BNA_PORT_F_DEVICE_READY) + bfa_fsm_send_event(port, PORT_E_START); +} + +void +bna_port_disable(struct bna_port *port, enum bna_cleanup_type type, + void (*cbfn)(void *, enum bna_cb_status)) +{ + if (type == BNA_SOFT_CLEANUP) { + (*cbfn)(port->bna->bnad, BNA_CB_SUCCESS); + return; + } + + port->stop_cbfn = cbfn; + port->stop_cbarg = port->bna->bnad; + + port->flags &= ~BNA_PORT_F_ENABLED; + + bfa_fsm_send_event(port, PORT_E_STOP); +} + +void +bna_port_pause_config(struct bna_port *port, + struct bna_pause_config *pause_config, + void |