diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-03-31 15:53:43 +0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-31 15:53:43 +0400 |
commit | 7bee946358c3cb957d4aa648fc5ab3cad0b232d0 (patch) | |
tree | 693061ebde2abc35ecc846e5084630d7225aaaff /drivers/net/qlge/qlge_mpi.c | |
parent | d820ac4c2fa881079e6b689d2098adce337558ae (diff) | |
parent | 15f7176eb1cccec0a332541285ee752b935c1c85 (diff) | |
download | linux-7bee946358c3cb957d4aa648fc5ab3cad0b232d0.tar.xz |
Merge branch 'linus' into locking-for-linus
Conflicts:
lib/Kconfig.debug
Diffstat (limited to 'drivers/net/qlge/qlge_mpi.c')
-rw-r--r-- | drivers/net/qlge/qlge_mpi.c | 835 |
1 files changed, 770 insertions, 65 deletions
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index fa31891b6e62..9f81b797f10b 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c @@ -1,6 +1,26 @@ #include "qlge.h" -static int ql_read_mbox_reg(struct ql_adapter *qdev, u32 reg, u32 *data) +static void ql_display_mb_sts(struct ql_adapter *qdev, + struct mbox_params *mbcp) +{ + int i; + static char *err_sts[] = { + "Command Complete", + "Command Not Supported", + "Host Interface Error", + "Checksum Error", + "Unused Completion Status", + "Test Failed", + "Command Parameter Error"}; + + QPRINTK(qdev, DRV, DEBUG, "%s.\n", + err_sts[mbcp->mbox_out[0] & 0x0000000f]); + for (i = 0; i < mbcp->out_count; i++) + QPRINTK(qdev, DRV, DEBUG, "mbox_out[%d] = 0x%.08x.\n", + i, mbcp->mbox_out[i]); +} + +int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data) { int status; /* wait for reg to come ready */ @@ -19,6 +39,32 @@ exit: return status; } +int ql_write_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 data) +{ + int status = 0; + /* wait for reg to come ready */ + status = ql_wait_reg_rdy(qdev, PROC_ADDR, PROC_ADDR_RDY, PROC_ADDR_ERR); + if (status) + goto exit; + /* write the data to the data reg */ + ql_write32(qdev, PROC_DATA, data); + /* trigger the write */ + ql_write32(qdev, PROC_ADDR, reg); + /* wait for reg to come ready */ + status = ql_wait_reg_rdy(qdev, PROC_ADDR, PROC_ADDR_RDY, PROC_ADDR_ERR); + if (status) + goto exit; +exit: + return status; +} + +int ql_soft_reset_mpi_risc(struct ql_adapter *qdev) +{ + int status; + status = ql_write_mpi_reg(qdev, 0x00001010, 1); + return status; +} + static int ql_get_mb_sts(struct ql_adapter *qdev, struct mbox_params *mbcp) { int i, status; @@ -28,7 +74,7 @@ static int ql_get_mb_sts(struct ql_adapter *qdev, struct mbox_params *mbcp) return -EBUSY; for (i = 0; i < mbcp->out_count; i++) { status = - ql_read_mbox_reg(qdev, qdev->mailbox_out + i, + ql_read_mpi_reg(qdev, qdev->mailbox_out + i, &mbcp->mbox_out[i]); if (status) { QPRINTK(qdev, DRV, ERR, "Failed mailbox read.\n"); @@ -39,102 +85,762 @@ static int ql_get_mb_sts(struct ql_adapter *qdev, struct mbox_params *mbcp) return status; } +/* Wait for a single mailbox command to complete. + * Returns zero on success. + */ +static int ql_wait_mbx_cmd_cmplt(struct ql_adapter *qdev) +{ + int count = 50; /* TODO: arbitrary for now. */ + u32 value; + + do { + value = ql_read32(qdev, STS); + if (value & STS_PI) + return 0; + udelay(UDELAY_DELAY); /* 10us */ + } while (--count); + return -ETIMEDOUT; +} + +/* Execute a single mailbox command. + * Caller must hold PROC_ADDR semaphore. + */ +static int ql_exec_mb_cmd(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int i, status; + + /* + * Make sure there's nothing pending. + * This shouldn't happen. + */ + if (ql_read32(qdev, CSR) & CSR_HRI) + return -EIO; + + status = ql_sem_spinlock(qdev, SEM_PROC_REG_MASK); + if (status) + return status; + + /* + * Fill the outbound mailboxes. + */ + for (i = 0; i < mbcp->in_count; i++) { + status = ql_write_mpi_reg(qdev, qdev->mailbox_in + i, + mbcp->mbox_in[i]); + if (status) + goto end; + } + /* + * Wake up the MPI firmware. + */ + ql_write32(qdev, CSR, CSR_CMD_SET_H2R_INT); +end: + ql_sem_unlock(qdev, SEM_PROC_REG_MASK); + return status; +} + +/* We are being asked by firmware to accept + * a change to the port. This is only + * a change to max frame sizes (Tx/Rx), pause + * paramters, or loopback mode. We wake up a worker + * to handler processing this since a mailbox command + * will need to be sent to ACK the request. + */ +static int ql_idc_req_aen(struct ql_adapter *qdev) +{ + int status; + struct mbox_params *mbcp = &qdev->idc_mbc; + + QPRINTK(qdev, DRV, ERR, "Enter!\n"); + /* Get the status data and start up a thread to + * handle the request. + */ + mbcp = &qdev->idc_mbc; + mbcp->out_count = 4; + status = ql_get_mb_sts(qdev, mbcp); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Could not read MPI, resetting ASIC!\n"); + ql_queue_asic_error(qdev); + } else { + /* Begin polled mode early so + * we don't get another interrupt + * when we leave mpi_worker. + */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); + queue_delayed_work(qdev->workqueue, &qdev->mpi_idc_work, 0); + } + return status; +} + +/* Process an inter-device event completion. + * If good, signal the caller's completion. + */ +static int ql_idc_cmplt_aen(struct ql_adapter *qdev) +{ + int status; + struct mbox_params *mbcp = &qdev->idc_mbc; + mbcp->out_count = 4; + status = ql_get_mb_sts(qdev, mbcp); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Could not read MPI, resetting RISC!\n"); + ql_queue_fw_error(qdev); + } else + /* Wake up the sleeping mpi_idc_work thread that is + * waiting for this event. + */ + complete(&qdev->ide_completion); + + return status; +} + static void ql_link_up(struct ql_adapter *qdev, struct mbox_params *mbcp) { + int status; mbcp->out_count = 2; - if (ql_get_mb_sts(qdev, mbcp)) - goto exit; + status = ql_get_mb_sts(qdev, mbcp); + if (status) { + QPRINTK(qdev, DRV, ERR, + "%s: Could not get mailbox status.\n", __func__); + return; + } qdev->link_status = mbcp->mbox_out[1]; QPRINTK(qdev, DRV, ERR, "Link Up.\n"); - QPRINTK(qdev, DRV, INFO, "Link Status = 0x%.08x.\n", mbcp->mbox_out[1]); - if (!netif_carrier_ok(qdev->ndev)) { - QPRINTK(qdev, LINK, INFO, "Link is Up.\n"); - netif_carrier_on(qdev->ndev); - netif_wake_queue(qdev->ndev); + + /* If we're coming back from an IDC event + * then set up the CAM and frame routing. + */ + if (test_bit(QL_CAM_RT_SET, &qdev->flags)) { + status = ql_cam_route_initialize(qdev); + if (status) { + QPRINTK(qdev, IFUP, ERR, + "Failed to init CAM/Routing tables.\n"); + return; + } else + clear_bit(QL_CAM_RT_SET, &qdev->flags); } -exit: - /* Clear the MPI firmware status. */ - ql_write32(qdev, CSR, CSR_CMD_CLR_R2PCI_INT); + + /* Queue up a worker to check the frame + * size information, and fix it if it's not + * to our liking. + */ + if (!test_bit(QL_PORT_CFG, &qdev->flags)) { + QPRINTK(qdev, DRV, ERR, "Queue Port Config Worker!\n"); + set_bit(QL_PORT_CFG, &qdev->flags); + /* Begin polled mode early so + * we don't get another interrupt + * when we leave mpi_worker dpc. + */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); + queue_delayed_work(qdev->workqueue, + &qdev->mpi_port_cfg_work, 0); + } + + netif_carrier_on(qdev->ndev); } static void ql_link_down(struct ql_adapter *qdev, struct mbox_params *mbcp) { + int status; + mbcp->out_count = 3; - if (ql_get_mb_sts(qdev, mbcp)) { - QPRINTK(qdev, DRV, ERR, "Firmware did not initialize!\n"); - goto exit; - } + status = ql_get_mb_sts(qdev, mbcp); + if (status) + QPRINTK(qdev, DRV, ERR, "Link down AEN broken!\n"); + + netif_carrier_off(qdev->ndev); +} + +static int ql_sfp_in(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status; + + mbcp->out_count = 5; + + status = ql_get_mb_sts(qdev, mbcp); + if (status) + QPRINTK(qdev, DRV, ERR, "SFP in AEN broken!\n"); + else + QPRINTK(qdev, DRV, ERR, "SFP insertion detected.\n"); + + return status; +} + +static int ql_sfp_out(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status; + + mbcp->out_count = 1; + + status = ql_get_mb_sts(qdev, mbcp); + if (status) + QPRINTK(qdev, DRV, ERR, "SFP out AEN broken!\n"); + else + QPRINTK(qdev, DRV, ERR, "SFP removal detected.\n"); + + return status; +} + +static int ql_aen_lost(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status; + + mbcp->out_count = 6; + + status = ql_get_mb_sts(qdev, mbcp); + if (status) + QPRINTK(qdev, DRV, ERR, "Lost AEN broken!\n"); + else { + int i; + QPRINTK(qdev, DRV, ERR, "Lost AEN detected.\n"); + for (i = 0; i < mbcp->out_count; i++) + QPRINTK(qdev, DRV, ERR, "mbox_out[%d] = 0x%.08x.\n", + i, mbcp->mbox_out[i]); - if (netif_carrier_ok(qdev->ndev)) { - QPRINTK(qdev, LINK, INFO, "Link is Down.\n"); - netif_carrier_off(qdev->ndev); - netif_stop_queue(qdev->ndev); } - QPRINTK(qdev, DRV, ERR, "Link Down.\n"); - QPRINTK(qdev, DRV, ERR, "Link Status = 0x%.08x.\n", mbcp->mbox_out[1]); -exit: - /* Clear the MPI firmware status. */ - ql_write32(qdev, CSR, CSR_CMD_CLR_R2PCI_INT); + + return status; } static void ql_init_fw_done(struct ql_adapter *qdev, struct mbox_params *mbcp) { + int status; + mbcp->out_count = 2; - if (ql_get_mb_sts(qdev, mbcp)) { + status = ql_get_mb_sts(qdev, mbcp); + if (status) { QPRINTK(qdev, DRV, ERR, "Firmware did not initialize!\n"); - goto exit; + } else { + QPRINTK(qdev, DRV, ERR, "Firmware Revision = 0x%.08x.\n", + mbcp->mbox_out[1]); + status = ql_cam_route_initialize(qdev); + if (status) + QPRINTK(qdev, IFUP, ERR, + "Failed to init CAM/Routing tables.\n"); } - QPRINTK(qdev, DRV, ERR, "Firmware initialized!\n"); - QPRINTK(qdev, DRV, ERR, "Firmware status = 0x%.08x.\n", - mbcp->mbox_out[0]); - QPRINTK(qdev, DRV, ERR, "Firmware Revision = 0x%.08x.\n", - mbcp->mbox_out[1]); -exit: - /* Clear the MPI firmware status. */ +} + +/* Process an async event and clear it unless it's an + * error condition. + * This can get called iteratively from the mpi_work thread + * when events arrive via an interrupt. + * It also gets called when a mailbox command is polling for + * it's completion. */ +static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status; + int orig_count = mbcp->out_count; + + /* Just get mailbox zero for now. */ + mbcp->out_count = 1; + status = ql_get_mb_sts(qdev, mbcp); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Could not read MPI, resetting ASIC!\n"); + ql_queue_asic_error(qdev); + goto end; + } + + switch (mbcp->mbox_out[0]) { + + /* This case is only active when we arrive here + * as a result of issuing a mailbox command to + * the firmware. + */ + case MB_CMD_STS_INTRMDT: + case MB_CMD_STS_GOOD: + case MB_CMD_STS_INVLD_CMD: + case MB_CMD_STS_XFC_ERR: + case MB_CMD_STS_CSUM_ERR: + case MB_CMD_STS_ERR: + case MB_CMD_STS_PARAM_ERR: + /* We can only get mailbox status if we're polling from an + * unfinished command. Get the rest of the status data and + * return back to the caller. + * We only end up here when we're polling for a mailbox + * command completion. + */ + mbcp->out_count = orig_count; + status = ql_get_mb_sts(qdev, mbcp); + return status; + + /* We are being asked by firmware to accept + * a change to the port. This is only + * a change to max frame sizes (Tx/Rx), pause + * paramters, or loopback mode. + */ + case AEN_IDC_REQ: + status = ql_idc_req_aen(qdev); + break; + + /* Process and inbound IDC event. + * This will happen when we're trying to + * change tx/rx max frame size, change pause + * paramters or loopback mode. + */ + case AEN_IDC_CMPLT: + case AEN_IDC_EXT: + status = ql_idc_cmplt_aen(qdev); + break; + + case AEN_LINK_UP: + ql_link_up(qdev, mbcp); + break; + + case AEN_LINK_DOWN: + ql_link_down(qdev, mbcp); + break; + + case AEN_FW_INIT_DONE: + /* If we're in process on executing the firmware, + * then convert the status to normal mailbox status. + */ + if (mbcp->mbox_in[0] == MB_CMD_EX_FW) { + mbcp->out_count = orig_count; + status = ql_get_mb_sts(qdev, mbcp); + mbcp->mbox_out[0] = MB_CMD_STS_GOOD; + return status; + } + ql_init_fw_done(qdev, mbcp); + break; + + case AEN_AEN_SFP_IN: + ql_sfp_in(qdev, mbcp); + break; + + case AEN_AEN_SFP_OUT: + ql_sfp_out(qdev, mbcp); + break; + + /* This event can arrive at boot time or after an + * MPI reset if the firmware failed to initialize. + */ + case AEN_FW_INIT_FAIL: + /* If we're in process on executing the firmware, + * then convert the status to normal mailbox status. + */ + if (mbcp->mbox_in[0] == MB_CMD_EX_FW) { + mbcp->out_count = orig_count; + status = ql_get_mb_sts(qdev, mbcp); + mbcp->mbox_out[0] = MB_CMD_STS_ERR; + return status; + } + QPRINTK(qdev, DRV, ERR, + "Firmware initialization failed.\n"); + status = -EIO; + ql_queue_fw_error(qdev); + break; + + case AEN_SYS_ERR: + QPRINTK(qdev, DRV, ERR, + "System Error.\n"); + ql_queue_fw_error(qdev); + status = -EIO; + break; + + case AEN_AEN_LOST: + ql_aen_lost(qdev, mbcp); + break; + + default: + QPRINTK(qdev, DRV, ERR, + "Unsupported AE %.08x.\n", mbcp->mbox_out[0]); + /* Clear the MPI firmware status. */ + } +end: ql_write32(qdev, CSR, CSR_CMD_CLR_R2PCI_INT); + return status; } -void ql_mpi_work(struct work_struct *work) +/* Execute a single mailbox command. + * mbcp is a pointer to an array of u32. Each + * element in the array contains the value for it's + * respective mailbox register. + */ +static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status, count; + + mutex_lock(&qdev->mpi_mutex); + + /* Begin polled mode for MPI */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); + + /* Load the mailbox registers and wake up MPI RISC. */ + status = ql_exec_mb_cmd(qdev, mbcp); + if (status) + goto end; + + + /* If we're generating a system error, then there's nothing + * to wait for. + */ + if (mbcp->mbox_in[0] == MB_CMD_MAKE_SYS_ERR) + goto end; + + /* Wait for the command to complete. We loop + * here because some AEN might arrive while + * we're waiting for the mailbox command to + * complete. If more than 5 arrive then we can + * assume something is wrong. */ + count = 5; + do { + /* Wait for the interrupt to come in. */ + status = ql_wait_mbx_cmd_cmplt(qdev); + if (status) + goto end; + + /* Process the event. If it's an AEN, it + * will be handled in-line or a worker + * will be spawned. If it's our completion + * we will catch it below. + */ + status = ql_mpi_handler(qdev, mbcp); + if (status) + goto end; + + /* It's either the completion for our mailbox + * command complete or an AEN. If it's our + * completion then get out. + */ + if (((mbcp->mbox_out[0] & 0x0000f000) == + MB_CMD_STS_GOOD) || + ((mbcp->mbox_out[0] & 0x0000f000) == + MB_CMD_STS_INTRMDT)) + break; + } while (--count); + + if (!count) { + QPRINTK(qdev, DRV, ERR, + "Timed out waiting for mailbox complete.\n"); + status = -ETIMEDOUT; + goto end; + } + + /* Now we can clear the interrupt condition + * and look at our status. + */ + ql_write32(qdev, CSR, CSR_CMD_CLR_R2PCI_INT); + + if (((mbcp->mbox_out[0] & 0x0000f000) != + MB_CMD_STS_GOOD) && + ((mbcp->mbox_out[0] & 0x0000f000) != + MB_CMD_STS_INTRMDT)) { + ql_display_mb_sts(qdev, mbcp); + status = -EIO; + } +end: + mutex_unlock(&qdev->mpi_mutex); + /* End polled mode for MPI */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI); + return status; +} + +/* Get functional state for MPI firmware. + * Returns zero on success. + */ +int ql_mb_get_fw_state(struct ql_adapter *qdev) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status = 0; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 1; + mbcp->out_count = 2; + + mbcp->mbox_in[0] = MB_CMD_GET_FW_STATE; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed Get Firmware State.\n"); + status = -EIO; + } + + /* If bit zero is set in mbx 1 then the firmware is + * running, but not initialized. This should never + * happen. + */ + if (mbcp->mbox_out[1] & 1) { + QPRINTK(qdev, DRV, ERR, + "Firmware waiting for initialization.\n"); + status = -EIO; + } + + return status; +} + +/* Send and ACK mailbox command to the firmware to + * let it continue with the change. + */ +int ql_mb_idc_ack(struct ql_adapter *qdev) { - struct ql_adapter *qdev = - container_of(work, struct ql_adapter, mpi_work.work); struct mbox_params mbc; struct mbox_params *mbcp = &mbc; + int status = 0; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 5; mbcp->out_count = 1; - while (ql_read32(qdev, STS) & STS_PI) { - if (ql_get_mb_sts(qdev, mbcp)) { - QPRINTK(qdev, DRV, ERR, - "Could not read MPI, resetting ASIC!\n"); - ql_queue_asic_error(qdev); - } + mbcp->mbox_in[0] = MB_CMD_IDC_ACK; + mbcp->mbox_in[1] = qdev->idc_mbc.mbox_out[1]; + mbcp->mbox_in[2] = qdev->idc_mbc.mbox_out[2]; + mbcp->mbox_in[3] = qdev->idc_mbc.mbox_out[3]; + mbcp->mbox_in[4] = qdev->idc_mbc.mbox_out[4]; - switch (mbcp->mbox_out[0]) { - case AEN_LINK_UP: - ql_link_up(qdev, mbcp); - break; - case AEN_LINK_DOWN: - ql_link_down(qdev, mbcp); - break; - case AEN_FW_INIT_DONE: - ql_init_fw_done(qdev, mbcp); + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed IDC ACK send.\n"); + status = -EIO; + } + return status; +} + +/* Get link settings and maximum frame size settings + * for the current port. + * Most likely will block. + */ +static int ql_mb_set_port_cfg(struct ql_adapter *qdev) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status = 0; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 3; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_SET_PORT_CFG; + mbcp->mbox_in[1] = qdev->link_config; + mbcp->mbox_in[2] = qdev->max_frame_size; + + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_INTRMDT) { + QPRINTK(qdev, DRV, ERR, + "Port Config sent, wait for IDC.\n"); + } else if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed Set Port Configuration.\n"); + status = -EIO; + } + return status; +} + +/* Get link settings and maximum frame size settings + * for the current port. + * Most likely will block. + */ +static int ql_mb_get_port_cfg(struct ql_adapter *qdev) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status = 0; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 1; + mbcp->out_count = 3; + + mbcp->mbox_in[0] = MB_CMD_GET_PORT_CFG; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) { + QPRINTK(qdev, DRV, ERR, + "Failed Get Port Configuration.\n"); + status = -EIO; + } else { + QPRINTK(qdev, DRV, DEBUG, + "Passed Get Port Configuration.\n"); + qdev->link_config = mbcp->mbox_out[1]; + qdev->max_frame_size = mbcp->mbox_out[2]; + } + return status; +} + +/* IDC - Inter Device Communication... + * Some firmware commands require consent of adjacent FCOE + * function. This function waits for the OK, or a + * counter-request for a little more time.i + * The firmware will complete the request if the other + * function doesn't respond. + */ +static int ql_idc_wait(struct ql_adapter *qdev) +{ + int status = -ETIMEDOUT; + long wait_time = 1 * HZ; + struct mbox_params *mbcp = &qdev->idc_mbc; + do { + /* Wait here for the command to complete + * via the IDC process. + */ + wait_time = + wait_for_completion_timeout(&qdev->ide_completion, + wait_time); + if (!wait_time) { + QPRINTK(qdev, DRV, ERR, + "IDC Timeout.\n"); break; - case MB_CMD_STS_GOOD: + } + /* Now examine the response from the IDC process. + * We might have a good completion or a request for + * more wait time. + */ + if (mbcp->mbox_out[0] == AEN_IDC_EXT) { + QPRINTK(qdev, DRV, ERR, + "IDC Time Extension from function.\n"); + wait_time += (mbcp->mbox_out[1] >> 8) & 0x0000000f; + } else if (mbcp->mbox_out[0] == AEN_IDC_CMPLT) { + QPRINTK(qdev, DRV, ERR, + "IDC Success.\n"); + status = 0; break; - case AEN_FW_INIT_FAIL: - case AEN_SYS_ERR: - case MB_CMD_STS_ERR: - ql_queue_fw_error(qdev); - default: - /* Clear the MPI firmware status. */ - ql_write32(qdev, CSR, CSR_CMD_CLR_R2PCI_INT); + } else { + QPRINTK(qdev, DRV, ERR, + "IDC: Invalid State 0x%.04x.\n", + mbcp->mbox_out[0]); + status = -EIO; break; } + } while (wait_time); + + return status; +} + +/* API called in work thread context to set new TX/RX + * maximum frame size values to match MTU. + */ +static int ql_set_port_cfg(struct ql_adapter *qdev) +{ + int status; + status = ql_mb_set_port_cfg(qdev); + if (status) + return status; + status = ql_idc_wait(qdev); + return status; +} + +/* The following routines are worker threads that process + * events that may sleep waiting for completion. + */ + +/* This thread gets the maximum TX and RX frame size values + * from the firmware and, if necessary, changes them to match + * the MTU setting. + */ +void ql_mpi_port_cfg_work(struct work_struct *work) +{ + struct ql_adapter *qdev = + container_of(work, struct ql_adapter, mpi_port_cfg_work.work); + struct net_device *ndev = qdev->ndev; + int status; + + status = ql_mb_get_port_cfg(qdev); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Bug: Failed to get port config data.\n"); + goto err; + } + + if (ndev->mtu <= 2500) + goto end; + else if (qdev->link_config & CFG_JUMBO_FRAME_SIZE && + qdev->max_frame_size == + CFG_DEFAULT_MAX_FRAME_SIZE) + goto end; + + qdev->link_config |= CFG_JUMBO_FRAME_SIZE; + qdev->max_frame_size = CFG_DEFAULT_MAX_FRAME_SIZE; + status = ql_set_port_cfg(qdev); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Bug: Failed to set port config data.\n"); + goto err; + } +end: + clear_bit(QL_PORT_CFG, &qdev->flags); + return; +err: + ql_queue_fw_error(qdev); + goto end; +} + +/* Process an inter-device request. This is issues by + * the firmware in response to another function requesting + * a change to the port. We set a flag to indicate a change + * has been made and then send a mailbox command ACKing + * the change request. + */ +void ql_mpi_idc_work(struct work_struct *work) +{ + struct ql_adapter *qdev = + container_of(work, struct ql_adapter, mpi_idc_work.work); + int status; + struct mbox_params *mbcp = &qdev->idc_mbc; + u32 aen; + + aen = mbcp->mbox_out[1] >> 16; + + switch (aen) { + default: + QPRINTK(qdev, DRV, ERR, + "Bug: Unhandled IDC action.\n"); + break; + case MB_CMD_PORT_RESET: + case MB_CMD_SET_PORT_CFG: + case MB_CMD_STOP_FW: + netif_carrier_off(qdev->ndev); + /* Signal the resulting link up AEN + * that the frame routing and mac addr + * needs to be set. + * */ + set_bit(QL_CAM_RT_SET, &qdev->flags); + status = ql_mb_idc_ack(qdev); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Bug: No pending IDC!\n"); + } + } +} + +void ql_mpi_work(struct work_struct *work) +{ + struct ql_adapter *qdev = + container_of(work, struct ql_adapter, mpi_work.work); + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + + mutex_lock(&qdev->mpi_mutex); + + while (ql_read32(qdev, STS) & STS_PI) { + memset(mbcp, 0, sizeof(struct mbox_params)); + mbcp->out_count = 1; + ql_mpi_handler(qdev, mbcp); } + + mutex_unlock(&qdev->mpi_mutex); ql_enable_completion_interrupt(qdev, 0); } @@ -142,9 +848,8 @@ void ql_mpi_reset_work(struct work_struct *work) { struct ql_adapter *qdev = container_of(work, struct ql_adapter, mpi_reset_work.work); - QPRINTK(qdev, DRV, ERR, - "Enter, qdev = %p..\n", qdev); - ql_write32(qdev, CSR, CSR_CMD_SET_RST); - msleep(50); - ql_write32(qdev, CSR, CSR_CMD_CLR_RST); + cancel_delayed_work_sync(&qdev->mpi_work); + cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); + cancel_delayed_work_sync(&qdev->mpi_idc_work); + ql_soft_reset_mpi_risc(qdev); } |