📄 myri10ge.c
字号:
* response in the confirmation address. The firmware should * write a -1 there to indicate it is alive and well */ dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus); dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus); buf[0] = htonl(dma_high); /* confirm addr MSW */ buf[1] = htonl(dma_low); /* confirm addr LSW */ buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */ buf[3] = htonl(dma_high); /* dummy addr MSW */ buf[4] = htonl(dma_low); /* dummy addr LSW */ buf[5] = htonl(enable); /* enable? */ submit = mgp->sram + MXGEFW_BOOT_DUMMY_RDMA; myri10ge_pio_copy(submit, &buf, sizeof(buf)); for (i = 0; mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20; i++) msleep(1); if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n", (enable ? "enable" : "disable"));}static intmyri10ge_validate_firmware(struct myri10ge_priv *mgp, struct mcp_gen_header *hdr){ struct device *dev = &mgp->pdev->dev; /* check firmware type */ if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) { dev_err(dev, "Bad firmware type: 0x%x\n", ntohl(hdr->mcp_type)); return -EINVAL; } /* save firmware version for ethtool */ strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version)); sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major, &mgp->fw_ver_minor, &mgp->fw_ver_tiny); if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR && mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) { dev_err(dev, "Found firmware version %s\n", mgp->fw_version); dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); return -EINVAL; } return 0;}static int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size){ unsigned crc, reread_crc; const struct firmware *fw; struct device *dev = &mgp->pdev->dev; struct mcp_gen_header *hdr; size_t hdr_offset; int status; unsigned i; if ((status = request_firmware(&fw, mgp->fw_name, dev)) < 0) { dev_err(dev, "Unable to load %s firmware image via hotplug\n", mgp->fw_name); status = -EINVAL; goto abort_with_nothing; } /* check size */ if (fw->size >= mgp->sram_size - MYRI10GE_FW_OFFSET || fw->size < MCP_HEADER_PTR_OFFSET + 4) { dev_err(dev, "Firmware size invalid:%d\n", (int)fw->size); status = -EINVAL; goto abort_with_fw; } /* check id */ hdr_offset = ntohl(*(__be32 *) (fw->data + MCP_HEADER_PTR_OFFSET)); if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->size) { dev_err(dev, "Bad firmware file\n"); status = -EINVAL; goto abort_with_fw; } hdr = (void *)(fw->data + hdr_offset); status = myri10ge_validate_firmware(mgp, hdr); if (status != 0) goto abort_with_fw; crc = crc32(~0, fw->data, fw->size); for (i = 0; i < fw->size; i += 256) { myri10ge_pio_copy(mgp->sram + MYRI10GE_FW_OFFSET + i, fw->data + i, min(256U, (unsigned)(fw->size - i))); mb(); readb(mgp->sram); } /* corruption checking is good for parity recovery and buggy chipset */ memcpy_fromio(fw->data, mgp->sram + MYRI10GE_FW_OFFSET, fw->size); reread_crc = crc32(~0, fw->data, fw->size); if (crc != reread_crc) { dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n", (unsigned)fw->size, reread_crc, crc); status = -EIO; goto abort_with_fw; } *size = (u32) fw->size;abort_with_fw: release_firmware(fw);abort_with_nothing: return status;}static int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp){ struct mcp_gen_header *hdr; struct device *dev = &mgp->pdev->dev; const size_t bytes = sizeof(struct mcp_gen_header); size_t hdr_offset; int status; /* find running firmware header */ hdr_offset = ntohl(__raw_readl(mgp->sram + MCP_HEADER_PTR_OFFSET)); if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) { dev_err(dev, "Running firmware has bad header offset (%d)\n", (int)hdr_offset); return -EIO; } /* copy header of running firmware from SRAM to host memory to * validate firmware */ hdr = kmalloc(bytes, GFP_KERNEL); if (hdr == NULL) { dev_err(dev, "could not malloc firmware hdr\n"); return -ENOMEM; } memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes); status = myri10ge_validate_firmware(mgp, hdr); kfree(hdr); /* check to see if adopted firmware has bug where adopting * it will cause broadcasts to be filtered unless the NIC * is kept in ALLMULTI mode */ if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 && mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) { mgp->adopted_rx_filter_bug = 1; dev_warn(dev, "Adopting fw %d.%d.%d: " "working around rx filter bug\n", mgp->fw_ver_major, mgp->fw_ver_minor, mgp->fw_ver_tiny); } return status;}static int myri10ge_load_firmware(struct myri10ge_priv *mgp){ char __iomem *submit; __be32 buf[16]; u32 dma_low, dma_high, size; int status, i; struct myri10ge_cmd cmd; size = 0; status = myri10ge_load_hotplug_firmware(mgp, &size); if (status) { dev_warn(&mgp->pdev->dev, "hotplug firmware loading failed\n"); /* Do not attempt to adopt firmware if there * was a bad crc */ if (status == -EIO) return status; status = myri10ge_adopt_running_firmware(mgp); if (status != 0) { dev_err(&mgp->pdev->dev, "failed to adopt running firmware\n"); return status; } dev_info(&mgp->pdev->dev, "Successfully adopted running firmware\n"); if (mgp->tx.boundary == 4096) { dev_warn(&mgp->pdev->dev, "Using firmware currently running on NIC" ". For optimal\n"); dev_warn(&mgp->pdev->dev, "performance consider loading optimized " "firmware\n"); dev_warn(&mgp->pdev->dev, "via hotplug\n"); } mgp->fw_name = "adopted"; mgp->tx.boundary = 2048; return status; } /* clear confirmation addr */ mgp->cmd->data = 0; mb(); /* send a reload command to the bootstrap MCP, and wait for the * response in the confirmation address. The firmware should * write a -1 there to indicate it is alive and well */ dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus); dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus); buf[0] = htonl(dma_high); /* confirm addr MSW */ buf[1] = htonl(dma_low); /* confirm addr LSW */ buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */ /* FIX: All newest firmware should un-protect the bottom of * the sram before handoff. However, the very first interfaces * do not. Therefore the handoff copy must skip the first 8 bytes */ buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */ buf[4] = htonl(size - 8); /* length of code */ buf[5] = htonl(8); /* where to copy to */ buf[6] = htonl(0); /* where to jump to */ submit = mgp->sram + MXGEFW_BOOT_HANDOFF; myri10ge_pio_copy(submit, &buf, sizeof(buf)); mb(); msleep(1); mb(); i = 0; while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20) { msleep(1); i++; } if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) { dev_err(&mgp->pdev->dev, "handoff failed\n"); return -ENXIO; } dev_info(&mgp->pdev->dev, "handoff confirmed\n"); myri10ge_dummy_rdma(mgp, 1); /* probe for IPv6 TSO support */ mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO; status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd, 0); if (status == 0) { mgp->max_tso6 = cmd.data0; mgp->features |= NETIF_F_TSO6; } return 0;}static int myri10ge_update_mac_address(struct myri10ge_priv *mgp, u8 * addr){ struct myri10ge_cmd cmd; int status; cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]); cmd.data1 = ((addr[4] << 8) | (addr[5])); status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd, 0); return status;}static int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause){ struct myri10ge_cmd cmd; int status, ctl; ctl = pause ? MXGEFW_ENABLE_FLOW_CONTROL : MXGEFW_DISABLE_FLOW_CONTROL; status = myri10ge_send_cmd(mgp, ctl, &cmd, 0); if (status) { printk(KERN_ERR "myri10ge: %s: Failed to set flow control mode\n", mgp->dev->name); return status; } mgp->pause = pause; return 0;}static voidmyri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc, int atomic){ struct myri10ge_cmd cmd; int status, ctl; ctl = promisc ? MXGEFW_ENABLE_PROMISC : MXGEFW_DISABLE_PROMISC; status = myri10ge_send_cmd(mgp, ctl, &cmd, atomic); if (status) printk(KERN_ERR "myri10ge: %s: Failed to set promisc mode\n", mgp->dev->name);}static int myri10ge_dma_test(struct myri10ge_priv *mgp, int test_type){ struct myri10ge_cmd cmd; int status; u32 len; struct page *dmatest_page; dma_addr_t dmatest_bus; char *test = " "; dmatest_page = alloc_page(GFP_KERNEL); if (!dmatest_page) return -ENOMEM; dmatest_bus = pci_map_page(mgp->pdev, dmatest_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); /* Run a small DMA test. * The magic multipliers to the length tell the firmware * to do DMA read, write, or read+write tests. The * results are returned in cmd.data0. The upper 16 * bits or the return is the number of transfers completed. * The lower 16 bits is the time in 0.5us ticks that the * transfers took to complete. */ len = mgp->tx.boundary; cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x10000; status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) { test = "read"; goto abort; } mgp->read_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff); cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x1; status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) { test = "write"; goto abort; } mgp->write_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff); cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x10001; status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) { test = "read/write"; goto abort; } mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) / (cmd.data0 & 0xffff);abort: pci_unmap_page(mgp->pdev, dmatest_bus, PAGE_SIZE, DMA_BIDIRECTIONAL); put_page(dmatest_page); if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) dev_warn(&mgp->pdev->dev, "DMA %s benchmark failed: %d\n", test, status); return status;}static int myri10ge_reset(struct myri10ge_priv *mgp){ struct myri10ge_cmd cmd; int status; size_t bytes; /* try to send a reset command to the card to see if it * is alive */ memset(&cmd, 0, sizeof(cmd)); status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0); if (status != 0) { dev_err(&mgp->pdev->dev, "failed reset\n"); return -ENXIO; } (void)myri10ge_dma_test(mgp, MXGEFW_DMA_TEST); /* Now exchange information about interrupts */ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry); memset(mgp->rx_done.entry, 0, bytes); cmd.data0 = (u32) bytes; status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0); cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus); cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus); status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA, &cmd, 0); status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0); mgp->irq_claim = (__iomem __be32 *) (mgp->sram + cmd.data0); status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd, 0); mgp->irq_deassert = (__iomem __be32 *) (mgp->sram + cmd.data0); status |= myri10ge_send_cmd (mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0); mgp->intr_coal_delay_ptr = (__iomem __be32 *) (mgp->sram + cmd.data0); if (status != 0) { dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n"); return status; } put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr); memset(mgp->rx_done.entry, 0, bytes); /* reset mcp/driver shared state back to 0 */ mgp->tx.req = 0; mgp->tx.done = 0; mgp->tx.pkt_start = 0; mgp->tx.pkt_done = 0; mgp->rx_big.cnt = 0; mgp->rx_small.cnt = 0; mgp->rx_done.idx = 0; mgp->rx_done.cnt = 0; mgp->link_changes = 0; status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr); myri10ge_change_pause(mgp, mgp->pause); myri10ge_set_multicast_list(mgp->dev); return status;}static inline voidmyri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst, struct mcp_kreq_ether_recv *src){ __be32 low; low = src->addr_low; src->addr_low = htonl(DMA_32BIT_MASK); myri10ge_pio_copy(dst, src, 4 * sizeof(*src)); mb(); myri10ge_pio_copy(dst + 4, src + 4, 4 * sizeof(*src)); mb(); src->addr_low = low; put_be32(low, &dst->addr_low); mb();}static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, __wsum hw_csum){ struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data); if ((skb->protocol == htons(ETH_P_8021Q)) && (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) || vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) { skb->csum = hw_csum; skb->ip_summed = CHECKSUM_COMPLETE; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -