/* * Copyright (c) 2012 HON HAI PRECISION IND.CO.,LTD. (FOXCONN) */ #include #include #include #include #include "ep8324_def.h" extern int fwloadbin; extern struct list_head ep8324_func_list; extern int fc_data_rate; static DEFINE_MUTEX(ep8324_fw_lock); #define FW_FC 0 #define FW_FCOE 1 #define FW_FILE_FC "8300fc.bin" #define FW_FILE_FCOE "8300fcoe.bin" static struct fw_blob ep8324_fw_blobs[] = { { .name = FW_FILE_FC, }, { .name = FW_FILE_FCOE, }, }; struct fw_blob * ep8324_request_firmware(struct ep8324_hw_data *ha) { struct fw_blob *blob = NULL; switch (ha->func_type) { case EP8324_FC: blob = &ep8324_fw_blobs[FW_FC]; break; case EP8324_FCOE: blob = &ep8324_fw_blobs[FW_FCOE]; break; default: printk(KERN_INFO "Unsupported fw type(%d)\n", ha->func_type); return NULL; } mutex_lock(&ep8324_fw_lock); if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) { printk(KERN_INFO "Failed to load firmware image (%s).\n", blob->name); blob->fw = NULL; blob = NULL; goto out; } out: mutex_unlock(&ep8324_fw_lock); return blob; } void ep8324_release_firmware(struct fw_blob *blob) { mutex_lock(&ep8324_fw_lock); release_firmware(blob->fw); mutex_unlock(&ep8324_fw_lock); } static int ep8324_read_flash_dword(struct ep8324_hw_data *ha, uint32_t addr, uint32_t *data) { int ret = 0; uint32_t cnt; struct device_reg_fc __iomem *reg = &ha->iobase->fc; /* FIXME fc only ? */ WRT_REG_DWORD(®->flash_addr, addr & ~FARX_DATA_FLAG); /* Wait for READ cycle to complete. */ for (cnt = 3000; (RD_REG_DWORD(®->flash_addr) & FARX_DATA_FLAG) == 0 && ret == 0; cnt--) { if (cnt) udelay(10); else ret = -ETIMEDOUT; cond_resched(); } if (ret == 0) *data = RD_REG_DWORD(®->flash_data); return ret; } int ep8324_read_flash_data(struct ep8324_hw_data *ha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { int ret = -EIO; uint32_t data; uint32_t i; for (i = 0; i < dwords; i++, faddr++) { ret = ep8324_read_flash_dword(ha, FARX_ACCESS_FLASH_DATA | faddr, &data); if (ret) break; dwptr[i] = cpu_to_le32(data); } return ret; } static int ep8324_write_flash_dword(struct ep8324_hw_data *ha, uint32_t addr, uint32_t data) { int ret; uint32_t cnt; struct device_reg_fc __iomem *reg = &ha->iobase->fc; /* FIXME fc only ? */ WRT_REG_DWORD(®->flash_data, data); RD_REG_DWORD(®->flash_data); /* PCI Posting. */ WRT_REG_DWORD(®->flash_addr, addr | FARX_DATA_FLAG); /* Wait for Write cycle to complete. */ ret = 0; for (cnt = 500000; (RD_REG_DWORD(®->flash_addr) & FARX_DATA_FLAG) && ret == 0; cnt--) { if (cnt) udelay(10); else ret = -ETIMEDOUT; cond_resched(); } return ret; } static int ep8324_unprotect_flash(struct ep8324_hw_data *ha) { int ret; struct device_reg_fc __iomem *reg = &ha->iobase->fc; /* FIXME fc only ? */ /* TODO Access Control MBX support */ /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ if (!ha->fdt_wrt_disable) goto done; /* Disable flash write-protection, first clear SR protection bit */ ret = ep8324_write_flash_dword(ha, FARX_ACCESS_FLASH_CONF | 0x101, 0); if (ret) goto done; /* Then write zero again to clear remaining SR bits.*/ ret = ep8324_write_flash_dword(ha, FARX_ACCESS_FLASH_CONF | 0x101, 0); done: return 0; } static int ep8324_protect_flash(struct ep8324_hw_data *ha) { uint32_t cnt; struct device_reg_fc __iomem *reg = &ha->iobase->fc; /* FIXME fc only ? */ /* TODO Access Control MBX support */ if (!ha->fdt_wrt_disable) goto skip_wrt_protect; /* Enable flash write-protection and wait for completion. */ ep8324_write_flash_dword(ha, FARX_ACCESS_FLASH_CONF | 0x101, ha->fdt_wrt_disable); for (cnt = 300; cnt; cnt--) { uint32_t data; ep8324_read_flash_dword(ha, FARX_ACCESS_FLASH_CONF | 0x005, &data); if (data & BIT_0) break; udelay(10); } skip_wrt_protect: /* Disable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ return 0; } static int ep8324_erase_sector(struct ep8324_hw_data *ha, uint32_t fdata) { /* TODO Access Control MBX support */ return ep8324_write_flash_dword(ha, ha->fdt_erase_cmd, (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); } int ep8324_write_flash_data(struct ep8324_hw_data *ha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { int ret; uint32_t liter; uint32_t sec_mask, rest_addr; uint32_t fdata; int burst_write = 1; rest_addr = (ha->fdt_block_size >> 2) - 1; sec_mask = ~rest_addr; ret = ep8324_unprotect_flash(ha); if (ret) { printk(KERN_INFO "Unable to unprotect flash for update.\n"); goto done; } for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { fdata = (faddr & sec_mask) << 2; /* Are we at the beginning of a sector? */ if ((faddr & rest_addr) == 0) { /* Do sector unprotect. */ if (ha->fdt_unprotect_sec_cmd) ep8324_write_flash_dword(ha, ha->fdt_unprotect_sec_cmd, (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); ret = ep8324_erase_sector(ha, fdata); if (ret) { printk(KERN_INFO "Unable to erase erase sector: address=%x.\n", faddr); break; } } /* Go with burst-write. */ if (burst_write && (liter + (EP8324_DMA_BUF_SIZE/4)) <= dwords) { memcpy(ha->dma_buf, dwptr, EP8324_DMA_BUF_SIZE); ret = ep8324_load_ram(ha, ha->dma, FARX_ACCESS_FLASH_DATA | faddr, EP8324_DMA_BUF_SIZE/4); if (ret) { printk(KERN_INFO "Unable to run burst-write mode, " "reverting to slow-write\n"); burst_write = 0; } else { liter += EP8324_DMA_BUF_SIZE/4 - 1; faddr += EP8324_DMA_BUF_SIZE/4 - 1; dwptr += EP8324_DMA_BUF_SIZE/4 - 1; continue; } } ret = ep8324_write_flash_dword(ha, FARX_ACCESS_FLASH_DATA | faddr, cpu_to_le32(*dwptr)); if (ret) { printk(KERN_INFO "Unable to program flash address=%x data=%x.\n", faddr, *dwptr); break; } /* Do sector protect. */ if (ha->fdt_unprotect_sec_cmd && ((faddr & rest_addr) == rest_addr)) ep8324_write_flash_dword(ha, ha->fdt_protect_sec_cmd, (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); } ret = ep8324_protect_flash(ha); if (ret) printk(KERN_INFO "Unable to protect flash after update.\n"); done: return ret; } static void ep8324_poll(struct ep8324_hw_data *ha) { unsigned long flags; local_irq_save(flags); ha->hw_ops->poll(ha); local_irq_restore(flags); } int ep8324_mailbox_command(struct ep8324_hw_data *ha, mbx_cmd_t *mcp) { int ret = 0; unsigned long flags = 0; device_reg_t __iomem *reg; uint16_t *iptr; uint16_t __iomem *optr; uint32_t cnt; uint32_t mboxes; unsigned long wait_time; reg = ha->iobase; /* TODO: make sure there was no out going mbx command */ ha->mcp = mcp; spin_lock_irqsave(&ha->hardware_lock, flags); optr = (uint16_t __iomem *)®->fc.mailbox0; iptr = mcp->mb; mboxes = mcp->out_mb; for (cnt = 0; cnt < MAILBOX_REGISTER_COUNT; cnt++) { if (mboxes & BIT_0) WRT_REG_WORD(optr, *iptr); mboxes >>= 1; optr++; iptr++; } ha->flags.mbox_int = 0; WRT_REG_DWORD(®->fc.hccr, HCCRX_SET_HOST_INT); spin_unlock_irqrestore(&ha->hardware_lock, flags); wait_time = jiffies + mcp->tov * HZ; /* wait at most tov secs */ while (!ha->flags.mbox_int) { if (time_after(jiffies, wait_time)) break; /* Check for pending interrupts. */ ep8324_poll(ha); } if (ha->flags.mbox_int) { uint16_t *iptr2; ha->flags.mbox_int = 0; if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE) { printk(KERN_DEBUG "MBX command(%x) failed %x\n", mcp->mb[0], ha->mailbox_out[0]); ret = -EIO; } /* Load return mailbox registers. */ iptr2 = mcp->mb; iptr = (uint16_t *)&ha->mailbox_out[0]; mboxes = mcp->in_mb; for (cnt = 0; cnt < MAILBOX_REGISTER_COUNT; cnt++) { if (mboxes & BIT_0) *iptr2 = *iptr; mboxes >>= 1; iptr2++; iptr++; } } else { uint16_t mb0; uint32_t ictrl; mb0 = RD_REG_WORD(®->fc.mailbox0); ictrl = RD_REG_DWORD(®->fc.ictrl); ret = -ETIMEDOUT; printk(KERN_DEBUG "MBX command timeout," "iocontrol=%x jiffies=%lx mb[0] = 0x%x\n", ictrl, jiffies, mb0); } /* Clean up */ ha->mcp = NULL; return ret; } int ep8324_load_ram(struct ep8324_hw_data *ha, dma_addr_t req_dma, uint32_t risc_addr, uint32_t risc_code_size) { mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED; mcp->mb[8] = MSW(risc_addr); mcp->out_mb = MBX_8|MBX_0; mcp->mb[1] = LSW(risc_addr); mcp->mb[2] = MSW(req_dma); mcp->mb[3] = LSW(req_dma); mcp->mb[6] = MSW(MSD(req_dma)); mcp->mb[7] = LSW(MSD(req_dma)); mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1; mcp->mb[4] = MSW(risc_code_size); mcp->mb[5] = LSW(risc_code_size); mcp->out_mb |= MBX_5|MBX_4; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; return ep8324_mailbox_command(ha, mcp); } int ep8324_verify_checksum(struct ep8324_hw_data *ha, uint32_t risc_addr) { int ret; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; mcp->mb[0] = MBC_VERIFY_CHECKSUM; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->out_mb |= MBX_2|MBX_1; mcp->in_mb |= MBX_2|MBX_1; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (ret) printk(KERN_INFO "Failed=%x check sum=%x.\n", ret, (mcp->mb[2] << 16) | mcp->mb[1]); return ret; } int ep8324_execute_fw(struct ep8324_hw_data *ha, uint32_t risc_addr) { mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; mcp->mb[0] = MBC_EXECUTE_FIRMWARE; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->mb[3] = 0; mcp->mb[4] = 0; /* TODO: option */ mcp->out_mb |= MBX_4|MBX_3|MBX_2|MBX_1; mcp->in_mb |= MBX_2|MBX_1; mcp->tov = MBX_TOV_SECONDS; return ep8324_mailbox_command(ha, mcp); } static int ep8324_setup_chip(struct ep8324_hw_data *ha) { int ret; uint32_t srisc_address = 0; ret = ha->hw_ops->load_risc(ha, &srisc_address); if (!ret) { dprintk("Verifying Checksum of loaded RISC code @%x.\n", srisc_address); ret = ep8324_verify_checksum(ha, srisc_address); if (!ret) { dprintk("Starting firmware @%x.\n", srisc_address); ret = ep8324_execute_fw(ha, srisc_address); if (!ret) { dprintk("Initialize firmware\n"); ret = ha->hw_ops->init_firmware(ha); } } } return ret; } static int ep8324_get_port_config(struct ep8324_hw_data *ha, uint16_t *config) { int ret; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; mcp->mb[0] = MBC_GET_PORT_CONFIG; mcp->out_mb = MBX_0; mcp->in_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (!ret) { memcpy(config, &mcp->mb[1], sizeof(uint16_t) * 4); dprintk("%s: %x %x %x %x %x\n", __func__, mcp->mb[0], config[0], config[1], config[2], config[3]); } return ret; } static int ep8324_set_port_config(struct ep8324_hw_data *ha, uint16_t *config) { int ret; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; mcp->mb[0] = MBC_SET_PORT_CONFIG; /* Copy all bits to preserve original setting */ memcpy(&mcp->mb[1], config, sizeof(uint16_t) * 4); mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; dprintk("%s: %x %x %x %x %x\n", __func__, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[4]); ret = ep8324_mailbox_command(ha, mcp); return ret; } #define ENABLE_INTERNAL_LOOPBACK 0x02 static int ep8324_set_internal_loopback(struct ep8324_hw_data *ha) { int ret = 0; uint16_t config[4]; uint16_t new_config[4]; unsigned long wait_time; memset(config, 0, sizeof(config)); memset(new_config, 0, sizeof(new_config)); ret = ep8324_get_port_config(ha, config); if (ret) { printk(KERN_INFO "get port config failed\n"); goto out; } new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; ha->flags.dcbx_int = 0; ret = ep8324_set_port_config(ha, new_config); if (ret) { printk(KERN_INFO "set port config failed.\n"); goto out; } wait_time = jiffies + 10 * HZ; while (!ha->flags.dcbx_int) { if (time_after(jiffies, wait_time)) break; /* Check for pending interrupts. */ ep8324_poll(ha); } if (ha->flags.dcbx_int) { ha->flags.dcbx_int = 0; dprintk(KERN_INFO "State change received\n"); } else { ret = -ETIMEDOUT; printk(KERN_DEBUG "State change notification not received\n"); } out: return ret; } #define INTERNAL_LOOPBACK_MASK 0x000E static int ep8324_reset_internal_loopback(struct ep8324_hw_data *ha, int wait) { int ret = 0; uint16_t config[4]; uint16_t new_config[4]; unsigned long wait_time; memset(config, 0, sizeof(config)); memset(new_config, 0, sizeof(new_config)); ret = ep8324_get_port_config(ha, config); if (ret) { printk(KERN_INFO "get port config failed\n"); goto out; } new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK; memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; if (wait) ha->flags.dcbx_int = 0; ret = ep8324_set_port_config(ha, new_config); if (ret) { printk(KERN_INFO "set port config failed.\n"); goto out; } if (wait) { wait_time = jiffies + 10 * HZ; while (!ha->flags.dcbx_int) { if (time_after(jiffies, wait_time)) break; /* Check for pending interrupts. */ ep8324_poll(ha); } if (ha->flags.dcbx_int) { ha->flags.dcbx_int = 0; dprintk("State change received\n"); } else { /* FIXME: * for external loopback test, * firmware version 6.02.00 does no post dcbx event */ printk(KERN_DEBUG "State change notification not received\n"); } } out: return ret; } static int ep8324_loopback_test(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; uint32_t iter_cnt = 1; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; memset(mcp->mb, 0 , sizeof(mcp->mb)); mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK; if (ha->loopback_path == EP8324_EXTERNAL_LOOPBACK) mcp->mb[1] = 2; /* external */ else mcp->mb[1] = 1; /* internal */ mcp->mb[2] = 0; /* FCF index (FCoE only) */ /* transfer count */ mcp->mb[10] = LSW(ha->data_transfer_count); mcp->mb[11] = MSW(ha->data_transfer_count); /* tx segment count */ mcp->mb[12] = ha->seg_count; /* rx segment count */ mcp->mb[13] = ha->seg_count; /* tx dsd address */ mcp->mb[14] = LSW(ha->tx_dsd_dma); mcp->mb[15] = MSW(ha->tx_dsd_dma); mcp->mb[20] = LSW(MSD(ha->tx_dsd_dma)); mcp->mb[21] = MSW(MSD(ha->tx_dsd_dma)); /* rx dsd address */ mcp->mb[16] = LSW(ha->rx_dsd_dma); mcp->mb[17] = MSW(ha->rx_dsd_dma); mcp->mb[6] = LSW(MSD(ha->rx_dsd_dma)); mcp->mb[7] = MSW(MSD(ha->rx_dsd_dma)); /* Iteration count */ mcp->mb[18] = LSW(iter_cnt); mcp->mb[19] = MSW(iter_cnt); mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15| MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0; mcp->out_mb |= MBX_2; /* FCoE */ mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (ret != -ETIMEDOUT) { cb->loopbackcb.mb[0] = mcp->mb[0]; cb->loopbackcb.mb[1] = mcp->mb[1]; cb->loopbackcb.mb[2] = mcp->mb[2]; cb->loopbackcb.mb[3] = mcp->mb[3]; cb->loopbackcb.mb[4] = mcp->mb[18]; cb->loopbackcb.mb[5] = mcp->mb[19]; ret = 0; dprintk("loopback test result: %x %x %x %x %x %x\n", mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[18], mcp->mb[19]); } return ret; } static int ep8324_execute_loopback(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; if (!ha->data_transfer_count || !ha->tx_data || !ha->tx_dsd || !ha->rx_data || !ha->rx_dsd) return -ENOMEM; /* clean up rx buffer */ memset(ha->rx_data, 0, ha->data_transfer_count); ret = ep8324_loopback_test(ha, cb); cb->loopbackcb.result = EP8324_LOOPBACK_TEST_ERROR; if (!ret) { if (cb->loopbackcb.mb[0] == MBS_COMMAND_COMPLETE) { /* compare rx buffer with tx buffer */ if (!strncmp(ha->tx_data, ha->rx_data, ha->data_transfer_count)) cb->loopbackcb.result = EP8324_LOOPBACK_NO_ERROR; } } if (ret == -ETIMEDOUT) ret = 0; return ret; } static void ep8324_loopback_cleanup(struct ep8324_hw_data *ha) { if (ha->tx_data) { dma_free_coherent(&ha->pdev->dev, ha->data_transfer_count, ha->tx_data, ha->tx_data_dma); ha->tx_data = NULL; ha->tx_data_dma = 0; } if (ha->rx_data) { dma_free_coherent(&ha->pdev->dev, ha->data_transfer_count, ha->rx_data, ha->rx_data_dma); ha->rx_data = NULL; ha->rx_data_dma = 0; } ha->data_transfer_count = 0; if (ha->tx_dsd) { dma_free_coherent(&ha->pdev->dev, sizeof(struct dsd)*ha->seg_count, ha->tx_dsd, ha->tx_dsd_dma); ha->tx_dsd = NULL; ha->tx_dsd_dma = 0; } if (ha->rx_dsd) { dma_free_coherent(&ha->pdev->dev, sizeof(struct dsd)*ha->seg_count, ha->rx_dsd, ha->rx_dsd_dma); ha->rx_dsd = NULL; ha->rx_dsd_dma = 0; } ha->seg_count = 0; if (ha->func_type == EP8324_FCOE && ha->loopback_path == EP8324_INTERNAL_LOOPBACK) ep8324_reset_internal_loopback(ha, 0); } static void dwmemset(void *s, uint32_t d, uint32_t count) { uint32_t *xs = s; while(count--) *xs++ = d; } static int ep8324_loopback_setup(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; int i; uint32_t seg_count; uint32_t seg_length; struct dsd *tx_dsd; struct dsd *rx_dsd; dma_addr_t tx_data_seg_address; dma_addr_t rx_data_seg_address; if (ha->data_transfer_count || ha->tx_data || ha->tx_dsd || ha->rx_data || ha->rx_dsd) return -EEXIST; seg_count = cb->loopbackcb.seg_count; seg_length = cb->loopbackcb.seg_length; ha->loopback_path = cb->loopbackcb.path; ha->data_transfer_count = seg_count * seg_length; ha->seg_count = seg_count; ha->tx_data = dma_alloc_coherent(&ha->pdev->dev, ha->data_transfer_count, &ha->tx_data_dma, GFP_KERNEL); if (!ha->tx_data) { printk(KERN_INFO "dma alloc failed for tx_data.\n"); ret = -ENOMEM; goto failed; } ha->rx_data = dma_alloc_coherent(&ha->pdev->dev, ha->data_transfer_count, &ha->rx_data_dma, GFP_KERNEL); if (!ha->rx_data) { printk(KERN_INFO "dma alloc failed for rx_data.\n"); ret = -ENOMEM; goto failed; } ha->tx_dsd = dma_alloc_coherent(&ha->pdev->dev, sizeof(struct dsd)*seg_count, &ha->tx_dsd_dma, GFP_KERNEL); if (!ha->tx_dsd) { printk(KERN_INFO "dma alloc failed for tx_dsd.\n"); ret = -ENOMEM; goto failed; } ha->rx_dsd = dma_alloc_coherent(&ha->pdev->dev, sizeof(struct dsd)*seg_count, &ha->rx_dsd_dma, GFP_KERNEL); if (!ha->rx_dsd) { printk(KERN_INFO "dma alloc failed for rx_dsd.\n"); ret = -ENOMEM; goto failed; } /* initialize test pattern in tx buffer */ dwmemset(ha->tx_data, cb->loopbackcb.pattern, ha->data_transfer_count >> 2); dprintk("tx %llx (%llx), rx %llx (%llx), seg count %d, size %x\n", ha->tx_data_dma, ha->tx_dsd_dma, ha->rx_data_dma, ha->rx_dsd_dma, seg_count, seg_length); /* setup data segment descriptor */ tx_dsd = (struct dsd*) ha->tx_dsd; rx_dsd = (struct dsd*) ha->rx_dsd; tx_data_seg_address = ha->tx_data_dma; rx_data_seg_address = ha->rx_data_dma; for (i = 0; i < seg_count; i++) { tx_dsd[i].data_seg_address[0] = LSW(tx_data_seg_address); tx_dsd[i].data_seg_address[1] = MSW(tx_data_seg_address); tx_dsd[i].data_seg_address[2] = LSW(MSD(tx_data_seg_address)); tx_dsd[i].data_seg_address[3] = MSW(MSD(tx_data_seg_address)); tx_dsd[i].data_seg_length[0] = LSW(seg_length); tx_dsd[i].data_seg_length[1] = MSW(seg_length); dprintk("txDSD[i] : %04x-%04x-%04x-%04x, %04x-%04x\n", tx_dsd[i].data_seg_address[3], tx_dsd[i].data_seg_address[2], tx_dsd[i].data_seg_address[1], tx_dsd[i].data_seg_address[0], tx_dsd[i].data_seg_length[1], tx_dsd[i].data_seg_length[0]); rx_dsd[i].data_seg_address[0] = LSW(rx_data_seg_address); rx_dsd[i].data_seg_address[1] = MSW(rx_data_seg_address); rx_dsd[i].data_seg_address[2] = LSW(MSD(rx_data_seg_address)); rx_dsd[i].data_seg_address[3] = MSW(MSD(rx_data_seg_address)); rx_dsd[i].data_seg_length[0] = LSW(seg_length); rx_dsd[i].data_seg_length[1] = MSW(seg_length); dprintk("rxDSD[i] : %04x-%04x-%04x-%04x, %04x-%04x\n", rx_dsd[i].data_seg_address[3], rx_dsd[i].data_seg_address[2], rx_dsd[i].data_seg_address[1], rx_dsd[i].data_seg_address[0], rx_dsd[i].data_seg_length[1], rx_dsd[i].data_seg_length[0]); tx_data_seg_address += seg_length; rx_data_seg_address += seg_length; } if (ha->func_type == EP8324_FC) { switch(cb->loopbackcb.link_speed) { case EP8324_LINK_SPEED_4G: fc_data_rate = FC_DATA_RATE_4G; break; case EP8324_LINK_SPEED_8G: fc_data_rate = FC_DATA_RATE_8G; break; case EP8324_LINK_SPEED_16G: fc_data_rate = FC_DATA_RATE_16G; break; default: fc_data_rate = FC_DATA_RATE_AUTO; break; } } /* re-initialize */ ha->hw_ops->reset_chip(ha); ret = ep8324_setup_chip(ha); if (ret) goto failed; if (ha->func_type == EP8324_FCOE) { if (ha->loopback_path == EP8324_INTERNAL_LOOPBACK) { ret = ep8324_set_internal_loopback(ha); if (ret) { printk(KERN_INFO "set internal loopback failed\n"); goto failed; } } else { ret = ep8324_reset_internal_loopback(ha, 1); if (ret) { printk(KERN_INFO "reset internal loopback failed\n"); goto failed; } } } else { if (ha->loopback_path == EP8324_EXTERNAL_LOOPBACK) { unsigned long wait_time; ha->flags.dcbx_int = 0; /* wait for peer to peer link up event */ wait_time = jiffies + 10 * HZ; while (!ha->flags.dcbx_int) { if (time_after(jiffies, wait_time)) break; ep8324_poll(ha); } if (ha->flags.dcbx_int) { ha->flags.dcbx_int = 0; } else { /* FIXME: still let go */ printk(KERN_INFO "wait for link up event timeout\n"); } } } return 0; failed: ep8324_loopback_cleanup(ha); return ret; } #define MAX_ECHO_PAYLOAD (252) static int ep8324_execute_echo(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; /* * use req_q and rsp_q as the tx and rx buffer */ memset(ha->req_q, 0x55, MAX_ECHO_PAYLOAD); memset(ha->rsp_q, 0, MAX_ECHO_PAYLOAD); memset(mcp->mb, 0 , sizeof(mcp->mb)); mcp->mb[0] = MBC_DIAGNOSTIC_ECHO; mcp->mb[1] = 0; /* ELS disabled */ //mcp->mb[2] = ; mcp->mb[16] = LSW(ha->rsp_q_dma); mcp->mb[17] = MSW(ha->rsp_q_dma); mcp->mb[6] = LSW(MSD(ha->rsp_q_dma)); mcp->mb[7] = MSW(MSD(ha->rsp_q_dma)); mcp->mb[10] = MAX_ECHO_PAYLOAD; mcp->mb[14] = LSW(ha->req_q_dma); mcp->mb[15] = MSW(ha->req_q_dma); mcp->mb[20] = LSW(MSD(ha->req_q_dma)); mcp->mb[21] = MSW(MSD(ha->req_q_dma)); mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15| MBX_14|MBX_10|MBX_7|MBX_6|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_3|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (ret != -ETIMEDOUT) { cb->echocb.mb[0] = mcp->mb[0]; cb->echocb.mb[1] = mcp->mb[1]; cb->echocb.mb[2] = mcp->mb[3]; ret = 0; } /* TODO: check receive buffer */ dprintk("echo test: %04x-%04x-%04x\n", mcp->mb[0], mcp->mb[1], mcp->mb[3]); return ret; } static int ep8324_load_risc(struct ep8324_hw_data *ha, int load_bin, uint32_t *srisc_address) { int ret; int fwloadbin_org; fwloadbin_org = fwloadbin; fwloadbin = load_bin; ret = ha->hw_ops->load_risc(ha, srisc_address); fwloadbin = fwloadbin_org; return ret; } static int ep8324_sfp_read(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; uint16_t device_address; uint16_t size; uint16_t offset; uint8_t *buf = &cb->sfpcb.data[0]; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; device_address = cb->sfpcb.device_address; size = cb->sfpcb.size; offset = cb->sfpcb.offset; memset(ha->dma_buf, 0, size); mcp->mb[0] = MBC_READ_SFP; mcp->mb[1] = device_address; mcp->mb[2] = MSW(ha->dma); mcp->mb[3] = LSW(ha->dma); mcp->mb[6] = MSW(MSD(ha->dma)); mcp->mb[7] = LSW(MSD(ha->dma)); mcp->mb[8] = size; mcp->mb[9] = offset; mcp->mb[10] = 0; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (!ret) memcpy(buf, ha->dma_buf, size); cb->sfpcb.mb0 = mcp->mb[0]; return ret; } static int ep8324_sfp_write(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int ret; uint16_t device_address; uint16_t size; uint16_t offset; uint8_t *buf = &cb->sfpcb.data[0]; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; device_address = cb->sfpcb.device_address; size = cb->sfpcb.size; offset = cb->sfpcb.offset; mcp->mb[0] = MBC_WRITE_SFP; mcp->mb[1] = device_address; if (size == 1) { mcp->mb[8] = buf[0]; mcp->mb[10] = 1; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_1|MBX_0; } else { memcpy(ha->dma_buf, buf, size); mcp->mb[2] = MSW(ha->dma); mcp->mb[3] = LSW(ha->dma); mcp->mb[6] = MSW(MSD(ha->dma)); mcp->mb[7] = LSW(MSD(ha->dma)); mcp->mb[8] = size; mcp->mb[10] = 0; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; } mcp->mb[9] = offset; mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); cb->sfpcb.mb0 = mcp->mb[0]; return ret; } static int ep8324_get_statistics(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int i = 0; int ret; uint16_t device_address; uint16_t size = sizeof(struct link_statistics) / 4; uint16_t offset; uint8_t buf[STATISTICS_BUFFER_SIZE] = ""; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; memset(ha->dma_buf, 0, size); mcp->mb[0] = MBC_GET_LINK_PRIV_STATS; mcp->mb[2] = MSW(ha->dma); mcp->mb[3] = LSW(ha->dma); mcp->mb[6] = MSW(MSD(ha->dma)); mcp->mb[7] = LSW(MSD(ha->dma)); mcp->mb[8] = size; mcp->mb[9] = 0; mcp->mb[10] = 0; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (!ret) memcpy(buf, ha->dma_buf, size); cb->sfpcb.mb0 = mcp->mb[0]; for(i=0; istatisticcb.statistics.link_fail_cnt)+i, &tmp, 4); } return ret; } static int ep8324_reset_statistics(struct ep8324_hw_data *ha, struct ep8324_ioctl_cb *cb) { int i = 0; int ret; uint16_t device_address; uint16_t size = sizeof(struct link_statistics) / 4; uint16_t offset; uint8_t buf[STATISTICS_BUFFER_SIZE] = ""; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; memset(ha->dma_buf, 0, size); mcp->mb[0] = MBC_GET_LINK_PRIV_STATS; mcp->mb[2] = MSW(ha->dma); mcp->mb[3] = LSW(ha->dma); mcp->mb[6] = MSW(MSD(ha->dma)); mcp->mb[7] = LSW(MSD(ha->dma)); mcp->mb[8] = size; mcp->mb[9] = 0; mcp->mb[10] = 1; mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; ret = ep8324_mailbox_command(ha, mcp); if (!ret) { memcpy(buf, ha->dma_buf, size); } cb->statisticcb.mb0 = mcp->mb[0]; return ret; } static void ep8324_list_funcs(void) { struct list_head *pos; struct ep8324_pci_func *func; printk(KERN_INFO "<---- EP8324 PCI FUNC LIST\n"); list_for_each(pos, &ep8324_func_list) { func = list_entry(pos, struct ep8324_pci_func, list); if (func->hw) { struct pci_dev *pdev = func->hw->pdev; printk(KERN_INFO "NAME: %s ----" "DEVICE_ID: %04x, BUS: %d, DEVFN: %02x\n", func->well_known_name, pdev->device, pdev->bus->number, pdev->devfn); } } } static struct ep8324_pci_func* get_func_by_name(char *name) { struct list_head *pos; struct ep8324_pci_func *func; list_for_each(pos, &ep8324_func_list) { func = list_entry(pos, struct ep8324_pci_func, list); if (!strcmp(func->well_known_name, name)) return func; } return NULL; } long ep8324_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; struct ep8324_ioctl_cb cb; struct ep8324_pci_func *func; struct ep8324_hw_data *ha; uint32_t *spi_buffer; if (cmd != EP8324_IOCTL_LIST_FUNC) { if (copy_from_user(&cb, (struct ep8324_ioctl_cb __user *) arg, sizeof(struct ep8324_ioctl_cb))) { printk(KERN_INFO "copy from user failed\n"); return -EFAULT; } func = get_func_by_name(cb.well_known_name); if (!func) { printk(KERN_INFO "%s can not find function handle\n", cb.well_known_name); return -ENODEV; } ha = func->hw; } switch (cmd) { case EP8324_IOCTL_LIST_FUNC: ep8324_list_funcs(); break; case EP8324_IOCTL_SETUP_CHIP: ret = ep8324_setup_chip(ha); break; case EP8324_IOCTL_RESET_CHIP: ret = -ENODEV; if (ha->hw_ops) { ha->hw_ops->reset_chip(ha); ret = 0; } break; case EP8324_IOCTL_SEND_MBX: ep8324_mailbox_command(ha, &cb.mc); ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_LOAD_RISC: ret = ep8324_load_risc(ha, cb.lrcb.fwloadbin, &cb.lrcb.srisc_address); if (!ret) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_SFP_READ: if (ha->risc_mode != RISC_FW_MODE/*RISC_ROM_MODE*/) { ret = ep8324_setup_chip(ha); if (ret) return ret; } ret = ep8324_sfp_read(ha, &cb); if (ret != -ETIMEDOUT) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_SFP_WRITE: if (ha->risc_mode != RISC_FW_MODE/*RISC_ROM_MODE*/) { ret = ep8324_setup_chip(ha); if (ret) return ret; } ret = ep8324_sfp_write(ha, &cb); if (ret != -ETIMEDOUT) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_SPI_READ: spi_buffer = kmalloc((cb.spicb.dwords * 4), GFP_KERNEL); if (spi_buffer == NULL) { printk(KERN_INFO "Out of memory\n"); ret = -ENOMEM; } else { ret = ep8324_read_flash_data(ha, spi_buffer, cb.spicb.faddr, cb.spicb.dwords); if (!ret) { ret = copy_to_user( ((struct ep8324_ioctl_cb __user*) arg)->spicb.dword_buf, spi_buffer, (cb.spicb.dwords * 4)); } kfree(spi_buffer); } break; case EP8324_IOCTL_SPI_WRITE: spi_buffer = kmalloc((cb.spicb.dwords * 4), GFP_KERNEL); if (spi_buffer == NULL) { printk(KERN_INFO "Out of memory\n"); ret = -ENOMEM; } else { ret = copy_from_user(spi_buffer, ((struct ep8324_ioctl_cb __user*) arg)->spicb.dword_buf, (cb.spicb.dwords * 4)); if (!ret) ret = ep8324_write_flash_data(ha, spi_buffer, cb.spicb.faddr, cb.spicb.dwords); kfree(spi_buffer); } break; case EP8324_IOCTL_LOOPBACK_TEST: switch(cb.loopbackcb.cmd) { case EP8324_LOOPBACK_SETUP: ret = ep8324_loopback_setup(ha, &cb); if (ret) { if (ret == -ENOMEM) cb.loopbackcb.result = EP8324_LOOPBACK_NO_MEMORY; else cb.loopbackcb.result = EP8324_LOOPBACK_SETUP_ERROR; } else { cb.loopbackcb.result = EP8324_LOOPBACK_NO_ERROR; } ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_LOOPBACK_EXECUTE: ret = ep8324_execute_loopback(ha, &cb); if (!ret) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_LOOPBACK_CLEANUP: ep8324_loopback_cleanup(ha); ret = 0; break; default: ret = -EINVAL; break; } break; case EP8324_IOCTL_ECHO_TEST: ret = ep8324_execute_echo(ha, &cb); if (!ret) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_GET_STATISTICS: ret = ep8324_get_statistics(ha, &cb); if (!ret) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; case EP8324_IOCTL_CLEAR_STATISTICS: ret = ep8324_reset_statistics(ha, &cb); if (!ret) ret = copy_to_user((void __user *)arg, &cb, sizeof(struct ep8324_ioctl_cb)); break; default: break; } return ret; } int ep8324_fw_init(void) { struct ep8324_pci_func *func_fc, *func_cna; struct ep8324_hw_data *ha_fc, *ha_cna; char well_known_name_fc[NAME_STR_SIZE + 1], well_known_name_cna[NAME_STR_SIZE + 1]; snprintf(well_known_name_fc, NAME_STR_SIZE+1, "FC-BB0F0P0"); func_fc = get_func_by_name(well_known_name_fc); if (!func_fc) { printk(KERN_INFO "%s can not find function handle\n", well_known_name_fc); } else { ha_fc = func_fc->hw; ep8324_setup_chip(ha_fc); printk(KERN_INFO "%s firmware initialized\n", well_known_name_fc); } snprintf(well_known_name_fc, NAME_STR_SIZE+1, "FC-BB0F0P1"); func_fc = get_func_by_name(well_known_name_fc); if (!func_fc) { printk(KERN_INFO "%s can not find function handle\n", well_known_name_fc); } else { ha_fc = func_fc->hw; ep8324_setup_chip(ha_fc); printk(KERN_INFO "%s firmware initialized\n", well_known_name_fc); } snprintf(well_known_name_cna, NAME_STR_SIZE+1, "CNA-BB0F0P0"); func_cna = get_func_by_name(well_known_name_cna); if (!func_cna) { printk(KERN_INFO "%s can not find function handle\n", well_known_name_cna); } else{ ha_cna = func_cna->hw; ep8324_setup_chip(ha_cna); printk(KERN_INFO "%s firmware initialized\n", well_known_name_cna); } snprintf(well_known_name_cna, NAME_STR_SIZE+1, "CNA-BB0F0P1"); func_cna = get_func_by_name(well_known_name_cna); if (!func_cna) { printk(KERN_INFO "%s can not find function handle\n", well_known_name_cna); } else{ ha_cna = func_cna->hw; ep8324_setup_chip(ha_cna); printk(KERN_INFO "%s firmware initialized\n", well_known_name_cna); } return 0; }