18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tehuti Networks(R) Network Driver 48c2ecf20Sopenharmony_ci * ethtool interface implementation 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Tehuti Networks Ltd. All rights reserved 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * RX HW/SW interaction overview 108c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 118c2ecf20Sopenharmony_ci * There are 2 types of RX communication channels between driver and NIC. 128c2ecf20Sopenharmony_ci * 1) RX Free Fifo - RXF - holds descriptors of empty buffers to accept incoming 138c2ecf20Sopenharmony_ci * traffic. This Fifo is filled by SW and is readen by HW. Each descriptor holds 148c2ecf20Sopenharmony_ci * info about buffer's location, size and ID. An ID field is used to identify a 158c2ecf20Sopenharmony_ci * buffer when it's returned with data via RXD Fifo (see below) 168c2ecf20Sopenharmony_ci * 2) RX Data Fifo - RXD - holds descriptors of full buffers. This Fifo is 178c2ecf20Sopenharmony_ci * filled by HW and is readen by SW. Each descriptor holds status and ID. 188c2ecf20Sopenharmony_ci * HW pops descriptor from RXF Fifo, stores ID, fills buffer with incoming data, 198c2ecf20Sopenharmony_ci * via dma moves it into host memory, builds new RXD descriptor with same ID, 208c2ecf20Sopenharmony_ci * pushes it into RXD Fifo and raises interrupt to indicate new RX data. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Current NIC configuration (registers + firmware) makes NIC use 2 RXF Fifos. 238c2ecf20Sopenharmony_ci * One holds 1.5K packets and another - 26K packets. Depending on incoming 248c2ecf20Sopenharmony_ci * packet size, HW desides on a RXF Fifo to pop buffer from. When packet is 258c2ecf20Sopenharmony_ci * filled with data, HW builds new RXD descriptor for it and push it into single 268c2ecf20Sopenharmony_ci * RXD Fifo. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * RX SW Data Structures 298c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~ 308c2ecf20Sopenharmony_ci * skb db - used to keep track of all skbs owned by SW and their dma addresses. 318c2ecf20Sopenharmony_ci * For RX case, ownership lasts from allocating new empty skb for RXF until 328c2ecf20Sopenharmony_ci * accepting full skb from RXD and passing it to OS. Each RXF Fifo has its own 338c2ecf20Sopenharmony_ci * skb db. Implemented as array with bitmask. 348c2ecf20Sopenharmony_ci * fifo - keeps info about fifo's size and location, relevant HW registers, 358c2ecf20Sopenharmony_ci * usage and skb db. Each RXD and RXF Fifo has its own fifo structure. 368c2ecf20Sopenharmony_ci * Implemented as simple struct. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * RX SW Execution Flow 398c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~ 408c2ecf20Sopenharmony_ci * Upon initialization (ifconfig up) driver creates RX fifos and initializes 418c2ecf20Sopenharmony_ci * relevant registers. At the end of init phase, driver enables interrupts. 428c2ecf20Sopenharmony_ci * NIC sees that there is no RXF buffers and raises 438c2ecf20Sopenharmony_ci * RD_INTR interrupt, isr fills skbs and Rx begins. 448c2ecf20Sopenharmony_ci * Driver has two receive operation modes: 458c2ecf20Sopenharmony_ci * NAPI - interrupt-driven mixed with polling 468c2ecf20Sopenharmony_ci * interrupt-driven only 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Interrupt-driven only flow is following. When buffer is ready, HW raises 498c2ecf20Sopenharmony_ci * interrupt and isr is called. isr collects all available packets 508c2ecf20Sopenharmony_ci * (bdx_rx_receive), refills skbs (bdx_rx_alloc_skbs) and exit. 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci * Rx buffer allocation note 538c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~ 548c2ecf20Sopenharmony_ci * Driver cares to feed such amount of RxF descriptors that respective amount of 558c2ecf20Sopenharmony_ci * RxD descriptors can not fill entire RxD fifo. The main reason is lack of 568c2ecf20Sopenharmony_ci * overflow check in Bordeaux for RxD fifo free/used size. 578c2ecf20Sopenharmony_ci * FIXME: this is NOT fully implemented, more work should be done 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include "tehuti.h" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const struct pci_device_id bdx_pci_tbl[] = { 668c2ecf20Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3009), }, 678c2ecf20Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3010), }, 688c2ecf20Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3014), }, 698c2ecf20Sopenharmony_ci { 0 } 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bdx_pci_tbl); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* Definitions needed by ISR or NAPI functions */ 758c2ecf20Sopenharmony_cistatic void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f); 768c2ecf20Sopenharmony_cistatic void bdx_tx_cleanup(struct bdx_priv *priv); 778c2ecf20Sopenharmony_cistatic int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Definitions needed by FW loading */ 808c2ecf20Sopenharmony_cistatic void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Definitions needed by hw_start */ 838c2ecf20Sopenharmony_cistatic int bdx_tx_init(struct bdx_priv *priv); 848c2ecf20Sopenharmony_cistatic int bdx_rx_init(struct bdx_priv *priv); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Definitions needed by bdx_close */ 878c2ecf20Sopenharmony_cistatic void bdx_rx_free(struct bdx_priv *priv); 888c2ecf20Sopenharmony_cistatic void bdx_tx_free(struct bdx_priv *priv); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Definitions needed by bdx_probe */ 918c2ecf20Sopenharmony_cistatic void bdx_set_ethtool_ops(struct net_device *netdev); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/************************************************************************* 948c2ecf20Sopenharmony_ci * Print Info * 958c2ecf20Sopenharmony_ci *************************************************************************/ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void print_hw_id(struct pci_dev *pdev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct pci_nic *nic = pci_get_drvdata(pdev); 1008c2ecf20Sopenharmony_ci u16 pci_link_status = 0; 1018c2ecf20Sopenharmony_ci u16 pci_ctrl = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_LINK_STATUS_REG, &pci_link_status); 1048c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_DEV_CTRL_REG, &pci_ctrl); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pr_info("%s%s\n", BDX_NIC_NAME, 1078c2ecf20Sopenharmony_ci nic->port_num == 1 ? "" : ", 2-Port"); 1088c2ecf20Sopenharmony_ci pr_info("srom 0x%x fpga %d build %u lane# %d max_pl 0x%x mrrs 0x%x\n", 1098c2ecf20Sopenharmony_ci readl(nic->regs + SROM_VER), readl(nic->regs + FPGA_VER) & 0xFFF, 1108c2ecf20Sopenharmony_ci readl(nic->regs + FPGA_SEED), 1118c2ecf20Sopenharmony_ci GET_LINK_STATUS_LANES(pci_link_status), 1128c2ecf20Sopenharmony_ci GET_DEV_CTRL_MAXPL(pci_ctrl), GET_DEV_CTRL_MRRS(pci_ctrl)); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void print_fw_id(struct pci_nic *nic) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci pr_info("fw 0x%x\n", readl(nic->regs + FW_VER)); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void print_eth_id(struct net_device *ndev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci netdev_info(ndev, "%s, Port %c\n", 1238c2ecf20Sopenharmony_ci BDX_NIC_NAME, (ndev->if_port == 0) ? 'A' : 'B'); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/************************************************************************* 1288c2ecf20Sopenharmony_ci * Code * 1298c2ecf20Sopenharmony_ci *************************************************************************/ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define bdx_enable_interrupts(priv) \ 1328c2ecf20Sopenharmony_ci do { WRITE_REG(priv, regIMR, IR_RUN); } while (0) 1338c2ecf20Sopenharmony_ci#define bdx_disable_interrupts(priv) \ 1348c2ecf20Sopenharmony_ci do { WRITE_REG(priv, regIMR, 0); } while (0) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/** 1378c2ecf20Sopenharmony_ci * bdx_fifo_init - create TX/RX descriptor fifo for host-NIC communication. 1388c2ecf20Sopenharmony_ci * @priv: NIC private structure 1398c2ecf20Sopenharmony_ci * @f: fifo to initialize 1408c2ecf20Sopenharmony_ci * @fsz_type: fifo size type: 0-4KB, 1-8KB, 2-16KB, 3-32KB 1418c2ecf20Sopenharmony_ci * @reg_CFG0: offsets of registers relative to base address 1428c2ecf20Sopenharmony_ci * @reg_CFG1: offsets of registers relative to base address 1438c2ecf20Sopenharmony_ci * @reg_RPTR: offsets of registers relative to base address 1448c2ecf20Sopenharmony_ci * @reg_WPTR: offsets of registers relative to base address 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * 1K extra space is allocated at the end of the fifo to simplify 1478c2ecf20Sopenharmony_ci * processing of descriptors that wraps around fifo's end 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic int 1538c2ecf20Sopenharmony_cibdx_fifo_init(struct bdx_priv *priv, struct fifo *f, int fsz_type, 1548c2ecf20Sopenharmony_ci u16 reg_CFG0, u16 reg_CFG1, u16 reg_RPTR, u16 reg_WPTR) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci u16 memsz = FIFO_SIZE * (1 << fsz_type); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci memset(f, 0, sizeof(struct fifo)); 1598c2ecf20Sopenharmony_ci /* dma_alloc_coherent gives us 4k-aligned memory */ 1608c2ecf20Sopenharmony_ci f->va = dma_alloc_coherent(&priv->pdev->dev, memsz + FIFO_EXTRA_SPACE, 1618c2ecf20Sopenharmony_ci &f->da, GFP_ATOMIC); 1628c2ecf20Sopenharmony_ci if (!f->va) { 1638c2ecf20Sopenharmony_ci pr_err("dma_alloc_coherent failed\n"); 1648c2ecf20Sopenharmony_ci RET(-ENOMEM); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci f->reg_CFG0 = reg_CFG0; 1678c2ecf20Sopenharmony_ci f->reg_CFG1 = reg_CFG1; 1688c2ecf20Sopenharmony_ci f->reg_RPTR = reg_RPTR; 1698c2ecf20Sopenharmony_ci f->reg_WPTR = reg_WPTR; 1708c2ecf20Sopenharmony_ci f->rptr = 0; 1718c2ecf20Sopenharmony_ci f->wptr = 0; 1728c2ecf20Sopenharmony_ci f->memsz = memsz; 1738c2ecf20Sopenharmony_ci f->size_mask = memsz - 1; 1748c2ecf20Sopenharmony_ci WRITE_REG(priv, reg_CFG0, (u32) ((f->da & TX_RX_CFG0_BASE) | fsz_type)); 1758c2ecf20Sopenharmony_ci WRITE_REG(priv, reg_CFG1, H32_64(f->da)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci RET(0); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * bdx_fifo_free - free all resources used by fifo 1828c2ecf20Sopenharmony_ci * @priv: NIC private structure 1838c2ecf20Sopenharmony_ci * @f: fifo to release 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic void bdx_fifo_free(struct bdx_priv *priv, struct fifo *f) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci ENTER; 1888c2ecf20Sopenharmony_ci if (f->va) { 1898c2ecf20Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 1908c2ecf20Sopenharmony_ci f->memsz + FIFO_EXTRA_SPACE, f->va, f->da); 1918c2ecf20Sopenharmony_ci f->va = NULL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci RET(); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * bdx_link_changed - notifies OS about hw link state. 1988c2ecf20Sopenharmony_ci * @priv: hw adapter structure 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_cistatic void bdx_link_changed(struct bdx_priv *priv) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u32 link = READ_REG(priv, regMAC_LNK_STAT) & MAC_LINK_STAT; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!link) { 2058c2ecf20Sopenharmony_ci if (netif_carrier_ok(priv->ndev)) { 2068c2ecf20Sopenharmony_ci netif_stop_queue(priv->ndev); 2078c2ecf20Sopenharmony_ci netif_carrier_off(priv->ndev); 2088c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "Link Down\n"); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci if (!netif_carrier_ok(priv->ndev)) { 2128c2ecf20Sopenharmony_ci netif_wake_queue(priv->ndev); 2138c2ecf20Sopenharmony_ci netif_carrier_on(priv->ndev); 2148c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "Link Up\n"); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void bdx_isr_extra(struct bdx_priv *priv, u32 isr) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci if (isr & IR_RX_FREE_0) { 2228c2ecf20Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 2238c2ecf20Sopenharmony_ci DBG("RX_FREE_0\n"); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (isr & IR_LNKCHG0) 2278c2ecf20Sopenharmony_ci bdx_link_changed(priv); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (isr & IR_PCIE_LINK) 2308c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "PCI-E Link Fault\n"); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (isr & IR_PCIE_TOUT) 2338c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "PCI-E Time Out\n"); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/** 2388c2ecf20Sopenharmony_ci * bdx_isr_napi - Interrupt Service Routine for Bordeaux NIC 2398c2ecf20Sopenharmony_ci * @irq: interrupt number 2408c2ecf20Sopenharmony_ci * @dev: network device 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Return IRQ_NONE if it was not our interrupt, IRQ_HANDLED - otherwise 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * It reads ISR register to know interrupt reasons, and proceed them one by one. 2458c2ecf20Sopenharmony_ci * Reasons of interest are: 2468c2ecf20Sopenharmony_ci * RX_DESC - new packet has arrived and RXD fifo holds its descriptor 2478c2ecf20Sopenharmony_ci * RX_FREE - number of free Rx buffers in RXF fifo gets low 2488c2ecf20Sopenharmony_ci * TX_FREE - packet was transmited and RXF fifo holds its descriptor 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic irqreturn_t bdx_isr_napi(int irq, void *dev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct net_device *ndev = dev; 2548c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 2558c2ecf20Sopenharmony_ci u32 isr; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ENTER; 2588c2ecf20Sopenharmony_ci isr = (READ_REG(priv, regISR) & IR_RUN); 2598c2ecf20Sopenharmony_ci if (unlikely(!isr)) { 2608c2ecf20Sopenharmony_ci bdx_enable_interrupts(priv); 2618c2ecf20Sopenharmony_ci return IRQ_NONE; /* Not our interrupt */ 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (isr & IR_EXTRA) 2658c2ecf20Sopenharmony_ci bdx_isr_extra(priv, isr); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (isr & (IR_RX_DESC_0 | IR_TX_FREE_0)) { 2688c2ecf20Sopenharmony_ci if (likely(napi_schedule_prep(&priv->napi))) { 2698c2ecf20Sopenharmony_ci __napi_schedule(&priv->napi); 2708c2ecf20Sopenharmony_ci RET(IRQ_HANDLED); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci /* NOTE: we get here if intr has slipped into window 2738c2ecf20Sopenharmony_ci * between these lines in bdx_poll: 2748c2ecf20Sopenharmony_ci * bdx_enable_interrupts(priv); 2758c2ecf20Sopenharmony_ci * return 0; 2768c2ecf20Sopenharmony_ci * currently intrs are disabled (since we read ISR), 2778c2ecf20Sopenharmony_ci * and we have failed to register next poll. 2788c2ecf20Sopenharmony_ci * so we read the regs to trigger chip 2798c2ecf20Sopenharmony_ci * and allow further interupts. */ 2808c2ecf20Sopenharmony_ci READ_REG(priv, regTXF_WPTR_0); 2818c2ecf20Sopenharmony_ci READ_REG(priv, regRXD_WPTR_0); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci bdx_enable_interrupts(priv); 2868c2ecf20Sopenharmony_ci RET(IRQ_HANDLED); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int bdx_poll(struct napi_struct *napi, int budget) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct bdx_priv *priv = container_of(napi, struct bdx_priv, napi); 2928c2ecf20Sopenharmony_ci int work_done; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ENTER; 2958c2ecf20Sopenharmony_ci bdx_tx_cleanup(priv); 2968c2ecf20Sopenharmony_ci work_done = bdx_rx_receive(priv, &priv->rxd_fifo0, budget); 2978c2ecf20Sopenharmony_ci if ((work_done < budget) || 2988c2ecf20Sopenharmony_ci (priv->napi_stop++ >= 30)) { 2998c2ecf20Sopenharmony_ci DBG("rx poll is done. backing to isr-driven\n"); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* from time to time we exit to let NAPI layer release 3028c2ecf20Sopenharmony_ci * device lock and allow waiting tasks (eg rmmod) to advance) */ 3038c2ecf20Sopenharmony_ci priv->napi_stop = 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 3068c2ecf20Sopenharmony_ci bdx_enable_interrupts(priv); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return work_done; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * bdx_fw_load - loads firmware to NIC 3138c2ecf20Sopenharmony_ci * @priv: NIC private structure 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * Firmware is loaded via TXD fifo, so it must be initialized first. 3168c2ecf20Sopenharmony_ci * Firware must be loaded once per NIC not per PCI device provided by NIC (NIC 3178c2ecf20Sopenharmony_ci * can have few of them). So all drivers use semaphore register to choose one 3188c2ecf20Sopenharmony_ci * that will actually load FW to NIC. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int bdx_fw_load(struct bdx_priv *priv) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 3248c2ecf20Sopenharmony_ci int master, i; 3258c2ecf20Sopenharmony_ci int rc; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ENTER; 3288c2ecf20Sopenharmony_ci master = READ_REG(priv, regINIT_SEMAPHORE); 3298c2ecf20Sopenharmony_ci if (!READ_REG(priv, regINIT_STATUS) && master) { 3308c2ecf20Sopenharmony_ci rc = request_firmware(&fw, "tehuti/bdx.bin", &priv->pdev->dev); 3318c2ecf20Sopenharmony_ci if (rc) 3328c2ecf20Sopenharmony_ci goto out; 3338c2ecf20Sopenharmony_ci bdx_tx_push_desc_safe(priv, (char *)fw->data, fw->size); 3348c2ecf20Sopenharmony_ci mdelay(100); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci for (i = 0; i < 200; i++) { 3378c2ecf20Sopenharmony_ci if (READ_REG(priv, regINIT_STATUS)) { 3388c2ecf20Sopenharmony_ci rc = 0; 3398c2ecf20Sopenharmony_ci goto out; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci mdelay(2); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci rc = -EIO; 3448c2ecf20Sopenharmony_ciout: 3458c2ecf20Sopenharmony_ci if (master) 3468c2ecf20Sopenharmony_ci WRITE_REG(priv, regINIT_SEMAPHORE, 1); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci release_firmware(fw); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (rc) { 3518c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "firmware loading failed\n"); 3528c2ecf20Sopenharmony_ci if (rc == -EIO) 3538c2ecf20Sopenharmony_ci DBG("VPC = 0x%x VIC = 0x%x INIT_STATUS = 0x%x i=%d\n", 3548c2ecf20Sopenharmony_ci READ_REG(priv, regVPC), 3558c2ecf20Sopenharmony_ci READ_REG(priv, regVIC), 3568c2ecf20Sopenharmony_ci READ_REG(priv, regINIT_STATUS), i); 3578c2ecf20Sopenharmony_ci RET(rc); 3588c2ecf20Sopenharmony_ci } else { 3598c2ecf20Sopenharmony_ci DBG("%s: firmware loading success\n", priv->ndev->name); 3608c2ecf20Sopenharmony_ci RET(0); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void bdx_restore_mac(struct net_device *ndev, struct bdx_priv *priv) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci u32 val; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ENTER; 3698c2ecf20Sopenharmony_ci DBG("mac0=%x mac1=%x mac2=%x\n", 3708c2ecf20Sopenharmony_ci READ_REG(priv, regUNC_MAC0_A), 3718c2ecf20Sopenharmony_ci READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci val = (ndev->dev_addr[0] << 8) | (ndev->dev_addr[1]); 3748c2ecf20Sopenharmony_ci WRITE_REG(priv, regUNC_MAC2_A, val); 3758c2ecf20Sopenharmony_ci val = (ndev->dev_addr[2] << 8) | (ndev->dev_addr[3]); 3768c2ecf20Sopenharmony_ci WRITE_REG(priv, regUNC_MAC1_A, val); 3778c2ecf20Sopenharmony_ci val = (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5]); 3788c2ecf20Sopenharmony_ci WRITE_REG(priv, regUNC_MAC0_A, val); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci DBG("mac0=%x mac1=%x mac2=%x\n", 3818c2ecf20Sopenharmony_ci READ_REG(priv, regUNC_MAC0_A), 3828c2ecf20Sopenharmony_ci READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); 3838c2ecf20Sopenharmony_ci RET(); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * bdx_hw_start - inits registers and starts HW's Rx and Tx engines 3888c2ecf20Sopenharmony_ci * @priv: NIC private structure 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_cistatic int bdx_hw_start(struct bdx_priv *priv) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int rc = -EIO; 3938c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ENTER; 3968c2ecf20Sopenharmony_ci bdx_link_changed(priv); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 10G overall max length (vlan, eth&ip header, ip payload, crc) */ 3998c2ecf20Sopenharmony_ci WRITE_REG(priv, regFRM_LENGTH, 0X3FE0); 4008c2ecf20Sopenharmony_ci WRITE_REG(priv, regPAUSE_QUANT, 0x96); 4018c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_FIFO_SECTION, 0x800010); 4028c2ecf20Sopenharmony_ci WRITE_REG(priv, regTX_FIFO_SECTION, 0xE00010); 4038c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_FULLNESS, 0); 4048c2ecf20Sopenharmony_ci WRITE_REG(priv, regTX_FULLNESS, 0); 4058c2ecf20Sopenharmony_ci WRITE_REG(priv, regCTRLST, 4068c2ecf20Sopenharmony_ci regCTRLST_BASE | regCTRLST_RX_ENA | regCTRLST_TX_ENA); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci WRITE_REG(priv, regVGLB, 0); 4098c2ecf20Sopenharmony_ci WRITE_REG(priv, regMAX_FRAME_A, 4108c2ecf20Sopenharmony_ci priv->rxf_fifo0.m.pktsz & MAX_FRAME_AB_VAL); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci DBG("RDINTCM=%08x\n", priv->rdintcm); /*NOTE: test script uses this */ 4138c2ecf20Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, priv->rdintcm); 4148c2ecf20Sopenharmony_ci WRITE_REG(priv, regRDINTCM2, 0); /*cpu_to_le32(rcm.val)); */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci DBG("TDINTCM=%08x\n", priv->tdintcm); /*NOTE: test script uses this */ 4178c2ecf20Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, priv->tdintcm); /* old val = 0x300064 */ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Enable timer interrupt once in 2 secs. */ 4208c2ecf20Sopenharmony_ci /*WRITE_REG(priv, regGTMR0, ((GTMR_SEC * 2) & GTMR_DATA)); */ 4218c2ecf20Sopenharmony_ci bdx_restore_mac(priv->ndev, priv); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, GMAC_RX_FILTER_OSEN | 4248c2ecf20Sopenharmony_ci GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci#define BDX_IRQ_TYPE ((priv->nic->irq_type == IRQ_MSI) ? 0 : IRQF_SHARED) 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci rc = request_irq(priv->pdev->irq, bdx_isr_napi, BDX_IRQ_TYPE, 4298c2ecf20Sopenharmony_ci ndev->name, ndev); 4308c2ecf20Sopenharmony_ci if (rc) 4318c2ecf20Sopenharmony_ci goto err_irq; 4328c2ecf20Sopenharmony_ci bdx_enable_interrupts(priv); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci RET(0); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cierr_irq: 4378c2ecf20Sopenharmony_ci RET(rc); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic void bdx_hw_stop(struct bdx_priv *priv) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci ENTER; 4438c2ecf20Sopenharmony_ci bdx_disable_interrupts(priv); 4448c2ecf20Sopenharmony_ci free_irq(priv->pdev->irq, priv->ndev); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci netif_carrier_off(priv->ndev); 4478c2ecf20Sopenharmony_ci netif_stop_queue(priv->ndev); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci RET(); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int bdx_hw_reset_direct(void __iomem *regs) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci u32 val, i; 4558c2ecf20Sopenharmony_ci ENTER; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* reset sequences: read, write 1, read, write 0 */ 4588c2ecf20Sopenharmony_ci val = readl(regs + regCLKPLL); 4598c2ecf20Sopenharmony_ci writel((val | CLKPLL_SFTRST) + 0x8, regs + regCLKPLL); 4608c2ecf20Sopenharmony_ci udelay(50); 4618c2ecf20Sopenharmony_ci val = readl(regs + regCLKPLL); 4628c2ecf20Sopenharmony_ci writel(val & ~CLKPLL_SFTRST, regs + regCLKPLL); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* check that the PLLs are locked and reset ended */ 4658c2ecf20Sopenharmony_ci for (i = 0; i < 70; i++, mdelay(10)) 4668c2ecf20Sopenharmony_ci if ((readl(regs + regCLKPLL) & CLKPLL_LKD) == CLKPLL_LKD) { 4678c2ecf20Sopenharmony_ci /* do any PCI-E read transaction */ 4688c2ecf20Sopenharmony_ci readl(regs + regRXD_CFG0_0); 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci pr_err("HW reset failed\n"); 4728c2ecf20Sopenharmony_ci return 1; /* failure */ 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int bdx_hw_reset(struct bdx_priv *priv) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci u32 val, i; 4788c2ecf20Sopenharmony_ci ENTER; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (priv->port == 0) { 4818c2ecf20Sopenharmony_ci /* reset sequences: read, write 1, read, write 0 */ 4828c2ecf20Sopenharmony_ci val = READ_REG(priv, regCLKPLL); 4838c2ecf20Sopenharmony_ci WRITE_REG(priv, regCLKPLL, (val | CLKPLL_SFTRST) + 0x8); 4848c2ecf20Sopenharmony_ci udelay(50); 4858c2ecf20Sopenharmony_ci val = READ_REG(priv, regCLKPLL); 4868c2ecf20Sopenharmony_ci WRITE_REG(priv, regCLKPLL, val & ~CLKPLL_SFTRST); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci /* check that the PLLs are locked and reset ended */ 4898c2ecf20Sopenharmony_ci for (i = 0; i < 70; i++, mdelay(10)) 4908c2ecf20Sopenharmony_ci if ((READ_REG(priv, regCLKPLL) & CLKPLL_LKD) == CLKPLL_LKD) { 4918c2ecf20Sopenharmony_ci /* do any PCI-E read transaction */ 4928c2ecf20Sopenharmony_ci READ_REG(priv, regRXD_CFG0_0); 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci pr_err("HW reset failed\n"); 4968c2ecf20Sopenharmony_ci return 1; /* failure */ 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int bdx_sw_reset(struct bdx_priv *priv) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci int i; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ENTER; 5048c2ecf20Sopenharmony_ci /* 1. load MAC (obsolete) */ 5058c2ecf20Sopenharmony_ci /* 2. disable Rx (and Tx) */ 5068c2ecf20Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, 0); 5078c2ecf20Sopenharmony_ci mdelay(100); 5088c2ecf20Sopenharmony_ci /* 3. disable port */ 5098c2ecf20Sopenharmony_ci WRITE_REG(priv, regDIS_PORT, 1); 5108c2ecf20Sopenharmony_ci /* 4. disable queue */ 5118c2ecf20Sopenharmony_ci WRITE_REG(priv, regDIS_QU, 1); 5128c2ecf20Sopenharmony_ci /* 5. wait until hw is disabled */ 5138c2ecf20Sopenharmony_ci for (i = 0; i < 50; i++) { 5148c2ecf20Sopenharmony_ci if (READ_REG(priv, regRST_PORT) & 1) 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci mdelay(10); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci if (i == 50) 5198c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "SW reset timeout. continuing anyway\n"); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* 6. disable intrs */ 5228c2ecf20Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, 0); 5238c2ecf20Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, 0); 5248c2ecf20Sopenharmony_ci WRITE_REG(priv, regIMR, 0); 5258c2ecf20Sopenharmony_ci READ_REG(priv, regISR); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* 7. reset queue */ 5288c2ecf20Sopenharmony_ci WRITE_REG(priv, regRST_QU, 1); 5298c2ecf20Sopenharmony_ci /* 8. reset port */ 5308c2ecf20Sopenharmony_ci WRITE_REG(priv, regRST_PORT, 1); 5318c2ecf20Sopenharmony_ci /* 9. zero all read and write pointers */ 5328c2ecf20Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 5338c2ecf20Sopenharmony_ci DBG("%x = %x\n", i, READ_REG(priv, i) & TXF_WPTR_WR_PTR); 5348c2ecf20Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 5358c2ecf20Sopenharmony_ci WRITE_REG(priv, i, 0); 5368c2ecf20Sopenharmony_ci /* 10. unseet port disable */ 5378c2ecf20Sopenharmony_ci WRITE_REG(priv, regDIS_PORT, 0); 5388c2ecf20Sopenharmony_ci /* 11. unset queue disable */ 5398c2ecf20Sopenharmony_ci WRITE_REG(priv, regDIS_QU, 0); 5408c2ecf20Sopenharmony_ci /* 12. unset queue reset */ 5418c2ecf20Sopenharmony_ci WRITE_REG(priv, regRST_QU, 0); 5428c2ecf20Sopenharmony_ci /* 13. unset port reset */ 5438c2ecf20Sopenharmony_ci WRITE_REG(priv, regRST_PORT, 0); 5448c2ecf20Sopenharmony_ci /* 14. enable Rx */ 5458c2ecf20Sopenharmony_ci /* skiped. will be done later */ 5468c2ecf20Sopenharmony_ci /* 15. save MAC (obsolete) */ 5478c2ecf20Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 5488c2ecf20Sopenharmony_ci DBG("%x = %x\n", i, READ_REG(priv, i) & TXF_WPTR_WR_PTR); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci RET(0); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* bdx_reset - performs right type of reset depending on hw type */ 5548c2ecf20Sopenharmony_cistatic int bdx_reset(struct bdx_priv *priv) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci ENTER; 5578c2ecf20Sopenharmony_ci RET((priv->pdev->device == 0x3009) 5588c2ecf20Sopenharmony_ci ? bdx_hw_reset(priv) 5598c2ecf20Sopenharmony_ci : bdx_sw_reset(priv)); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/** 5638c2ecf20Sopenharmony_ci * bdx_close - Disables a network interface 5648c2ecf20Sopenharmony_ci * @ndev: network interface device structure 5658c2ecf20Sopenharmony_ci * 5668c2ecf20Sopenharmony_ci * Returns 0, this is not allowed to fail 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * The close entry point is called when an interface is de-activated 5698c2ecf20Sopenharmony_ci * by the OS. The hardware is still under the drivers control, but 5708c2ecf20Sopenharmony_ci * needs to be disabled. A global MAC reset is issued to stop the 5718c2ecf20Sopenharmony_ci * hardware, and all transmit and receive resources are freed. 5728c2ecf20Sopenharmony_ci **/ 5738c2ecf20Sopenharmony_cistatic int bdx_close(struct net_device *ndev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct bdx_priv *priv = NULL; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ENTER; 5788c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci bdx_reset(priv); 5838c2ecf20Sopenharmony_ci bdx_hw_stop(priv); 5848c2ecf20Sopenharmony_ci bdx_rx_free(priv); 5858c2ecf20Sopenharmony_ci bdx_tx_free(priv); 5868c2ecf20Sopenharmony_ci RET(0); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/** 5908c2ecf20Sopenharmony_ci * bdx_open - Called when a network interface is made active 5918c2ecf20Sopenharmony_ci * @ndev: network interface device structure 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * The open entry point is called when a network interface is made 5968c2ecf20Sopenharmony_ci * active by the system (IFF_UP). At this point all resources needed 5978c2ecf20Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt 5988c2ecf20Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started, 5998c2ecf20Sopenharmony_ci * and the stack is notified that the interface is ready. 6008c2ecf20Sopenharmony_ci **/ 6018c2ecf20Sopenharmony_cistatic int bdx_open(struct net_device *ndev) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct bdx_priv *priv; 6048c2ecf20Sopenharmony_ci int rc; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ENTER; 6078c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 6088c2ecf20Sopenharmony_ci bdx_reset(priv); 6098c2ecf20Sopenharmony_ci if (netif_running(ndev)) 6108c2ecf20Sopenharmony_ci netif_stop_queue(priv->ndev); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if ((rc = bdx_tx_init(priv)) || 6138c2ecf20Sopenharmony_ci (rc = bdx_rx_init(priv)) || 6148c2ecf20Sopenharmony_ci (rc = bdx_fw_load(priv))) 6158c2ecf20Sopenharmony_ci goto err; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci rc = bdx_hw_start(priv); 6208c2ecf20Sopenharmony_ci if (rc) 6218c2ecf20Sopenharmony_ci goto err; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci print_fw_id(priv->nic); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci RET(0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cierr: 6308c2ecf20Sopenharmony_ci bdx_close(ndev); 6318c2ecf20Sopenharmony_ci RET(rc); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic int bdx_range_check(struct bdx_priv *priv, u32 offset) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci return (offset > (u32) (BDX_REGS_SIZE / priv->nic->port_num)) ? 6378c2ecf20Sopenharmony_ci -EINVAL : 0; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 6438c2ecf20Sopenharmony_ci u32 data[3]; 6448c2ecf20Sopenharmony_ci int error; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ENTER; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci DBG("jiffies=%ld cmd=%d\n", jiffies, cmd); 6498c2ecf20Sopenharmony_ci if (cmd != SIOCDEVPRIVATE) { 6508c2ecf20Sopenharmony_ci error = copy_from_user(data, ifr->ifr_data, sizeof(data)); 6518c2ecf20Sopenharmony_ci if (error) { 6528c2ecf20Sopenharmony_ci pr_err("can't copy from user\n"); 6538c2ecf20Sopenharmony_ci RET(-EFAULT); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci DBG("%d 0x%x 0x%x\n", data[0], data[1], data[2]); 6568c2ecf20Sopenharmony_ci } else { 6578c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 6618c2ecf20Sopenharmony_ci return -EPERM; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci switch (data[0]) { 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci case BDX_OP_READ: 6668c2ecf20Sopenharmony_ci error = bdx_range_check(priv, data[1]); 6678c2ecf20Sopenharmony_ci if (error < 0) 6688c2ecf20Sopenharmony_ci return error; 6698c2ecf20Sopenharmony_ci data[2] = READ_REG(priv, data[1]); 6708c2ecf20Sopenharmony_ci DBG("read_reg(0x%x)=0x%x (dec %d)\n", data[1], data[2], 6718c2ecf20Sopenharmony_ci data[2]); 6728c2ecf20Sopenharmony_ci error = copy_to_user(ifr->ifr_data, data, sizeof(data)); 6738c2ecf20Sopenharmony_ci if (error) 6748c2ecf20Sopenharmony_ci RET(-EFAULT); 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci case BDX_OP_WRITE: 6788c2ecf20Sopenharmony_ci error = bdx_range_check(priv, data[1]); 6798c2ecf20Sopenharmony_ci if (error < 0) 6808c2ecf20Sopenharmony_ci return error; 6818c2ecf20Sopenharmony_ci WRITE_REG(priv, data[1], data[2]); 6828c2ecf20Sopenharmony_ci DBG("write_reg(0x%x, 0x%x)\n", data[1], data[2]); 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci default: 6868c2ecf20Sopenharmony_ci RET(-EOPNOTSUPP); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci return 0; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic int bdx_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci ENTER; 6948c2ecf20Sopenharmony_ci if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) 6958c2ecf20Sopenharmony_ci RET(bdx_ioctl_priv(ndev, ifr, cmd)); 6968c2ecf20Sopenharmony_ci else 6978c2ecf20Sopenharmony_ci RET(-EOPNOTSUPP); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/** 7018c2ecf20Sopenharmony_ci * __bdx_vlan_rx_vid - private helper for adding/killing VLAN vid 7028c2ecf20Sopenharmony_ci * @ndev: network device 7038c2ecf20Sopenharmony_ci * @vid: VLAN vid 7048c2ecf20Sopenharmony_ci * @enable: enable or disable vlan 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * Passes VLAN filter table to hardware 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_cistatic void __bdx_vlan_rx_vid(struct net_device *ndev, uint16_t vid, int enable) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 7118c2ecf20Sopenharmony_ci u32 reg, bit, val; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ENTER; 7148c2ecf20Sopenharmony_ci DBG2("vid=%d value=%d\n", (int)vid, enable); 7158c2ecf20Sopenharmony_ci if (unlikely(vid >= 4096)) { 7168c2ecf20Sopenharmony_ci pr_err("invalid VID: %u (> 4096)\n", vid); 7178c2ecf20Sopenharmony_ci RET(); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci reg = regVLAN_0 + (vid / 32) * 4; 7208c2ecf20Sopenharmony_ci bit = 1 << vid % 32; 7218c2ecf20Sopenharmony_ci val = READ_REG(priv, reg); 7228c2ecf20Sopenharmony_ci DBG2("reg=%x, val=%x, bit=%d\n", reg, val, bit); 7238c2ecf20Sopenharmony_ci if (enable) 7248c2ecf20Sopenharmony_ci val |= bit; 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci val &= ~bit; 7278c2ecf20Sopenharmony_ci DBG2("new val %x\n", val); 7288c2ecf20Sopenharmony_ci WRITE_REG(priv, reg, val); 7298c2ecf20Sopenharmony_ci RET(); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/** 7338c2ecf20Sopenharmony_ci * bdx_vlan_rx_add_vid - kernel hook for adding VLAN vid to hw filtering table 7348c2ecf20Sopenharmony_ci * @ndev: network device 7358c2ecf20Sopenharmony_ci * @proto: unused 7368c2ecf20Sopenharmony_ci * @vid: VLAN vid to add 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_cistatic int bdx_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci __bdx_vlan_rx_vid(ndev, vid, 1); 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/** 7458c2ecf20Sopenharmony_ci * bdx_vlan_rx_kill_vid - kernel hook for killing VLAN vid in hw filtering table 7468c2ecf20Sopenharmony_ci * @ndev: network device 7478c2ecf20Sopenharmony_ci * @proto: unused 7488c2ecf20Sopenharmony_ci * @vid: VLAN vid to kill 7498c2ecf20Sopenharmony_ci */ 7508c2ecf20Sopenharmony_cistatic int bdx_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci __bdx_vlan_rx_vid(ndev, vid, 0); 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci/** 7578c2ecf20Sopenharmony_ci * bdx_change_mtu - Change the Maximum Transfer Unit 7588c2ecf20Sopenharmony_ci * @ndev: network interface device structure 7598c2ecf20Sopenharmony_ci * @new_mtu: new value for maximum frame size 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_cistatic int bdx_change_mtu(struct net_device *ndev, int new_mtu) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci ENTER; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ndev->mtu = new_mtu; 7688c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 7698c2ecf20Sopenharmony_ci bdx_close(ndev); 7708c2ecf20Sopenharmony_ci bdx_open(ndev); 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci RET(0); 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic void bdx_setmulti(struct net_device *ndev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci u32 rxf_val = 7808c2ecf20Sopenharmony_ci GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB | GMAC_RX_FILTER_OSEN; 7818c2ecf20Sopenharmony_ci int i; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci ENTER; 7848c2ecf20Sopenharmony_ci /* IMF - imperfect (hash) rx multicat filter */ 7858c2ecf20Sopenharmony_ci /* PMF - perfect rx multicat filter */ 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* FIXME: RXE(OFF) */ 7888c2ecf20Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 7898c2ecf20Sopenharmony_ci rxf_val |= GMAC_RX_FILTER_PRM; 7908c2ecf20Sopenharmony_ci } else if (ndev->flags & IFF_ALLMULTI) { 7918c2ecf20Sopenharmony_ci /* set IMF to accept all multicast frmaes */ 7928c2ecf20Sopenharmony_ci for (i = 0; i < MAC_MCST_HASH_NUM; i++) 7938c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_MCST_HASH0 + i * 4, ~0); 7948c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(ndev)) { 7958c2ecf20Sopenharmony_ci u8 hash; 7968c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 7978c2ecf20Sopenharmony_ci u32 reg, val; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* set IMF to deny all multicast frames */ 8008c2ecf20Sopenharmony_ci for (i = 0; i < MAC_MCST_HASH_NUM; i++) 8018c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_MCST_HASH0 + i * 4, 0); 8028c2ecf20Sopenharmony_ci /* set PMF to deny all multicast frames */ 8038c2ecf20Sopenharmony_ci for (i = 0; i < MAC_MCST_NUM; i++) { 8048c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_MAC_MCST0 + i * 8, 0); 8058c2ecf20Sopenharmony_ci WRITE_REG(priv, regRX_MAC_MCST1 + i * 8, 0); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* use PMF to accept first MAC_MCST_NUM (15) addresses */ 8098c2ecf20Sopenharmony_ci /* TBD: sort addresses and write them in ascending order 8108c2ecf20Sopenharmony_ci * into RX_MAC_MCST regs. we skip this phase now and accept ALL 8118c2ecf20Sopenharmony_ci * multicast frames throu IMF */ 8128c2ecf20Sopenharmony_ci /* accept the rest of addresses throu IMF */ 8138c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 8148c2ecf20Sopenharmony_ci hash = 0; 8158c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 8168c2ecf20Sopenharmony_ci hash ^= ha->addr[i]; 8178c2ecf20Sopenharmony_ci reg = regRX_MCST_HASH0 + ((hash >> 5) << 2); 8188c2ecf20Sopenharmony_ci val = READ_REG(priv, reg); 8198c2ecf20Sopenharmony_ci val |= (1 << (hash % 32)); 8208c2ecf20Sopenharmony_ci WRITE_REG(priv, reg, val); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci } else { 8248c2ecf20Sopenharmony_ci DBG("only own mac %d\n", netdev_mc_count(ndev)); 8258c2ecf20Sopenharmony_ci rxf_val |= GMAC_RX_FILTER_AB; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, rxf_val); 8288c2ecf20Sopenharmony_ci /* enable RX */ 8298c2ecf20Sopenharmony_ci /* FIXME: RXE(ON) */ 8308c2ecf20Sopenharmony_ci RET(); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int bdx_set_mac(struct net_device *ndev, void *p) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 8368c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ENTER; 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci if (netif_running(dev)) 8418c2ecf20Sopenharmony_ci return -EBUSY 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 8448c2ecf20Sopenharmony_ci bdx_restore_mac(ndev, priv); 8458c2ecf20Sopenharmony_ci RET(0); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int bdx_read_mac(struct bdx_priv *priv) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci u16 macAddress[3], i; 8518c2ecf20Sopenharmony_ci ENTER; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci macAddress[2] = READ_REG(priv, regUNC_MAC0_A); 8548c2ecf20Sopenharmony_ci macAddress[2] = READ_REG(priv, regUNC_MAC0_A); 8558c2ecf20Sopenharmony_ci macAddress[1] = READ_REG(priv, regUNC_MAC1_A); 8568c2ecf20Sopenharmony_ci macAddress[1] = READ_REG(priv, regUNC_MAC1_A); 8578c2ecf20Sopenharmony_ci macAddress[0] = READ_REG(priv, regUNC_MAC2_A); 8588c2ecf20Sopenharmony_ci macAddress[0] = READ_REG(priv, regUNC_MAC2_A); 8598c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 8608c2ecf20Sopenharmony_ci priv->ndev->dev_addr[i * 2 + 1] = macAddress[i]; 8618c2ecf20Sopenharmony_ci priv->ndev->dev_addr[i * 2] = macAddress[i] >> 8; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci RET(0); 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic u64 bdx_read_l2stat(struct bdx_priv *priv, int reg) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci u64 val; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci val = READ_REG(priv, reg); 8718c2ecf20Sopenharmony_ci val |= ((u64) READ_REG(priv, reg + 8)) << 32; 8728c2ecf20Sopenharmony_ci return val; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/*Do the statistics-update work*/ 8768c2ecf20Sopenharmony_cistatic void bdx_update_stats(struct bdx_priv *priv) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct bdx_stats *stats = &priv->hw_stats; 8798c2ecf20Sopenharmony_ci u64 *stats_vector = (u64 *) stats; 8808c2ecf20Sopenharmony_ci int i; 8818c2ecf20Sopenharmony_ci int addr; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /*Fill HW structure */ 8848c2ecf20Sopenharmony_ci addr = 0x7200; 8858c2ecf20Sopenharmony_ci /*First 12 statistics - 0x7200 - 0x72B0 */ 8868c2ecf20Sopenharmony_ci for (i = 0; i < 12; i++) { 8878c2ecf20Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 8888c2ecf20Sopenharmony_ci addr += 0x10; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci BDX_ASSERT(addr != 0x72C0); 8918c2ecf20Sopenharmony_ci /* 0x72C0-0x72E0 RSRV */ 8928c2ecf20Sopenharmony_ci addr = 0x72F0; 8938c2ecf20Sopenharmony_ci for (; i < 16; i++) { 8948c2ecf20Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 8958c2ecf20Sopenharmony_ci addr += 0x10; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci BDX_ASSERT(addr != 0x7330); 8988c2ecf20Sopenharmony_ci /* 0x7330-0x7360 RSRV */ 8998c2ecf20Sopenharmony_ci addr = 0x7370; 9008c2ecf20Sopenharmony_ci for (; i < 19; i++) { 9018c2ecf20Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 9028c2ecf20Sopenharmony_ci addr += 0x10; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci BDX_ASSERT(addr != 0x73A0); 9058c2ecf20Sopenharmony_ci /* 0x73A0-0x73B0 RSRV */ 9068c2ecf20Sopenharmony_ci addr = 0x73C0; 9078c2ecf20Sopenharmony_ci for (; i < 23; i++) { 9088c2ecf20Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 9098c2ecf20Sopenharmony_ci addr += 0x10; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci BDX_ASSERT(addr != 0x7400); 9128c2ecf20Sopenharmony_ci BDX_ASSERT((sizeof(struct bdx_stats) / sizeof(u64)) != i); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic void print_rxdd(struct rxd_desc *rxdd, u32 rxd_val1, u16 len, 9168c2ecf20Sopenharmony_ci u16 rxd_vlan); 9178c2ecf20Sopenharmony_cistatic void print_rxfd(struct rxf_desc *rxfd); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci/************************************************************************* 9208c2ecf20Sopenharmony_ci * Rx DB * 9218c2ecf20Sopenharmony_ci *************************************************************************/ 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic void bdx_rxdb_destroy(struct rxdb *db) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci vfree(db); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic struct rxdb *bdx_rxdb_create(int nelem) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct rxdb *db; 9318c2ecf20Sopenharmony_ci int i; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci db = vmalloc(sizeof(struct rxdb) 9348c2ecf20Sopenharmony_ci + (nelem * sizeof(int)) 9358c2ecf20Sopenharmony_ci + (nelem * sizeof(struct rx_map))); 9368c2ecf20Sopenharmony_ci if (likely(db != NULL)) { 9378c2ecf20Sopenharmony_ci db->stack = (int *)(db + 1); 9388c2ecf20Sopenharmony_ci db->elems = (void *)(db->stack + nelem); 9398c2ecf20Sopenharmony_ci db->nelem = nelem; 9408c2ecf20Sopenharmony_ci db->top = nelem; 9418c2ecf20Sopenharmony_ci for (i = 0; i < nelem; i++) 9428c2ecf20Sopenharmony_ci db->stack[i] = nelem - i - 1; /* to make first allocs 9438c2ecf20Sopenharmony_ci close to db struct*/ 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return db; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic inline int bdx_rxdb_alloc_elem(struct rxdb *db) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci BDX_ASSERT(db->top <= 0); 9528c2ecf20Sopenharmony_ci return db->stack[--(db->top)]; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic inline void *bdx_rxdb_addr_elem(struct rxdb *db, int n) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci BDX_ASSERT((n < 0) || (n >= db->nelem)); 9588c2ecf20Sopenharmony_ci return db->elems + n; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic inline int bdx_rxdb_available(struct rxdb *db) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci return db->top; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic inline void bdx_rxdb_free_elem(struct rxdb *db, int n) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci BDX_ASSERT((n >= db->nelem) || (n < 0)); 9698c2ecf20Sopenharmony_ci db->stack[(db->top)++] = n; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci/************************************************************************* 9738c2ecf20Sopenharmony_ci * Rx Init * 9748c2ecf20Sopenharmony_ci *************************************************************************/ 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/** 9778c2ecf20Sopenharmony_ci * bdx_rx_init - initialize RX all related HW and SW resources 9788c2ecf20Sopenharmony_ci * @priv: NIC private structure 9798c2ecf20Sopenharmony_ci * 9808c2ecf20Sopenharmony_ci * Returns 0 on success, negative value on failure 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * It creates rxf and rxd fifos, update relevant HW registers, preallocate 9838c2ecf20Sopenharmony_ci * skb for rx. It assumes that Rx is desabled in HW 9848c2ecf20Sopenharmony_ci * funcs are grouped for better cache usage 9858c2ecf20Sopenharmony_ci * 9868c2ecf20Sopenharmony_ci * RxD fifo is smaller than RxF fifo by design. Upon high load, RxD will be 9878c2ecf20Sopenharmony_ci * filled and packets will be dropped by nic without getting into host or 9888c2ecf20Sopenharmony_ci * cousing interrupt. Anyway, in that condition, host has no chance to process 9898c2ecf20Sopenharmony_ci * all packets, but dropping in nic is cheaper, since it takes 0 cpu cycles 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci/* TBD: ensure proper packet size */ 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int bdx_rx_init(struct bdx_priv *priv) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci ENTER; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (bdx_fifo_init(priv, &priv->rxd_fifo0.m, priv->rxd_size, 9998c2ecf20Sopenharmony_ci regRXD_CFG0_0, regRXD_CFG1_0, 10008c2ecf20Sopenharmony_ci regRXD_RPTR_0, regRXD_WPTR_0)) 10018c2ecf20Sopenharmony_ci goto err_mem; 10028c2ecf20Sopenharmony_ci if (bdx_fifo_init(priv, &priv->rxf_fifo0.m, priv->rxf_size, 10038c2ecf20Sopenharmony_ci regRXF_CFG0_0, regRXF_CFG1_0, 10048c2ecf20Sopenharmony_ci regRXF_RPTR_0, regRXF_WPTR_0)) 10058c2ecf20Sopenharmony_ci goto err_mem; 10068c2ecf20Sopenharmony_ci priv->rxdb = bdx_rxdb_create(priv->rxf_fifo0.m.memsz / 10078c2ecf20Sopenharmony_ci sizeof(struct rxf_desc)); 10088c2ecf20Sopenharmony_ci if (!priv->rxdb) 10098c2ecf20Sopenharmony_ci goto err_mem; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci priv->rxf_fifo0.m.pktsz = priv->ndev->mtu + VLAN_ETH_HLEN; 10128c2ecf20Sopenharmony_ci return 0; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cierr_mem: 10158c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "Rx init failed\n"); 10168c2ecf20Sopenharmony_ci return -ENOMEM; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci/** 10208c2ecf20Sopenharmony_ci * bdx_rx_free_skbs - frees and unmaps all skbs allocated for the fifo 10218c2ecf20Sopenharmony_ci * @priv: NIC private structure 10228c2ecf20Sopenharmony_ci * @f: RXF fifo 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_cistatic void bdx_rx_free_skbs(struct bdx_priv *priv, struct rxf_fifo *f) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct rx_map *dm; 10278c2ecf20Sopenharmony_ci struct rxdb *db = priv->rxdb; 10288c2ecf20Sopenharmony_ci u16 i; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci ENTER; 10318c2ecf20Sopenharmony_ci DBG("total=%d free=%d busy=%d\n", db->nelem, bdx_rxdb_available(db), 10328c2ecf20Sopenharmony_ci db->nelem - bdx_rxdb_available(db)); 10338c2ecf20Sopenharmony_ci while (bdx_rxdb_available(db) > 0) { 10348c2ecf20Sopenharmony_ci i = bdx_rxdb_alloc_elem(db); 10358c2ecf20Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, i); 10368c2ecf20Sopenharmony_ci dm->dma = 0; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci for (i = 0; i < db->nelem; i++) { 10398c2ecf20Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, i); 10408c2ecf20Sopenharmony_ci if (dm->dma) { 10418c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, dm->dma, 10428c2ecf20Sopenharmony_ci f->m.pktsz, DMA_FROM_DEVICE); 10438c2ecf20Sopenharmony_ci dev_kfree_skb(dm->skb); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/** 10498c2ecf20Sopenharmony_ci * bdx_rx_free - release all Rx resources 10508c2ecf20Sopenharmony_ci * @priv: NIC private structure 10518c2ecf20Sopenharmony_ci * 10528c2ecf20Sopenharmony_ci * It assumes that Rx is desabled in HW 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_cistatic void bdx_rx_free(struct bdx_priv *priv) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci ENTER; 10578c2ecf20Sopenharmony_ci if (priv->rxdb) { 10588c2ecf20Sopenharmony_ci bdx_rx_free_skbs(priv, &priv->rxf_fifo0); 10598c2ecf20Sopenharmony_ci bdx_rxdb_destroy(priv->rxdb); 10608c2ecf20Sopenharmony_ci priv->rxdb = NULL; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci bdx_fifo_free(priv, &priv->rxf_fifo0.m); 10638c2ecf20Sopenharmony_ci bdx_fifo_free(priv, &priv->rxd_fifo0.m); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci RET(); 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/************************************************************************* 10698c2ecf20Sopenharmony_ci * Rx Engine * 10708c2ecf20Sopenharmony_ci *************************************************************************/ 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci/** 10738c2ecf20Sopenharmony_ci * bdx_rx_alloc_skbs - fill rxf fifo with new skbs 10748c2ecf20Sopenharmony_ci * @priv: nic's private structure 10758c2ecf20Sopenharmony_ci * @f: RXF fifo that needs skbs 10768c2ecf20Sopenharmony_ci * 10778c2ecf20Sopenharmony_ci * It allocates skbs, build rxf descs and push it (rxf descr) into rxf fifo. 10788c2ecf20Sopenharmony_ci * skb's virtual and physical addresses are stored in skb db. 10798c2ecf20Sopenharmony_ci * To calculate free space, func uses cached values of RPTR and WPTR 10808c2ecf20Sopenharmony_ci * When needed, it also updates RPTR and WPTR. 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci/* TBD: do not update WPTR if no desc were written */ 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci struct sk_buff *skb; 10888c2ecf20Sopenharmony_ci struct rxf_desc *rxfd; 10898c2ecf20Sopenharmony_ci struct rx_map *dm; 10908c2ecf20Sopenharmony_ci int dno, delta, idx; 10918c2ecf20Sopenharmony_ci struct rxdb *db = priv->rxdb; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ENTER; 10948c2ecf20Sopenharmony_ci dno = bdx_rxdb_available(db) - 1; 10958c2ecf20Sopenharmony_ci while (dno > 0) { 10968c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(priv->ndev, f->m.pktsz + NET_IP_ALIGN); 10978c2ecf20Sopenharmony_ci if (!skb) 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci idx = bdx_rxdb_alloc_elem(db); 11038c2ecf20Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, idx); 11048c2ecf20Sopenharmony_ci dm->dma = dma_map_single(&priv->pdev->dev, skb->data, 11058c2ecf20Sopenharmony_ci f->m.pktsz, DMA_FROM_DEVICE); 11068c2ecf20Sopenharmony_ci dm->skb = skb; 11078c2ecf20Sopenharmony_ci rxfd = (struct rxf_desc *)(f->m.va + f->m.wptr); 11088c2ecf20Sopenharmony_ci rxfd->info = CPU_CHIP_SWAP32(0x10003); /* INFO=1 BC=3 */ 11098c2ecf20Sopenharmony_ci rxfd->va_lo = idx; 11108c2ecf20Sopenharmony_ci rxfd->pa_lo = CPU_CHIP_SWAP32(L32_64(dm->dma)); 11118c2ecf20Sopenharmony_ci rxfd->pa_hi = CPU_CHIP_SWAP32(H32_64(dm->dma)); 11128c2ecf20Sopenharmony_ci rxfd->len = CPU_CHIP_SWAP32(f->m.pktsz); 11138c2ecf20Sopenharmony_ci print_rxfd(rxfd); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci f->m.wptr += sizeof(struct rxf_desc); 11168c2ecf20Sopenharmony_ci delta = f->m.wptr - f->m.memsz; 11178c2ecf20Sopenharmony_ci if (unlikely(delta >= 0)) { 11188c2ecf20Sopenharmony_ci f->m.wptr = delta; 11198c2ecf20Sopenharmony_ci if (delta > 0) { 11208c2ecf20Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, delta); 11218c2ecf20Sopenharmony_ci DBG("wrapped descriptor\n"); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci dno--; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci /*TBD: to do - delayed rxf wptr like in txd */ 11278c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 11288c2ecf20Sopenharmony_ci RET(); 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic inline void 11328c2ecf20Sopenharmony_ciNETIF_RX_MUX(struct bdx_priv *priv, u32 rxd_val1, u16 rxd_vlan, 11338c2ecf20Sopenharmony_ci struct sk_buff *skb) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci ENTER; 11368c2ecf20Sopenharmony_ci DBG("rxdd->flags.bits.vtag=%d\n", GET_RXD_VTAG(rxd_val1)); 11378c2ecf20Sopenharmony_ci if (GET_RXD_VTAG(rxd_val1)) { 11388c2ecf20Sopenharmony_ci DBG("%s: vlan rcv vlan '%x' vtag '%x'\n", 11398c2ecf20Sopenharmony_ci priv->ndev->name, 11408c2ecf20Sopenharmony_ci GET_RXD_VLAN_ID(rxd_vlan), 11418c2ecf20Sopenharmony_ci GET_RXD_VTAG(rxd_val1)); 11428c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), GET_RXD_VLAN_TCI(rxd_vlan)); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci netif_receive_skb(skb); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void bdx_recycle_skb(struct bdx_priv *priv, struct rxd_desc *rxdd) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct rxf_desc *rxfd; 11508c2ecf20Sopenharmony_ci struct rx_map *dm; 11518c2ecf20Sopenharmony_ci struct rxf_fifo *f; 11528c2ecf20Sopenharmony_ci struct rxdb *db; 11538c2ecf20Sopenharmony_ci int delta; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci ENTER; 11568c2ecf20Sopenharmony_ci DBG("priv=%p rxdd=%p\n", priv, rxdd); 11578c2ecf20Sopenharmony_ci f = &priv->rxf_fifo0; 11588c2ecf20Sopenharmony_ci db = priv->rxdb; 11598c2ecf20Sopenharmony_ci DBG("db=%p f=%p\n", db, f); 11608c2ecf20Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, rxdd->va_lo); 11618c2ecf20Sopenharmony_ci DBG("dm=%p\n", dm); 11628c2ecf20Sopenharmony_ci rxfd = (struct rxf_desc *)(f->m.va + f->m.wptr); 11638c2ecf20Sopenharmony_ci rxfd->info = CPU_CHIP_SWAP32(0x10003); /* INFO=1 BC=3 */ 11648c2ecf20Sopenharmony_ci rxfd->va_lo = rxdd->va_lo; 11658c2ecf20Sopenharmony_ci rxfd->pa_lo = CPU_CHIP_SWAP32(L32_64(dm->dma)); 11668c2ecf20Sopenharmony_ci rxfd->pa_hi = CPU_CHIP_SWAP32(H32_64(dm->dma)); 11678c2ecf20Sopenharmony_ci rxfd->len = CPU_CHIP_SWAP32(f->m.pktsz); 11688c2ecf20Sopenharmony_ci print_rxfd(rxfd); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci f->m.wptr += sizeof(struct rxf_desc); 11718c2ecf20Sopenharmony_ci delta = f->m.wptr - f->m.memsz; 11728c2ecf20Sopenharmony_ci if (unlikely(delta >= 0)) { 11738c2ecf20Sopenharmony_ci f->m.wptr = delta; 11748c2ecf20Sopenharmony_ci if (delta > 0) { 11758c2ecf20Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, delta); 11768c2ecf20Sopenharmony_ci DBG("wrapped descriptor\n"); 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci RET(); 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci/** 11838c2ecf20Sopenharmony_ci * bdx_rx_receive - receives full packets from RXD fifo and pass them to OS 11848c2ecf20Sopenharmony_ci * NOTE: a special treatment is given to non-continuous descriptors 11858c2ecf20Sopenharmony_ci * that start near the end, wraps around and continue at the beginning. a second 11868c2ecf20Sopenharmony_ci * part is copied right after the first, and then descriptor is interpreted as 11878c2ecf20Sopenharmony_ci * normal. fifo has an extra space to allow such operations 11888c2ecf20Sopenharmony_ci * @priv: nic's private structure 11898c2ecf20Sopenharmony_ci * @f: RXF fifo that needs skbs 11908c2ecf20Sopenharmony_ci * @budget: maximum number of packets to receive 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci/* TBD: replace memcpy func call by explicite inline asm */ 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct net_device *ndev = priv->ndev; 11988c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb2; 11998c2ecf20Sopenharmony_ci struct rxd_desc *rxdd; 12008c2ecf20Sopenharmony_ci struct rx_map *dm; 12018c2ecf20Sopenharmony_ci struct rxf_fifo *rxf_fifo; 12028c2ecf20Sopenharmony_ci int tmp_len, size; 12038c2ecf20Sopenharmony_ci int done = 0; 12048c2ecf20Sopenharmony_ci int max_done = BDX_MAX_RX_DONE; 12058c2ecf20Sopenharmony_ci struct rxdb *db = NULL; 12068c2ecf20Sopenharmony_ci /* Unmarshalled descriptor - copy of descriptor in host order */ 12078c2ecf20Sopenharmony_ci u32 rxd_val1; 12088c2ecf20Sopenharmony_ci u16 len; 12098c2ecf20Sopenharmony_ci u16 rxd_vlan; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci ENTER; 12128c2ecf20Sopenharmony_ci max_done = budget; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci f->m.wptr = READ_REG(priv, f->m.reg_WPTR) & TXF_WPTR_WR_PTR; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci size = f->m.wptr - f->m.rptr; 12178c2ecf20Sopenharmony_ci if (size < 0) 12188c2ecf20Sopenharmony_ci size = f->m.memsz + size; /* size is negative :-) */ 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci while (size > 0) { 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci rxdd = (struct rxd_desc *)(f->m.va + f->m.rptr); 12238c2ecf20Sopenharmony_ci rxd_val1 = CPU_CHIP_SWAP32(rxdd->rxd_val1); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci len = CPU_CHIP_SWAP16(rxdd->len); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci rxd_vlan = CPU_CHIP_SWAP16(rxdd->rxd_vlan); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci print_rxdd(rxdd, rxd_val1, len, rxd_vlan); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci tmp_len = GET_RXD_BC(rxd_val1) << 3; 12328c2ecf20Sopenharmony_ci BDX_ASSERT(tmp_len <= 0); 12338c2ecf20Sopenharmony_ci size -= tmp_len; 12348c2ecf20Sopenharmony_ci if (size < 0) /* test for partially arrived descriptor */ 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci f->m.rptr += tmp_len; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci tmp_len = f->m.rptr - f->m.memsz; 12408c2ecf20Sopenharmony_ci if (unlikely(tmp_len >= 0)) { 12418c2ecf20Sopenharmony_ci f->m.rptr = tmp_len; 12428c2ecf20Sopenharmony_ci if (tmp_len > 0) { 12438c2ecf20Sopenharmony_ci DBG("wrapped desc rptr=%d tmp_len=%d\n", 12448c2ecf20Sopenharmony_ci f->m.rptr, tmp_len); 12458c2ecf20Sopenharmony_ci memcpy(f->m.va + f->m.memsz, f->m.va, tmp_len); 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (unlikely(GET_RXD_ERR(rxd_val1))) { 12508c2ecf20Sopenharmony_ci DBG("rxd_err = 0x%x\n", GET_RXD_ERR(rxd_val1)); 12518c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 12528c2ecf20Sopenharmony_ci bdx_recycle_skb(priv, rxdd); 12538c2ecf20Sopenharmony_ci continue; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci rxf_fifo = &priv->rxf_fifo0; 12578c2ecf20Sopenharmony_ci db = priv->rxdb; 12588c2ecf20Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, rxdd->va_lo); 12598c2ecf20Sopenharmony_ci skb = dm->skb; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (len < BDX_COPYBREAK && 12628c2ecf20Sopenharmony_ci (skb2 = netdev_alloc_skb(priv->ndev, len + NET_IP_ALIGN))) { 12638c2ecf20Sopenharmony_ci skb_reserve(skb2, NET_IP_ALIGN); 12648c2ecf20Sopenharmony_ci /*skb_put(skb2, len); */ 12658c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&priv->pdev->dev, dm->dma, 12668c2ecf20Sopenharmony_ci rxf_fifo->m.pktsz, 12678c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 12688c2ecf20Sopenharmony_ci memcpy(skb2->data, skb->data, len); 12698c2ecf20Sopenharmony_ci bdx_recycle_skb(priv, rxdd); 12708c2ecf20Sopenharmony_ci skb = skb2; 12718c2ecf20Sopenharmony_ci } else { 12728c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, dm->dma, 12738c2ecf20Sopenharmony_ci rxf_fifo->m.pktsz, DMA_FROM_DEVICE); 12748c2ecf20Sopenharmony_ci bdx_rxdb_free_elem(db, rxdd->va_lo); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += len; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci skb_put(skb, len); 12808c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* Non-IP packets aren't checksum-offloaded */ 12838c2ecf20Sopenharmony_ci if (GET_RXD_PKT_ID(rxd_val1) == 0) 12848c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 12858c2ecf20Sopenharmony_ci else 12868c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci NETIF_RX_MUX(priv, rxd_val1, rxd_vlan, skb); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (++done >= max_done) 12918c2ecf20Sopenharmony_ci break; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci ndev->stats.rx_packets += done; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* FIXME: do smth to minimize pci accesses */ 12978c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_RPTR, f->m.rptr & TXF_WPTR_WR_PTR); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci RET(done); 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/************************************************************************* 13058c2ecf20Sopenharmony_ci * Debug / Temprorary Code * 13068c2ecf20Sopenharmony_ci *************************************************************************/ 13078c2ecf20Sopenharmony_cistatic void print_rxdd(struct rxd_desc *rxdd, u32 rxd_val1, u16 len, 13088c2ecf20Sopenharmony_ci u16 rxd_vlan) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci DBG("ERROR: rxdd bc %d rxfq %d to %d type %d err %d rxp %d pkt_id %d vtag %d len %d vlan_id %d cfi %d prio %d va_lo %d va_hi %d\n", 13118c2ecf20Sopenharmony_ci GET_RXD_BC(rxd_val1), GET_RXD_RXFQ(rxd_val1), GET_RXD_TO(rxd_val1), 13128c2ecf20Sopenharmony_ci GET_RXD_TYPE(rxd_val1), GET_RXD_ERR(rxd_val1), 13138c2ecf20Sopenharmony_ci GET_RXD_RXP(rxd_val1), GET_RXD_PKT_ID(rxd_val1), 13148c2ecf20Sopenharmony_ci GET_RXD_VTAG(rxd_val1), len, GET_RXD_VLAN_ID(rxd_vlan), 13158c2ecf20Sopenharmony_ci GET_RXD_CFI(rxd_vlan), GET_RXD_PRIO(rxd_vlan), rxdd->va_lo, 13168c2ecf20Sopenharmony_ci rxdd->va_hi); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic void print_rxfd(struct rxf_desc *rxfd) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci DBG("=== RxF desc CHIP ORDER/ENDIANNESS =============\n" 13228c2ecf20Sopenharmony_ci "info 0x%x va_lo %u pa_lo 0x%x pa_hi 0x%x len 0x%x\n", 13238c2ecf20Sopenharmony_ci rxfd->info, rxfd->va_lo, rxfd->pa_lo, rxfd->pa_hi, rxfd->len); 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/* 13278c2ecf20Sopenharmony_ci * TX HW/SW interaction overview 13288c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13298c2ecf20Sopenharmony_ci * There are 2 types of TX communication channels between driver and NIC. 13308c2ecf20Sopenharmony_ci * 1) TX Free Fifo - TXF - holds ack descriptors for sent packets 13318c2ecf20Sopenharmony_ci * 2) TX Data Fifo - TXD - holds descriptors of full buffers. 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * Currently NIC supports TSO, checksuming and gather DMA 13348c2ecf20Sopenharmony_ci * UFO and IP fragmentation is on the way 13358c2ecf20Sopenharmony_ci * 13368c2ecf20Sopenharmony_ci * RX SW Data Structures 13378c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~ 13388c2ecf20Sopenharmony_ci * txdb - used to keep track of all skbs owned by SW and their dma addresses. 13398c2ecf20Sopenharmony_ci * For TX case, ownership lasts from geting packet via hard_xmit and until HW 13408c2ecf20Sopenharmony_ci * acknowledges sent by TXF descriptors. 13418c2ecf20Sopenharmony_ci * Implemented as cyclic buffer. 13428c2ecf20Sopenharmony_ci * fifo - keeps info about fifo's size and location, relevant HW registers, 13438c2ecf20Sopenharmony_ci * usage and skb db. Each RXD and RXF Fifo has its own fifo structure. 13448c2ecf20Sopenharmony_ci * Implemented as simple struct. 13458c2ecf20Sopenharmony_ci * 13468c2ecf20Sopenharmony_ci * TX SW Execution Flow 13478c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~ 13488c2ecf20Sopenharmony_ci * OS calls driver's hard_xmit method with packet to sent. 13498c2ecf20Sopenharmony_ci * Driver creates DMA mappings, builds TXD descriptors and kicks HW 13508c2ecf20Sopenharmony_ci * by updating TXD WPTR. 13518c2ecf20Sopenharmony_ci * When packet is sent, HW write us TXF descriptor and SW frees original skb. 13528c2ecf20Sopenharmony_ci * To prevent TXD fifo overflow without reading HW registers every time, 13538c2ecf20Sopenharmony_ci * SW deploys "tx level" technique. 13548c2ecf20Sopenharmony_ci * Upon strart up, tx level is initialized to TXD fifo length. 13558c2ecf20Sopenharmony_ci * For every sent packet, SW gets its TXD descriptor sizei 13568c2ecf20Sopenharmony_ci * (from precalculated array) and substructs it from tx level. 13578c2ecf20Sopenharmony_ci * The size is also stored in txdb. When TXF ack arrives, SW fetch size of 13588c2ecf20Sopenharmony_ci * original TXD descriptor from txdb and adds it to tx level. 13598c2ecf20Sopenharmony_ci * When Tx level drops under some predefined treshhold, the driver 13608c2ecf20Sopenharmony_ci * stops the TX queue. When TX level rises above that level, 13618c2ecf20Sopenharmony_ci * the tx queue is enabled again. 13628c2ecf20Sopenharmony_ci * 13638c2ecf20Sopenharmony_ci * This technique avoids eccessive reading of RPTR and WPTR registers. 13648c2ecf20Sopenharmony_ci * As our benchmarks shows, it adds 1.5 Gbit/sec to NIS's throuput. 13658c2ecf20Sopenharmony_ci */ 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci/** 13688c2ecf20Sopenharmony_ci * __bdx_tx_db_ptr_next - helper function, increment read/write pointer + wrap 13698c2ecf20Sopenharmony_ci * @db: tx data base 13708c2ecf20Sopenharmony_ci * @pptr: read or write pointer 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_cistatic inline void __bdx_tx_db_ptr_next(struct txdb *db, struct tx_map **pptr) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci BDX_ASSERT(db == NULL || pptr == NULL); /* sanity */ 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci BDX_ASSERT(*pptr != db->rptr && /* expect either read */ 13778c2ecf20Sopenharmony_ci *pptr != db->wptr); /* or write pointer */ 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci BDX_ASSERT(*pptr < db->start || /* pointer has to be */ 13808c2ecf20Sopenharmony_ci *pptr >= db->end); /* in range */ 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci ++*pptr; 13838c2ecf20Sopenharmony_ci if (unlikely(*pptr == db->end)) 13848c2ecf20Sopenharmony_ci *pptr = db->start; 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci/** 13888c2ecf20Sopenharmony_ci * bdx_tx_db_inc_rptr - increment read pointer 13898c2ecf20Sopenharmony_ci * @db: tx data base 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_cistatic inline void bdx_tx_db_inc_rptr(struct txdb *db) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci BDX_ASSERT(db->rptr == db->wptr); /* can't read from empty db */ 13948c2ecf20Sopenharmony_ci __bdx_tx_db_ptr_next(db, &db->rptr); 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/** 13988c2ecf20Sopenharmony_ci * bdx_tx_db_inc_wptr - increment write pointer 13998c2ecf20Sopenharmony_ci * @db: tx data base 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_cistatic inline void bdx_tx_db_inc_wptr(struct txdb *db) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci __bdx_tx_db_ptr_next(db, &db->wptr); 14048c2ecf20Sopenharmony_ci BDX_ASSERT(db->rptr == db->wptr); /* we can not get empty db as 14058c2ecf20Sopenharmony_ci a result of write */ 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci/** 14098c2ecf20Sopenharmony_ci * bdx_tx_db_init - creates and initializes tx db 14108c2ecf20Sopenharmony_ci * @d: tx data base 14118c2ecf20Sopenharmony_ci * @sz_type: size of tx fifo 14128c2ecf20Sopenharmony_ci * 14138c2ecf20Sopenharmony_ci * Returns 0 on success, error code otherwise 14148c2ecf20Sopenharmony_ci */ 14158c2ecf20Sopenharmony_cistatic int bdx_tx_db_init(struct txdb *d, int sz_type) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci int memsz = FIFO_SIZE * (1 << (sz_type + 1)); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci d->start = vmalloc(memsz); 14208c2ecf20Sopenharmony_ci if (!d->start) 14218c2ecf20Sopenharmony_ci return -ENOMEM; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* 14248c2ecf20Sopenharmony_ci * In order to differentiate between db is empty and db is full 14258c2ecf20Sopenharmony_ci * states at least one element should always be empty in order to 14268c2ecf20Sopenharmony_ci * avoid rptr == wptr which means db is empty 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_ci d->size = memsz / sizeof(struct tx_map) - 1; 14298c2ecf20Sopenharmony_ci d->end = d->start + d->size + 1; /* just after last element */ 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* all dbs are created equally empty */ 14328c2ecf20Sopenharmony_ci d->rptr = d->start; 14338c2ecf20Sopenharmony_ci d->wptr = d->start; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return 0; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci/** 14398c2ecf20Sopenharmony_ci * bdx_tx_db_close - closes tx db and frees all memory 14408c2ecf20Sopenharmony_ci * @d: tx data base 14418c2ecf20Sopenharmony_ci */ 14428c2ecf20Sopenharmony_cistatic void bdx_tx_db_close(struct txdb *d) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci BDX_ASSERT(d == NULL); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci vfree(d->start); 14478c2ecf20Sopenharmony_ci d->start = NULL; 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci/************************************************************************* 14518c2ecf20Sopenharmony_ci * Tx Engine * 14528c2ecf20Sopenharmony_ci *************************************************************************/ 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci/* sizes of tx desc (including padding if needed) as function 14558c2ecf20Sopenharmony_ci * of skb's frag number */ 14568c2ecf20Sopenharmony_cistatic struct { 14578c2ecf20Sopenharmony_ci u16 bytes; 14588c2ecf20Sopenharmony_ci u16 qwords; /* qword = 64 bit */ 14598c2ecf20Sopenharmony_ci} txd_sizes[MAX_SKB_FRAGS + 1]; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci/** 14628c2ecf20Sopenharmony_ci * bdx_tx_map_skb - creates and stores dma mappings for skb's data blocks 14638c2ecf20Sopenharmony_ci * @priv: NIC private structure 14648c2ecf20Sopenharmony_ci * @skb: socket buffer to map 14658c2ecf20Sopenharmony_ci * @txdd: TX descriptor to use 14668c2ecf20Sopenharmony_ci * 14678c2ecf20Sopenharmony_ci * It makes dma mappings for skb's data blocks and writes them to PBL of 14688c2ecf20Sopenharmony_ci * new tx descriptor. It also stores them in the tx db, so they could be 14698c2ecf20Sopenharmony_ci * unmaped after data was sent. It is reponsibility of a caller to make 14708c2ecf20Sopenharmony_ci * sure that there is enough space in the tx db. Last element holds pointer 14718c2ecf20Sopenharmony_ci * to skb itself and marked with zero length 14728c2ecf20Sopenharmony_ci */ 14738c2ecf20Sopenharmony_cistatic inline void 14748c2ecf20Sopenharmony_cibdx_tx_map_skb(struct bdx_priv *priv, struct sk_buff *skb, 14758c2ecf20Sopenharmony_ci struct txd_desc *txdd) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci struct txdb *db = &priv->txdb; 14788c2ecf20Sopenharmony_ci struct pbl *pbl = &txdd->pbl[0]; 14798c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 14808c2ecf20Sopenharmony_ci int i; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci db->wptr->len = skb_headlen(skb); 14838c2ecf20Sopenharmony_ci db->wptr->addr.dma = dma_map_single(&priv->pdev->dev, skb->data, 14848c2ecf20Sopenharmony_ci db->wptr->len, DMA_TO_DEVICE); 14858c2ecf20Sopenharmony_ci pbl->len = CPU_CHIP_SWAP32(db->wptr->len); 14868c2ecf20Sopenharmony_ci pbl->pa_lo = CPU_CHIP_SWAP32(L32_64(db->wptr->addr.dma)); 14878c2ecf20Sopenharmony_ci pbl->pa_hi = CPU_CHIP_SWAP32(H32_64(db->wptr->addr.dma)); 14888c2ecf20Sopenharmony_ci DBG("=== pbl len: 0x%x ================\n", pbl->len); 14898c2ecf20Sopenharmony_ci DBG("=== pbl pa_lo: 0x%x ================\n", pbl->pa_lo); 14908c2ecf20Sopenharmony_ci DBG("=== pbl pa_hi: 0x%x ================\n", pbl->pa_hi); 14918c2ecf20Sopenharmony_ci bdx_tx_db_inc_wptr(db); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 14948c2ecf20Sopenharmony_ci const skb_frag_t *frag; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 14978c2ecf20Sopenharmony_ci db->wptr->len = skb_frag_size(frag); 14988c2ecf20Sopenharmony_ci db->wptr->addr.dma = skb_frag_dma_map(&priv->pdev->dev, frag, 14998c2ecf20Sopenharmony_ci 0, skb_frag_size(frag), 15008c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci pbl++; 15038c2ecf20Sopenharmony_ci pbl->len = CPU_CHIP_SWAP32(db->wptr->len); 15048c2ecf20Sopenharmony_ci pbl->pa_lo = CPU_CHIP_SWAP32(L32_64(db->wptr->addr.dma)); 15058c2ecf20Sopenharmony_ci pbl->pa_hi = CPU_CHIP_SWAP32(H32_64(db->wptr->addr.dma)); 15068c2ecf20Sopenharmony_ci bdx_tx_db_inc_wptr(db); 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* add skb clean up info. */ 15108c2ecf20Sopenharmony_ci db->wptr->len = -txd_sizes[nr_frags].bytes; 15118c2ecf20Sopenharmony_ci db->wptr->addr.skb = skb; 15128c2ecf20Sopenharmony_ci bdx_tx_db_inc_wptr(db); 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci/* init_txd_sizes - precalculate sizes of descriptors for skbs up to 16 frags 15168c2ecf20Sopenharmony_ci * number of frags is used as index to fetch correct descriptors size, 15178c2ecf20Sopenharmony_ci * instead of calculating it each time */ 15188c2ecf20Sopenharmony_cistatic void __init init_txd_sizes(void) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci int i, lwords; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* 7 - is number of lwords in txd with one phys buffer 15238c2ecf20Sopenharmony_ci * 3 - is number of lwords used for every additional phys buffer */ 15248c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SKB_FRAGS + 1; i++) { 15258c2ecf20Sopenharmony_ci lwords = 7 + (i * 3); 15268c2ecf20Sopenharmony_ci if (lwords & 1) 15278c2ecf20Sopenharmony_ci lwords++; /* pad it with 1 lword */ 15288c2ecf20Sopenharmony_ci txd_sizes[i].qwords = lwords >> 1; 15298c2ecf20Sopenharmony_ci txd_sizes[i].bytes = lwords << 2; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci/* bdx_tx_init - initialize all Tx related stuff. 15348c2ecf20Sopenharmony_ci * Namely, TXD and TXF fifos, database etc */ 15358c2ecf20Sopenharmony_cistatic int bdx_tx_init(struct bdx_priv *priv) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci if (bdx_fifo_init(priv, &priv->txd_fifo0.m, priv->txd_size, 15388c2ecf20Sopenharmony_ci regTXD_CFG0_0, 15398c2ecf20Sopenharmony_ci regTXD_CFG1_0, regTXD_RPTR_0, regTXD_WPTR_0)) 15408c2ecf20Sopenharmony_ci goto err_mem; 15418c2ecf20Sopenharmony_ci if (bdx_fifo_init(priv, &priv->txf_fifo0.m, priv->txf_size, 15428c2ecf20Sopenharmony_ci regTXF_CFG0_0, 15438c2ecf20Sopenharmony_ci regTXF_CFG1_0, regTXF_RPTR_0, regTXF_WPTR_0)) 15448c2ecf20Sopenharmony_ci goto err_mem; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* The TX db has to keep mappings for all packets sent (on TxD) 15478c2ecf20Sopenharmony_ci * and not yet reclaimed (on TxF) */ 15488c2ecf20Sopenharmony_ci if (bdx_tx_db_init(&priv->txdb, max(priv->txd_size, priv->txf_size))) 15498c2ecf20Sopenharmony_ci goto err_mem; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci priv->tx_level = BDX_MAX_TX_LEVEL; 15528c2ecf20Sopenharmony_ci#ifdef BDX_DELAY_WPTR 15538c2ecf20Sopenharmony_ci priv->tx_update_mark = priv->tx_level - 1024; 15548c2ecf20Sopenharmony_ci#endif 15558c2ecf20Sopenharmony_ci return 0; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_cierr_mem: 15588c2ecf20Sopenharmony_ci netdev_err(priv->ndev, "Tx init failed\n"); 15598c2ecf20Sopenharmony_ci return -ENOMEM; 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci/** 15638c2ecf20Sopenharmony_ci * bdx_tx_space - calculates available space in TX fifo 15648c2ecf20Sopenharmony_ci * @priv: NIC private structure 15658c2ecf20Sopenharmony_ci * 15668c2ecf20Sopenharmony_ci * Returns available space in TX fifo in bytes 15678c2ecf20Sopenharmony_ci */ 15688c2ecf20Sopenharmony_cistatic inline int bdx_tx_space(struct bdx_priv *priv) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 15718c2ecf20Sopenharmony_ci int fsize; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci f->m.rptr = READ_REG(priv, f->m.reg_RPTR) & TXF_WPTR_WR_PTR; 15748c2ecf20Sopenharmony_ci fsize = f->m.rptr - f->m.wptr; 15758c2ecf20Sopenharmony_ci if (fsize <= 0) 15768c2ecf20Sopenharmony_ci fsize = f->m.memsz + fsize; 15778c2ecf20Sopenharmony_ci return fsize; 15788c2ecf20Sopenharmony_ci} 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci/** 15818c2ecf20Sopenharmony_ci * bdx_tx_transmit - send packet to NIC 15828c2ecf20Sopenharmony_ci * @skb: packet to send 15838c2ecf20Sopenharmony_ci * @ndev: network device assigned to NIC 15848c2ecf20Sopenharmony_ci * Return codes: 15858c2ecf20Sopenharmony_ci * o NETDEV_TX_OK everything ok. 15868c2ecf20Sopenharmony_ci * o NETDEV_TX_BUSY Cannot transmit packet, try later 15878c2ecf20Sopenharmony_ci * Usually a bug, means queue start/stop flow control is broken in 15888c2ecf20Sopenharmony_ci * the driver. Note: the driver must NOT put the skb in its DMA ring. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_cistatic netdev_tx_t bdx_tx_transmit(struct sk_buff *skb, 15918c2ecf20Sopenharmony_ci struct net_device *ndev) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 15948c2ecf20Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 15958c2ecf20Sopenharmony_ci int txd_checksum = 7; /* full checksum */ 15968c2ecf20Sopenharmony_ci int txd_lgsnd = 0; 15978c2ecf20Sopenharmony_ci int txd_vlan_id = 0; 15988c2ecf20Sopenharmony_ci int txd_vtag = 0; 15998c2ecf20Sopenharmony_ci int txd_mss = 0; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 16028c2ecf20Sopenharmony_ci struct txd_desc *txdd; 16038c2ecf20Sopenharmony_ci int len; 16048c2ecf20Sopenharmony_ci unsigned long flags; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci ENTER; 16078c2ecf20Sopenharmony_ci local_irq_save(flags); 16088c2ecf20Sopenharmony_ci spin_lock(&priv->tx_lock); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci /* build tx descriptor */ 16118c2ecf20Sopenharmony_ci BDX_ASSERT(f->m.wptr >= f->m.memsz); /* started with valid wptr */ 16128c2ecf20Sopenharmony_ci txdd = (struct txd_desc *)(f->m.va + f->m.wptr); 16138c2ecf20Sopenharmony_ci if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) 16148c2ecf20Sopenharmony_ci txd_checksum = 0; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->gso_size) { 16178c2ecf20Sopenharmony_ci txd_mss = skb_shinfo(skb)->gso_size; 16188c2ecf20Sopenharmony_ci txd_lgsnd = 1; 16198c2ecf20Sopenharmony_ci DBG("skb %p skb len %d gso size = %d\n", skb, skb->len, 16208c2ecf20Sopenharmony_ci txd_mss); 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 16248c2ecf20Sopenharmony_ci /*Cut VLAN ID to 12 bits */ 16258c2ecf20Sopenharmony_ci txd_vlan_id = skb_vlan_tag_get(skb) & BITS_MASK(12); 16268c2ecf20Sopenharmony_ci txd_vtag = 1; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci txdd->length = CPU_CHIP_SWAP16(skb->len); 16308c2ecf20Sopenharmony_ci txdd->mss = CPU_CHIP_SWAP16(txd_mss); 16318c2ecf20Sopenharmony_ci txdd->txd_val1 = 16328c2ecf20Sopenharmony_ci CPU_CHIP_SWAP32(TXD_W1_VAL 16338c2ecf20Sopenharmony_ci (txd_sizes[nr_frags].qwords, txd_checksum, txd_vtag, 16348c2ecf20Sopenharmony_ci txd_lgsnd, txd_vlan_id)); 16358c2ecf20Sopenharmony_ci DBG("=== TxD desc =====================\n"); 16368c2ecf20Sopenharmony_ci DBG("=== w1: 0x%x ================\n", txdd->txd_val1); 16378c2ecf20Sopenharmony_ci DBG("=== w2: mss 0x%x len 0x%x\n", txdd->mss, txdd->length); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci bdx_tx_map_skb(priv, skb, txdd); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci /* increment TXD write pointer. In case of 16428c2ecf20Sopenharmony_ci fifo wrapping copy reminder of the descriptor 16438c2ecf20Sopenharmony_ci to the beginning */ 16448c2ecf20Sopenharmony_ci f->m.wptr += txd_sizes[nr_frags].bytes; 16458c2ecf20Sopenharmony_ci len = f->m.wptr - f->m.memsz; 16468c2ecf20Sopenharmony_ci if (unlikely(len >= 0)) { 16478c2ecf20Sopenharmony_ci f->m.wptr = len; 16488c2ecf20Sopenharmony_ci if (len > 0) { 16498c2ecf20Sopenharmony_ci BDX_ASSERT(len > f->m.memsz); 16508c2ecf20Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, len); 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci BDX_ASSERT(f->m.wptr >= f->m.memsz); /* finished with valid wptr */ 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci priv->tx_level -= txd_sizes[nr_frags].bytes; 16568c2ecf20Sopenharmony_ci BDX_ASSERT(priv->tx_level <= 0 || priv->tx_level > BDX_MAX_TX_LEVEL); 16578c2ecf20Sopenharmony_ci#ifdef BDX_DELAY_WPTR 16588c2ecf20Sopenharmony_ci if (priv->tx_level > priv->tx_update_mark) { 16598c2ecf20Sopenharmony_ci /* Force memory writes to complete before letting h/w 16608c2ecf20Sopenharmony_ci know there are new descriptors to fetch. 16618c2ecf20Sopenharmony_ci (might be needed on platforms like IA64) 16628c2ecf20Sopenharmony_ci wmb(); */ 16638c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 16648c2ecf20Sopenharmony_ci } else { 16658c2ecf20Sopenharmony_ci if (priv->tx_noupd++ > BDX_NO_UPD_PACKETS) { 16668c2ecf20Sopenharmony_ci priv->tx_noupd = 0; 16678c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, 16688c2ecf20Sopenharmony_ci f->m.wptr & TXF_WPTR_WR_PTR); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci#else 16728c2ecf20Sopenharmony_ci /* Force memory writes to complete before letting h/w 16738c2ecf20Sopenharmony_ci know there are new descriptors to fetch. 16748c2ecf20Sopenharmony_ci (might be needed on platforms like IA64) 16758c2ecf20Sopenharmony_ci wmb(); */ 16768c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci#endif 16798c2ecf20Sopenharmony_ci#ifdef BDX_LLTX 16808c2ecf20Sopenharmony_ci netif_trans_update(ndev); /* NETIF_F_LLTX driver :( */ 16818c2ecf20Sopenharmony_ci#endif 16828c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 16838c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci if (priv->tx_level < BDX_MIN_TX_LEVEL) { 16868c2ecf20Sopenharmony_ci DBG("%s: %s: TX Q STOP level %d\n", 16878c2ecf20Sopenharmony_ci BDX_DRV_NAME, ndev->name, priv->tx_level); 16888c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->tx_lock, flags); 16928c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci/** 16968c2ecf20Sopenharmony_ci * bdx_tx_cleanup - clean TXF fifo, run in the context of IRQ. 16978c2ecf20Sopenharmony_ci * @priv: bdx adapter 16988c2ecf20Sopenharmony_ci * 16998c2ecf20Sopenharmony_ci * It scans TXF fifo for descriptors, frees DMA mappings and reports to OS 17008c2ecf20Sopenharmony_ci * that those packets were sent 17018c2ecf20Sopenharmony_ci */ 17028c2ecf20Sopenharmony_cistatic void bdx_tx_cleanup(struct bdx_priv *priv) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci struct txf_fifo *f = &priv->txf_fifo0; 17058c2ecf20Sopenharmony_ci struct txdb *db = &priv->txdb; 17068c2ecf20Sopenharmony_ci int tx_level = 0; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci ENTER; 17098c2ecf20Sopenharmony_ci f->m.wptr = READ_REG(priv, f->m.reg_WPTR) & TXF_WPTR_MASK; 17108c2ecf20Sopenharmony_ci BDX_ASSERT(f->m.rptr >= f->m.memsz); /* started with valid rptr */ 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci while (f->m.wptr != f->m.rptr) { 17138c2ecf20Sopenharmony_ci f->m.rptr += BDX_TXF_DESC_SZ; 17148c2ecf20Sopenharmony_ci f->m.rptr &= f->m.size_mask; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci /* unmap all the fragments */ 17178c2ecf20Sopenharmony_ci /* first has to come tx_maps containing dma */ 17188c2ecf20Sopenharmony_ci BDX_ASSERT(db->rptr->len == 0); 17198c2ecf20Sopenharmony_ci do { 17208c2ecf20Sopenharmony_ci BDX_ASSERT(db->rptr->addr.dma == 0); 17218c2ecf20Sopenharmony_ci dma_unmap_page(&priv->pdev->dev, db->rptr->addr.dma, 17228c2ecf20Sopenharmony_ci db->rptr->len, DMA_TO_DEVICE); 17238c2ecf20Sopenharmony_ci bdx_tx_db_inc_rptr(db); 17248c2ecf20Sopenharmony_ci } while (db->rptr->len > 0); 17258c2ecf20Sopenharmony_ci tx_level -= db->rptr->len; /* '-' koz len is negative */ 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci /* now should come skb pointer - free it */ 17288c2ecf20Sopenharmony_ci dev_consume_skb_irq(db->rptr->addr.skb); 17298c2ecf20Sopenharmony_ci bdx_tx_db_inc_rptr(db); 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* let h/w know which TXF descriptors were cleaned */ 17338c2ecf20Sopenharmony_ci BDX_ASSERT((f->m.wptr & TXF_WPTR_WR_PTR) >= f->m.memsz); 17348c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_RPTR, f->m.rptr & TXF_WPTR_WR_PTR); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* We reclaimed resources, so in case the Q is stopped by xmit callback, 17378c2ecf20Sopenharmony_ci * we resume the transmission and use tx_lock to synchronize with xmit.*/ 17388c2ecf20Sopenharmony_ci spin_lock(&priv->tx_lock); 17398c2ecf20Sopenharmony_ci priv->tx_level += tx_level; 17408c2ecf20Sopenharmony_ci BDX_ASSERT(priv->tx_level <= 0 || priv->tx_level > BDX_MAX_TX_LEVEL); 17418c2ecf20Sopenharmony_ci#ifdef BDX_DELAY_WPTR 17428c2ecf20Sopenharmony_ci if (priv->tx_noupd) { 17438c2ecf20Sopenharmony_ci priv->tx_noupd = 0; 17448c2ecf20Sopenharmony_ci WRITE_REG(priv, priv->txd_fifo0.m.reg_WPTR, 17458c2ecf20Sopenharmony_ci priv->txd_fifo0.m.wptr & TXF_WPTR_WR_PTR); 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci#endif 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci if (unlikely(netif_queue_stopped(priv->ndev) && 17508c2ecf20Sopenharmony_ci netif_carrier_ok(priv->ndev) && 17518c2ecf20Sopenharmony_ci (priv->tx_level >= BDX_MIN_TX_LEVEL))) { 17528c2ecf20Sopenharmony_ci DBG("%s: %s: TX Q WAKE level %d\n", 17538c2ecf20Sopenharmony_ci BDX_DRV_NAME, priv->ndev->name, priv->tx_level); 17548c2ecf20Sopenharmony_ci netif_wake_queue(priv->ndev); 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci spin_unlock(&priv->tx_lock); 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci/** 17608c2ecf20Sopenharmony_ci * bdx_tx_free_skbs - frees all skbs from TXD fifo. 17618c2ecf20Sopenharmony_ci * @priv: NIC private structure 17628c2ecf20Sopenharmony_ci * 17638c2ecf20Sopenharmony_ci * It gets called when OS stops this dev, eg upon "ifconfig down" or rmmod 17648c2ecf20Sopenharmony_ci */ 17658c2ecf20Sopenharmony_cistatic void bdx_tx_free_skbs(struct bdx_priv *priv) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct txdb *db = &priv->txdb; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci ENTER; 17708c2ecf20Sopenharmony_ci while (db->rptr != db->wptr) { 17718c2ecf20Sopenharmony_ci if (likely(db->rptr->len)) 17728c2ecf20Sopenharmony_ci dma_unmap_page(&priv->pdev->dev, db->rptr->addr.dma, 17738c2ecf20Sopenharmony_ci db->rptr->len, DMA_TO_DEVICE); 17748c2ecf20Sopenharmony_ci else 17758c2ecf20Sopenharmony_ci dev_kfree_skb(db->rptr->addr.skb); 17768c2ecf20Sopenharmony_ci bdx_tx_db_inc_rptr(db); 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci RET(); 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci/* bdx_tx_free - frees all Tx resources */ 17828c2ecf20Sopenharmony_cistatic void bdx_tx_free(struct bdx_priv *priv) 17838c2ecf20Sopenharmony_ci{ 17848c2ecf20Sopenharmony_ci ENTER; 17858c2ecf20Sopenharmony_ci bdx_tx_free_skbs(priv); 17868c2ecf20Sopenharmony_ci bdx_fifo_free(priv, &priv->txd_fifo0.m); 17878c2ecf20Sopenharmony_ci bdx_fifo_free(priv, &priv->txf_fifo0.m); 17888c2ecf20Sopenharmony_ci bdx_tx_db_close(&priv->txdb); 17898c2ecf20Sopenharmony_ci} 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci/** 17928c2ecf20Sopenharmony_ci * bdx_tx_push_desc - push descriptor to TxD fifo 17938c2ecf20Sopenharmony_ci * @priv: NIC private structure 17948c2ecf20Sopenharmony_ci * @data: desc's data 17958c2ecf20Sopenharmony_ci * @size: desc's size 17968c2ecf20Sopenharmony_ci * 17978c2ecf20Sopenharmony_ci * Pushes desc to TxD fifo and overlaps it if needed. 17988c2ecf20Sopenharmony_ci * NOTE: this func does not check for available space. this is responsibility 17998c2ecf20Sopenharmony_ci * of the caller. Neither does it check that data size is smaller than 18008c2ecf20Sopenharmony_ci * fifo size. 18018c2ecf20Sopenharmony_ci */ 18028c2ecf20Sopenharmony_cistatic void bdx_tx_push_desc(struct bdx_priv *priv, void *data, int size) 18038c2ecf20Sopenharmony_ci{ 18048c2ecf20Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 18058c2ecf20Sopenharmony_ci int i = f->m.memsz - f->m.wptr; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci if (size == 0) 18088c2ecf20Sopenharmony_ci return; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (i > size) { 18118c2ecf20Sopenharmony_ci memcpy(f->m.va + f->m.wptr, data, size); 18128c2ecf20Sopenharmony_ci f->m.wptr += size; 18138c2ecf20Sopenharmony_ci } else { 18148c2ecf20Sopenharmony_ci memcpy(f->m.va + f->m.wptr, data, i); 18158c2ecf20Sopenharmony_ci f->m.wptr = size - i; 18168c2ecf20Sopenharmony_ci memcpy(f->m.va, data + i, f->m.wptr); 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci/** 18228c2ecf20Sopenharmony_ci * bdx_tx_push_desc_safe - push descriptor to TxD fifo in a safe way 18238c2ecf20Sopenharmony_ci * @priv: NIC private structure 18248c2ecf20Sopenharmony_ci * @data: desc's data 18258c2ecf20Sopenharmony_ci * @size: desc's size 18268c2ecf20Sopenharmony_ci * 18278c2ecf20Sopenharmony_ci * NOTE: this func does check for available space and, if necessary, waits for 18288c2ecf20Sopenharmony_ci * NIC to read existing data before writing new one. 18298c2ecf20Sopenharmony_ci */ 18308c2ecf20Sopenharmony_cistatic void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci int timer = 0; 18338c2ecf20Sopenharmony_ci ENTER; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci while (size > 0) { 18368c2ecf20Sopenharmony_ci /* we substruct 8 because when fifo is full rptr == wptr 18378c2ecf20Sopenharmony_ci which also means that fifo is empty, we can understand 18388c2ecf20Sopenharmony_ci the difference, but could hw do the same ??? :) */ 18398c2ecf20Sopenharmony_ci int avail = bdx_tx_space(priv) - 8; 18408c2ecf20Sopenharmony_ci if (avail <= 0) { 18418c2ecf20Sopenharmony_ci if (timer++ > 300) { /* prevent endless loop */ 18428c2ecf20Sopenharmony_ci DBG("timeout while writing desc to TxD fifo\n"); 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci udelay(50); /* give hw a chance to clean fifo */ 18468c2ecf20Sopenharmony_ci continue; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci avail = min(avail, size); 18498c2ecf20Sopenharmony_ci DBG("about to push %d bytes starting %p size %d\n", avail, 18508c2ecf20Sopenharmony_ci data, size); 18518c2ecf20Sopenharmony_ci bdx_tx_push_desc(priv, data, avail); 18528c2ecf20Sopenharmony_ci size -= avail; 18538c2ecf20Sopenharmony_ci data += avail; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci RET(); 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_cistatic const struct net_device_ops bdx_netdev_ops = { 18598c2ecf20Sopenharmony_ci .ndo_open = bdx_open, 18608c2ecf20Sopenharmony_ci .ndo_stop = bdx_close, 18618c2ecf20Sopenharmony_ci .ndo_start_xmit = bdx_tx_transmit, 18628c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18638c2ecf20Sopenharmony_ci .ndo_do_ioctl = bdx_ioctl, 18648c2ecf20Sopenharmony_ci .ndo_set_rx_mode = bdx_setmulti, 18658c2ecf20Sopenharmony_ci .ndo_change_mtu = bdx_change_mtu, 18668c2ecf20Sopenharmony_ci .ndo_set_mac_address = bdx_set_mac, 18678c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = bdx_vlan_rx_add_vid, 18688c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = bdx_vlan_rx_kill_vid, 18698c2ecf20Sopenharmony_ci}; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci/** 18728c2ecf20Sopenharmony_ci * bdx_probe - Device Initialization Routine 18738c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 18748c2ecf20Sopenharmony_ci * @ent: entry in bdx_pci_tbl 18758c2ecf20Sopenharmony_ci * 18768c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure 18778c2ecf20Sopenharmony_ci * 18788c2ecf20Sopenharmony_ci * bdx_probe initializes an adapter identified by a pci_dev structure. 18798c2ecf20Sopenharmony_ci * The OS initialization, configuring of the adapter private structure, 18808c2ecf20Sopenharmony_ci * and a hardware reset occur. 18818c2ecf20Sopenharmony_ci * 18828c2ecf20Sopenharmony_ci * functions and their order used as explained in 18838c2ecf20Sopenharmony_ci * /usr/src/linux/Documentation/DMA-{API,mapping}.txt 18848c2ecf20Sopenharmony_ci * 18858c2ecf20Sopenharmony_ci */ 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci/* TBD: netif_msg should be checked and implemented. I disable it for now */ 18888c2ecf20Sopenharmony_cistatic int 18898c2ecf20Sopenharmony_cibdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct net_device *ndev; 18928c2ecf20Sopenharmony_ci struct bdx_priv *priv; 18938c2ecf20Sopenharmony_ci int err, pci_using_dac, port; 18948c2ecf20Sopenharmony_ci unsigned long pciaddr; 18958c2ecf20Sopenharmony_ci u32 regionSize; 18968c2ecf20Sopenharmony_ci struct pci_nic *nic; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci ENTER; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci nic = vmalloc(sizeof(*nic)); 19018c2ecf20Sopenharmony_ci if (!nic) 19028c2ecf20Sopenharmony_ci RET(-ENOMEM); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci /************** pci *****************/ 19058c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 19068c2ecf20Sopenharmony_ci if (err) /* it triggers interrupt, dunno why. */ 19078c2ecf20Sopenharmony_ci goto err_pci; /* it's not a problem though */ 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci if (!(err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) && 19108c2ecf20Sopenharmony_ci !(err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)))) { 19118c2ecf20Sopenharmony_ci pci_using_dac = 1; 19128c2ecf20Sopenharmony_ci } else { 19138c2ecf20Sopenharmony_ci if ((err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) || 19148c2ecf20Sopenharmony_ci (err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)))) { 19158c2ecf20Sopenharmony_ci pr_err("No usable DMA configuration, aborting\n"); 19168c2ecf20Sopenharmony_ci goto err_dma; 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci pci_using_dac = 0; 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, BDX_DRV_NAME); 19228c2ecf20Sopenharmony_ci if (err) 19238c2ecf20Sopenharmony_ci goto err_dma; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci pci_set_master(pdev); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci pciaddr = pci_resource_start(pdev, 0); 19288c2ecf20Sopenharmony_ci if (!pciaddr) { 19298c2ecf20Sopenharmony_ci err = -EIO; 19308c2ecf20Sopenharmony_ci pr_err("no MMIO resource\n"); 19318c2ecf20Sopenharmony_ci goto err_out_res; 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci regionSize = pci_resource_len(pdev, 0); 19348c2ecf20Sopenharmony_ci if (regionSize < BDX_REGS_SIZE) { 19358c2ecf20Sopenharmony_ci err = -EIO; 19368c2ecf20Sopenharmony_ci pr_err("MMIO resource (%x) too small\n", regionSize); 19378c2ecf20Sopenharmony_ci goto err_out_res; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci nic->regs = ioremap(pciaddr, regionSize); 19418c2ecf20Sopenharmony_ci if (!nic->regs) { 19428c2ecf20Sopenharmony_ci err = -EIO; 19438c2ecf20Sopenharmony_ci pr_err("ioremap failed\n"); 19448c2ecf20Sopenharmony_ci goto err_out_res; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (pdev->irq < 2) { 19488c2ecf20Sopenharmony_ci err = -EIO; 19498c2ecf20Sopenharmony_ci pr_err("invalid irq (%d)\n", pdev->irq); 19508c2ecf20Sopenharmony_ci goto err_out_iomap; 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, nic); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (pdev->device == 0x3014) 19558c2ecf20Sopenharmony_ci nic->port_num = 2; 19568c2ecf20Sopenharmony_ci else 19578c2ecf20Sopenharmony_ci nic->port_num = 1; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci print_hw_id(pdev); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci bdx_hw_reset_direct(nic->regs); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci nic->irq_type = IRQ_INTX; 19648c2ecf20Sopenharmony_ci#ifdef BDX_MSI 19658c2ecf20Sopenharmony_ci if ((readl(nic->regs + FPGA_VER) & 0xFFF) >= 378) { 19668c2ecf20Sopenharmony_ci err = pci_enable_msi(pdev); 19678c2ecf20Sopenharmony_ci if (err) 19688c2ecf20Sopenharmony_ci pr_err("Can't enable msi. error is %d\n", err); 19698c2ecf20Sopenharmony_ci else 19708c2ecf20Sopenharmony_ci nic->irq_type = IRQ_MSI; 19718c2ecf20Sopenharmony_ci } else 19728c2ecf20Sopenharmony_ci DBG("HW does not support MSI\n"); 19738c2ecf20Sopenharmony_ci#endif 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /************** netdev **************/ 19768c2ecf20Sopenharmony_ci for (port = 0; port < nic->port_num; port++) { 19778c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct bdx_priv)); 19788c2ecf20Sopenharmony_ci if (!ndev) { 19798c2ecf20Sopenharmony_ci err = -ENOMEM; 19808c2ecf20Sopenharmony_ci goto err_out_iomap; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci ndev->netdev_ops = &bdx_netdev_ops; 19848c2ecf20Sopenharmony_ci ndev->tx_queue_len = BDX_NDEV_TXQ_LEN; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci bdx_set_ethtool_ops(ndev); /* ethtool interface */ 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci /* these fields are used for info purposes only 19898c2ecf20Sopenharmony_ci * so we can have them same for all ports of the board */ 19908c2ecf20Sopenharmony_ci ndev->if_port = port; 19918c2ecf20Sopenharmony_ci ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO 19928c2ecf20Sopenharmony_ci | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | 19938c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM 19948c2ecf20Sopenharmony_ci ; 19958c2ecf20Sopenharmony_ci ndev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | 19968c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci if (pci_using_dac) 19998c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HIGHDMA; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci /************** priv ****************/ 20028c2ecf20Sopenharmony_ci priv = nic->priv[port] = netdev_priv(ndev); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci priv->pBdxRegs = nic->regs + port * 0x8000; 20058c2ecf20Sopenharmony_ci priv->port = port; 20068c2ecf20Sopenharmony_ci priv->pdev = pdev; 20078c2ecf20Sopenharmony_ci priv->ndev = ndev; 20088c2ecf20Sopenharmony_ci priv->nic = nic; 20098c2ecf20Sopenharmony_ci priv->msg_enable = BDX_DEF_MSG_ENABLE; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci netif_napi_add(ndev, &priv->napi, bdx_poll, 64); 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if ((readl(nic->regs + FPGA_VER) & 0xFFF) == 308) { 20148c2ecf20Sopenharmony_ci DBG("HW statistics not supported\n"); 20158c2ecf20Sopenharmony_ci priv->stats_flag = 0; 20168c2ecf20Sopenharmony_ci } else { 20178c2ecf20Sopenharmony_ci priv->stats_flag = 1; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci /* Initialize fifo sizes. */ 20218c2ecf20Sopenharmony_ci priv->txd_size = 2; 20228c2ecf20Sopenharmony_ci priv->txf_size = 2; 20238c2ecf20Sopenharmony_ci priv->rxd_size = 2; 20248c2ecf20Sopenharmony_ci priv->rxf_size = 3; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* Initialize the initial coalescing registers. */ 20278c2ecf20Sopenharmony_ci priv->rdintcm = INT_REG_VAL(0x20, 1, 4, 12); 20288c2ecf20Sopenharmony_ci priv->tdintcm = INT_REG_VAL(0x20, 1, 0, 12); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci /* ndev->xmit_lock spinlock is not used. 20318c2ecf20Sopenharmony_ci * Private priv->tx_lock is used for synchronization 20328c2ecf20Sopenharmony_ci * between transmit and TX irq cleanup. In addition 20338c2ecf20Sopenharmony_ci * set multicast list callback has to use priv->tx_lock. 20348c2ecf20Sopenharmony_ci */ 20358c2ecf20Sopenharmony_ci#ifdef BDX_LLTX 20368c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_LLTX; 20378c2ecf20Sopenharmony_ci#endif 20388c2ecf20Sopenharmony_ci /* MTU range: 60 - 16384 */ 20398c2ecf20Sopenharmony_ci ndev->min_mtu = ETH_ZLEN; 20408c2ecf20Sopenharmony_ci ndev->max_mtu = BDX_MAX_MTU; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci spin_lock_init(&priv->tx_lock); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci /*bdx_hw_reset(priv); */ 20458c2ecf20Sopenharmony_ci if (bdx_read_mac(priv)) { 20468c2ecf20Sopenharmony_ci pr_err("load MAC address failed\n"); 20478c2ecf20Sopenharmony_ci err = -EFAULT; 20488c2ecf20Sopenharmony_ci goto err_out_iomap; 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 20518c2ecf20Sopenharmony_ci err = register_netdev(ndev); 20528c2ecf20Sopenharmony_ci if (err) { 20538c2ecf20Sopenharmony_ci pr_err("register_netdev failed\n"); 20548c2ecf20Sopenharmony_ci goto err_out_free; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 20578c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci print_eth_id(ndev); 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci RET(0); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_cierr_out_free: 20648c2ecf20Sopenharmony_ci free_netdev(ndev); 20658c2ecf20Sopenharmony_cierr_out_iomap: 20668c2ecf20Sopenharmony_ci iounmap(nic->regs); 20678c2ecf20Sopenharmony_cierr_out_res: 20688c2ecf20Sopenharmony_ci pci_release_regions(pdev); 20698c2ecf20Sopenharmony_cierr_dma: 20708c2ecf20Sopenharmony_ci pci_disable_device(pdev); 20718c2ecf20Sopenharmony_cierr_pci: 20728c2ecf20Sopenharmony_ci vfree(nic); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci RET(err); 20758c2ecf20Sopenharmony_ci} 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci/****************** Ethtool interface *********************/ 20788c2ecf20Sopenharmony_ci/* get strings for statistics counters */ 20798c2ecf20Sopenharmony_cistatic const char 20808c2ecf20Sopenharmony_ci bdx_stat_names[][ETH_GSTRING_LEN] = { 20818c2ecf20Sopenharmony_ci "InUCast", /* 0x7200 */ 20828c2ecf20Sopenharmony_ci "InMCast", /* 0x7210 */ 20838c2ecf20Sopenharmony_ci "InBCast", /* 0x7220 */ 20848c2ecf20Sopenharmony_ci "InPkts", /* 0x7230 */ 20858c2ecf20Sopenharmony_ci "InErrors", /* 0x7240 */ 20868c2ecf20Sopenharmony_ci "InDropped", /* 0x7250 */ 20878c2ecf20Sopenharmony_ci "FrameTooLong", /* 0x7260 */ 20888c2ecf20Sopenharmony_ci "FrameSequenceErrors", /* 0x7270 */ 20898c2ecf20Sopenharmony_ci "InVLAN", /* 0x7280 */ 20908c2ecf20Sopenharmony_ci "InDroppedDFE", /* 0x7290 */ 20918c2ecf20Sopenharmony_ci "InDroppedIntFull", /* 0x72A0 */ 20928c2ecf20Sopenharmony_ci "InFrameAlignErrors", /* 0x72B0 */ 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* 0x72C0-0x72E0 RSRV */ 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci "OutUCast", /* 0x72F0 */ 20978c2ecf20Sopenharmony_ci "OutMCast", /* 0x7300 */ 20988c2ecf20Sopenharmony_ci "OutBCast", /* 0x7310 */ 20998c2ecf20Sopenharmony_ci "OutPkts", /* 0x7320 */ 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci /* 0x7330-0x7360 RSRV */ 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci "OutVLAN", /* 0x7370 */ 21048c2ecf20Sopenharmony_ci "InUCastOctects", /* 0x7380 */ 21058c2ecf20Sopenharmony_ci "OutUCastOctects", /* 0x7390 */ 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci /* 0x73A0-0x73B0 RSRV */ 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci "InBCastOctects", /* 0x73C0 */ 21108c2ecf20Sopenharmony_ci "OutBCastOctects", /* 0x73D0 */ 21118c2ecf20Sopenharmony_ci "InOctects", /* 0x73E0 */ 21128c2ecf20Sopenharmony_ci "OutOctects", /* 0x73F0 */ 21138c2ecf20Sopenharmony_ci}; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci/* 21168c2ecf20Sopenharmony_ci * bdx_get_link_ksettings - get device-specific settings 21178c2ecf20Sopenharmony_ci * @netdev 21188c2ecf20Sopenharmony_ci * @ecmd 21198c2ecf20Sopenharmony_ci */ 21208c2ecf20Sopenharmony_cistatic int bdx_get_link_ksettings(struct net_device *netdev, 21218c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 21228c2ecf20Sopenharmony_ci{ 21238c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, supported); 21248c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, 21258c2ecf20Sopenharmony_ci 10000baseT_Full); 21268c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, FIBRE); 21278c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 21288c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, 21298c2ecf20Sopenharmony_ci 10000baseT_Full); 21308c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, FIBRE); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci ecmd->base.speed = SPEED_10000; 21338c2ecf20Sopenharmony_ci ecmd->base.duplex = DUPLEX_FULL; 21348c2ecf20Sopenharmony_ci ecmd->base.port = PORT_FIBRE; 21358c2ecf20Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci return 0; 21388c2ecf20Sopenharmony_ci} 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci/* 21418c2ecf20Sopenharmony_ci * bdx_get_drvinfo - report driver information 21428c2ecf20Sopenharmony_ci * @netdev 21438c2ecf20Sopenharmony_ci * @drvinfo 21448c2ecf20Sopenharmony_ci */ 21458c2ecf20Sopenharmony_cistatic void 21468c2ecf20Sopenharmony_cibdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) 21478c2ecf20Sopenharmony_ci{ 21488c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); 21518c2ecf20Sopenharmony_ci strlcpy(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); 21528c2ecf20Sopenharmony_ci strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); 21538c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, pci_name(priv->pdev), 21548c2ecf20Sopenharmony_ci sizeof(drvinfo->bus_info)); 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci/* 21588c2ecf20Sopenharmony_ci * bdx_get_coalesce - get interrupt coalescing parameters 21598c2ecf20Sopenharmony_ci * @netdev 21608c2ecf20Sopenharmony_ci * @ecoal 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_cistatic int 21638c2ecf20Sopenharmony_cibdx_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) 21648c2ecf20Sopenharmony_ci{ 21658c2ecf20Sopenharmony_ci u32 rdintcm; 21668c2ecf20Sopenharmony_ci u32 tdintcm; 21678c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci rdintcm = priv->rdintcm; 21708c2ecf20Sopenharmony_ci tdintcm = priv->tdintcm; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* PCK_TH measures in multiples of FIFO bytes 21738c2ecf20Sopenharmony_ci We translate to packets */ 21748c2ecf20Sopenharmony_ci ecoal->rx_coalesce_usecs = GET_INT_COAL(rdintcm) * INT_COAL_MULT; 21758c2ecf20Sopenharmony_ci ecoal->rx_max_coalesced_frames = 21768c2ecf20Sopenharmony_ci ((GET_PCK_TH(rdintcm) * PCK_TH_MULT) / sizeof(struct rxf_desc)); 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci ecoal->tx_coalesce_usecs = GET_INT_COAL(tdintcm) * INT_COAL_MULT; 21798c2ecf20Sopenharmony_ci ecoal->tx_max_coalesced_frames = 21808c2ecf20Sopenharmony_ci ((GET_PCK_TH(tdintcm) * PCK_TH_MULT) / BDX_TXF_DESC_SZ); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* adaptive parameters ignored */ 21838c2ecf20Sopenharmony_ci return 0; 21848c2ecf20Sopenharmony_ci} 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci/* 21878c2ecf20Sopenharmony_ci * bdx_set_coalesce - set interrupt coalescing parameters 21888c2ecf20Sopenharmony_ci * @netdev 21898c2ecf20Sopenharmony_ci * @ecoal 21908c2ecf20Sopenharmony_ci */ 21918c2ecf20Sopenharmony_cistatic int 21928c2ecf20Sopenharmony_cibdx_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci u32 rdintcm; 21958c2ecf20Sopenharmony_ci u32 tdintcm; 21968c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 21978c2ecf20Sopenharmony_ci int rx_coal; 21988c2ecf20Sopenharmony_ci int tx_coal; 21998c2ecf20Sopenharmony_ci int rx_max_coal; 22008c2ecf20Sopenharmony_ci int tx_max_coal; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* Check for valid input */ 22038c2ecf20Sopenharmony_ci rx_coal = ecoal->rx_coalesce_usecs / INT_COAL_MULT; 22048c2ecf20Sopenharmony_ci tx_coal = ecoal->tx_coalesce_usecs / INT_COAL_MULT; 22058c2ecf20Sopenharmony_ci rx_max_coal = ecoal->rx_max_coalesced_frames; 22068c2ecf20Sopenharmony_ci tx_max_coal = ecoal->tx_max_coalesced_frames; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci /* Translate from packets to multiples of FIFO bytes */ 22098c2ecf20Sopenharmony_ci rx_max_coal = 22108c2ecf20Sopenharmony_ci (((rx_max_coal * sizeof(struct rxf_desc)) + PCK_TH_MULT - 1) 22118c2ecf20Sopenharmony_ci / PCK_TH_MULT); 22128c2ecf20Sopenharmony_ci tx_max_coal = 22138c2ecf20Sopenharmony_ci (((tx_max_coal * BDX_TXF_DESC_SZ) + PCK_TH_MULT - 1) 22148c2ecf20Sopenharmony_ci / PCK_TH_MULT); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF) || 22178c2ecf20Sopenharmony_ci (rx_max_coal > 0xF) || (tx_max_coal > 0xF)) 22188c2ecf20Sopenharmony_ci return -EINVAL; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci rdintcm = INT_REG_VAL(rx_coal, GET_INT_COAL_RC(priv->rdintcm), 22218c2ecf20Sopenharmony_ci GET_RXF_TH(priv->rdintcm), rx_max_coal); 22228c2ecf20Sopenharmony_ci tdintcm = INT_REG_VAL(tx_coal, GET_INT_COAL_RC(priv->tdintcm), 0, 22238c2ecf20Sopenharmony_ci tx_max_coal); 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci priv->rdintcm = rdintcm; 22268c2ecf20Sopenharmony_ci priv->tdintcm = tdintcm; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, rdintcm); 22298c2ecf20Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, tdintcm); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci return 0; 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci/* Convert RX fifo size to number of pending packets */ 22358c2ecf20Sopenharmony_cistatic inline int bdx_rx_fifo_size_to_packets(int rx_size) 22368c2ecf20Sopenharmony_ci{ 22378c2ecf20Sopenharmony_ci return (FIFO_SIZE * (1 << rx_size)) / sizeof(struct rxf_desc); 22388c2ecf20Sopenharmony_ci} 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci/* Convert TX fifo size to number of pending packets */ 22418c2ecf20Sopenharmony_cistatic inline int bdx_tx_fifo_size_to_packets(int tx_size) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci return (FIFO_SIZE * (1 << tx_size)) / BDX_TXF_DESC_SZ; 22448c2ecf20Sopenharmony_ci} 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci/* 22478c2ecf20Sopenharmony_ci * bdx_get_ringparam - report ring sizes 22488c2ecf20Sopenharmony_ci * @netdev 22498c2ecf20Sopenharmony_ci * @ring 22508c2ecf20Sopenharmony_ci */ 22518c2ecf20Sopenharmony_cistatic void 22528c2ecf20Sopenharmony_cibdx_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) 22538c2ecf20Sopenharmony_ci{ 22548c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci /*max_pending - the maximum-sized FIFO we allow */ 22578c2ecf20Sopenharmony_ci ring->rx_max_pending = bdx_rx_fifo_size_to_packets(3); 22588c2ecf20Sopenharmony_ci ring->tx_max_pending = bdx_tx_fifo_size_to_packets(3); 22598c2ecf20Sopenharmony_ci ring->rx_pending = bdx_rx_fifo_size_to_packets(priv->rxf_size); 22608c2ecf20Sopenharmony_ci ring->tx_pending = bdx_tx_fifo_size_to_packets(priv->txd_size); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci/* 22648c2ecf20Sopenharmony_ci * bdx_set_ringparam - set ring sizes 22658c2ecf20Sopenharmony_ci * @netdev 22668c2ecf20Sopenharmony_ci * @ring 22678c2ecf20Sopenharmony_ci */ 22688c2ecf20Sopenharmony_cistatic int 22698c2ecf20Sopenharmony_cibdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) 22708c2ecf20Sopenharmony_ci{ 22718c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 22728c2ecf20Sopenharmony_ci int rx_size = 0; 22738c2ecf20Sopenharmony_ci int tx_size = 0; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci for (; rx_size < 4; rx_size++) { 22768c2ecf20Sopenharmony_ci if (bdx_rx_fifo_size_to_packets(rx_size) >= ring->rx_pending) 22778c2ecf20Sopenharmony_ci break; 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci if (rx_size == 4) 22808c2ecf20Sopenharmony_ci rx_size = 3; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci for (; tx_size < 4; tx_size++) { 22838c2ecf20Sopenharmony_ci if (bdx_tx_fifo_size_to_packets(tx_size) >= ring->tx_pending) 22848c2ecf20Sopenharmony_ci break; 22858c2ecf20Sopenharmony_ci } 22868c2ecf20Sopenharmony_ci if (tx_size == 4) 22878c2ecf20Sopenharmony_ci tx_size = 3; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci /*Is there anything to do? */ 22908c2ecf20Sopenharmony_ci if ((rx_size == priv->rxf_size) && 22918c2ecf20Sopenharmony_ci (tx_size == priv->txd_size)) 22928c2ecf20Sopenharmony_ci return 0; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci priv->rxf_size = rx_size; 22958c2ecf20Sopenharmony_ci if (rx_size > 1) 22968c2ecf20Sopenharmony_ci priv->rxd_size = rx_size - 1; 22978c2ecf20Sopenharmony_ci else 22988c2ecf20Sopenharmony_ci priv->rxd_size = rx_size; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci priv->txf_size = priv->txd_size = tx_size; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 23038c2ecf20Sopenharmony_ci bdx_close(netdev); 23048c2ecf20Sopenharmony_ci bdx_open(netdev); 23058c2ecf20Sopenharmony_ci } 23068c2ecf20Sopenharmony_ci return 0; 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci/* 23108c2ecf20Sopenharmony_ci * bdx_get_strings - return a set of strings that describe the requested objects 23118c2ecf20Sopenharmony_ci * @netdev 23128c2ecf20Sopenharmony_ci * @data 23138c2ecf20Sopenharmony_ci */ 23148c2ecf20Sopenharmony_cistatic void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci switch (stringset) { 23178c2ecf20Sopenharmony_ci case ETH_SS_STATS: 23188c2ecf20Sopenharmony_ci memcpy(data, *bdx_stat_names, sizeof(bdx_stat_names)); 23198c2ecf20Sopenharmony_ci break; 23208c2ecf20Sopenharmony_ci } 23218c2ecf20Sopenharmony_ci} 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci/* 23248c2ecf20Sopenharmony_ci * bdx_get_sset_count - return number of statistics or tests 23258c2ecf20Sopenharmony_ci * @netdev 23268c2ecf20Sopenharmony_ci */ 23278c2ecf20Sopenharmony_cistatic int bdx_get_sset_count(struct net_device *netdev, int stringset) 23288c2ecf20Sopenharmony_ci{ 23298c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci switch (stringset) { 23328c2ecf20Sopenharmony_ci case ETH_SS_STATS: 23338c2ecf20Sopenharmony_ci BDX_ASSERT(ARRAY_SIZE(bdx_stat_names) 23348c2ecf20Sopenharmony_ci != sizeof(struct bdx_stats) / sizeof(u64)); 23358c2ecf20Sopenharmony_ci return (priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci return -EINVAL; 23398c2ecf20Sopenharmony_ci} 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci/* 23428c2ecf20Sopenharmony_ci * bdx_get_ethtool_stats - return device's hardware L2 statistics 23438c2ecf20Sopenharmony_ci * @netdev 23448c2ecf20Sopenharmony_ci * @stats 23458c2ecf20Sopenharmony_ci * @data 23468c2ecf20Sopenharmony_ci */ 23478c2ecf20Sopenharmony_cistatic void bdx_get_ethtool_stats(struct net_device *netdev, 23488c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 23498c2ecf20Sopenharmony_ci{ 23508c2ecf20Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci if (priv->stats_flag) { 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci /* Update stats from HW */ 23558c2ecf20Sopenharmony_ci bdx_update_stats(priv); 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci /* Copy data to user buffer */ 23588c2ecf20Sopenharmony_ci memcpy(data, &priv->hw_stats, sizeof(priv->hw_stats)); 23598c2ecf20Sopenharmony_ci } 23608c2ecf20Sopenharmony_ci} 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci/* 23638c2ecf20Sopenharmony_ci * bdx_set_ethtool_ops - ethtool interface implementation 23648c2ecf20Sopenharmony_ci * @netdev 23658c2ecf20Sopenharmony_ci */ 23668c2ecf20Sopenharmony_cistatic void bdx_set_ethtool_ops(struct net_device *netdev) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci static const struct ethtool_ops bdx_ethtool_ops = { 23698c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 23708c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 23718c2ecf20Sopenharmony_ci .get_drvinfo = bdx_get_drvinfo, 23728c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 23738c2ecf20Sopenharmony_ci .get_coalesce = bdx_get_coalesce, 23748c2ecf20Sopenharmony_ci .set_coalesce = bdx_set_coalesce, 23758c2ecf20Sopenharmony_ci .get_ringparam = bdx_get_ringparam, 23768c2ecf20Sopenharmony_ci .set_ringparam = bdx_set_ringparam, 23778c2ecf20Sopenharmony_ci .get_strings = bdx_get_strings, 23788c2ecf20Sopenharmony_ci .get_sset_count = bdx_get_sset_count, 23798c2ecf20Sopenharmony_ci .get_ethtool_stats = bdx_get_ethtool_stats, 23808c2ecf20Sopenharmony_ci .get_link_ksettings = bdx_get_link_ksettings, 23818c2ecf20Sopenharmony_ci }; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci netdev->ethtool_ops = &bdx_ethtool_ops; 23848c2ecf20Sopenharmony_ci} 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci/** 23878c2ecf20Sopenharmony_ci * bdx_remove - Device Removal Routine 23888c2ecf20Sopenharmony_ci * @pdev: PCI device information struct 23898c2ecf20Sopenharmony_ci * 23908c2ecf20Sopenharmony_ci * bdx_remove is called by the PCI subsystem to alert the driver 23918c2ecf20Sopenharmony_ci * that it should release a PCI device. The could be caused by a 23928c2ecf20Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from 23938c2ecf20Sopenharmony_ci * memory. 23948c2ecf20Sopenharmony_ci **/ 23958c2ecf20Sopenharmony_cistatic void bdx_remove(struct pci_dev *pdev) 23968c2ecf20Sopenharmony_ci{ 23978c2ecf20Sopenharmony_ci struct pci_nic *nic = pci_get_drvdata(pdev); 23988c2ecf20Sopenharmony_ci struct net_device *ndev; 23998c2ecf20Sopenharmony_ci int port; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci for (port = 0; port < nic->port_num; port++) { 24028c2ecf20Sopenharmony_ci ndev = nic->priv[port]->ndev; 24038c2ecf20Sopenharmony_ci unregister_netdev(ndev); 24048c2ecf20Sopenharmony_ci free_netdev(ndev); 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /*bdx_hw_reset_direct(nic->regs); */ 24088c2ecf20Sopenharmony_ci#ifdef BDX_MSI 24098c2ecf20Sopenharmony_ci if (nic->irq_type == IRQ_MSI) 24108c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 24118c2ecf20Sopenharmony_ci#endif 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci iounmap(nic->regs); 24148c2ecf20Sopenharmony_ci pci_release_regions(pdev); 24158c2ecf20Sopenharmony_ci pci_disable_device(pdev); 24168c2ecf20Sopenharmony_ci vfree(nic); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci RET(); 24198c2ecf20Sopenharmony_ci} 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_cistatic struct pci_driver bdx_pci_driver = { 24228c2ecf20Sopenharmony_ci .name = BDX_DRV_NAME, 24238c2ecf20Sopenharmony_ci .id_table = bdx_pci_tbl, 24248c2ecf20Sopenharmony_ci .probe = bdx_probe, 24258c2ecf20Sopenharmony_ci .remove = bdx_remove, 24268c2ecf20Sopenharmony_ci}; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci/* 24298c2ecf20Sopenharmony_ci * print_driver_id - print parameters of the driver build 24308c2ecf20Sopenharmony_ci */ 24318c2ecf20Sopenharmony_cistatic void __init print_driver_id(void) 24328c2ecf20Sopenharmony_ci{ 24338c2ecf20Sopenharmony_ci pr_info("%s, %s\n", BDX_DRV_DESC, BDX_DRV_VERSION); 24348c2ecf20Sopenharmony_ci pr_info("Options: hw_csum %s\n", BDX_MSI_STRING); 24358c2ecf20Sopenharmony_ci} 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_cistatic int __init bdx_module_init(void) 24388c2ecf20Sopenharmony_ci{ 24398c2ecf20Sopenharmony_ci ENTER; 24408c2ecf20Sopenharmony_ci init_txd_sizes(); 24418c2ecf20Sopenharmony_ci print_driver_id(); 24428c2ecf20Sopenharmony_ci RET(pci_register_driver(&bdx_pci_driver)); 24438c2ecf20Sopenharmony_ci} 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_cimodule_init(bdx_module_init); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_cistatic void __exit bdx_module_exit(void) 24488c2ecf20Sopenharmony_ci{ 24498c2ecf20Sopenharmony_ci ENTER; 24508c2ecf20Sopenharmony_ci pci_unregister_driver(&bdx_pci_driver); 24518c2ecf20Sopenharmony_ci RET(); 24528c2ecf20Sopenharmony_ci} 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_cimodule_exit(bdx_module_exit); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 24578c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 24588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(BDX_DRV_DESC); 24598c2ecf20Sopenharmony_ciMODULE_FIRMWARE("tehuti/bdx.bin"); 2460