18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * QLogic qlcnic NIC Driver 48c2ecf20Sopenharmony_ci * Copyright (c) 2009-2013 QLogic Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "qlcnic.h" 88c2ecf20Sopenharmony_ci#include "qlcnic_hw.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistruct crb_addr_pair { 118c2ecf20Sopenharmony_ci u32 addr; 128c2ecf20Sopenharmony_ci u32 data; 138c2ecf20Sopenharmony_ci}; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define QLCNIC_MAX_CRB_XFORM 60 168c2ecf20Sopenharmony_cistatic unsigned int crb_addr_xform[QLCNIC_MAX_CRB_XFORM]; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define crb_addr_transform(name) \ 198c2ecf20Sopenharmony_ci (crb_addr_xform[QLCNIC_HW_PX_MAP_CRB_##name] = \ 208c2ecf20Sopenharmony_ci QLCNIC_HW_CRB_HUB_AGT_ADR_##name << 20) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define QLCNIC_ADDR_ERROR (0xffffffff) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int 258c2ecf20Sopenharmony_ciqlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void crb_addr_transform_setup(void) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci crb_addr_transform(XDMA); 308c2ecf20Sopenharmony_ci crb_addr_transform(TIMR); 318c2ecf20Sopenharmony_ci crb_addr_transform(SRE); 328c2ecf20Sopenharmony_ci crb_addr_transform(SQN3); 338c2ecf20Sopenharmony_ci crb_addr_transform(SQN2); 348c2ecf20Sopenharmony_ci crb_addr_transform(SQN1); 358c2ecf20Sopenharmony_ci crb_addr_transform(SQN0); 368c2ecf20Sopenharmony_ci crb_addr_transform(SQS3); 378c2ecf20Sopenharmony_ci crb_addr_transform(SQS2); 388c2ecf20Sopenharmony_ci crb_addr_transform(SQS1); 398c2ecf20Sopenharmony_ci crb_addr_transform(SQS0); 408c2ecf20Sopenharmony_ci crb_addr_transform(RPMX7); 418c2ecf20Sopenharmony_ci crb_addr_transform(RPMX6); 428c2ecf20Sopenharmony_ci crb_addr_transform(RPMX5); 438c2ecf20Sopenharmony_ci crb_addr_transform(RPMX4); 448c2ecf20Sopenharmony_ci crb_addr_transform(RPMX3); 458c2ecf20Sopenharmony_ci crb_addr_transform(RPMX2); 468c2ecf20Sopenharmony_ci crb_addr_transform(RPMX1); 478c2ecf20Sopenharmony_ci crb_addr_transform(RPMX0); 488c2ecf20Sopenharmony_ci crb_addr_transform(ROMUSB); 498c2ecf20Sopenharmony_ci crb_addr_transform(SN); 508c2ecf20Sopenharmony_ci crb_addr_transform(QMN); 518c2ecf20Sopenharmony_ci crb_addr_transform(QMS); 528c2ecf20Sopenharmony_ci crb_addr_transform(PGNI); 538c2ecf20Sopenharmony_ci crb_addr_transform(PGND); 548c2ecf20Sopenharmony_ci crb_addr_transform(PGN3); 558c2ecf20Sopenharmony_ci crb_addr_transform(PGN2); 568c2ecf20Sopenharmony_ci crb_addr_transform(PGN1); 578c2ecf20Sopenharmony_ci crb_addr_transform(PGN0); 588c2ecf20Sopenharmony_ci crb_addr_transform(PGSI); 598c2ecf20Sopenharmony_ci crb_addr_transform(PGSD); 608c2ecf20Sopenharmony_ci crb_addr_transform(PGS3); 618c2ecf20Sopenharmony_ci crb_addr_transform(PGS2); 628c2ecf20Sopenharmony_ci crb_addr_transform(PGS1); 638c2ecf20Sopenharmony_ci crb_addr_transform(PGS0); 648c2ecf20Sopenharmony_ci crb_addr_transform(PS); 658c2ecf20Sopenharmony_ci crb_addr_transform(PH); 668c2ecf20Sopenharmony_ci crb_addr_transform(NIU); 678c2ecf20Sopenharmony_ci crb_addr_transform(I2Q); 688c2ecf20Sopenharmony_ci crb_addr_transform(EG); 698c2ecf20Sopenharmony_ci crb_addr_transform(MN); 708c2ecf20Sopenharmony_ci crb_addr_transform(MS); 718c2ecf20Sopenharmony_ci crb_addr_transform(CAS2); 728c2ecf20Sopenharmony_ci crb_addr_transform(CAS1); 738c2ecf20Sopenharmony_ci crb_addr_transform(CAS0); 748c2ecf20Sopenharmony_ci crb_addr_transform(CAM); 758c2ecf20Sopenharmony_ci crb_addr_transform(C2C1); 768c2ecf20Sopenharmony_ci crb_addr_transform(C2C0); 778c2ecf20Sopenharmony_ci crb_addr_transform(SMB); 788c2ecf20Sopenharmony_ci crb_addr_transform(OCM0); 798c2ecf20Sopenharmony_ci crb_addr_transform(I2C0); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_civoid qlcnic_release_rx_buffers(struct qlcnic_adapter *adapter) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx; 858c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 868c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *rx_buf; 878c2ecf20Sopenharmony_ci int i, ring; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci recv_ctx = adapter->recv_ctx; 908c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 918c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 928c2ecf20Sopenharmony_ci for (i = 0; i < rds_ring->num_desc; ++i) { 938c2ecf20Sopenharmony_ci rx_buf = &(rds_ring->rx_buf_arr[i]); 948c2ecf20Sopenharmony_ci if (rx_buf->skb == NULL) 958c2ecf20Sopenharmony_ci continue; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci pci_unmap_single(adapter->pdev, 988c2ecf20Sopenharmony_ci rx_buf->dma, 998c2ecf20Sopenharmony_ci rds_ring->dma_size, 1008c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dev_kfree_skb_any(rx_buf->skb); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid qlcnic_reset_rx_buffers_list(struct qlcnic_adapter *adapter) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx; 1108c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 1118c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *rx_buf; 1128c2ecf20Sopenharmony_ci int i, ring; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci recv_ctx = adapter->recv_ctx; 1158c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 1168c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rds_ring->free_list); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rx_buf = rds_ring->rx_buf_arr; 1218c2ecf20Sopenharmony_ci for (i = 0; i < rds_ring->num_desc; i++) { 1228c2ecf20Sopenharmony_ci list_add_tail(&rx_buf->list, 1238c2ecf20Sopenharmony_ci &rds_ring->free_list); 1248c2ecf20Sopenharmony_ci rx_buf++; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_civoid qlcnic_release_tx_buffers(struct qlcnic_adapter *adapter, 1308c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct qlcnic_cmd_buffer *cmd_buf; 1338c2ecf20Sopenharmony_ci struct qlcnic_skb_frag *buffrag; 1348c2ecf20Sopenharmony_ci int i, j; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock(&tx_ring->tx_clean_lock); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci cmd_buf = tx_ring->cmd_buf_arr; 1398c2ecf20Sopenharmony_ci for (i = 0; i < tx_ring->num_desc; i++) { 1408c2ecf20Sopenharmony_ci buffrag = cmd_buf->frag_array; 1418c2ecf20Sopenharmony_ci if (buffrag->dma) { 1428c2ecf20Sopenharmony_ci pci_unmap_single(adapter->pdev, buffrag->dma, 1438c2ecf20Sopenharmony_ci buffrag->length, PCI_DMA_TODEVICE); 1448c2ecf20Sopenharmony_ci buffrag->dma = 0ULL; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci for (j = 1; j < cmd_buf->frag_count; j++) { 1478c2ecf20Sopenharmony_ci buffrag++; 1488c2ecf20Sopenharmony_ci if (buffrag->dma) { 1498c2ecf20Sopenharmony_ci pci_unmap_page(adapter->pdev, buffrag->dma, 1508c2ecf20Sopenharmony_ci buffrag->length, 1518c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 1528c2ecf20Sopenharmony_ci buffrag->dma = 0ULL; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci if (cmd_buf->skb) { 1568c2ecf20Sopenharmony_ci dev_kfree_skb_any(cmd_buf->skb); 1578c2ecf20Sopenharmony_ci cmd_buf->skb = NULL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci cmd_buf++; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_unlock(&tx_ring->tx_clean_lock); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_civoid qlcnic_free_sw_resources(struct qlcnic_adapter *adapter) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx; 1688c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 1698c2ecf20Sopenharmony_ci int ring; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci recv_ctx = adapter->recv_ctx; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (recv_ctx->rds_rings == NULL) 1748c2ecf20Sopenharmony_ci return; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 1778c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 1788c2ecf20Sopenharmony_ci vfree(rds_ring->rx_buf_arr); 1798c2ecf20Sopenharmony_ci rds_ring->rx_buf_arr = NULL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci kfree(recv_ctx->rds_rings); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciint qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx; 1878c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 1888c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 1898c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *rx_buf; 1908c2ecf20Sopenharmony_ci int ring, i; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci recv_ctx = adapter->recv_ctx; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rds_ring = kcalloc(adapter->max_rds_rings, 1958c2ecf20Sopenharmony_ci sizeof(struct qlcnic_host_rds_ring), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (rds_ring == NULL) 1978c2ecf20Sopenharmony_ci goto err_out; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci recv_ctx->rds_rings = rds_ring; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 2028c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 2038c2ecf20Sopenharmony_ci switch (ring) { 2048c2ecf20Sopenharmony_ci case RCV_RING_NORMAL: 2058c2ecf20Sopenharmony_ci rds_ring->num_desc = adapter->num_rxd; 2068c2ecf20Sopenharmony_ci rds_ring->dma_size = QLCNIC_P3P_RX_BUF_MAX_LEN; 2078c2ecf20Sopenharmony_ci rds_ring->skb_size = rds_ring->dma_size + NET_IP_ALIGN; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci case RCV_RING_JUMBO: 2118c2ecf20Sopenharmony_ci rds_ring->num_desc = adapter->num_jumbo_rxd; 2128c2ecf20Sopenharmony_ci rds_ring->dma_size = 2138c2ecf20Sopenharmony_ci QLCNIC_P3P_RX_JUMBO_BUF_MAX_LEN; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (adapter->ahw->capabilities & 2168c2ecf20Sopenharmony_ci QLCNIC_FW_CAPABILITY_HW_LRO) 2178c2ecf20Sopenharmony_ci rds_ring->dma_size += QLCNIC_LRO_BUFFER_EXTRA; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci rds_ring->skb_size = 2208c2ecf20Sopenharmony_ci rds_ring->dma_size + NET_IP_ALIGN; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci rds_ring->rx_buf_arr = vzalloc(RCV_BUFF_RINGSIZE(rds_ring)); 2248c2ecf20Sopenharmony_ci if (rds_ring->rx_buf_arr == NULL) 2258c2ecf20Sopenharmony_ci goto err_out; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rds_ring->free_list); 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * Now go through all of them, set reference handles 2308c2ecf20Sopenharmony_ci * and put them in the queues. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci rx_buf = rds_ring->rx_buf_arr; 2338c2ecf20Sopenharmony_ci for (i = 0; i < rds_ring->num_desc; i++) { 2348c2ecf20Sopenharmony_ci list_add_tail(&rx_buf->list, 2358c2ecf20Sopenharmony_ci &rds_ring->free_list); 2368c2ecf20Sopenharmony_ci rx_buf->ref_handle = i; 2378c2ecf20Sopenharmony_ci rx_buf++; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci spin_lock_init(&rds_ring->lock); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 2438c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 2448c2ecf20Sopenharmony_ci sds_ring->irq = adapter->msix_entries[ring].vector; 2458c2ecf20Sopenharmony_ci sds_ring->adapter = adapter; 2468c2ecf20Sopenharmony_ci sds_ring->num_desc = adapter->num_rxd; 2478c2ecf20Sopenharmony_ci if (qlcnic_82xx_check(adapter)) { 2488c2ecf20Sopenharmony_ci if (qlcnic_check_multi_tx(adapter) && 2498c2ecf20Sopenharmony_ci !adapter->ahw->diag_test) 2508c2ecf20Sopenharmony_ci sds_ring->tx_ring = &adapter->tx_ring[ring]; 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci sds_ring->tx_ring = &adapter->tx_ring[0]; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RCV_DESC_RINGS; i++) 2558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sds_ring->free_list[i]); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cierr_out: 2618c2ecf20Sopenharmony_ci qlcnic_free_sw_resources(adapter); 2628c2ecf20Sopenharmony_ci return -ENOMEM; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * Utility to translate from internal Phantom CRB address 2678c2ecf20Sopenharmony_ci * to external PCI CRB address. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic u32 qlcnic_decode_crb_addr(u32 addr) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int i; 2728c2ecf20Sopenharmony_ci u32 base_addr, offset, pci_base; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci crb_addr_transform_setup(); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pci_base = QLCNIC_ADDR_ERROR; 2778c2ecf20Sopenharmony_ci base_addr = addr & 0xfff00000; 2788c2ecf20Sopenharmony_ci offset = addr & 0x000fffff; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci for (i = 0; i < QLCNIC_MAX_CRB_XFORM; i++) { 2818c2ecf20Sopenharmony_ci if (crb_addr_xform[i] == base_addr) { 2828c2ecf20Sopenharmony_ci pci_base = i << 20; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (pci_base == QLCNIC_ADDR_ERROR) 2878c2ecf20Sopenharmony_ci return pci_base; 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci return pci_base + offset; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#define QLCNIC_MAX_ROM_WAIT_USEC 100 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int qlcnic_wait_rom_done(struct qlcnic_adapter *adapter) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci long timeout = 0; 2978c2ecf20Sopenharmony_ci long done = 0; 2988c2ecf20Sopenharmony_ci int err = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci cond_resched(); 3018c2ecf20Sopenharmony_ci while (done == 0) { 3028c2ecf20Sopenharmony_ci done = QLCRD32(adapter, QLCNIC_ROMUSB_GLB_STATUS, &err); 3038c2ecf20Sopenharmony_ci done &= 2; 3048c2ecf20Sopenharmony_ci if (++timeout >= QLCNIC_MAX_ROM_WAIT_USEC) { 3058c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 3068c2ecf20Sopenharmony_ci "Timeout reached waiting for rom done"); 3078c2ecf20Sopenharmony_ci return -EIO; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci udelay(1); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int do_rom_fast_read(struct qlcnic_adapter *adapter, 3158c2ecf20Sopenharmony_ci u32 addr, u32 *valp) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci int err = 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_ADDRESS, addr); 3208c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); 3218c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_ABYTE_CNT, 3); 3228c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_INSTR_OPCODE, 0xb); 3238c2ecf20Sopenharmony_ci if (qlcnic_wait_rom_done(adapter)) { 3248c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Error waiting for rom done\n"); 3258c2ecf20Sopenharmony_ci return -EIO; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci /* reset abyte_cnt and dummy_byte_cnt */ 3288c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_ABYTE_CNT, 0); 3298c2ecf20Sopenharmony_ci udelay(10); 3308c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci *valp = QLCRD32(adapter, QLCNIC_ROMUSB_ROM_RDATA, &err); 3338c2ecf20Sopenharmony_ci if (err == -EIO) 3348c2ecf20Sopenharmony_ci return err; 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int do_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr, 3398c2ecf20Sopenharmony_ci u8 *bytes, size_t size) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int addridx; 3428c2ecf20Sopenharmony_ci int ret = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci for (addridx = addr; addridx < (addr + size); addridx += 4) { 3458c2ecf20Sopenharmony_ci int v; 3468c2ecf20Sopenharmony_ci ret = do_rom_fast_read(adapter, addridx, &v); 3478c2ecf20Sopenharmony_ci if (ret != 0) 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci *(__le32 *)bytes = cpu_to_le32(v); 3508c2ecf20Sopenharmony_ci bytes += 4; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciint 3578c2ecf20Sopenharmony_ciqlcnic_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr, 3588c2ecf20Sopenharmony_ci u8 *bytes, size_t size) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = qlcnic_rom_lock(adapter); 3638c2ecf20Sopenharmony_ci if (ret < 0) 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = do_rom_fast_read_words(adapter, addr, bytes, size); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci qlcnic_rom_unlock(adapter); 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciint qlcnic_rom_fast_read(struct qlcnic_adapter *adapter, u32 addr, u32 *valp) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int ret; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (qlcnic_rom_lock(adapter) != 0) 3778c2ecf20Sopenharmony_ci return -EIO; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret = do_rom_fast_read(adapter, addr, valp); 3808c2ecf20Sopenharmony_ci qlcnic_rom_unlock(adapter); 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciint qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int addr, err = 0; 3878c2ecf20Sopenharmony_ci int i, n, init_delay; 3888c2ecf20Sopenharmony_ci struct crb_addr_pair *buf; 3898c2ecf20Sopenharmony_ci unsigned offset; 3908c2ecf20Sopenharmony_ci u32 off, val; 3918c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_CMDPEG_STATE, 0); 3948c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_RCVPEG_STATE, 0); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Halt all the indiviual PEGs and other blocks */ 3978c2ecf20Sopenharmony_ci /* disable all I2Q */ 3988c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x10, 0x0); 3998c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x14, 0x0); 4008c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x18, 0x0); 4018c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x1c, 0x0); 4028c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x20, 0x0); 4038c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x24, 0x0); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* disable all niu interrupts */ 4068c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0x40, 0xff); 4078c2ecf20Sopenharmony_ci /* disable xge rx/tx */ 4088c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0x70000, 0x00); 4098c2ecf20Sopenharmony_ci /* disable xg1 rx/tx */ 4108c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0x80000, 0x00); 4118c2ecf20Sopenharmony_ci /* disable sideband mac */ 4128c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0x90000, 0x00); 4138c2ecf20Sopenharmony_ci /* disable ap0 mac */ 4148c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0xa0000, 0x00); 4158c2ecf20Sopenharmony_ci /* disable ap1 mac */ 4168c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_NIU + 0xb0000, 0x00); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* halt sre */ 4198c2ecf20Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_CRB_SRE + 0x1000, &err); 4208c2ecf20Sopenharmony_ci if (err == -EIO) 4218c2ecf20Sopenharmony_ci return err; 4228c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_SRE + 0x1000, val & (~(0x1))); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* halt epg */ 4258c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_EPG + 0x1300, 0x1); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* halt timers */ 4288c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x0, 0x0); 4298c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x8, 0x0); 4308c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x10, 0x0); 4318c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x18, 0x0); 4328c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x100, 0x0); 4338c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x200, 0x0); 4348c2ecf20Sopenharmony_ci /* halt pegs */ 4358c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c, 1); 4368c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c, 1); 4378c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c, 1); 4388c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c, 1); 4398c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c, 1); 4408c2ecf20Sopenharmony_ci msleep(20); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* big hammer don't reset CAM block on reset */ 4438c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Init HW CRB block */ 4468c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, 0, &n) != 0 || (n != 0xcafecafe) || 4478c2ecf20Sopenharmony_ci qlcnic_rom_fast_read(adapter, 4, &n) != 0) { 4488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ERROR Reading crb_init area: val:%x\n", n); 4498c2ecf20Sopenharmony_ci return -EIO; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci offset = n & 0xffffU; 4528c2ecf20Sopenharmony_ci n = (n >> 16) & 0xffffU; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (n >= 1024) { 4558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "QLOGIC card flash not initialized.\n"); 4568c2ecf20Sopenharmony_ci return -EIO; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci buf = kcalloc(n, sizeof(struct crb_addr_pair), GFP_KERNEL); 4608c2ecf20Sopenharmony_ci if (buf == NULL) 4618c2ecf20Sopenharmony_ci return -ENOMEM; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 4648c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, 8*i + 4*offset, &val) != 0 || 4658c2ecf20Sopenharmony_ci qlcnic_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) { 4668c2ecf20Sopenharmony_ci kfree(buf); 4678c2ecf20Sopenharmony_ci return -EIO; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci buf[i].addr = addr; 4718c2ecf20Sopenharmony_ci buf[i].data = val; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci off = qlcnic_decode_crb_addr(buf[i].addr); 4778c2ecf20Sopenharmony_ci if (off == QLCNIC_ADDR_ERROR) { 4788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CRB init value out of range %x\n", 4798c2ecf20Sopenharmony_ci buf[i].addr); 4808c2ecf20Sopenharmony_ci continue; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci off += QLCNIC_PCI_CRBSPACE; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (off & 1) 4858c2ecf20Sopenharmony_ci continue; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* skipping cold reboot MAGIC */ 4888c2ecf20Sopenharmony_ci if (off == QLCNIC_CAM_RAM(0x1fc)) 4898c2ecf20Sopenharmony_ci continue; 4908c2ecf20Sopenharmony_ci if (off == (QLCNIC_CRB_I2C0 + 0x1c)) 4918c2ecf20Sopenharmony_ci continue; 4928c2ecf20Sopenharmony_ci if (off == (ROMUSB_GLB + 0xbc)) /* do not reset PCI */ 4938c2ecf20Sopenharmony_ci continue; 4948c2ecf20Sopenharmony_ci if (off == (ROMUSB_GLB + 0xa8)) 4958c2ecf20Sopenharmony_ci continue; 4968c2ecf20Sopenharmony_ci if (off == (ROMUSB_GLB + 0xc8)) /* core clock */ 4978c2ecf20Sopenharmony_ci continue; 4988c2ecf20Sopenharmony_ci if (off == (ROMUSB_GLB + 0x24)) /* MN clock */ 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci if (off == (ROMUSB_GLB + 0x1c)) /* MS clock */ 5018c2ecf20Sopenharmony_ci continue; 5028c2ecf20Sopenharmony_ci if ((off & 0x0ff00000) == QLCNIC_CRB_DDR_NET) 5038c2ecf20Sopenharmony_ci continue; 5048c2ecf20Sopenharmony_ci /* skip the function enable register */ 5058c2ecf20Sopenharmony_ci if (off == QLCNIC_PCIE_REG(PCIE_SETUP_FUNCTION)) 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci if (off == QLCNIC_PCIE_REG(PCIE_SETUP_FUNCTION2)) 5088c2ecf20Sopenharmony_ci continue; 5098c2ecf20Sopenharmony_ci if ((off & 0x0ff00000) == QLCNIC_CRB_SMB) 5108c2ecf20Sopenharmony_ci continue; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci init_delay = 1; 5138c2ecf20Sopenharmony_ci /* After writing this register, HW needs time for CRB */ 5148c2ecf20Sopenharmony_ci /* to quiet down (else crb_window returns 0xffffffff) */ 5158c2ecf20Sopenharmony_ci if (off == QLCNIC_ROMUSB_GLB_SW_RESET) 5168c2ecf20Sopenharmony_ci init_delay = 1000; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci QLCWR32(adapter, off, buf[i].data); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci msleep(init_delay); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci kfree(buf); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Initialize protocol process engine */ 5258c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_D + 0xec, 0x1e); 5268c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_D + 0x4c, 8); 5278c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_I + 0x4c, 8); 5288c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x8, 0); 5298c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0xc, 0); 5308c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x8, 0); 5318c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0xc, 0); 5328c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x8, 0); 5338c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0xc, 0); 5348c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x8, 0); 5358c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0xc, 0); 5368c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x8, 0); 5378c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0xc, 0); 5388c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_PEG_HALT_STATUS1, 0); 5418c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_PEG_HALT_STATUS2, 0); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic int qlcnic_cmd_peg_ready(struct qlcnic_adapter *adapter) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci u32 val; 5498c2ecf20Sopenharmony_ci int retries = QLCNIC_CMDPEG_CHECK_RETRY_COUNT; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci do { 5528c2ecf20Sopenharmony_ci val = QLC_SHARED_REG_RD32(adapter, QLCNIC_CMDPEG_STATE); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci switch (val) { 5558c2ecf20Sopenharmony_ci case PHAN_INITIALIZE_COMPLETE: 5568c2ecf20Sopenharmony_ci case PHAN_INITIALIZE_ACK: 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci case PHAN_INITIALIZE_FAILED: 5598c2ecf20Sopenharmony_ci goto out_err; 5608c2ecf20Sopenharmony_ci default: 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci msleep(QLCNIC_CMDPEG_CHECK_DELAY); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci } while (--retries); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_CMDPEG_STATE, 5698c2ecf20Sopenharmony_ci PHAN_INITIALIZE_FAILED); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciout_err: 5728c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Command Peg initialization not " 5738c2ecf20Sopenharmony_ci "complete, state: 0x%x.\n", val); 5748c2ecf20Sopenharmony_ci return -EIO; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int 5788c2ecf20Sopenharmony_ciqlcnic_receive_peg_ready(struct qlcnic_adapter *adapter) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci u32 val; 5818c2ecf20Sopenharmony_ci int retries = QLCNIC_RCVPEG_CHECK_RETRY_COUNT; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci do { 5848c2ecf20Sopenharmony_ci val = QLC_SHARED_REG_RD32(adapter, QLCNIC_RCVPEG_STATE); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (val == PHAN_PEG_RCV_INITIALIZED) 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci msleep(QLCNIC_RCVPEG_CHECK_DELAY); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci } while (--retries); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Receive Peg initialization not complete, state: 0x%x.\n", 5948c2ecf20Sopenharmony_ci val); 5958c2ecf20Sopenharmony_ci return -EIO; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciint 5998c2ecf20Sopenharmony_ciqlcnic_check_fw_status(struct qlcnic_adapter *adapter) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci int err; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci err = qlcnic_cmd_peg_ready(adapter); 6048c2ecf20Sopenharmony_ci if (err) 6058c2ecf20Sopenharmony_ci return err; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci err = qlcnic_receive_peg_ready(adapter); 6088c2ecf20Sopenharmony_ci if (err) 6098c2ecf20Sopenharmony_ci return err; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_CMDPEG_STATE, PHAN_INITIALIZE_ACK); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciint 6178c2ecf20Sopenharmony_ciqlcnic_setup_idc_param(struct qlcnic_adapter *adapter) { 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci int timeo; 6208c2ecf20Sopenharmony_ci u32 val; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci val = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_PARTITION_INFO); 6238c2ecf20Sopenharmony_ci val = QLC_DEV_GET_DRV(val, adapter->portnum); 6248c2ecf20Sopenharmony_ci if ((val & 0x3) != QLCNIC_TYPE_NIC) { 6258c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 6268c2ecf20Sopenharmony_ci "Not an Ethernet NIC func=%u\n", val); 6278c2ecf20Sopenharmony_ci return -EIO; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci adapter->ahw->physical_port = (val >> 2); 6308c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, QLCNIC_ROM_DEV_INIT_TIMEOUT, &timeo)) 6318c2ecf20Sopenharmony_ci timeo = QLCNIC_INIT_TIMEOUT_SECS; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci adapter->dev_init_timeo = timeo; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, QLCNIC_ROM_DRV_RESET_TIMEOUT, &timeo)) 6368c2ecf20Sopenharmony_ci timeo = QLCNIC_RESET_TIMEOUT_SECS; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci adapter->reset_ack_timeo = timeo; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int qlcnic_get_flt_entry(struct qlcnic_adapter *adapter, u8 region, 6448c2ecf20Sopenharmony_ci struct qlcnic_flt_entry *region_entry) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct qlcnic_flt_header flt_hdr; 6478c2ecf20Sopenharmony_ci struct qlcnic_flt_entry *flt_entry; 6488c2ecf20Sopenharmony_ci int i = 0, ret; 6498c2ecf20Sopenharmony_ci u32 entry_size; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci memset(region_entry, 0, sizeof(struct qlcnic_flt_entry)); 6528c2ecf20Sopenharmony_ci ret = qlcnic_rom_fast_read_words(adapter, QLCNIC_FLT_LOCATION, 6538c2ecf20Sopenharmony_ci (u8 *)&flt_hdr, 6548c2ecf20Sopenharmony_ci sizeof(struct qlcnic_flt_header)); 6558c2ecf20Sopenharmony_ci if (ret) { 6568c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 6578c2ecf20Sopenharmony_ci "error reading flash layout header\n"); 6588c2ecf20Sopenharmony_ci return -EIO; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci entry_size = flt_hdr.len - sizeof(struct qlcnic_flt_header); 6628c2ecf20Sopenharmony_ci flt_entry = vzalloc(entry_size); 6638c2ecf20Sopenharmony_ci if (flt_entry == NULL) 6648c2ecf20Sopenharmony_ci return -EIO; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci ret = qlcnic_rom_fast_read_words(adapter, QLCNIC_FLT_LOCATION + 6678c2ecf20Sopenharmony_ci sizeof(struct qlcnic_flt_header), 6688c2ecf20Sopenharmony_ci (u8 *)flt_entry, entry_size); 6698c2ecf20Sopenharmony_ci if (ret) { 6708c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 6718c2ecf20Sopenharmony_ci "error reading flash layout entries\n"); 6728c2ecf20Sopenharmony_ci goto err_out; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci while (i < (entry_size/sizeof(struct qlcnic_flt_entry))) { 6768c2ecf20Sopenharmony_ci if (flt_entry[i].region == region) 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci i++; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci if (i >= (entry_size/sizeof(struct qlcnic_flt_entry))) { 6818c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 6828c2ecf20Sopenharmony_ci "region=%x not found in %d regions\n", region, i); 6838c2ecf20Sopenharmony_ci ret = -EIO; 6848c2ecf20Sopenharmony_ci goto err_out; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci memcpy(region_entry, &flt_entry[i], sizeof(struct qlcnic_flt_entry)); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cierr_out: 6898c2ecf20Sopenharmony_ci vfree(flt_entry); 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ciint 6948c2ecf20Sopenharmony_ciqlcnic_check_flash_fw_ver(struct qlcnic_adapter *adapter) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct qlcnic_flt_entry fw_entry; 6978c2ecf20Sopenharmony_ci u32 ver = -1, min_ver; 6988c2ecf20Sopenharmony_ci int ret; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (adapter->ahw->revision_id == QLCNIC_P3P_C0) 7018c2ecf20Sopenharmony_ci ret = qlcnic_get_flt_entry(adapter, QLCNIC_C0_FW_IMAGE_REGION, 7028c2ecf20Sopenharmony_ci &fw_entry); 7038c2ecf20Sopenharmony_ci else 7048c2ecf20Sopenharmony_ci ret = qlcnic_get_flt_entry(adapter, QLCNIC_B0_FW_IMAGE_REGION, 7058c2ecf20Sopenharmony_ci &fw_entry); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (!ret) 7088c2ecf20Sopenharmony_ci /* 0-4:-signature, 4-8:-fw version */ 7098c2ecf20Sopenharmony_ci qlcnic_rom_fast_read(adapter, fw_entry.start_addr + 4, 7108c2ecf20Sopenharmony_ci (int *)&ver); 7118c2ecf20Sopenharmony_ci else 7128c2ecf20Sopenharmony_ci qlcnic_rom_fast_read(adapter, QLCNIC_FW_VERSION_OFFSET, 7138c2ecf20Sopenharmony_ci (int *)&ver); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ver = QLCNIC_DECODE_VERSION(ver); 7168c2ecf20Sopenharmony_ci min_ver = QLCNIC_MIN_FW_VERSION; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (ver < min_ver) { 7198c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 7208c2ecf20Sopenharmony_ci "firmware version %d.%d.%d unsupported." 7218c2ecf20Sopenharmony_ci "Min supported version %d.%d.%d\n", 7228c2ecf20Sopenharmony_ci _major(ver), _minor(ver), _build(ver), 7238c2ecf20Sopenharmony_ci _major(min_ver), _minor(min_ver), _build(min_ver)); 7248c2ecf20Sopenharmony_ci return -EINVAL; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int 7318c2ecf20Sopenharmony_ciqlcnic_has_mn(struct qlcnic_adapter *adapter) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci u32 capability = 0; 7348c2ecf20Sopenharmony_ci int err = 0; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci capability = QLCRD32(adapter, QLCNIC_PEG_TUNE_CAPABILITY, &err); 7378c2ecf20Sopenharmony_ci if (err == -EIO) 7388c2ecf20Sopenharmony_ci return err; 7398c2ecf20Sopenharmony_ci if (capability & QLCNIC_PEG_TUNE_MN_PRESENT) 7408c2ecf20Sopenharmony_ci return 1; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic 7468c2ecf20Sopenharmony_cistruct uni_table_desc *qlcnic_get_table_desc(const u8 *unirom, int section) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci u32 i, entries; 7498c2ecf20Sopenharmony_ci struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0]; 7508c2ecf20Sopenharmony_ci entries = le32_to_cpu(directory->num_entries); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci for (i = 0; i < entries; i++) { 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci u32 offs = le32_to_cpu(directory->findex) + 7558c2ecf20Sopenharmony_ci i * le32_to_cpu(directory->entry_size); 7568c2ecf20Sopenharmony_ci u32 tab_type = le32_to_cpu(*((__le32 *)&unirom[offs] + 8)); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (tab_type == section) 7598c2ecf20Sopenharmony_ci return (struct uni_table_desc *) &unirom[offs]; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return NULL; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci#define FILEHEADER_SIZE (14 * 4) 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int 7688c2ecf20Sopenharmony_ciqlcnic_validate_header(struct qlcnic_adapter *adapter) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 7718c2ecf20Sopenharmony_ci struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0]; 7728c2ecf20Sopenharmony_ci u32 entries, entry_size, tab_size, fw_file_size; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci fw_file_size = adapter->fw->size; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (fw_file_size < FILEHEADER_SIZE) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci entries = le32_to_cpu(directory->num_entries); 7808c2ecf20Sopenharmony_ci entry_size = le32_to_cpu(directory->entry_size); 7818c2ecf20Sopenharmony_ci tab_size = le32_to_cpu(directory->findex) + (entries * entry_size); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (fw_file_size < tab_size) 7848c2ecf20Sopenharmony_ci return -EINVAL; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int 7908c2ecf20Sopenharmony_ciqlcnic_validate_bootld(struct qlcnic_adapter *adapter) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct uni_table_desc *tab_desc; 7938c2ecf20Sopenharmony_ci struct uni_data_desc *descr; 7948c2ecf20Sopenharmony_ci u32 offs, tab_size, data_size, idx; 7958c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 7968c2ecf20Sopenharmony_ci __le32 temp; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci temp = *((__le32 *)&unirom[adapter->file_prd_off] + 7998c2ecf20Sopenharmony_ci QLCNIC_UNI_BOOTLD_IDX_OFF); 8008c2ecf20Sopenharmony_ci idx = le32_to_cpu(temp); 8018c2ecf20Sopenharmony_ci tab_desc = qlcnic_get_table_desc(unirom, QLCNIC_UNI_DIR_SECT_BOOTLD); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (!tab_desc) 8048c2ecf20Sopenharmony_ci return -EINVAL; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci tab_size = le32_to_cpu(tab_desc->findex) + 8078c2ecf20Sopenharmony_ci le32_to_cpu(tab_desc->entry_size) * (idx + 1); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (adapter->fw->size < tab_size) 8108c2ecf20Sopenharmony_ci return -EINVAL; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci offs = le32_to_cpu(tab_desc->findex) + 8138c2ecf20Sopenharmony_ci le32_to_cpu(tab_desc->entry_size) * idx; 8148c2ecf20Sopenharmony_ci descr = (struct uni_data_desc *)&unirom[offs]; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci data_size = le32_to_cpu(descr->findex) + le32_to_cpu(descr->size); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (adapter->fw->size < data_size) 8198c2ecf20Sopenharmony_ci return -EINVAL; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int 8258c2ecf20Sopenharmony_ciqlcnic_validate_fw(struct qlcnic_adapter *adapter) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct uni_table_desc *tab_desc; 8288c2ecf20Sopenharmony_ci struct uni_data_desc *descr; 8298c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 8308c2ecf20Sopenharmony_ci u32 offs, tab_size, data_size, idx; 8318c2ecf20Sopenharmony_ci __le32 temp; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci temp = *((__le32 *)&unirom[adapter->file_prd_off] + 8348c2ecf20Sopenharmony_ci QLCNIC_UNI_FIRMWARE_IDX_OFF); 8358c2ecf20Sopenharmony_ci idx = le32_to_cpu(temp); 8368c2ecf20Sopenharmony_ci tab_desc = qlcnic_get_table_desc(unirom, QLCNIC_UNI_DIR_SECT_FW); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (!tab_desc) 8398c2ecf20Sopenharmony_ci return -EINVAL; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci tab_size = le32_to_cpu(tab_desc->findex) + 8428c2ecf20Sopenharmony_ci le32_to_cpu(tab_desc->entry_size) * (idx + 1); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (adapter->fw->size < tab_size) 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci offs = le32_to_cpu(tab_desc->findex) + 8488c2ecf20Sopenharmony_ci le32_to_cpu(tab_desc->entry_size) * idx; 8498c2ecf20Sopenharmony_ci descr = (struct uni_data_desc *)&unirom[offs]; 8508c2ecf20Sopenharmony_ci data_size = le32_to_cpu(descr->findex) + le32_to_cpu(descr->size); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (adapter->fw->size < data_size) 8538c2ecf20Sopenharmony_ci return -EINVAL; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int 8598c2ecf20Sopenharmony_ciqlcnic_validate_product_offs(struct qlcnic_adapter *adapter) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct uni_table_desc *ptab_descr; 8628c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 8638c2ecf20Sopenharmony_ci int mn_present = qlcnic_has_mn(adapter); 8648c2ecf20Sopenharmony_ci u32 entries, entry_size, tab_size, i; 8658c2ecf20Sopenharmony_ci __le32 temp; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ptab_descr = qlcnic_get_table_desc(unirom, 8688c2ecf20Sopenharmony_ci QLCNIC_UNI_DIR_SECT_PRODUCT_TBL); 8698c2ecf20Sopenharmony_ci if (!ptab_descr) 8708c2ecf20Sopenharmony_ci return -EINVAL; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci entries = le32_to_cpu(ptab_descr->num_entries); 8738c2ecf20Sopenharmony_ci entry_size = le32_to_cpu(ptab_descr->entry_size); 8748c2ecf20Sopenharmony_ci tab_size = le32_to_cpu(ptab_descr->findex) + (entries * entry_size); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (adapter->fw->size < tab_size) 8778c2ecf20Sopenharmony_ci return -EINVAL; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cinomn: 8808c2ecf20Sopenharmony_ci for (i = 0; i < entries; i++) { 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci u32 flags, file_chiprev, offs; 8838c2ecf20Sopenharmony_ci u8 chiprev = adapter->ahw->revision_id; 8848c2ecf20Sopenharmony_ci u32 flagbit; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci offs = le32_to_cpu(ptab_descr->findex) + 8878c2ecf20Sopenharmony_ci i * le32_to_cpu(ptab_descr->entry_size); 8888c2ecf20Sopenharmony_ci temp = *((__le32 *)&unirom[offs] + QLCNIC_UNI_FLAGS_OFF); 8898c2ecf20Sopenharmony_ci flags = le32_to_cpu(temp); 8908c2ecf20Sopenharmony_ci temp = *((__le32 *)&unirom[offs] + QLCNIC_UNI_CHIP_REV_OFF); 8918c2ecf20Sopenharmony_ci file_chiprev = le32_to_cpu(temp); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci flagbit = mn_present ? 1 : 2; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if ((chiprev == file_chiprev) && 8968c2ecf20Sopenharmony_ci ((1ULL << flagbit) & flags)) { 8978c2ecf20Sopenharmony_ci adapter->file_prd_off = offs; 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci if (mn_present) { 9028c2ecf20Sopenharmony_ci mn_present = 0; 9038c2ecf20Sopenharmony_ci goto nomn; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci return -EINVAL; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic int 9098c2ecf20Sopenharmony_ciqlcnic_validate_unified_romimage(struct qlcnic_adapter *adapter) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci if (qlcnic_validate_header(adapter)) { 9128c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 9138c2ecf20Sopenharmony_ci "unified image: header validation failed\n"); 9148c2ecf20Sopenharmony_ci return -EINVAL; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (qlcnic_validate_product_offs(adapter)) { 9188c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 9198c2ecf20Sopenharmony_ci "unified image: product validation failed\n"); 9208c2ecf20Sopenharmony_ci return -EINVAL; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (qlcnic_validate_bootld(adapter)) { 9248c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 9258c2ecf20Sopenharmony_ci "unified image: bootld validation failed\n"); 9268c2ecf20Sopenharmony_ci return -EINVAL; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (qlcnic_validate_fw(adapter)) { 9308c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 9318c2ecf20Sopenharmony_ci "unified image: firmware validation failed\n"); 9328c2ecf20Sopenharmony_ci return -EINVAL; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic 9398c2ecf20Sopenharmony_cistruct uni_data_desc *qlcnic_get_data_desc(struct qlcnic_adapter *adapter, 9408c2ecf20Sopenharmony_ci u32 section, u32 idx_offset) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 9438c2ecf20Sopenharmony_ci struct uni_table_desc *tab_desc; 9448c2ecf20Sopenharmony_ci u32 offs, idx; 9458c2ecf20Sopenharmony_ci __le32 temp; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci temp = *((__le32 *)&unirom[adapter->file_prd_off] + idx_offset); 9488c2ecf20Sopenharmony_ci idx = le32_to_cpu(temp); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci tab_desc = qlcnic_get_table_desc(unirom, section); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (tab_desc == NULL) 9538c2ecf20Sopenharmony_ci return NULL; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci offs = le32_to_cpu(tab_desc->findex) + 9568c2ecf20Sopenharmony_ci le32_to_cpu(tab_desc->entry_size) * idx; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci return (struct uni_data_desc *)&unirom[offs]; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic u8 * 9628c2ecf20Sopenharmony_ciqlcnic_get_bootld_offs(struct qlcnic_adapter *adapter) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci u32 offs = QLCNIC_BOOTLD_START; 9658c2ecf20Sopenharmony_ci struct uni_data_desc *data_desc; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci data_desc = qlcnic_get_data_desc(adapter, QLCNIC_UNI_DIR_SECT_BOOTLD, 9688c2ecf20Sopenharmony_ci QLCNIC_UNI_BOOTLD_IDX_OFF); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type == QLCNIC_UNIFIED_ROMIMAGE) 9718c2ecf20Sopenharmony_ci offs = le32_to_cpu(data_desc->findex); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return (u8 *)&adapter->fw->data[offs]; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic u8 * 9778c2ecf20Sopenharmony_ciqlcnic_get_fw_offs(struct qlcnic_adapter *adapter) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci u32 offs = QLCNIC_IMAGE_START; 9808c2ecf20Sopenharmony_ci struct uni_data_desc *data_desc; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci data_desc = qlcnic_get_data_desc(adapter, QLCNIC_UNI_DIR_SECT_FW, 9838c2ecf20Sopenharmony_ci QLCNIC_UNI_FIRMWARE_IDX_OFF); 9848c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type == QLCNIC_UNIFIED_ROMIMAGE) 9858c2ecf20Sopenharmony_ci offs = le32_to_cpu(data_desc->findex); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return (u8 *)&adapter->fw->data[offs]; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic u32 qlcnic_get_fw_size(struct qlcnic_adapter *adapter) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct uni_data_desc *data_desc; 9938c2ecf20Sopenharmony_ci const u8 *unirom = adapter->fw->data; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci data_desc = qlcnic_get_data_desc(adapter, QLCNIC_UNI_DIR_SECT_FW, 9968c2ecf20Sopenharmony_ci QLCNIC_UNI_FIRMWARE_IDX_OFF); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type == QLCNIC_UNIFIED_ROMIMAGE) 9998c2ecf20Sopenharmony_ci return le32_to_cpu(data_desc->size); 10008c2ecf20Sopenharmony_ci else 10018c2ecf20Sopenharmony_ci return le32_to_cpu(*(__le32 *)&unirom[QLCNIC_FW_SIZE_OFFSET]); 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic u32 qlcnic_get_fw_version(struct qlcnic_adapter *adapter) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci struct uni_data_desc *fw_data_desc; 10078c2ecf20Sopenharmony_ci const struct firmware *fw = adapter->fw; 10088c2ecf20Sopenharmony_ci u32 major, minor, sub; 10098c2ecf20Sopenharmony_ci __le32 version_offset; 10108c2ecf20Sopenharmony_ci const u8 *ver_str; 10118c2ecf20Sopenharmony_ci int i, ret; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type != QLCNIC_UNIFIED_ROMIMAGE) { 10148c2ecf20Sopenharmony_ci version_offset = *(__le32 *)&fw->data[QLCNIC_FW_VERSION_OFFSET]; 10158c2ecf20Sopenharmony_ci return le32_to_cpu(version_offset); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci fw_data_desc = qlcnic_get_data_desc(adapter, QLCNIC_UNI_DIR_SECT_FW, 10198c2ecf20Sopenharmony_ci QLCNIC_UNI_FIRMWARE_IDX_OFF); 10208c2ecf20Sopenharmony_ci ver_str = fw->data + le32_to_cpu(fw_data_desc->findex) + 10218c2ecf20Sopenharmony_ci le32_to_cpu(fw_data_desc->size) - 17; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci for (i = 0; i < 12; i++) { 10248c2ecf20Sopenharmony_ci if (!strncmp(&ver_str[i], "REV=", 4)) { 10258c2ecf20Sopenharmony_ci ret = sscanf(&ver_str[i+4], "%u.%u.%u ", 10268c2ecf20Sopenharmony_ci &major, &minor, &sub); 10278c2ecf20Sopenharmony_ci if (ret != 3) 10288c2ecf20Sopenharmony_ci return 0; 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci return major + (minor << 8) + (sub << 16); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci return 0; 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic u32 qlcnic_get_bios_version(struct qlcnic_adapter *adapter) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci const struct firmware *fw = adapter->fw; 10408c2ecf20Sopenharmony_ci u32 bios_ver, prd_off = adapter->file_prd_off; 10418c2ecf20Sopenharmony_ci u8 *version_offset; 10428c2ecf20Sopenharmony_ci __le32 temp; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type != QLCNIC_UNIFIED_ROMIMAGE) { 10458c2ecf20Sopenharmony_ci version_offset = (u8 *)&fw->data[QLCNIC_BIOS_VERSION_OFFSET]; 10468c2ecf20Sopenharmony_ci return le32_to_cpu(*(__le32 *)version_offset); 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci temp = *((__le32 *)(&fw->data[prd_off]) + QLCNIC_UNI_BIOS_VERSION_OFF); 10508c2ecf20Sopenharmony_ci bios_ver = le32_to_cpu(temp); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci return (bios_ver << 16) + ((bios_ver >> 8) & 0xff00) + (bios_ver >> 24); 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic void qlcnic_rom_lock_recovery(struct qlcnic_adapter *adapter) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci if (qlcnic_pcie_sem_lock(adapter, 2, QLCNIC_ROM_LOCK_ID)) 10588c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "Resetting rom_lock\n"); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci qlcnic_pcie_sem_unlock(adapter, 2); 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int 10648c2ecf20Sopenharmony_ciqlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci u32 heartbeat, ret = -EIO; 10678c2ecf20Sopenharmony_ci int retries = QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci adapter->heartbeat = QLC_SHARED_REG_RD32(adapter, 10708c2ecf20Sopenharmony_ci QLCNIC_PEG_ALIVE_COUNTER); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci do { 10738c2ecf20Sopenharmony_ci msleep(QLCNIC_HEARTBEAT_PERIOD_MSECS); 10748c2ecf20Sopenharmony_ci heartbeat = QLC_SHARED_REG_RD32(adapter, 10758c2ecf20Sopenharmony_ci QLCNIC_PEG_ALIVE_COUNTER); 10768c2ecf20Sopenharmony_ci if (heartbeat != adapter->heartbeat) { 10778c2ecf20Sopenharmony_ci ret = QLCNIC_RCODE_SUCCESS; 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci } while (--retries); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci return ret; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ciint 10868c2ecf20Sopenharmony_ciqlcnic_need_fw_reset(struct qlcnic_adapter *adapter) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_FW_HANG) || 10898c2ecf20Sopenharmony_ci qlcnic_check_fw_hearbeat(adapter)) { 10908c2ecf20Sopenharmony_ci qlcnic_rom_lock_recovery(adapter); 10918c2ecf20Sopenharmony_ci return 1; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (adapter->need_fw_reset) 10958c2ecf20Sopenharmony_ci return 1; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (adapter->fw) 10988c2ecf20Sopenharmony_ci return 1; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return 0; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic const char *fw_name[] = { 11048c2ecf20Sopenharmony_ci QLCNIC_UNIFIED_ROMIMAGE_NAME, 11058c2ecf20Sopenharmony_ci QLCNIC_FLASH_ROMIMAGE_NAME, 11068c2ecf20Sopenharmony_ci}; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ciint 11098c2ecf20Sopenharmony_ciqlcnic_load_firmware(struct qlcnic_adapter *adapter) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci __le64 *ptr64; 11128c2ecf20Sopenharmony_ci u32 i, flashaddr, size; 11138c2ecf20Sopenharmony_ci const struct firmware *fw = adapter->fw; 11148c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "loading firmware from %s\n", 11178c2ecf20Sopenharmony_ci fw_name[adapter->ahw->fw_type]); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (fw) { 11208c2ecf20Sopenharmony_ci u64 data; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci size = (QLCNIC_IMAGE_START - QLCNIC_BOOTLD_START) / 8; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ptr64 = (__le64 *)qlcnic_get_bootld_offs(adapter); 11258c2ecf20Sopenharmony_ci flashaddr = QLCNIC_BOOTLD_START; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 11288c2ecf20Sopenharmony_ci data = le64_to_cpu(ptr64[i]); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (qlcnic_pci_mem_write_2M(adapter, flashaddr, data)) 11318c2ecf20Sopenharmony_ci return -EIO; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci flashaddr += 8; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci size = qlcnic_get_fw_size(adapter) / 8; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci ptr64 = (__le64 *)qlcnic_get_fw_offs(adapter); 11398c2ecf20Sopenharmony_ci flashaddr = QLCNIC_IMAGE_START; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 11428c2ecf20Sopenharmony_ci data = le64_to_cpu(ptr64[i]); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (qlcnic_pci_mem_write_2M(adapter, 11458c2ecf20Sopenharmony_ci flashaddr, data)) 11468c2ecf20Sopenharmony_ci return -EIO; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci flashaddr += 8; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci size = qlcnic_get_fw_size(adapter) % 8; 11528c2ecf20Sopenharmony_ci if (size) { 11538c2ecf20Sopenharmony_ci data = le64_to_cpu(ptr64[i]); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (qlcnic_pci_mem_write_2M(adapter, 11568c2ecf20Sopenharmony_ci flashaddr, data)) 11578c2ecf20Sopenharmony_ci return -EIO; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci } else { 11618c2ecf20Sopenharmony_ci u64 data; 11628c2ecf20Sopenharmony_ci u32 hi, lo; 11638c2ecf20Sopenharmony_ci int ret; 11648c2ecf20Sopenharmony_ci struct qlcnic_flt_entry bootld_entry; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci ret = qlcnic_get_flt_entry(adapter, QLCNIC_BOOTLD_REGION, 11678c2ecf20Sopenharmony_ci &bootld_entry); 11688c2ecf20Sopenharmony_ci if (!ret) { 11698c2ecf20Sopenharmony_ci size = bootld_entry.size / 8; 11708c2ecf20Sopenharmony_ci flashaddr = bootld_entry.start_addr; 11718c2ecf20Sopenharmony_ci } else { 11728c2ecf20Sopenharmony_ci size = (QLCNIC_IMAGE_START - QLCNIC_BOOTLD_START) / 8; 11738c2ecf20Sopenharmony_ci flashaddr = QLCNIC_BOOTLD_START; 11748c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 11758c2ecf20Sopenharmony_ci "using legacy method to get flash fw region"); 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 11798c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, 11808c2ecf20Sopenharmony_ci flashaddr, (int *)&lo) != 0) 11818c2ecf20Sopenharmony_ci return -EIO; 11828c2ecf20Sopenharmony_ci if (qlcnic_rom_fast_read(adapter, 11838c2ecf20Sopenharmony_ci flashaddr + 4, (int *)&hi) != 0) 11848c2ecf20Sopenharmony_ci return -EIO; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci data = (((u64)hi << 32) | lo); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (qlcnic_pci_mem_write_2M(adapter, 11898c2ecf20Sopenharmony_ci flashaddr, data)) 11908c2ecf20Sopenharmony_ci return -EIO; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci flashaddr += 8; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x18, 0x1020); 11988c2ecf20Sopenharmony_ci QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0x80001e); 11998c2ecf20Sopenharmony_ci return 0; 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic int 12038c2ecf20Sopenharmony_ciqlcnic_validate_firmware(struct qlcnic_adapter *adapter) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci u32 val; 12068c2ecf20Sopenharmony_ci u32 ver, bios, min_size; 12078c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 12088c2ecf20Sopenharmony_ci const struct firmware *fw = adapter->fw; 12098c2ecf20Sopenharmony_ci u8 fw_type = adapter->ahw->fw_type; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (fw_type == QLCNIC_UNIFIED_ROMIMAGE) { 12128c2ecf20Sopenharmony_ci if (qlcnic_validate_unified_romimage(adapter)) 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci min_size = QLCNIC_UNI_FW_MIN_SIZE; 12168c2ecf20Sopenharmony_ci } else { 12178c2ecf20Sopenharmony_ci val = le32_to_cpu(*(__le32 *)&fw->data[QLCNIC_FW_MAGIC_OFFSET]); 12188c2ecf20Sopenharmony_ci if (val != QLCNIC_BDINFO_MAGIC) 12198c2ecf20Sopenharmony_ci return -EINVAL; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci min_size = QLCNIC_FW_MIN_SIZE; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (fw->size < min_size) 12258c2ecf20Sopenharmony_ci return -EINVAL; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci val = qlcnic_get_fw_version(adapter); 12288c2ecf20Sopenharmony_ci ver = QLCNIC_DECODE_VERSION(val); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (ver < QLCNIC_MIN_FW_VERSION) { 12318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 12328c2ecf20Sopenharmony_ci "%s: firmware version %d.%d.%d unsupported\n", 12338c2ecf20Sopenharmony_ci fw_name[fw_type], _major(ver), _minor(ver), _build(ver)); 12348c2ecf20Sopenharmony_ci return -EINVAL; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci val = qlcnic_get_bios_version(adapter); 12388c2ecf20Sopenharmony_ci qlcnic_rom_fast_read(adapter, QLCNIC_BIOS_VERSION_OFFSET, (int *)&bios); 12398c2ecf20Sopenharmony_ci if (val != bios) { 12408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: firmware bios is incompatible\n", 12418c2ecf20Sopenharmony_ci fw_name[fw_type]); 12428c2ecf20Sopenharmony_ci return -EINVAL; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci QLC_SHARED_REG_WR32(adapter, QLCNIC_FW_IMG_VALID, QLCNIC_BDINFO_MAGIC); 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void 12508c2ecf20Sopenharmony_ciqlcnic_get_next_fwtype(struct qlcnic_adapter *adapter) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci u8 fw_type; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci switch (adapter->ahw->fw_type) { 12558c2ecf20Sopenharmony_ci case QLCNIC_UNKNOWN_ROMIMAGE: 12568c2ecf20Sopenharmony_ci fw_type = QLCNIC_UNIFIED_ROMIMAGE; 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci case QLCNIC_UNIFIED_ROMIMAGE: 12608c2ecf20Sopenharmony_ci default: 12618c2ecf20Sopenharmony_ci fw_type = QLCNIC_FLASH_ROMIMAGE; 12628c2ecf20Sopenharmony_ci break; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci adapter->ahw->fw_type = fw_type; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_civoid qlcnic_request_firmware(struct qlcnic_adapter *adapter) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 12738c2ecf20Sopenharmony_ci int rc; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci adapter->ahw->fw_type = QLCNIC_UNKNOWN_ROMIMAGE; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cinext: 12788c2ecf20Sopenharmony_ci qlcnic_get_next_fwtype(adapter); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (adapter->ahw->fw_type == QLCNIC_FLASH_ROMIMAGE) { 12818c2ecf20Sopenharmony_ci adapter->fw = NULL; 12828c2ecf20Sopenharmony_ci } else { 12838c2ecf20Sopenharmony_ci rc = request_firmware(&adapter->fw, 12848c2ecf20Sopenharmony_ci fw_name[adapter->ahw->fw_type], 12858c2ecf20Sopenharmony_ci &pdev->dev); 12868c2ecf20Sopenharmony_ci if (rc != 0) 12878c2ecf20Sopenharmony_ci goto next; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci rc = qlcnic_validate_firmware(adapter); 12908c2ecf20Sopenharmony_ci if (rc != 0) { 12918c2ecf20Sopenharmony_ci release_firmware(adapter->fw); 12928c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 12938c2ecf20Sopenharmony_ci goto next; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_civoid 13008c2ecf20Sopenharmony_ciqlcnic_release_firmware(struct qlcnic_adapter *adapter) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci release_firmware(adapter->fw); 13038c2ecf20Sopenharmony_ci adapter->fw = NULL; 13048c2ecf20Sopenharmony_ci} 1305