162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Tehuti Networks(R) Network Driver 462306a36Sopenharmony_ci * ethtool interface implementation 562306a36Sopenharmony_ci * Copyright (C) 2007 Tehuti Networks Ltd. All rights reserved 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * RX HW/SW interaction overview 1062306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1162306a36Sopenharmony_ci * There are 2 types of RX communication channels between driver and NIC. 1262306a36Sopenharmony_ci * 1) RX Free Fifo - RXF - holds descriptors of empty buffers to accept incoming 1362306a36Sopenharmony_ci * traffic. This Fifo is filled by SW and is readen by HW. Each descriptor holds 1462306a36Sopenharmony_ci * info about buffer's location, size and ID. An ID field is used to identify a 1562306a36Sopenharmony_ci * buffer when it's returned with data via RXD Fifo (see below) 1662306a36Sopenharmony_ci * 2) RX Data Fifo - RXD - holds descriptors of full buffers. This Fifo is 1762306a36Sopenharmony_ci * filled by HW and is readen by SW. Each descriptor holds status and ID. 1862306a36Sopenharmony_ci * HW pops descriptor from RXF Fifo, stores ID, fills buffer with incoming data, 1962306a36Sopenharmony_ci * via dma moves it into host memory, builds new RXD descriptor with same ID, 2062306a36Sopenharmony_ci * pushes it into RXD Fifo and raises interrupt to indicate new RX data. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Current NIC configuration (registers + firmware) makes NIC use 2 RXF Fifos. 2362306a36Sopenharmony_ci * One holds 1.5K packets and another - 26K packets. Depending on incoming 2462306a36Sopenharmony_ci * packet size, HW desides on a RXF Fifo to pop buffer from. When packet is 2562306a36Sopenharmony_ci * filled with data, HW builds new RXD descriptor for it and push it into single 2662306a36Sopenharmony_ci * RXD Fifo. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * RX SW Data Structures 2962306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~ 3062306a36Sopenharmony_ci * skb db - used to keep track of all skbs owned by SW and their dma addresses. 3162306a36Sopenharmony_ci * For RX case, ownership lasts from allocating new empty skb for RXF until 3262306a36Sopenharmony_ci * accepting full skb from RXD and passing it to OS. Each RXF Fifo has its own 3362306a36Sopenharmony_ci * skb db. Implemented as array with bitmask. 3462306a36Sopenharmony_ci * fifo - keeps info about fifo's size and location, relevant HW registers, 3562306a36Sopenharmony_ci * usage and skb db. Each RXD and RXF Fifo has its own fifo structure. 3662306a36Sopenharmony_ci * Implemented as simple struct. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * RX SW Execution Flow 3962306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~ 4062306a36Sopenharmony_ci * Upon initialization (ifconfig up) driver creates RX fifos and initializes 4162306a36Sopenharmony_ci * relevant registers. At the end of init phase, driver enables interrupts. 4262306a36Sopenharmony_ci * NIC sees that there is no RXF buffers and raises 4362306a36Sopenharmony_ci * RD_INTR interrupt, isr fills skbs and Rx begins. 4462306a36Sopenharmony_ci * Driver has two receive operation modes: 4562306a36Sopenharmony_ci * NAPI - interrupt-driven mixed with polling 4662306a36Sopenharmony_ci * interrupt-driven only 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Interrupt-driven only flow is following. When buffer is ready, HW raises 4962306a36Sopenharmony_ci * interrupt and isr is called. isr collects all available packets 5062306a36Sopenharmony_ci * (bdx_rx_receive), refills skbs (bdx_rx_alloc_skbs) and exit. 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci * Rx buffer allocation note 5362306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~ 5462306a36Sopenharmony_ci * Driver cares to feed such amount of RxF descriptors that respective amount of 5562306a36Sopenharmony_ci * RxD descriptors can not fill entire RxD fifo. The main reason is lack of 5662306a36Sopenharmony_ci * overflow check in Bordeaux for RxD fifo free/used size. 5762306a36Sopenharmony_ci * FIXME: this is NOT fully implemented, more work should be done 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#include "tehuti.h" 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct pci_device_id bdx_pci_tbl[] = { 6662306a36Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3009), }, 6762306a36Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3010), }, 6862306a36Sopenharmony_ci { PCI_VDEVICE(TEHUTI, 0x3014), }, 6962306a36Sopenharmony_ci { 0 } 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bdx_pci_tbl); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Definitions needed by ISR or NAPI functions */ 7562306a36Sopenharmony_cistatic void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f); 7662306a36Sopenharmony_cistatic void bdx_tx_cleanup(struct bdx_priv *priv); 7762306a36Sopenharmony_cistatic int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Definitions needed by FW loading */ 8062306a36Sopenharmony_cistatic void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Definitions needed by hw_start */ 8362306a36Sopenharmony_cistatic int bdx_tx_init(struct bdx_priv *priv); 8462306a36Sopenharmony_cistatic int bdx_rx_init(struct bdx_priv *priv); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Definitions needed by bdx_close */ 8762306a36Sopenharmony_cistatic void bdx_rx_free(struct bdx_priv *priv); 8862306a36Sopenharmony_cistatic void bdx_tx_free(struct bdx_priv *priv); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Definitions needed by bdx_probe */ 9162306a36Sopenharmony_cistatic void bdx_set_ethtool_ops(struct net_device *netdev); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/************************************************************************* 9462306a36Sopenharmony_ci * Print Info * 9562306a36Sopenharmony_ci *************************************************************************/ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void print_hw_id(struct pci_dev *pdev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct pci_nic *nic = pci_get_drvdata(pdev); 10062306a36Sopenharmony_ci u16 pci_link_status = 0; 10162306a36Sopenharmony_ci u16 pci_ctrl = 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_LINK_STATUS_REG, &pci_link_status); 10462306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_DEV_CTRL_REG, &pci_ctrl); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci pr_info("%s%s\n", BDX_NIC_NAME, 10762306a36Sopenharmony_ci nic->port_num == 1 ? "" : ", 2-Port"); 10862306a36Sopenharmony_ci pr_info("srom 0x%x fpga %d build %u lane# %d max_pl 0x%x mrrs 0x%x\n", 10962306a36Sopenharmony_ci readl(nic->regs + SROM_VER), readl(nic->regs + FPGA_VER) & 0xFFF, 11062306a36Sopenharmony_ci readl(nic->regs + FPGA_SEED), 11162306a36Sopenharmony_ci GET_LINK_STATUS_LANES(pci_link_status), 11262306a36Sopenharmony_ci GET_DEV_CTRL_MAXPL(pci_ctrl), GET_DEV_CTRL_MRRS(pci_ctrl)); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void print_fw_id(struct pci_nic *nic) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci pr_info("fw 0x%x\n", readl(nic->regs + FW_VER)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void print_eth_id(struct net_device *ndev) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci netdev_info(ndev, "%s, Port %c\n", 12362306a36Sopenharmony_ci BDX_NIC_NAME, (ndev->if_port == 0) ? 'A' : 'B'); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/************************************************************************* 12862306a36Sopenharmony_ci * Code * 12962306a36Sopenharmony_ci *************************************************************************/ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define bdx_enable_interrupts(priv) \ 13262306a36Sopenharmony_ci do { WRITE_REG(priv, regIMR, IR_RUN); } while (0) 13362306a36Sopenharmony_ci#define bdx_disable_interrupts(priv) \ 13462306a36Sopenharmony_ci do { WRITE_REG(priv, regIMR, 0); } while (0) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * bdx_fifo_init - create TX/RX descriptor fifo for host-NIC communication. 13862306a36Sopenharmony_ci * @priv: NIC private structure 13962306a36Sopenharmony_ci * @f: fifo to initialize 14062306a36Sopenharmony_ci * @fsz_type: fifo size type: 0-4KB, 1-8KB, 2-16KB, 3-32KB 14162306a36Sopenharmony_ci * @reg_CFG0: offsets of registers relative to base address 14262306a36Sopenharmony_ci * @reg_CFG1: offsets of registers relative to base address 14362306a36Sopenharmony_ci * @reg_RPTR: offsets of registers relative to base address 14462306a36Sopenharmony_ci * @reg_WPTR: offsets of registers relative to base address 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * 1K extra space is allocated at the end of the fifo to simplify 14762306a36Sopenharmony_ci * processing of descriptors that wraps around fifo's end 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * Returns 0 on success, negative value on failure 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic int 15362306a36Sopenharmony_cibdx_fifo_init(struct bdx_priv *priv, struct fifo *f, int fsz_type, 15462306a36Sopenharmony_ci u16 reg_CFG0, u16 reg_CFG1, u16 reg_RPTR, u16 reg_WPTR) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci u16 memsz = FIFO_SIZE * (1 << fsz_type); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci memset(f, 0, sizeof(struct fifo)); 15962306a36Sopenharmony_ci /* dma_alloc_coherent gives us 4k-aligned memory */ 16062306a36Sopenharmony_ci f->va = dma_alloc_coherent(&priv->pdev->dev, memsz + FIFO_EXTRA_SPACE, 16162306a36Sopenharmony_ci &f->da, GFP_ATOMIC); 16262306a36Sopenharmony_ci if (!f->va) { 16362306a36Sopenharmony_ci pr_err("dma_alloc_coherent failed\n"); 16462306a36Sopenharmony_ci RET(-ENOMEM); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci f->reg_CFG0 = reg_CFG0; 16762306a36Sopenharmony_ci f->reg_CFG1 = reg_CFG1; 16862306a36Sopenharmony_ci f->reg_RPTR = reg_RPTR; 16962306a36Sopenharmony_ci f->reg_WPTR = reg_WPTR; 17062306a36Sopenharmony_ci f->rptr = 0; 17162306a36Sopenharmony_ci f->wptr = 0; 17262306a36Sopenharmony_ci f->memsz = memsz; 17362306a36Sopenharmony_ci f->size_mask = memsz - 1; 17462306a36Sopenharmony_ci WRITE_REG(priv, reg_CFG0, (u32) ((f->da & TX_RX_CFG0_BASE) | fsz_type)); 17562306a36Sopenharmony_ci WRITE_REG(priv, reg_CFG1, H32_64(f->da)); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci RET(0); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * bdx_fifo_free - free all resources used by fifo 18262306a36Sopenharmony_ci * @priv: NIC private structure 18362306a36Sopenharmony_ci * @f: fifo to release 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic void bdx_fifo_free(struct bdx_priv *priv, struct fifo *f) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci ENTER; 18862306a36Sopenharmony_ci if (f->va) { 18962306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 19062306a36Sopenharmony_ci f->memsz + FIFO_EXTRA_SPACE, f->va, f->da); 19162306a36Sopenharmony_ci f->va = NULL; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci RET(); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * bdx_link_changed - notifies OS about hw link state. 19862306a36Sopenharmony_ci * @priv: hw adapter structure 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void bdx_link_changed(struct bdx_priv *priv) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u32 link = READ_REG(priv, regMAC_LNK_STAT) & MAC_LINK_STAT; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!link) { 20562306a36Sopenharmony_ci if (netif_carrier_ok(priv->ndev)) { 20662306a36Sopenharmony_ci netif_stop_queue(priv->ndev); 20762306a36Sopenharmony_ci netif_carrier_off(priv->ndev); 20862306a36Sopenharmony_ci netdev_err(priv->ndev, "Link Down\n"); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci if (!netif_carrier_ok(priv->ndev)) { 21262306a36Sopenharmony_ci netif_wake_queue(priv->ndev); 21362306a36Sopenharmony_ci netif_carrier_on(priv->ndev); 21462306a36Sopenharmony_ci netdev_err(priv->ndev, "Link Up\n"); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void bdx_isr_extra(struct bdx_priv *priv, u32 isr) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (isr & IR_RX_FREE_0) { 22262306a36Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 22362306a36Sopenharmony_ci DBG("RX_FREE_0\n"); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (isr & IR_LNKCHG0) 22762306a36Sopenharmony_ci bdx_link_changed(priv); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (isr & IR_PCIE_LINK) 23062306a36Sopenharmony_ci netdev_err(priv->ndev, "PCI-E Link Fault\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (isr & IR_PCIE_TOUT) 23362306a36Sopenharmony_ci netdev_err(priv->ndev, "PCI-E Time Out\n"); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/** 23862306a36Sopenharmony_ci * bdx_isr_napi - Interrupt Service Routine for Bordeaux NIC 23962306a36Sopenharmony_ci * @irq: interrupt number 24062306a36Sopenharmony_ci * @dev: network device 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * Return IRQ_NONE if it was not our interrupt, IRQ_HANDLED - otherwise 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * It reads ISR register to know interrupt reasons, and proceed them one by one. 24562306a36Sopenharmony_ci * Reasons of interest are: 24662306a36Sopenharmony_ci * RX_DESC - new packet has arrived and RXD fifo holds its descriptor 24762306a36Sopenharmony_ci * RX_FREE - number of free Rx buffers in RXF fifo gets low 24862306a36Sopenharmony_ci * TX_FREE - packet was transmited and RXF fifo holds its descriptor 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic irqreturn_t bdx_isr_napi(int irq, void *dev) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct net_device *ndev = dev; 25462306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 25562306a36Sopenharmony_ci u32 isr; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ENTER; 25862306a36Sopenharmony_ci isr = (READ_REG(priv, regISR) & IR_RUN); 25962306a36Sopenharmony_ci if (unlikely(!isr)) { 26062306a36Sopenharmony_ci bdx_enable_interrupts(priv); 26162306a36Sopenharmony_ci return IRQ_NONE; /* Not our interrupt */ 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (isr & IR_EXTRA) 26562306a36Sopenharmony_ci bdx_isr_extra(priv, isr); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (isr & (IR_RX_DESC_0 | IR_TX_FREE_0)) { 26862306a36Sopenharmony_ci if (likely(napi_schedule_prep(&priv->napi))) { 26962306a36Sopenharmony_ci __napi_schedule(&priv->napi); 27062306a36Sopenharmony_ci RET(IRQ_HANDLED); 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci /* NOTE: we get here if intr has slipped into window 27362306a36Sopenharmony_ci * between these lines in bdx_poll: 27462306a36Sopenharmony_ci * bdx_enable_interrupts(priv); 27562306a36Sopenharmony_ci * return 0; 27662306a36Sopenharmony_ci * currently intrs are disabled (since we read ISR), 27762306a36Sopenharmony_ci * and we have failed to register next poll. 27862306a36Sopenharmony_ci * so we read the regs to trigger chip 27962306a36Sopenharmony_ci * and allow further interupts. */ 28062306a36Sopenharmony_ci READ_REG(priv, regTXF_WPTR_0); 28162306a36Sopenharmony_ci READ_REG(priv, regRXD_WPTR_0); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci bdx_enable_interrupts(priv); 28662306a36Sopenharmony_ci RET(IRQ_HANDLED); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int bdx_poll(struct napi_struct *napi, int budget) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct bdx_priv *priv = container_of(napi, struct bdx_priv, napi); 29262306a36Sopenharmony_ci int work_done; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ENTER; 29562306a36Sopenharmony_ci bdx_tx_cleanup(priv); 29662306a36Sopenharmony_ci work_done = bdx_rx_receive(priv, &priv->rxd_fifo0, budget); 29762306a36Sopenharmony_ci if ((work_done < budget) || 29862306a36Sopenharmony_ci (priv->napi_stop++ >= 30)) { 29962306a36Sopenharmony_ci DBG("rx poll is done. backing to isr-driven\n"); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* from time to time we exit to let NAPI layer release 30262306a36Sopenharmony_ci * device lock and allow waiting tasks (eg rmmod) to advance) */ 30362306a36Sopenharmony_ci priv->napi_stop = 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci napi_complete_done(napi, work_done); 30662306a36Sopenharmony_ci bdx_enable_interrupts(priv); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci return work_done; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/** 31262306a36Sopenharmony_ci * bdx_fw_load - loads firmware to NIC 31362306a36Sopenharmony_ci * @priv: NIC private structure 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Firmware is loaded via TXD fifo, so it must be initialized first. 31662306a36Sopenharmony_ci * Firware must be loaded once per NIC not per PCI device provided by NIC (NIC 31762306a36Sopenharmony_ci * can have few of them). So all drivers use semaphore register to choose one 31862306a36Sopenharmony_ci * that will actually load FW to NIC. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int bdx_fw_load(struct bdx_priv *priv) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci const struct firmware *fw = NULL; 32462306a36Sopenharmony_ci int master, i; 32562306a36Sopenharmony_ci int rc; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ENTER; 32862306a36Sopenharmony_ci master = READ_REG(priv, regINIT_SEMAPHORE); 32962306a36Sopenharmony_ci if (!READ_REG(priv, regINIT_STATUS) && master) { 33062306a36Sopenharmony_ci rc = request_firmware(&fw, "tehuti/bdx.bin", &priv->pdev->dev); 33162306a36Sopenharmony_ci if (rc) 33262306a36Sopenharmony_ci goto out; 33362306a36Sopenharmony_ci bdx_tx_push_desc_safe(priv, (char *)fw->data, fw->size); 33462306a36Sopenharmony_ci mdelay(100); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci for (i = 0; i < 200; i++) { 33762306a36Sopenharmony_ci if (READ_REG(priv, regINIT_STATUS)) { 33862306a36Sopenharmony_ci rc = 0; 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci mdelay(2); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci rc = -EIO; 34462306a36Sopenharmony_ciout: 34562306a36Sopenharmony_ci if (master) 34662306a36Sopenharmony_ci WRITE_REG(priv, regINIT_SEMAPHORE, 1); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci release_firmware(fw); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (rc) { 35162306a36Sopenharmony_ci netdev_err(priv->ndev, "firmware loading failed\n"); 35262306a36Sopenharmony_ci if (rc == -EIO) 35362306a36Sopenharmony_ci DBG("VPC = 0x%x VIC = 0x%x INIT_STATUS = 0x%x i=%d\n", 35462306a36Sopenharmony_ci READ_REG(priv, regVPC), 35562306a36Sopenharmony_ci READ_REG(priv, regVIC), 35662306a36Sopenharmony_ci READ_REG(priv, regINIT_STATUS), i); 35762306a36Sopenharmony_ci RET(rc); 35862306a36Sopenharmony_ci } else { 35962306a36Sopenharmony_ci DBG("%s: firmware loading success\n", priv->ndev->name); 36062306a36Sopenharmony_ci RET(0); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void bdx_restore_mac(struct net_device *ndev, struct bdx_priv *priv) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci u32 val; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ENTER; 36962306a36Sopenharmony_ci DBG("mac0=%x mac1=%x mac2=%x\n", 37062306a36Sopenharmony_ci READ_REG(priv, regUNC_MAC0_A), 37162306a36Sopenharmony_ci READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci val = (ndev->dev_addr[0] << 8) | (ndev->dev_addr[1]); 37462306a36Sopenharmony_ci WRITE_REG(priv, regUNC_MAC2_A, val); 37562306a36Sopenharmony_ci val = (ndev->dev_addr[2] << 8) | (ndev->dev_addr[3]); 37662306a36Sopenharmony_ci WRITE_REG(priv, regUNC_MAC1_A, val); 37762306a36Sopenharmony_ci val = (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5]); 37862306a36Sopenharmony_ci WRITE_REG(priv, regUNC_MAC0_A, val); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci DBG("mac0=%x mac1=%x mac2=%x\n", 38162306a36Sopenharmony_ci READ_REG(priv, regUNC_MAC0_A), 38262306a36Sopenharmony_ci READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); 38362306a36Sopenharmony_ci RET(); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * bdx_hw_start - inits registers and starts HW's Rx and Tx engines 38862306a36Sopenharmony_ci * @priv: NIC private structure 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic int bdx_hw_start(struct bdx_priv *priv) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int rc = -EIO; 39362306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ENTER; 39662306a36Sopenharmony_ci bdx_link_changed(priv); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 10G overall max length (vlan, eth&ip header, ip payload, crc) */ 39962306a36Sopenharmony_ci WRITE_REG(priv, regFRM_LENGTH, 0X3FE0); 40062306a36Sopenharmony_ci WRITE_REG(priv, regPAUSE_QUANT, 0x96); 40162306a36Sopenharmony_ci WRITE_REG(priv, regRX_FIFO_SECTION, 0x800010); 40262306a36Sopenharmony_ci WRITE_REG(priv, regTX_FIFO_SECTION, 0xE00010); 40362306a36Sopenharmony_ci WRITE_REG(priv, regRX_FULLNESS, 0); 40462306a36Sopenharmony_ci WRITE_REG(priv, regTX_FULLNESS, 0); 40562306a36Sopenharmony_ci WRITE_REG(priv, regCTRLST, 40662306a36Sopenharmony_ci regCTRLST_BASE | regCTRLST_RX_ENA | regCTRLST_TX_ENA); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci WRITE_REG(priv, regVGLB, 0); 40962306a36Sopenharmony_ci WRITE_REG(priv, regMAX_FRAME_A, 41062306a36Sopenharmony_ci priv->rxf_fifo0.m.pktsz & MAX_FRAME_AB_VAL); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci DBG("RDINTCM=%08x\n", priv->rdintcm); /*NOTE: test script uses this */ 41362306a36Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, priv->rdintcm); 41462306a36Sopenharmony_ci WRITE_REG(priv, regRDINTCM2, 0); /*cpu_to_le32(rcm.val)); */ 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci DBG("TDINTCM=%08x\n", priv->tdintcm); /*NOTE: test script uses this */ 41762306a36Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, priv->tdintcm); /* old val = 0x300064 */ 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Enable timer interrupt once in 2 secs. */ 42062306a36Sopenharmony_ci /*WRITE_REG(priv, regGTMR0, ((GTMR_SEC * 2) & GTMR_DATA)); */ 42162306a36Sopenharmony_ci bdx_restore_mac(priv->ndev, priv); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, GMAC_RX_FILTER_OSEN | 42462306a36Sopenharmony_ci GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci#define BDX_IRQ_TYPE ((priv->nic->irq_type == IRQ_MSI) ? 0 : IRQF_SHARED) 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci rc = request_irq(priv->pdev->irq, bdx_isr_napi, BDX_IRQ_TYPE, 42962306a36Sopenharmony_ci ndev->name, ndev); 43062306a36Sopenharmony_ci if (rc) 43162306a36Sopenharmony_ci goto err_irq; 43262306a36Sopenharmony_ci bdx_enable_interrupts(priv); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci RET(0); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cierr_irq: 43762306a36Sopenharmony_ci RET(rc); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void bdx_hw_stop(struct bdx_priv *priv) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci ENTER; 44362306a36Sopenharmony_ci bdx_disable_interrupts(priv); 44462306a36Sopenharmony_ci free_irq(priv->pdev->irq, priv->ndev); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci netif_carrier_off(priv->ndev); 44762306a36Sopenharmony_ci netif_stop_queue(priv->ndev); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci RET(); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int bdx_hw_reset_direct(void __iomem *regs) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci u32 val, i; 45562306a36Sopenharmony_ci ENTER; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* reset sequences: read, write 1, read, write 0 */ 45862306a36Sopenharmony_ci val = readl(regs + regCLKPLL); 45962306a36Sopenharmony_ci writel((val | CLKPLL_SFTRST) + 0x8, regs + regCLKPLL); 46062306a36Sopenharmony_ci udelay(50); 46162306a36Sopenharmony_ci val = readl(regs + regCLKPLL); 46262306a36Sopenharmony_ci writel(val & ~CLKPLL_SFTRST, regs + regCLKPLL); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* check that the PLLs are locked and reset ended */ 46562306a36Sopenharmony_ci for (i = 0; i < 70; i++, mdelay(10)) 46662306a36Sopenharmony_ci if ((readl(regs + regCLKPLL) & CLKPLL_LKD) == CLKPLL_LKD) { 46762306a36Sopenharmony_ci /* do any PCI-E read transaction */ 46862306a36Sopenharmony_ci readl(regs + regRXD_CFG0_0); 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci pr_err("HW reset failed\n"); 47262306a36Sopenharmony_ci return 1; /* failure */ 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int bdx_hw_reset(struct bdx_priv *priv) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci u32 val, i; 47862306a36Sopenharmony_ci ENTER; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (priv->port == 0) { 48162306a36Sopenharmony_ci /* reset sequences: read, write 1, read, write 0 */ 48262306a36Sopenharmony_ci val = READ_REG(priv, regCLKPLL); 48362306a36Sopenharmony_ci WRITE_REG(priv, regCLKPLL, (val | CLKPLL_SFTRST) + 0x8); 48462306a36Sopenharmony_ci udelay(50); 48562306a36Sopenharmony_ci val = READ_REG(priv, regCLKPLL); 48662306a36Sopenharmony_ci WRITE_REG(priv, regCLKPLL, val & ~CLKPLL_SFTRST); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci /* check that the PLLs are locked and reset ended */ 48962306a36Sopenharmony_ci for (i = 0; i < 70; i++, mdelay(10)) 49062306a36Sopenharmony_ci if ((READ_REG(priv, regCLKPLL) & CLKPLL_LKD) == CLKPLL_LKD) { 49162306a36Sopenharmony_ci /* do any PCI-E read transaction */ 49262306a36Sopenharmony_ci READ_REG(priv, regRXD_CFG0_0); 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci pr_err("HW reset failed\n"); 49662306a36Sopenharmony_ci return 1; /* failure */ 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int bdx_sw_reset(struct bdx_priv *priv) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int i; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ENTER; 50462306a36Sopenharmony_ci /* 1. load MAC (obsolete) */ 50562306a36Sopenharmony_ci /* 2. disable Rx (and Tx) */ 50662306a36Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, 0); 50762306a36Sopenharmony_ci mdelay(100); 50862306a36Sopenharmony_ci /* 3. disable port */ 50962306a36Sopenharmony_ci WRITE_REG(priv, regDIS_PORT, 1); 51062306a36Sopenharmony_ci /* 4. disable queue */ 51162306a36Sopenharmony_ci WRITE_REG(priv, regDIS_QU, 1); 51262306a36Sopenharmony_ci /* 5. wait until hw is disabled */ 51362306a36Sopenharmony_ci for (i = 0; i < 50; i++) { 51462306a36Sopenharmony_ci if (READ_REG(priv, regRST_PORT) & 1) 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci mdelay(10); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci if (i == 50) 51962306a36Sopenharmony_ci netdev_err(priv->ndev, "SW reset timeout. continuing anyway\n"); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 6. disable intrs */ 52262306a36Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, 0); 52362306a36Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, 0); 52462306a36Sopenharmony_ci WRITE_REG(priv, regIMR, 0); 52562306a36Sopenharmony_ci READ_REG(priv, regISR); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 7. reset queue */ 52862306a36Sopenharmony_ci WRITE_REG(priv, regRST_QU, 1); 52962306a36Sopenharmony_ci /* 8. reset port */ 53062306a36Sopenharmony_ci WRITE_REG(priv, regRST_PORT, 1); 53162306a36Sopenharmony_ci /* 9. zero all read and write pointers */ 53262306a36Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 53362306a36Sopenharmony_ci DBG("%x = %x\n", i, READ_REG(priv, i) & TXF_WPTR_WR_PTR); 53462306a36Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 53562306a36Sopenharmony_ci WRITE_REG(priv, i, 0); 53662306a36Sopenharmony_ci /* 10. unseet port disable */ 53762306a36Sopenharmony_ci WRITE_REG(priv, regDIS_PORT, 0); 53862306a36Sopenharmony_ci /* 11. unset queue disable */ 53962306a36Sopenharmony_ci WRITE_REG(priv, regDIS_QU, 0); 54062306a36Sopenharmony_ci /* 12. unset queue reset */ 54162306a36Sopenharmony_ci WRITE_REG(priv, regRST_QU, 0); 54262306a36Sopenharmony_ci /* 13. unset port reset */ 54362306a36Sopenharmony_ci WRITE_REG(priv, regRST_PORT, 0); 54462306a36Sopenharmony_ci /* 14. enable Rx */ 54562306a36Sopenharmony_ci /* skiped. will be done later */ 54662306a36Sopenharmony_ci /* 15. save MAC (obsolete) */ 54762306a36Sopenharmony_ci for (i = regTXD_WPTR_0; i <= regTXF_RPTR_3; i += 0x10) 54862306a36Sopenharmony_ci DBG("%x = %x\n", i, READ_REG(priv, i) & TXF_WPTR_WR_PTR); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci RET(0); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* bdx_reset - performs right type of reset depending on hw type */ 55462306a36Sopenharmony_cistatic int bdx_reset(struct bdx_priv *priv) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci ENTER; 55762306a36Sopenharmony_ci RET((priv->pdev->device == 0x3009) 55862306a36Sopenharmony_ci ? bdx_hw_reset(priv) 55962306a36Sopenharmony_ci : bdx_sw_reset(priv)); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/** 56362306a36Sopenharmony_ci * bdx_close - Disables a network interface 56462306a36Sopenharmony_ci * @ndev: network interface device structure 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci * Returns 0, this is not allowed to fail 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * The close entry point is called when an interface is de-activated 56962306a36Sopenharmony_ci * by the OS. The hardware is still under the drivers control, but 57062306a36Sopenharmony_ci * needs to be disabled. A global MAC reset is issued to stop the 57162306a36Sopenharmony_ci * hardware, and all transmit and receive resources are freed. 57262306a36Sopenharmony_ci **/ 57362306a36Sopenharmony_cistatic int bdx_close(struct net_device *ndev) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct bdx_priv *priv = NULL; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ENTER; 57862306a36Sopenharmony_ci priv = netdev_priv(ndev); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci napi_disable(&priv->napi); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci bdx_reset(priv); 58362306a36Sopenharmony_ci bdx_hw_stop(priv); 58462306a36Sopenharmony_ci bdx_rx_free(priv); 58562306a36Sopenharmony_ci bdx_tx_free(priv); 58662306a36Sopenharmony_ci RET(0); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/** 59062306a36Sopenharmony_ci * bdx_open - Called when a network interface is made active 59162306a36Sopenharmony_ci * @ndev: network interface device structure 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * Returns 0 on success, negative value on failure 59462306a36Sopenharmony_ci * 59562306a36Sopenharmony_ci * The open entry point is called when a network interface is made 59662306a36Sopenharmony_ci * active by the system (IFF_UP). At this point all resources needed 59762306a36Sopenharmony_ci * for transmit and receive operations are allocated, the interrupt 59862306a36Sopenharmony_ci * handler is registered with the OS, the watchdog timer is started, 59962306a36Sopenharmony_ci * and the stack is notified that the interface is ready. 60062306a36Sopenharmony_ci **/ 60162306a36Sopenharmony_cistatic int bdx_open(struct net_device *ndev) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct bdx_priv *priv; 60462306a36Sopenharmony_ci int rc; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ENTER; 60762306a36Sopenharmony_ci priv = netdev_priv(ndev); 60862306a36Sopenharmony_ci bdx_reset(priv); 60962306a36Sopenharmony_ci if (netif_running(ndev)) 61062306a36Sopenharmony_ci netif_stop_queue(priv->ndev); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if ((rc = bdx_tx_init(priv)) || 61362306a36Sopenharmony_ci (rc = bdx_rx_init(priv)) || 61462306a36Sopenharmony_ci (rc = bdx_fw_load(priv))) 61562306a36Sopenharmony_ci goto err; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci rc = bdx_hw_start(priv); 62062306a36Sopenharmony_ci if (rc) 62162306a36Sopenharmony_ci goto err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci napi_enable(&priv->napi); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci print_fw_id(priv->nic); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci RET(0); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cierr: 63062306a36Sopenharmony_ci bdx_close(ndev); 63162306a36Sopenharmony_ci RET(rc); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int bdx_range_check(struct bdx_priv *priv, u32 offset) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci return (offset > (u32) (BDX_REGS_SIZE / priv->nic->port_num)) ? 63762306a36Sopenharmony_ci -EINVAL : 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int bdx_siocdevprivate(struct net_device *ndev, struct ifreq *ifr, 64162306a36Sopenharmony_ci void __user *udata, int cmd) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 64462306a36Sopenharmony_ci u32 data[3]; 64562306a36Sopenharmony_ci int error; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ENTER; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci DBG("jiffies=%ld cmd=%d\n", jiffies, cmd); 65062306a36Sopenharmony_ci if (cmd != SIOCDEVPRIVATE) { 65162306a36Sopenharmony_ci error = copy_from_user(data, udata, sizeof(data)); 65262306a36Sopenharmony_ci if (error) { 65362306a36Sopenharmony_ci pr_err("can't copy from user\n"); 65462306a36Sopenharmony_ci RET(-EFAULT); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci DBG("%d 0x%x 0x%x\n", data[0], data[1], data[2]); 65762306a36Sopenharmony_ci } else { 65862306a36Sopenharmony_ci return -EOPNOTSUPP; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 66262306a36Sopenharmony_ci return -EPERM; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci switch (data[0]) { 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci case BDX_OP_READ: 66762306a36Sopenharmony_ci error = bdx_range_check(priv, data[1]); 66862306a36Sopenharmony_ci if (error < 0) 66962306a36Sopenharmony_ci return error; 67062306a36Sopenharmony_ci data[2] = READ_REG(priv, data[1]); 67162306a36Sopenharmony_ci DBG("read_reg(0x%x)=0x%x (dec %d)\n", data[1], data[2], 67262306a36Sopenharmony_ci data[2]); 67362306a36Sopenharmony_ci error = copy_to_user(udata, data, sizeof(data)); 67462306a36Sopenharmony_ci if (error) 67562306a36Sopenharmony_ci RET(-EFAULT); 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci case BDX_OP_WRITE: 67962306a36Sopenharmony_ci error = bdx_range_check(priv, data[1]); 68062306a36Sopenharmony_ci if (error < 0) 68162306a36Sopenharmony_ci return error; 68262306a36Sopenharmony_ci WRITE_REG(priv, data[1], data[2]); 68362306a36Sopenharmony_ci DBG("write_reg(0x%x, 0x%x)\n", data[1], data[2]); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci default: 68762306a36Sopenharmony_ci RET(-EOPNOTSUPP); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/** 69362306a36Sopenharmony_ci * __bdx_vlan_rx_vid - private helper for adding/killing VLAN vid 69462306a36Sopenharmony_ci * @ndev: network device 69562306a36Sopenharmony_ci * @vid: VLAN vid 69662306a36Sopenharmony_ci * @enable: enable or disable vlan 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Passes VLAN filter table to hardware 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_cistatic void __bdx_vlan_rx_vid(struct net_device *ndev, uint16_t vid, int enable) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 70362306a36Sopenharmony_ci u32 reg, bit, val; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ENTER; 70662306a36Sopenharmony_ci DBG2("vid=%d value=%d\n", (int)vid, enable); 70762306a36Sopenharmony_ci if (unlikely(vid >= 4096)) { 70862306a36Sopenharmony_ci pr_err("invalid VID: %u (> 4096)\n", vid); 70962306a36Sopenharmony_ci RET(); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci reg = regVLAN_0 + (vid / 32) * 4; 71262306a36Sopenharmony_ci bit = 1 << vid % 32; 71362306a36Sopenharmony_ci val = READ_REG(priv, reg); 71462306a36Sopenharmony_ci DBG2("reg=%x, val=%x, bit=%d\n", reg, val, bit); 71562306a36Sopenharmony_ci if (enable) 71662306a36Sopenharmony_ci val |= bit; 71762306a36Sopenharmony_ci else 71862306a36Sopenharmony_ci val &= ~bit; 71962306a36Sopenharmony_ci DBG2("new val %x\n", val); 72062306a36Sopenharmony_ci WRITE_REG(priv, reg, val); 72162306a36Sopenharmony_ci RET(); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/** 72562306a36Sopenharmony_ci * bdx_vlan_rx_add_vid - kernel hook for adding VLAN vid to hw filtering table 72662306a36Sopenharmony_ci * @ndev: network device 72762306a36Sopenharmony_ci * @proto: unused 72862306a36Sopenharmony_ci * @vid: VLAN vid to add 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic int bdx_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci __bdx_vlan_rx_vid(ndev, vid, 1); 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * bdx_vlan_rx_kill_vid - kernel hook for killing VLAN vid in hw filtering table 73862306a36Sopenharmony_ci * @ndev: network device 73962306a36Sopenharmony_ci * @proto: unused 74062306a36Sopenharmony_ci * @vid: VLAN vid to kill 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_cistatic int bdx_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci __bdx_vlan_rx_vid(ndev, vid, 0); 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * bdx_change_mtu - Change the Maximum Transfer Unit 75062306a36Sopenharmony_ci * @ndev: network interface device structure 75162306a36Sopenharmony_ci * @new_mtu: new value for maximum frame size 75262306a36Sopenharmony_ci * 75362306a36Sopenharmony_ci * Returns 0 on success, negative on failure 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_cistatic int bdx_change_mtu(struct net_device *ndev, int new_mtu) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci ENTER; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ndev->mtu = new_mtu; 76062306a36Sopenharmony_ci if (netif_running(ndev)) { 76162306a36Sopenharmony_ci bdx_close(ndev); 76262306a36Sopenharmony_ci bdx_open(ndev); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci RET(0); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic void bdx_setmulti(struct net_device *ndev) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci u32 rxf_val = 77262306a36Sopenharmony_ci GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB | GMAC_RX_FILTER_OSEN; 77362306a36Sopenharmony_ci int i; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci ENTER; 77662306a36Sopenharmony_ci /* IMF - imperfect (hash) rx multicat filter */ 77762306a36Sopenharmony_ci /* PMF - perfect rx multicat filter */ 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* FIXME: RXE(OFF) */ 78062306a36Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 78162306a36Sopenharmony_ci rxf_val |= GMAC_RX_FILTER_PRM; 78262306a36Sopenharmony_ci } else if (ndev->flags & IFF_ALLMULTI) { 78362306a36Sopenharmony_ci /* set IMF to accept all multicast frmaes */ 78462306a36Sopenharmony_ci for (i = 0; i < MAC_MCST_HASH_NUM; i++) 78562306a36Sopenharmony_ci WRITE_REG(priv, regRX_MCST_HASH0 + i * 4, ~0); 78662306a36Sopenharmony_ci } else if (!netdev_mc_empty(ndev)) { 78762306a36Sopenharmony_ci u8 hash; 78862306a36Sopenharmony_ci struct netdev_hw_addr *ha; 78962306a36Sopenharmony_ci u32 reg, val; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* set IMF to deny all multicast frames */ 79262306a36Sopenharmony_ci for (i = 0; i < MAC_MCST_HASH_NUM; i++) 79362306a36Sopenharmony_ci WRITE_REG(priv, regRX_MCST_HASH0 + i * 4, 0); 79462306a36Sopenharmony_ci /* set PMF to deny all multicast frames */ 79562306a36Sopenharmony_ci for (i = 0; i < MAC_MCST_NUM; i++) { 79662306a36Sopenharmony_ci WRITE_REG(priv, regRX_MAC_MCST0 + i * 8, 0); 79762306a36Sopenharmony_ci WRITE_REG(priv, regRX_MAC_MCST1 + i * 8, 0); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* use PMF to accept first MAC_MCST_NUM (15) addresses */ 80162306a36Sopenharmony_ci /* TBD: sort addresses and write them in ascending order 80262306a36Sopenharmony_ci * into RX_MAC_MCST regs. we skip this phase now and accept ALL 80362306a36Sopenharmony_ci * multicast frames throu IMF */ 80462306a36Sopenharmony_ci /* accept the rest of addresses throu IMF */ 80562306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 80662306a36Sopenharmony_ci hash = 0; 80762306a36Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 80862306a36Sopenharmony_ci hash ^= ha->addr[i]; 80962306a36Sopenharmony_ci reg = regRX_MCST_HASH0 + ((hash >> 5) << 2); 81062306a36Sopenharmony_ci val = READ_REG(priv, reg); 81162306a36Sopenharmony_ci val |= (1 << (hash % 32)); 81262306a36Sopenharmony_ci WRITE_REG(priv, reg, val); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci } else { 81662306a36Sopenharmony_ci DBG("only own mac %d\n", netdev_mc_count(ndev)); 81762306a36Sopenharmony_ci rxf_val |= GMAC_RX_FILTER_AB; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci WRITE_REG(priv, regGMAC_RXF_A, rxf_val); 82062306a36Sopenharmony_ci /* enable RX */ 82162306a36Sopenharmony_ci /* FIXME: RXE(ON) */ 82262306a36Sopenharmony_ci RET(); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int bdx_set_mac(struct net_device *ndev, void *p) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 82862306a36Sopenharmony_ci struct sockaddr *addr = p; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci ENTER; 83162306a36Sopenharmony_ci /* 83262306a36Sopenharmony_ci if (netif_running(dev)) 83362306a36Sopenharmony_ci return -EBUSY 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci eth_hw_addr_set(ndev, addr->sa_data); 83662306a36Sopenharmony_ci bdx_restore_mac(ndev, priv); 83762306a36Sopenharmony_ci RET(0); 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int bdx_read_mac(struct bdx_priv *priv) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci u16 macAddress[3], i; 84362306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 84462306a36Sopenharmony_ci ENTER; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci macAddress[2] = READ_REG(priv, regUNC_MAC0_A); 84762306a36Sopenharmony_ci macAddress[2] = READ_REG(priv, regUNC_MAC0_A); 84862306a36Sopenharmony_ci macAddress[1] = READ_REG(priv, regUNC_MAC1_A); 84962306a36Sopenharmony_ci macAddress[1] = READ_REG(priv, regUNC_MAC1_A); 85062306a36Sopenharmony_ci macAddress[0] = READ_REG(priv, regUNC_MAC2_A); 85162306a36Sopenharmony_ci macAddress[0] = READ_REG(priv, regUNC_MAC2_A); 85262306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 85362306a36Sopenharmony_ci addr[i * 2 + 1] = macAddress[i]; 85462306a36Sopenharmony_ci addr[i * 2] = macAddress[i] >> 8; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci eth_hw_addr_set(priv->ndev, addr); 85762306a36Sopenharmony_ci RET(0); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic u64 bdx_read_l2stat(struct bdx_priv *priv, int reg) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci u64 val; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci val = READ_REG(priv, reg); 86562306a36Sopenharmony_ci val |= ((u64) READ_REG(priv, reg + 8)) << 32; 86662306a36Sopenharmony_ci return val; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/*Do the statistics-update work*/ 87062306a36Sopenharmony_cistatic void bdx_update_stats(struct bdx_priv *priv) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct bdx_stats *stats = &priv->hw_stats; 87362306a36Sopenharmony_ci u64 *stats_vector = (u64 *) stats; 87462306a36Sopenharmony_ci int i; 87562306a36Sopenharmony_ci int addr; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /*Fill HW structure */ 87862306a36Sopenharmony_ci addr = 0x7200; 87962306a36Sopenharmony_ci /*First 12 statistics - 0x7200 - 0x72B0 */ 88062306a36Sopenharmony_ci for (i = 0; i < 12; i++) { 88162306a36Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 88262306a36Sopenharmony_ci addr += 0x10; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci BDX_ASSERT(addr != 0x72C0); 88562306a36Sopenharmony_ci /* 0x72C0-0x72E0 RSRV */ 88662306a36Sopenharmony_ci addr = 0x72F0; 88762306a36Sopenharmony_ci for (; i < 16; i++) { 88862306a36Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 88962306a36Sopenharmony_ci addr += 0x10; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci BDX_ASSERT(addr != 0x7330); 89262306a36Sopenharmony_ci /* 0x7330-0x7360 RSRV */ 89362306a36Sopenharmony_ci addr = 0x7370; 89462306a36Sopenharmony_ci for (; i < 19; i++) { 89562306a36Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 89662306a36Sopenharmony_ci addr += 0x10; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci BDX_ASSERT(addr != 0x73A0); 89962306a36Sopenharmony_ci /* 0x73A0-0x73B0 RSRV */ 90062306a36Sopenharmony_ci addr = 0x73C0; 90162306a36Sopenharmony_ci for (; i < 23; i++) { 90262306a36Sopenharmony_ci stats_vector[i] = bdx_read_l2stat(priv, addr); 90362306a36Sopenharmony_ci addr += 0x10; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci BDX_ASSERT(addr != 0x7400); 90662306a36Sopenharmony_ci BDX_ASSERT((sizeof(struct bdx_stats) / sizeof(u64)) != i); 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic void print_rxdd(struct rxd_desc *rxdd, u32 rxd_val1, u16 len, 91062306a36Sopenharmony_ci u16 rxd_vlan); 91162306a36Sopenharmony_cistatic void print_rxfd(struct rxf_desc *rxfd); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci/************************************************************************* 91462306a36Sopenharmony_ci * Rx DB * 91562306a36Sopenharmony_ci *************************************************************************/ 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic void bdx_rxdb_destroy(struct rxdb *db) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci vfree(db); 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic struct rxdb *bdx_rxdb_create(int nelem) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct rxdb *db; 92562306a36Sopenharmony_ci int i; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci db = vmalloc(sizeof(struct rxdb) 92862306a36Sopenharmony_ci + (nelem * sizeof(int)) 92962306a36Sopenharmony_ci + (nelem * sizeof(struct rx_map))); 93062306a36Sopenharmony_ci if (likely(db != NULL)) { 93162306a36Sopenharmony_ci db->stack = (int *)(db + 1); 93262306a36Sopenharmony_ci db->elems = (void *)(db->stack + nelem); 93362306a36Sopenharmony_ci db->nelem = nelem; 93462306a36Sopenharmony_ci db->top = nelem; 93562306a36Sopenharmony_ci for (i = 0; i < nelem; i++) 93662306a36Sopenharmony_ci db->stack[i] = nelem - i - 1; /* to make first allocs 93762306a36Sopenharmony_ci close to db struct*/ 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return db; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic inline int bdx_rxdb_alloc_elem(struct rxdb *db) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci BDX_ASSERT(db->top <= 0); 94662306a36Sopenharmony_ci return db->stack[--(db->top)]; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic inline void *bdx_rxdb_addr_elem(struct rxdb *db, int n) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci BDX_ASSERT((n < 0) || (n >= db->nelem)); 95262306a36Sopenharmony_ci return db->elems + n; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic inline int bdx_rxdb_available(struct rxdb *db) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci return db->top; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic inline void bdx_rxdb_free_elem(struct rxdb *db, int n) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci BDX_ASSERT((n >= db->nelem) || (n < 0)); 96362306a36Sopenharmony_ci db->stack[(db->top)++] = n; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/************************************************************************* 96762306a36Sopenharmony_ci * Rx Init * 96862306a36Sopenharmony_ci *************************************************************************/ 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci/** 97162306a36Sopenharmony_ci * bdx_rx_init - initialize RX all related HW and SW resources 97262306a36Sopenharmony_ci * @priv: NIC private structure 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * Returns 0 on success, negative value on failure 97562306a36Sopenharmony_ci * 97662306a36Sopenharmony_ci * It creates rxf and rxd fifos, update relevant HW registers, preallocate 97762306a36Sopenharmony_ci * skb for rx. It assumes that Rx is desabled in HW 97862306a36Sopenharmony_ci * funcs are grouped for better cache usage 97962306a36Sopenharmony_ci * 98062306a36Sopenharmony_ci * RxD fifo is smaller than RxF fifo by design. Upon high load, RxD will be 98162306a36Sopenharmony_ci * filled and packets will be dropped by nic without getting into host or 98262306a36Sopenharmony_ci * cousing interrupt. Anyway, in that condition, host has no chance to process 98362306a36Sopenharmony_ci * all packets, but dropping in nic is cheaper, since it takes 0 cpu cycles 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/* TBD: ensure proper packet size */ 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int bdx_rx_init(struct bdx_priv *priv) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci ENTER; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (bdx_fifo_init(priv, &priv->rxd_fifo0.m, priv->rxd_size, 99362306a36Sopenharmony_ci regRXD_CFG0_0, regRXD_CFG1_0, 99462306a36Sopenharmony_ci regRXD_RPTR_0, regRXD_WPTR_0)) 99562306a36Sopenharmony_ci goto err_mem; 99662306a36Sopenharmony_ci if (bdx_fifo_init(priv, &priv->rxf_fifo0.m, priv->rxf_size, 99762306a36Sopenharmony_ci regRXF_CFG0_0, regRXF_CFG1_0, 99862306a36Sopenharmony_ci regRXF_RPTR_0, regRXF_WPTR_0)) 99962306a36Sopenharmony_ci goto err_mem; 100062306a36Sopenharmony_ci priv->rxdb = bdx_rxdb_create(priv->rxf_fifo0.m.memsz / 100162306a36Sopenharmony_ci sizeof(struct rxf_desc)); 100262306a36Sopenharmony_ci if (!priv->rxdb) 100362306a36Sopenharmony_ci goto err_mem; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci priv->rxf_fifo0.m.pktsz = priv->ndev->mtu + VLAN_ETH_HLEN; 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cierr_mem: 100962306a36Sopenharmony_ci netdev_err(priv->ndev, "Rx init failed\n"); 101062306a36Sopenharmony_ci return -ENOMEM; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/** 101462306a36Sopenharmony_ci * bdx_rx_free_skbs - frees and unmaps all skbs allocated for the fifo 101562306a36Sopenharmony_ci * @priv: NIC private structure 101662306a36Sopenharmony_ci * @f: RXF fifo 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_cistatic void bdx_rx_free_skbs(struct bdx_priv *priv, struct rxf_fifo *f) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct rx_map *dm; 102162306a36Sopenharmony_ci struct rxdb *db = priv->rxdb; 102262306a36Sopenharmony_ci u16 i; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci ENTER; 102562306a36Sopenharmony_ci DBG("total=%d free=%d busy=%d\n", db->nelem, bdx_rxdb_available(db), 102662306a36Sopenharmony_ci db->nelem - bdx_rxdb_available(db)); 102762306a36Sopenharmony_ci while (bdx_rxdb_available(db) > 0) { 102862306a36Sopenharmony_ci i = bdx_rxdb_alloc_elem(db); 102962306a36Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, i); 103062306a36Sopenharmony_ci dm->dma = 0; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci for (i = 0; i < db->nelem; i++) { 103362306a36Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, i); 103462306a36Sopenharmony_ci if (dm->dma) { 103562306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, dm->dma, 103662306a36Sopenharmony_ci f->m.pktsz, DMA_FROM_DEVICE); 103762306a36Sopenharmony_ci dev_kfree_skb(dm->skb); 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci/** 104362306a36Sopenharmony_ci * bdx_rx_free - release all Rx resources 104462306a36Sopenharmony_ci * @priv: NIC private structure 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * It assumes that Rx is desabled in HW 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_cistatic void bdx_rx_free(struct bdx_priv *priv) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci ENTER; 105162306a36Sopenharmony_ci if (priv->rxdb) { 105262306a36Sopenharmony_ci bdx_rx_free_skbs(priv, &priv->rxf_fifo0); 105362306a36Sopenharmony_ci bdx_rxdb_destroy(priv->rxdb); 105462306a36Sopenharmony_ci priv->rxdb = NULL; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci bdx_fifo_free(priv, &priv->rxf_fifo0.m); 105762306a36Sopenharmony_ci bdx_fifo_free(priv, &priv->rxd_fifo0.m); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci RET(); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci/************************************************************************* 106362306a36Sopenharmony_ci * Rx Engine * 106462306a36Sopenharmony_ci *************************************************************************/ 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/** 106762306a36Sopenharmony_ci * bdx_rx_alloc_skbs - fill rxf fifo with new skbs 106862306a36Sopenharmony_ci * @priv: nic's private structure 106962306a36Sopenharmony_ci * @f: RXF fifo that needs skbs 107062306a36Sopenharmony_ci * 107162306a36Sopenharmony_ci * It allocates skbs, build rxf descs and push it (rxf descr) into rxf fifo. 107262306a36Sopenharmony_ci * skb's virtual and physical addresses are stored in skb db. 107362306a36Sopenharmony_ci * To calculate free space, func uses cached values of RPTR and WPTR 107462306a36Sopenharmony_ci * When needed, it also updates RPTR and WPTR. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/* TBD: do not update WPTR if no desc were written */ 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci struct sk_buff *skb; 108262306a36Sopenharmony_ci struct rxf_desc *rxfd; 108362306a36Sopenharmony_ci struct rx_map *dm; 108462306a36Sopenharmony_ci int dno, delta, idx; 108562306a36Sopenharmony_ci struct rxdb *db = priv->rxdb; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci ENTER; 108862306a36Sopenharmony_ci dno = bdx_rxdb_available(db) - 1; 108962306a36Sopenharmony_ci while (dno > 0) { 109062306a36Sopenharmony_ci skb = netdev_alloc_skb(priv->ndev, f->m.pktsz + NET_IP_ALIGN); 109162306a36Sopenharmony_ci if (!skb) 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci idx = bdx_rxdb_alloc_elem(db); 109762306a36Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, idx); 109862306a36Sopenharmony_ci dm->dma = dma_map_single(&priv->pdev->dev, skb->data, 109962306a36Sopenharmony_ci f->m.pktsz, DMA_FROM_DEVICE); 110062306a36Sopenharmony_ci dm->skb = skb; 110162306a36Sopenharmony_ci rxfd = (struct rxf_desc *)(f->m.va + f->m.wptr); 110262306a36Sopenharmony_ci rxfd->info = CPU_CHIP_SWAP32(0x10003); /* INFO=1 BC=3 */ 110362306a36Sopenharmony_ci rxfd->va_lo = idx; 110462306a36Sopenharmony_ci rxfd->pa_lo = CPU_CHIP_SWAP32(L32_64(dm->dma)); 110562306a36Sopenharmony_ci rxfd->pa_hi = CPU_CHIP_SWAP32(H32_64(dm->dma)); 110662306a36Sopenharmony_ci rxfd->len = CPU_CHIP_SWAP32(f->m.pktsz); 110762306a36Sopenharmony_ci print_rxfd(rxfd); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci f->m.wptr += sizeof(struct rxf_desc); 111062306a36Sopenharmony_ci delta = f->m.wptr - f->m.memsz; 111162306a36Sopenharmony_ci if (unlikely(delta >= 0)) { 111262306a36Sopenharmony_ci f->m.wptr = delta; 111362306a36Sopenharmony_ci if (delta > 0) { 111462306a36Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, delta); 111562306a36Sopenharmony_ci DBG("wrapped descriptor\n"); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci dno--; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci /*TBD: to do - delayed rxf wptr like in txd */ 112162306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 112262306a36Sopenharmony_ci RET(); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic inline void 112662306a36Sopenharmony_ciNETIF_RX_MUX(struct bdx_priv *priv, u32 rxd_val1, u16 rxd_vlan, 112762306a36Sopenharmony_ci struct sk_buff *skb) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci ENTER; 113062306a36Sopenharmony_ci DBG("rxdd->flags.bits.vtag=%d\n", GET_RXD_VTAG(rxd_val1)); 113162306a36Sopenharmony_ci if (GET_RXD_VTAG(rxd_val1)) { 113262306a36Sopenharmony_ci DBG("%s: vlan rcv vlan '%x' vtag '%x'\n", 113362306a36Sopenharmony_ci priv->ndev->name, 113462306a36Sopenharmony_ci GET_RXD_VLAN_ID(rxd_vlan), 113562306a36Sopenharmony_ci GET_RXD_VTAG(rxd_val1)); 113662306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), GET_RXD_VLAN_TCI(rxd_vlan)); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci netif_receive_skb(skb); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic void bdx_recycle_skb(struct bdx_priv *priv, struct rxd_desc *rxdd) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct rxf_desc *rxfd; 114462306a36Sopenharmony_ci struct rx_map *dm; 114562306a36Sopenharmony_ci struct rxf_fifo *f; 114662306a36Sopenharmony_ci struct rxdb *db; 114762306a36Sopenharmony_ci int delta; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci ENTER; 115062306a36Sopenharmony_ci DBG("priv=%p rxdd=%p\n", priv, rxdd); 115162306a36Sopenharmony_ci f = &priv->rxf_fifo0; 115262306a36Sopenharmony_ci db = priv->rxdb; 115362306a36Sopenharmony_ci DBG("db=%p f=%p\n", db, f); 115462306a36Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, rxdd->va_lo); 115562306a36Sopenharmony_ci DBG("dm=%p\n", dm); 115662306a36Sopenharmony_ci rxfd = (struct rxf_desc *)(f->m.va + f->m.wptr); 115762306a36Sopenharmony_ci rxfd->info = CPU_CHIP_SWAP32(0x10003); /* INFO=1 BC=3 */ 115862306a36Sopenharmony_ci rxfd->va_lo = rxdd->va_lo; 115962306a36Sopenharmony_ci rxfd->pa_lo = CPU_CHIP_SWAP32(L32_64(dm->dma)); 116062306a36Sopenharmony_ci rxfd->pa_hi = CPU_CHIP_SWAP32(H32_64(dm->dma)); 116162306a36Sopenharmony_ci rxfd->len = CPU_CHIP_SWAP32(f->m.pktsz); 116262306a36Sopenharmony_ci print_rxfd(rxfd); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci f->m.wptr += sizeof(struct rxf_desc); 116562306a36Sopenharmony_ci delta = f->m.wptr - f->m.memsz; 116662306a36Sopenharmony_ci if (unlikely(delta >= 0)) { 116762306a36Sopenharmony_ci f->m.wptr = delta; 116862306a36Sopenharmony_ci if (delta > 0) { 116962306a36Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, delta); 117062306a36Sopenharmony_ci DBG("wrapped descriptor\n"); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci RET(); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci/** 117762306a36Sopenharmony_ci * bdx_rx_receive - receives full packets from RXD fifo and pass them to OS 117862306a36Sopenharmony_ci * NOTE: a special treatment is given to non-continuous descriptors 117962306a36Sopenharmony_ci * that start near the end, wraps around and continue at the beginning. a second 118062306a36Sopenharmony_ci * part is copied right after the first, and then descriptor is interpreted as 118162306a36Sopenharmony_ci * normal. fifo has an extra space to allow such operations 118262306a36Sopenharmony_ci * @priv: nic's private structure 118362306a36Sopenharmony_ci * @f: RXF fifo that needs skbs 118462306a36Sopenharmony_ci * @budget: maximum number of packets to receive 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci/* TBD: replace memcpy func call by explicite inline asm */ 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 119262306a36Sopenharmony_ci struct sk_buff *skb, *skb2; 119362306a36Sopenharmony_ci struct rxd_desc *rxdd; 119462306a36Sopenharmony_ci struct rx_map *dm; 119562306a36Sopenharmony_ci struct rxf_fifo *rxf_fifo; 119662306a36Sopenharmony_ci int tmp_len, size; 119762306a36Sopenharmony_ci int done = 0; 119862306a36Sopenharmony_ci int max_done = BDX_MAX_RX_DONE; 119962306a36Sopenharmony_ci struct rxdb *db = NULL; 120062306a36Sopenharmony_ci /* Unmarshalled descriptor - copy of descriptor in host order */ 120162306a36Sopenharmony_ci u32 rxd_val1; 120262306a36Sopenharmony_ci u16 len; 120362306a36Sopenharmony_ci u16 rxd_vlan; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci ENTER; 120662306a36Sopenharmony_ci max_done = budget; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci f->m.wptr = READ_REG(priv, f->m.reg_WPTR) & TXF_WPTR_WR_PTR; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci size = f->m.wptr - f->m.rptr; 121162306a36Sopenharmony_ci if (size < 0) 121262306a36Sopenharmony_ci size = f->m.memsz + size; /* size is negative :-) */ 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci while (size > 0) { 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci rxdd = (struct rxd_desc *)(f->m.va + f->m.rptr); 121762306a36Sopenharmony_ci rxd_val1 = CPU_CHIP_SWAP32(rxdd->rxd_val1); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci len = CPU_CHIP_SWAP16(rxdd->len); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci rxd_vlan = CPU_CHIP_SWAP16(rxdd->rxd_vlan); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci print_rxdd(rxdd, rxd_val1, len, rxd_vlan); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci tmp_len = GET_RXD_BC(rxd_val1) << 3; 122662306a36Sopenharmony_ci BDX_ASSERT(tmp_len <= 0); 122762306a36Sopenharmony_ci size -= tmp_len; 122862306a36Sopenharmony_ci if (size < 0) /* test for partially arrived descriptor */ 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci f->m.rptr += tmp_len; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci tmp_len = f->m.rptr - f->m.memsz; 123462306a36Sopenharmony_ci if (unlikely(tmp_len >= 0)) { 123562306a36Sopenharmony_ci f->m.rptr = tmp_len; 123662306a36Sopenharmony_ci if (tmp_len > 0) { 123762306a36Sopenharmony_ci DBG("wrapped desc rptr=%d tmp_len=%d\n", 123862306a36Sopenharmony_ci f->m.rptr, tmp_len); 123962306a36Sopenharmony_ci memcpy(f->m.va + f->m.memsz, f->m.va, tmp_len); 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (unlikely(GET_RXD_ERR(rxd_val1))) { 124462306a36Sopenharmony_ci DBG("rxd_err = 0x%x\n", GET_RXD_ERR(rxd_val1)); 124562306a36Sopenharmony_ci ndev->stats.rx_errors++; 124662306a36Sopenharmony_ci bdx_recycle_skb(priv, rxdd); 124762306a36Sopenharmony_ci continue; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci rxf_fifo = &priv->rxf_fifo0; 125162306a36Sopenharmony_ci db = priv->rxdb; 125262306a36Sopenharmony_ci dm = bdx_rxdb_addr_elem(db, rxdd->va_lo); 125362306a36Sopenharmony_ci skb = dm->skb; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (len < BDX_COPYBREAK && 125662306a36Sopenharmony_ci (skb2 = netdev_alloc_skb(priv->ndev, len + NET_IP_ALIGN))) { 125762306a36Sopenharmony_ci skb_reserve(skb2, NET_IP_ALIGN); 125862306a36Sopenharmony_ci /*skb_put(skb2, len); */ 125962306a36Sopenharmony_ci dma_sync_single_for_cpu(&priv->pdev->dev, dm->dma, 126062306a36Sopenharmony_ci rxf_fifo->m.pktsz, 126162306a36Sopenharmony_ci DMA_FROM_DEVICE); 126262306a36Sopenharmony_ci memcpy(skb2->data, skb->data, len); 126362306a36Sopenharmony_ci bdx_recycle_skb(priv, rxdd); 126462306a36Sopenharmony_ci skb = skb2; 126562306a36Sopenharmony_ci } else { 126662306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, dm->dma, 126762306a36Sopenharmony_ci rxf_fifo->m.pktsz, DMA_FROM_DEVICE); 126862306a36Sopenharmony_ci bdx_rxdb_free_elem(db, rxdd->va_lo); 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci ndev->stats.rx_bytes += len; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci skb_put(skb, len); 127462306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* Non-IP packets aren't checksum-offloaded */ 127762306a36Sopenharmony_ci if (GET_RXD_PKT_ID(rxd_val1) == 0) 127862306a36Sopenharmony_ci skb_checksum_none_assert(skb); 127962306a36Sopenharmony_ci else 128062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci NETIF_RX_MUX(priv, rxd_val1, rxd_vlan, skb); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (++done >= max_done) 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci ndev->stats.rx_packets += done; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* FIXME: do smth to minimize pci accesses */ 129162306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_RPTR, f->m.rptr & TXF_WPTR_WR_PTR); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci RET(done); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci/************************************************************************* 129962306a36Sopenharmony_ci * Debug / Temprorary Code * 130062306a36Sopenharmony_ci *************************************************************************/ 130162306a36Sopenharmony_cistatic void print_rxdd(struct rxd_desc *rxdd, u32 rxd_val1, u16 len, 130262306a36Sopenharmony_ci u16 rxd_vlan) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_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", 130562306a36Sopenharmony_ci GET_RXD_BC(rxd_val1), GET_RXD_RXFQ(rxd_val1), GET_RXD_TO(rxd_val1), 130662306a36Sopenharmony_ci GET_RXD_TYPE(rxd_val1), GET_RXD_ERR(rxd_val1), 130762306a36Sopenharmony_ci GET_RXD_RXP(rxd_val1), GET_RXD_PKT_ID(rxd_val1), 130862306a36Sopenharmony_ci GET_RXD_VTAG(rxd_val1), len, GET_RXD_VLAN_ID(rxd_vlan), 130962306a36Sopenharmony_ci GET_RXD_CFI(rxd_vlan), GET_RXD_PRIO(rxd_vlan), rxdd->va_lo, 131062306a36Sopenharmony_ci rxdd->va_hi); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic void print_rxfd(struct rxf_desc *rxfd) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci DBG("=== RxF desc CHIP ORDER/ENDIANNESS =============\n" 131662306a36Sopenharmony_ci "info 0x%x va_lo %u pa_lo 0x%x pa_hi 0x%x len 0x%x\n", 131762306a36Sopenharmony_ci rxfd->info, rxfd->va_lo, rxfd->pa_lo, rxfd->pa_hi, rxfd->len); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/* 132162306a36Sopenharmony_ci * TX HW/SW interaction overview 132262306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 132362306a36Sopenharmony_ci * There are 2 types of TX communication channels between driver and NIC. 132462306a36Sopenharmony_ci * 1) TX Free Fifo - TXF - holds ack descriptors for sent packets 132562306a36Sopenharmony_ci * 2) TX Data Fifo - TXD - holds descriptors of full buffers. 132662306a36Sopenharmony_ci * 132762306a36Sopenharmony_ci * Currently NIC supports TSO, checksuming and gather DMA 132862306a36Sopenharmony_ci * UFO and IP fragmentation is on the way 132962306a36Sopenharmony_ci * 133062306a36Sopenharmony_ci * RX SW Data Structures 133162306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~ 133262306a36Sopenharmony_ci * txdb - used to keep track of all skbs owned by SW and their dma addresses. 133362306a36Sopenharmony_ci * For TX case, ownership lasts from geting packet via hard_xmit and until HW 133462306a36Sopenharmony_ci * acknowledges sent by TXF descriptors. 133562306a36Sopenharmony_ci * Implemented as cyclic buffer. 133662306a36Sopenharmony_ci * fifo - keeps info about fifo's size and location, relevant HW registers, 133762306a36Sopenharmony_ci * usage and skb db. Each RXD and RXF Fifo has its own fifo structure. 133862306a36Sopenharmony_ci * Implemented as simple struct. 133962306a36Sopenharmony_ci * 134062306a36Sopenharmony_ci * TX SW Execution Flow 134162306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~ 134262306a36Sopenharmony_ci * OS calls driver's hard_xmit method with packet to sent. 134362306a36Sopenharmony_ci * Driver creates DMA mappings, builds TXD descriptors and kicks HW 134462306a36Sopenharmony_ci * by updating TXD WPTR. 134562306a36Sopenharmony_ci * When packet is sent, HW write us TXF descriptor and SW frees original skb. 134662306a36Sopenharmony_ci * To prevent TXD fifo overflow without reading HW registers every time, 134762306a36Sopenharmony_ci * SW deploys "tx level" technique. 134862306a36Sopenharmony_ci * Upon strart up, tx level is initialized to TXD fifo length. 134962306a36Sopenharmony_ci * For every sent packet, SW gets its TXD descriptor sizei 135062306a36Sopenharmony_ci * (from precalculated array) and substructs it from tx level. 135162306a36Sopenharmony_ci * The size is also stored in txdb. When TXF ack arrives, SW fetch size of 135262306a36Sopenharmony_ci * original TXD descriptor from txdb and adds it to tx level. 135362306a36Sopenharmony_ci * When Tx level drops under some predefined treshhold, the driver 135462306a36Sopenharmony_ci * stops the TX queue. When TX level rises above that level, 135562306a36Sopenharmony_ci * the tx queue is enabled again. 135662306a36Sopenharmony_ci * 135762306a36Sopenharmony_ci * This technique avoids eccessive reading of RPTR and WPTR registers. 135862306a36Sopenharmony_ci * As our benchmarks shows, it adds 1.5 Gbit/sec to NIS's throuput. 135962306a36Sopenharmony_ci */ 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci/** 136262306a36Sopenharmony_ci * __bdx_tx_db_ptr_next - helper function, increment read/write pointer + wrap 136362306a36Sopenharmony_ci * @db: tx data base 136462306a36Sopenharmony_ci * @pptr: read or write pointer 136562306a36Sopenharmony_ci */ 136662306a36Sopenharmony_cistatic inline void __bdx_tx_db_ptr_next(struct txdb *db, struct tx_map **pptr) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci BDX_ASSERT(db == NULL || pptr == NULL); /* sanity */ 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci BDX_ASSERT(*pptr != db->rptr && /* expect either read */ 137162306a36Sopenharmony_ci *pptr != db->wptr); /* or write pointer */ 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci BDX_ASSERT(*pptr < db->start || /* pointer has to be */ 137462306a36Sopenharmony_ci *pptr >= db->end); /* in range */ 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci ++*pptr; 137762306a36Sopenharmony_ci if (unlikely(*pptr == db->end)) 137862306a36Sopenharmony_ci *pptr = db->start; 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci/** 138262306a36Sopenharmony_ci * bdx_tx_db_inc_rptr - increment read pointer 138362306a36Sopenharmony_ci * @db: tx data base 138462306a36Sopenharmony_ci */ 138562306a36Sopenharmony_cistatic inline void bdx_tx_db_inc_rptr(struct txdb *db) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci BDX_ASSERT(db->rptr == db->wptr); /* can't read from empty db */ 138862306a36Sopenharmony_ci __bdx_tx_db_ptr_next(db, &db->rptr); 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci/** 139262306a36Sopenharmony_ci * bdx_tx_db_inc_wptr - increment write pointer 139362306a36Sopenharmony_ci * @db: tx data base 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_cistatic inline void bdx_tx_db_inc_wptr(struct txdb *db) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci __bdx_tx_db_ptr_next(db, &db->wptr); 139862306a36Sopenharmony_ci BDX_ASSERT(db->rptr == db->wptr); /* we can not get empty db as 139962306a36Sopenharmony_ci a result of write */ 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci/** 140362306a36Sopenharmony_ci * bdx_tx_db_init - creates and initializes tx db 140462306a36Sopenharmony_ci * @d: tx data base 140562306a36Sopenharmony_ci * @sz_type: size of tx fifo 140662306a36Sopenharmony_ci * 140762306a36Sopenharmony_ci * Returns 0 on success, error code otherwise 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_cistatic int bdx_tx_db_init(struct txdb *d, int sz_type) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci int memsz = FIFO_SIZE * (1 << (sz_type + 1)); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci d->start = vmalloc(memsz); 141462306a36Sopenharmony_ci if (!d->start) 141562306a36Sopenharmony_ci return -ENOMEM; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* 141862306a36Sopenharmony_ci * In order to differentiate between db is empty and db is full 141962306a36Sopenharmony_ci * states at least one element should always be empty in order to 142062306a36Sopenharmony_ci * avoid rptr == wptr which means db is empty 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_ci d->size = memsz / sizeof(struct tx_map) - 1; 142362306a36Sopenharmony_ci d->end = d->start + d->size + 1; /* just after last element */ 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* all dbs are created equally empty */ 142662306a36Sopenharmony_ci d->rptr = d->start; 142762306a36Sopenharmony_ci d->wptr = d->start; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci return 0; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci/** 143362306a36Sopenharmony_ci * bdx_tx_db_close - closes tx db and frees all memory 143462306a36Sopenharmony_ci * @d: tx data base 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_cistatic void bdx_tx_db_close(struct txdb *d) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci BDX_ASSERT(d == NULL); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci vfree(d->start); 144162306a36Sopenharmony_ci d->start = NULL; 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci/************************************************************************* 144562306a36Sopenharmony_ci * Tx Engine * 144662306a36Sopenharmony_ci *************************************************************************/ 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci/* sizes of tx desc (including padding if needed) as function 144962306a36Sopenharmony_ci * of skb's frag number */ 145062306a36Sopenharmony_cistatic struct { 145162306a36Sopenharmony_ci u16 bytes; 145262306a36Sopenharmony_ci u16 qwords; /* qword = 64 bit */ 145362306a36Sopenharmony_ci} txd_sizes[MAX_SKB_FRAGS + 1]; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci/** 145662306a36Sopenharmony_ci * bdx_tx_map_skb - creates and stores dma mappings for skb's data blocks 145762306a36Sopenharmony_ci * @priv: NIC private structure 145862306a36Sopenharmony_ci * @skb: socket buffer to map 145962306a36Sopenharmony_ci * @txdd: TX descriptor to use 146062306a36Sopenharmony_ci * 146162306a36Sopenharmony_ci * It makes dma mappings for skb's data blocks and writes them to PBL of 146262306a36Sopenharmony_ci * new tx descriptor. It also stores them in the tx db, so they could be 146362306a36Sopenharmony_ci * unmaped after data was sent. It is reponsibility of a caller to make 146462306a36Sopenharmony_ci * sure that there is enough space in the tx db. Last element holds pointer 146562306a36Sopenharmony_ci * to skb itself and marked with zero length 146662306a36Sopenharmony_ci */ 146762306a36Sopenharmony_cistatic inline void 146862306a36Sopenharmony_cibdx_tx_map_skb(struct bdx_priv *priv, struct sk_buff *skb, 146962306a36Sopenharmony_ci struct txd_desc *txdd) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct txdb *db = &priv->txdb; 147262306a36Sopenharmony_ci struct pbl *pbl = &txdd->pbl[0]; 147362306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 147462306a36Sopenharmony_ci int i; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci db->wptr->len = skb_headlen(skb); 147762306a36Sopenharmony_ci db->wptr->addr.dma = dma_map_single(&priv->pdev->dev, skb->data, 147862306a36Sopenharmony_ci db->wptr->len, DMA_TO_DEVICE); 147962306a36Sopenharmony_ci pbl->len = CPU_CHIP_SWAP32(db->wptr->len); 148062306a36Sopenharmony_ci pbl->pa_lo = CPU_CHIP_SWAP32(L32_64(db->wptr->addr.dma)); 148162306a36Sopenharmony_ci pbl->pa_hi = CPU_CHIP_SWAP32(H32_64(db->wptr->addr.dma)); 148262306a36Sopenharmony_ci DBG("=== pbl len: 0x%x ================\n", pbl->len); 148362306a36Sopenharmony_ci DBG("=== pbl pa_lo: 0x%x ================\n", pbl->pa_lo); 148462306a36Sopenharmony_ci DBG("=== pbl pa_hi: 0x%x ================\n", pbl->pa_hi); 148562306a36Sopenharmony_ci bdx_tx_db_inc_wptr(db); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 148862306a36Sopenharmony_ci const skb_frag_t *frag; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 149162306a36Sopenharmony_ci db->wptr->len = skb_frag_size(frag); 149262306a36Sopenharmony_ci db->wptr->addr.dma = skb_frag_dma_map(&priv->pdev->dev, frag, 149362306a36Sopenharmony_ci 0, skb_frag_size(frag), 149462306a36Sopenharmony_ci DMA_TO_DEVICE); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci pbl++; 149762306a36Sopenharmony_ci pbl->len = CPU_CHIP_SWAP32(db->wptr->len); 149862306a36Sopenharmony_ci pbl->pa_lo = CPU_CHIP_SWAP32(L32_64(db->wptr->addr.dma)); 149962306a36Sopenharmony_ci pbl->pa_hi = CPU_CHIP_SWAP32(H32_64(db->wptr->addr.dma)); 150062306a36Sopenharmony_ci bdx_tx_db_inc_wptr(db); 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* add skb clean up info. */ 150462306a36Sopenharmony_ci db->wptr->len = -txd_sizes[nr_frags].bytes; 150562306a36Sopenharmony_ci db->wptr->addr.skb = skb; 150662306a36Sopenharmony_ci bdx_tx_db_inc_wptr(db); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci/* init_txd_sizes - precalculate sizes of descriptors for skbs up to 16 frags 151062306a36Sopenharmony_ci * number of frags is used as index to fetch correct descriptors size, 151162306a36Sopenharmony_ci * instead of calculating it each time */ 151262306a36Sopenharmony_cistatic void __init init_txd_sizes(void) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci int i, lwords; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* 7 - is number of lwords in txd with one phys buffer 151762306a36Sopenharmony_ci * 3 - is number of lwords used for every additional phys buffer */ 151862306a36Sopenharmony_ci for (i = 0; i < MAX_SKB_FRAGS + 1; i++) { 151962306a36Sopenharmony_ci lwords = 7 + (i * 3); 152062306a36Sopenharmony_ci if (lwords & 1) 152162306a36Sopenharmony_ci lwords++; /* pad it with 1 lword */ 152262306a36Sopenharmony_ci txd_sizes[i].qwords = lwords >> 1; 152362306a36Sopenharmony_ci txd_sizes[i].bytes = lwords << 2; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci/* bdx_tx_init - initialize all Tx related stuff. 152862306a36Sopenharmony_ci * Namely, TXD and TXF fifos, database etc */ 152962306a36Sopenharmony_cistatic int bdx_tx_init(struct bdx_priv *priv) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci if (bdx_fifo_init(priv, &priv->txd_fifo0.m, priv->txd_size, 153262306a36Sopenharmony_ci regTXD_CFG0_0, 153362306a36Sopenharmony_ci regTXD_CFG1_0, regTXD_RPTR_0, regTXD_WPTR_0)) 153462306a36Sopenharmony_ci goto err_mem; 153562306a36Sopenharmony_ci if (bdx_fifo_init(priv, &priv->txf_fifo0.m, priv->txf_size, 153662306a36Sopenharmony_ci regTXF_CFG0_0, 153762306a36Sopenharmony_ci regTXF_CFG1_0, regTXF_RPTR_0, regTXF_WPTR_0)) 153862306a36Sopenharmony_ci goto err_mem; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci /* The TX db has to keep mappings for all packets sent (on TxD) 154162306a36Sopenharmony_ci * and not yet reclaimed (on TxF) */ 154262306a36Sopenharmony_ci if (bdx_tx_db_init(&priv->txdb, max(priv->txd_size, priv->txf_size))) 154362306a36Sopenharmony_ci goto err_mem; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci priv->tx_level = BDX_MAX_TX_LEVEL; 154662306a36Sopenharmony_ci#ifdef BDX_DELAY_WPTR 154762306a36Sopenharmony_ci priv->tx_update_mark = priv->tx_level - 1024; 154862306a36Sopenharmony_ci#endif 154962306a36Sopenharmony_ci return 0; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cierr_mem: 155262306a36Sopenharmony_ci netdev_err(priv->ndev, "Tx init failed\n"); 155362306a36Sopenharmony_ci return -ENOMEM; 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci/** 155762306a36Sopenharmony_ci * bdx_tx_space - calculates available space in TX fifo 155862306a36Sopenharmony_ci * @priv: NIC private structure 155962306a36Sopenharmony_ci * 156062306a36Sopenharmony_ci * Returns available space in TX fifo in bytes 156162306a36Sopenharmony_ci */ 156262306a36Sopenharmony_cistatic inline int bdx_tx_space(struct bdx_priv *priv) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 156562306a36Sopenharmony_ci int fsize; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci f->m.rptr = READ_REG(priv, f->m.reg_RPTR) & TXF_WPTR_WR_PTR; 156862306a36Sopenharmony_ci fsize = f->m.rptr - f->m.wptr; 156962306a36Sopenharmony_ci if (fsize <= 0) 157062306a36Sopenharmony_ci fsize = f->m.memsz + fsize; 157162306a36Sopenharmony_ci return fsize; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci/** 157562306a36Sopenharmony_ci * bdx_tx_transmit - send packet to NIC 157662306a36Sopenharmony_ci * @skb: packet to send 157762306a36Sopenharmony_ci * @ndev: network device assigned to NIC 157862306a36Sopenharmony_ci * Return codes: 157962306a36Sopenharmony_ci * o NETDEV_TX_OK everything ok. 158062306a36Sopenharmony_ci * o NETDEV_TX_BUSY Cannot transmit packet, try later 158162306a36Sopenharmony_ci * Usually a bug, means queue start/stop flow control is broken in 158262306a36Sopenharmony_ci * the driver. Note: the driver must NOT put the skb in its DMA ring. 158362306a36Sopenharmony_ci */ 158462306a36Sopenharmony_cistatic netdev_tx_t bdx_tx_transmit(struct sk_buff *skb, 158562306a36Sopenharmony_ci struct net_device *ndev) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(ndev); 158862306a36Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 158962306a36Sopenharmony_ci int txd_checksum = 7; /* full checksum */ 159062306a36Sopenharmony_ci int txd_lgsnd = 0; 159162306a36Sopenharmony_ci int txd_vlan_id = 0; 159262306a36Sopenharmony_ci int txd_vtag = 0; 159362306a36Sopenharmony_ci int txd_mss = 0; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 159662306a36Sopenharmony_ci struct txd_desc *txdd; 159762306a36Sopenharmony_ci int len; 159862306a36Sopenharmony_ci unsigned long flags; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci ENTER; 160162306a36Sopenharmony_ci local_irq_save(flags); 160262306a36Sopenharmony_ci spin_lock(&priv->tx_lock); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* build tx descriptor */ 160562306a36Sopenharmony_ci BDX_ASSERT(f->m.wptr >= f->m.memsz); /* started with valid wptr */ 160662306a36Sopenharmony_ci txdd = (struct txd_desc *)(f->m.va + f->m.wptr); 160762306a36Sopenharmony_ci if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) 160862306a36Sopenharmony_ci txd_checksum = 0; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_size) { 161162306a36Sopenharmony_ci txd_mss = skb_shinfo(skb)->gso_size; 161262306a36Sopenharmony_ci txd_lgsnd = 1; 161362306a36Sopenharmony_ci DBG("skb %p skb len %d gso size = %d\n", skb, skb->len, 161462306a36Sopenharmony_ci txd_mss); 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 161862306a36Sopenharmony_ci /*Cut VLAN ID to 12 bits */ 161962306a36Sopenharmony_ci txd_vlan_id = skb_vlan_tag_get(skb) & BITS_MASK(12); 162062306a36Sopenharmony_ci txd_vtag = 1; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci txdd->length = CPU_CHIP_SWAP16(skb->len); 162462306a36Sopenharmony_ci txdd->mss = CPU_CHIP_SWAP16(txd_mss); 162562306a36Sopenharmony_ci txdd->txd_val1 = 162662306a36Sopenharmony_ci CPU_CHIP_SWAP32(TXD_W1_VAL 162762306a36Sopenharmony_ci (txd_sizes[nr_frags].qwords, txd_checksum, txd_vtag, 162862306a36Sopenharmony_ci txd_lgsnd, txd_vlan_id)); 162962306a36Sopenharmony_ci DBG("=== TxD desc =====================\n"); 163062306a36Sopenharmony_ci DBG("=== w1: 0x%x ================\n", txdd->txd_val1); 163162306a36Sopenharmony_ci DBG("=== w2: mss 0x%x len 0x%x\n", txdd->mss, txdd->length); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci bdx_tx_map_skb(priv, skb, txdd); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* increment TXD write pointer. In case of 163662306a36Sopenharmony_ci fifo wrapping copy reminder of the descriptor 163762306a36Sopenharmony_ci to the beginning */ 163862306a36Sopenharmony_ci f->m.wptr += txd_sizes[nr_frags].bytes; 163962306a36Sopenharmony_ci len = f->m.wptr - f->m.memsz; 164062306a36Sopenharmony_ci if (unlikely(len >= 0)) { 164162306a36Sopenharmony_ci f->m.wptr = len; 164262306a36Sopenharmony_ci if (len > 0) { 164362306a36Sopenharmony_ci BDX_ASSERT(len > f->m.memsz); 164462306a36Sopenharmony_ci memcpy(f->m.va, f->m.va + f->m.memsz, len); 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci BDX_ASSERT(f->m.wptr >= f->m.memsz); /* finished with valid wptr */ 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci priv->tx_level -= txd_sizes[nr_frags].bytes; 165062306a36Sopenharmony_ci BDX_ASSERT(priv->tx_level <= 0 || priv->tx_level > BDX_MAX_TX_LEVEL); 165162306a36Sopenharmony_ci#ifdef BDX_DELAY_WPTR 165262306a36Sopenharmony_ci if (priv->tx_level > priv->tx_update_mark) { 165362306a36Sopenharmony_ci /* Force memory writes to complete before letting h/w 165462306a36Sopenharmony_ci know there are new descriptors to fetch. 165562306a36Sopenharmony_ci (might be needed on platforms like IA64) 165662306a36Sopenharmony_ci wmb(); */ 165762306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 165862306a36Sopenharmony_ci } else { 165962306a36Sopenharmony_ci if (priv->tx_noupd++ > BDX_NO_UPD_PACKETS) { 166062306a36Sopenharmony_ci priv->tx_noupd = 0; 166162306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, 166262306a36Sopenharmony_ci f->m.wptr & TXF_WPTR_WR_PTR); 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci#else 166662306a36Sopenharmony_ci /* Force memory writes to complete before letting h/w 166762306a36Sopenharmony_ci know there are new descriptors to fetch. 166862306a36Sopenharmony_ci (might be needed on platforms like IA64) 166962306a36Sopenharmony_ci wmb(); */ 167062306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci#endif 167362306a36Sopenharmony_ci#ifdef BDX_LLTX 167462306a36Sopenharmony_ci netif_trans_update(ndev); /* NETIF_F_LLTX driver :( */ 167562306a36Sopenharmony_ci#endif 167662306a36Sopenharmony_ci ndev->stats.tx_packets++; 167762306a36Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci if (priv->tx_level < BDX_MIN_TX_LEVEL) { 168062306a36Sopenharmony_ci DBG("%s: %s: TX Q STOP level %d\n", 168162306a36Sopenharmony_ci BDX_DRV_NAME, ndev->name, priv->tx_level); 168262306a36Sopenharmony_ci netif_stop_queue(ndev); 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->tx_lock, flags); 168662306a36Sopenharmony_ci return NETDEV_TX_OK; 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci/** 169062306a36Sopenharmony_ci * bdx_tx_cleanup - clean TXF fifo, run in the context of IRQ. 169162306a36Sopenharmony_ci * @priv: bdx adapter 169262306a36Sopenharmony_ci * 169362306a36Sopenharmony_ci * It scans TXF fifo for descriptors, frees DMA mappings and reports to OS 169462306a36Sopenharmony_ci * that those packets were sent 169562306a36Sopenharmony_ci */ 169662306a36Sopenharmony_cistatic void bdx_tx_cleanup(struct bdx_priv *priv) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci struct txf_fifo *f = &priv->txf_fifo0; 169962306a36Sopenharmony_ci struct txdb *db = &priv->txdb; 170062306a36Sopenharmony_ci int tx_level = 0; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci ENTER; 170362306a36Sopenharmony_ci f->m.wptr = READ_REG(priv, f->m.reg_WPTR) & TXF_WPTR_MASK; 170462306a36Sopenharmony_ci BDX_ASSERT(f->m.rptr >= f->m.memsz); /* started with valid rptr */ 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci while (f->m.wptr != f->m.rptr) { 170762306a36Sopenharmony_ci f->m.rptr += BDX_TXF_DESC_SZ; 170862306a36Sopenharmony_ci f->m.rptr &= f->m.size_mask; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci /* unmap all the fragments */ 171162306a36Sopenharmony_ci /* first has to come tx_maps containing dma */ 171262306a36Sopenharmony_ci BDX_ASSERT(db->rptr->len == 0); 171362306a36Sopenharmony_ci do { 171462306a36Sopenharmony_ci BDX_ASSERT(db->rptr->addr.dma == 0); 171562306a36Sopenharmony_ci dma_unmap_page(&priv->pdev->dev, db->rptr->addr.dma, 171662306a36Sopenharmony_ci db->rptr->len, DMA_TO_DEVICE); 171762306a36Sopenharmony_ci bdx_tx_db_inc_rptr(db); 171862306a36Sopenharmony_ci } while (db->rptr->len > 0); 171962306a36Sopenharmony_ci tx_level -= db->rptr->len; /* '-' koz len is negative */ 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci /* now should come skb pointer - free it */ 172262306a36Sopenharmony_ci dev_consume_skb_irq(db->rptr->addr.skb); 172362306a36Sopenharmony_ci bdx_tx_db_inc_rptr(db); 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* let h/w know which TXF descriptors were cleaned */ 172762306a36Sopenharmony_ci BDX_ASSERT((f->m.wptr & TXF_WPTR_WR_PTR) >= f->m.memsz); 172862306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_RPTR, f->m.rptr & TXF_WPTR_WR_PTR); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* We reclaimed resources, so in case the Q is stopped by xmit callback, 173162306a36Sopenharmony_ci * we resume the transmission and use tx_lock to synchronize with xmit.*/ 173262306a36Sopenharmony_ci spin_lock(&priv->tx_lock); 173362306a36Sopenharmony_ci priv->tx_level += tx_level; 173462306a36Sopenharmony_ci BDX_ASSERT(priv->tx_level <= 0 || priv->tx_level > BDX_MAX_TX_LEVEL); 173562306a36Sopenharmony_ci#ifdef BDX_DELAY_WPTR 173662306a36Sopenharmony_ci if (priv->tx_noupd) { 173762306a36Sopenharmony_ci priv->tx_noupd = 0; 173862306a36Sopenharmony_ci WRITE_REG(priv, priv->txd_fifo0.m.reg_WPTR, 173962306a36Sopenharmony_ci priv->txd_fifo0.m.wptr & TXF_WPTR_WR_PTR); 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci#endif 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (unlikely(netif_queue_stopped(priv->ndev) && 174462306a36Sopenharmony_ci netif_carrier_ok(priv->ndev) && 174562306a36Sopenharmony_ci (priv->tx_level >= BDX_MIN_TX_LEVEL))) { 174662306a36Sopenharmony_ci DBG("%s: %s: TX Q WAKE level %d\n", 174762306a36Sopenharmony_ci BDX_DRV_NAME, priv->ndev->name, priv->tx_level); 174862306a36Sopenharmony_ci netif_wake_queue(priv->ndev); 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci spin_unlock(&priv->tx_lock); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci/** 175462306a36Sopenharmony_ci * bdx_tx_free_skbs - frees all skbs from TXD fifo. 175562306a36Sopenharmony_ci * @priv: NIC private structure 175662306a36Sopenharmony_ci * 175762306a36Sopenharmony_ci * It gets called when OS stops this dev, eg upon "ifconfig down" or rmmod 175862306a36Sopenharmony_ci */ 175962306a36Sopenharmony_cistatic void bdx_tx_free_skbs(struct bdx_priv *priv) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci struct txdb *db = &priv->txdb; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci ENTER; 176462306a36Sopenharmony_ci while (db->rptr != db->wptr) { 176562306a36Sopenharmony_ci if (likely(db->rptr->len)) 176662306a36Sopenharmony_ci dma_unmap_page(&priv->pdev->dev, db->rptr->addr.dma, 176762306a36Sopenharmony_ci db->rptr->len, DMA_TO_DEVICE); 176862306a36Sopenharmony_ci else 176962306a36Sopenharmony_ci dev_kfree_skb(db->rptr->addr.skb); 177062306a36Sopenharmony_ci bdx_tx_db_inc_rptr(db); 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci RET(); 177362306a36Sopenharmony_ci} 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci/* bdx_tx_free - frees all Tx resources */ 177662306a36Sopenharmony_cistatic void bdx_tx_free(struct bdx_priv *priv) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci ENTER; 177962306a36Sopenharmony_ci bdx_tx_free_skbs(priv); 178062306a36Sopenharmony_ci bdx_fifo_free(priv, &priv->txd_fifo0.m); 178162306a36Sopenharmony_ci bdx_fifo_free(priv, &priv->txf_fifo0.m); 178262306a36Sopenharmony_ci bdx_tx_db_close(&priv->txdb); 178362306a36Sopenharmony_ci} 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci/** 178662306a36Sopenharmony_ci * bdx_tx_push_desc - push descriptor to TxD fifo 178762306a36Sopenharmony_ci * @priv: NIC private structure 178862306a36Sopenharmony_ci * @data: desc's data 178962306a36Sopenharmony_ci * @size: desc's size 179062306a36Sopenharmony_ci * 179162306a36Sopenharmony_ci * Pushes desc to TxD fifo and overlaps it if needed. 179262306a36Sopenharmony_ci * NOTE: this func does not check for available space. this is responsibility 179362306a36Sopenharmony_ci * of the caller. Neither does it check that data size is smaller than 179462306a36Sopenharmony_ci * fifo size. 179562306a36Sopenharmony_ci */ 179662306a36Sopenharmony_cistatic void bdx_tx_push_desc(struct bdx_priv *priv, void *data, int size) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci struct txd_fifo *f = &priv->txd_fifo0; 179962306a36Sopenharmony_ci int i = f->m.memsz - f->m.wptr; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (size == 0) 180262306a36Sopenharmony_ci return; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (i > size) { 180562306a36Sopenharmony_ci memcpy(f->m.va + f->m.wptr, data, size); 180662306a36Sopenharmony_ci f->m.wptr += size; 180762306a36Sopenharmony_ci } else { 180862306a36Sopenharmony_ci memcpy(f->m.va + f->m.wptr, data, i); 180962306a36Sopenharmony_ci f->m.wptr = size - i; 181062306a36Sopenharmony_ci memcpy(f->m.va, data + i, f->m.wptr); 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci WRITE_REG(priv, f->m.reg_WPTR, f->m.wptr & TXF_WPTR_WR_PTR); 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci/** 181662306a36Sopenharmony_ci * bdx_tx_push_desc_safe - push descriptor to TxD fifo in a safe way 181762306a36Sopenharmony_ci * @priv: NIC private structure 181862306a36Sopenharmony_ci * @data: desc's data 181962306a36Sopenharmony_ci * @size: desc's size 182062306a36Sopenharmony_ci * 182162306a36Sopenharmony_ci * NOTE: this func does check for available space and, if necessary, waits for 182262306a36Sopenharmony_ci * NIC to read existing data before writing new one. 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_cistatic void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci int timer = 0; 182762306a36Sopenharmony_ci ENTER; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci while (size > 0) { 183062306a36Sopenharmony_ci /* we substruct 8 because when fifo is full rptr == wptr 183162306a36Sopenharmony_ci which also means that fifo is empty, we can understand 183262306a36Sopenharmony_ci the difference, but could hw do the same ??? :) */ 183362306a36Sopenharmony_ci int avail = bdx_tx_space(priv) - 8; 183462306a36Sopenharmony_ci if (avail <= 0) { 183562306a36Sopenharmony_ci if (timer++ > 300) { /* prevent endless loop */ 183662306a36Sopenharmony_ci DBG("timeout while writing desc to TxD fifo\n"); 183762306a36Sopenharmony_ci break; 183862306a36Sopenharmony_ci } 183962306a36Sopenharmony_ci udelay(50); /* give hw a chance to clean fifo */ 184062306a36Sopenharmony_ci continue; 184162306a36Sopenharmony_ci } 184262306a36Sopenharmony_ci avail = min(avail, size); 184362306a36Sopenharmony_ci DBG("about to push %d bytes starting %p size %d\n", avail, 184462306a36Sopenharmony_ci data, size); 184562306a36Sopenharmony_ci bdx_tx_push_desc(priv, data, avail); 184662306a36Sopenharmony_ci size -= avail; 184762306a36Sopenharmony_ci data += avail; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci RET(); 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic const struct net_device_ops bdx_netdev_ops = { 185362306a36Sopenharmony_ci .ndo_open = bdx_open, 185462306a36Sopenharmony_ci .ndo_stop = bdx_close, 185562306a36Sopenharmony_ci .ndo_start_xmit = bdx_tx_transmit, 185662306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 185762306a36Sopenharmony_ci .ndo_siocdevprivate = bdx_siocdevprivate, 185862306a36Sopenharmony_ci .ndo_set_rx_mode = bdx_setmulti, 185962306a36Sopenharmony_ci .ndo_change_mtu = bdx_change_mtu, 186062306a36Sopenharmony_ci .ndo_set_mac_address = bdx_set_mac, 186162306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = bdx_vlan_rx_add_vid, 186262306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = bdx_vlan_rx_kill_vid, 186362306a36Sopenharmony_ci}; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci/** 186662306a36Sopenharmony_ci * bdx_probe - Device Initialization Routine 186762306a36Sopenharmony_ci * @pdev: PCI device information struct 186862306a36Sopenharmony_ci * @ent: entry in bdx_pci_tbl 186962306a36Sopenharmony_ci * 187062306a36Sopenharmony_ci * Returns 0 on success, negative on failure 187162306a36Sopenharmony_ci * 187262306a36Sopenharmony_ci * bdx_probe initializes an adapter identified by a pci_dev structure. 187362306a36Sopenharmony_ci * The OS initialization, configuring of the adapter private structure, 187462306a36Sopenharmony_ci * and a hardware reset occur. 187562306a36Sopenharmony_ci * 187662306a36Sopenharmony_ci * functions and their order used as explained in 187762306a36Sopenharmony_ci * /usr/src/linux/Documentation/DMA-{API,mapping}.txt 187862306a36Sopenharmony_ci * 187962306a36Sopenharmony_ci */ 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci/* TBD: netif_msg should be checked and implemented. I disable it for now */ 188262306a36Sopenharmony_cistatic int 188362306a36Sopenharmony_cibdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci struct net_device *ndev; 188662306a36Sopenharmony_ci struct bdx_priv *priv; 188762306a36Sopenharmony_ci unsigned long pciaddr; 188862306a36Sopenharmony_ci u32 regionSize; 188962306a36Sopenharmony_ci struct pci_nic *nic; 189062306a36Sopenharmony_ci int err, port; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci ENTER; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci nic = vmalloc(sizeof(*nic)); 189562306a36Sopenharmony_ci if (!nic) 189662306a36Sopenharmony_ci RET(-ENOMEM); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci /************** pci *****************/ 189962306a36Sopenharmony_ci err = pci_enable_device(pdev); 190062306a36Sopenharmony_ci if (err) /* it triggers interrupt, dunno why. */ 190162306a36Sopenharmony_ci goto err_pci; /* it's not a problem though */ 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 190462306a36Sopenharmony_ci if (err) { 190562306a36Sopenharmony_ci pr_err("No usable DMA configuration, aborting\n"); 190662306a36Sopenharmony_ci goto err_dma; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci err = pci_request_regions(pdev, BDX_DRV_NAME); 191062306a36Sopenharmony_ci if (err) 191162306a36Sopenharmony_ci goto err_dma; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci pci_set_master(pdev); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci pciaddr = pci_resource_start(pdev, 0); 191662306a36Sopenharmony_ci if (!pciaddr) { 191762306a36Sopenharmony_ci err = -EIO; 191862306a36Sopenharmony_ci pr_err("no MMIO resource\n"); 191962306a36Sopenharmony_ci goto err_out_res; 192062306a36Sopenharmony_ci } 192162306a36Sopenharmony_ci regionSize = pci_resource_len(pdev, 0); 192262306a36Sopenharmony_ci if (regionSize < BDX_REGS_SIZE) { 192362306a36Sopenharmony_ci err = -EIO; 192462306a36Sopenharmony_ci pr_err("MMIO resource (%x) too small\n", regionSize); 192562306a36Sopenharmony_ci goto err_out_res; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci nic->regs = ioremap(pciaddr, regionSize); 192962306a36Sopenharmony_ci if (!nic->regs) { 193062306a36Sopenharmony_ci err = -EIO; 193162306a36Sopenharmony_ci pr_err("ioremap failed\n"); 193262306a36Sopenharmony_ci goto err_out_res; 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci if (pdev->irq < 2) { 193662306a36Sopenharmony_ci err = -EIO; 193762306a36Sopenharmony_ci pr_err("invalid irq (%d)\n", pdev->irq); 193862306a36Sopenharmony_ci goto err_out_iomap; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci pci_set_drvdata(pdev, nic); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci if (pdev->device == 0x3014) 194362306a36Sopenharmony_ci nic->port_num = 2; 194462306a36Sopenharmony_ci else 194562306a36Sopenharmony_ci nic->port_num = 1; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci print_hw_id(pdev); 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci bdx_hw_reset_direct(nic->regs); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci nic->irq_type = IRQ_INTX; 195262306a36Sopenharmony_ci#ifdef BDX_MSI 195362306a36Sopenharmony_ci if ((readl(nic->regs + FPGA_VER) & 0xFFF) >= 378) { 195462306a36Sopenharmony_ci err = pci_enable_msi(pdev); 195562306a36Sopenharmony_ci if (err) 195662306a36Sopenharmony_ci pr_err("Can't enable msi. error is %d\n", err); 195762306a36Sopenharmony_ci else 195862306a36Sopenharmony_ci nic->irq_type = IRQ_MSI; 195962306a36Sopenharmony_ci } else 196062306a36Sopenharmony_ci DBG("HW does not support MSI\n"); 196162306a36Sopenharmony_ci#endif 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci /************** netdev **************/ 196462306a36Sopenharmony_ci for (port = 0; port < nic->port_num; port++) { 196562306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct bdx_priv)); 196662306a36Sopenharmony_ci if (!ndev) { 196762306a36Sopenharmony_ci err = -ENOMEM; 196862306a36Sopenharmony_ci goto err_out_iomap; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci ndev->netdev_ops = &bdx_netdev_ops; 197262306a36Sopenharmony_ci ndev->tx_queue_len = BDX_NDEV_TXQ_LEN; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci bdx_set_ethtool_ops(ndev); /* ethtool interface */ 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* these fields are used for info purposes only 197762306a36Sopenharmony_ci * so we can have them same for all ports of the board */ 197862306a36Sopenharmony_ci ndev->if_port = port; 197962306a36Sopenharmony_ci ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | 198062306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | 198162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM | 198262306a36Sopenharmony_ci NETIF_F_HIGHDMA; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci ndev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | 198562306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /************** priv ****************/ 198862306a36Sopenharmony_ci priv = nic->priv[port] = netdev_priv(ndev); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci priv->pBdxRegs = nic->regs + port * 0x8000; 199162306a36Sopenharmony_ci priv->port = port; 199262306a36Sopenharmony_ci priv->pdev = pdev; 199362306a36Sopenharmony_ci priv->ndev = ndev; 199462306a36Sopenharmony_ci priv->nic = nic; 199562306a36Sopenharmony_ci priv->msg_enable = BDX_DEF_MSG_ENABLE; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci netif_napi_add(ndev, &priv->napi, bdx_poll); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if ((readl(nic->regs + FPGA_VER) & 0xFFF) == 308) { 200062306a36Sopenharmony_ci DBG("HW statistics not supported\n"); 200162306a36Sopenharmony_ci priv->stats_flag = 0; 200262306a36Sopenharmony_ci } else { 200362306a36Sopenharmony_ci priv->stats_flag = 1; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* Initialize fifo sizes. */ 200762306a36Sopenharmony_ci priv->txd_size = 2; 200862306a36Sopenharmony_ci priv->txf_size = 2; 200962306a36Sopenharmony_ci priv->rxd_size = 2; 201062306a36Sopenharmony_ci priv->rxf_size = 3; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* Initialize the initial coalescing registers. */ 201362306a36Sopenharmony_ci priv->rdintcm = INT_REG_VAL(0x20, 1, 4, 12); 201462306a36Sopenharmony_ci priv->tdintcm = INT_REG_VAL(0x20, 1, 0, 12); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* ndev->xmit_lock spinlock is not used. 201762306a36Sopenharmony_ci * Private priv->tx_lock is used for synchronization 201862306a36Sopenharmony_ci * between transmit and TX irq cleanup. In addition 201962306a36Sopenharmony_ci * set multicast list callback has to use priv->tx_lock. 202062306a36Sopenharmony_ci */ 202162306a36Sopenharmony_ci#ifdef BDX_LLTX 202262306a36Sopenharmony_ci ndev->features |= NETIF_F_LLTX; 202362306a36Sopenharmony_ci#endif 202462306a36Sopenharmony_ci /* MTU range: 60 - 16384 */ 202562306a36Sopenharmony_ci ndev->min_mtu = ETH_ZLEN; 202662306a36Sopenharmony_ci ndev->max_mtu = BDX_MAX_MTU; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci spin_lock_init(&priv->tx_lock); 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /*bdx_hw_reset(priv); */ 203162306a36Sopenharmony_ci if (bdx_read_mac(priv)) { 203262306a36Sopenharmony_ci pr_err("load MAC address failed\n"); 203362306a36Sopenharmony_ci err = -EFAULT; 203462306a36Sopenharmony_ci goto err_out_iomap; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 203762306a36Sopenharmony_ci err = register_netdev(ndev); 203862306a36Sopenharmony_ci if (err) { 203962306a36Sopenharmony_ci pr_err("register_netdev failed\n"); 204062306a36Sopenharmony_ci goto err_out_free; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci netif_carrier_off(ndev); 204362306a36Sopenharmony_ci netif_stop_queue(ndev); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci print_eth_id(ndev); 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci RET(0); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_cierr_out_free: 205062306a36Sopenharmony_ci free_netdev(ndev); 205162306a36Sopenharmony_cierr_out_iomap: 205262306a36Sopenharmony_ci iounmap(nic->regs); 205362306a36Sopenharmony_cierr_out_res: 205462306a36Sopenharmony_ci pci_release_regions(pdev); 205562306a36Sopenharmony_cierr_dma: 205662306a36Sopenharmony_ci pci_disable_device(pdev); 205762306a36Sopenharmony_cierr_pci: 205862306a36Sopenharmony_ci vfree(nic); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci RET(err); 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci/****************** Ethtool interface *********************/ 206462306a36Sopenharmony_ci/* get strings for statistics counters */ 206562306a36Sopenharmony_cistatic const char 206662306a36Sopenharmony_ci bdx_stat_names[][ETH_GSTRING_LEN] = { 206762306a36Sopenharmony_ci "InUCast", /* 0x7200 */ 206862306a36Sopenharmony_ci "InMCast", /* 0x7210 */ 206962306a36Sopenharmony_ci "InBCast", /* 0x7220 */ 207062306a36Sopenharmony_ci "InPkts", /* 0x7230 */ 207162306a36Sopenharmony_ci "InErrors", /* 0x7240 */ 207262306a36Sopenharmony_ci "InDropped", /* 0x7250 */ 207362306a36Sopenharmony_ci "FrameTooLong", /* 0x7260 */ 207462306a36Sopenharmony_ci "FrameSequenceErrors", /* 0x7270 */ 207562306a36Sopenharmony_ci "InVLAN", /* 0x7280 */ 207662306a36Sopenharmony_ci "InDroppedDFE", /* 0x7290 */ 207762306a36Sopenharmony_ci "InDroppedIntFull", /* 0x72A0 */ 207862306a36Sopenharmony_ci "InFrameAlignErrors", /* 0x72B0 */ 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* 0x72C0-0x72E0 RSRV */ 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci "OutUCast", /* 0x72F0 */ 208362306a36Sopenharmony_ci "OutMCast", /* 0x7300 */ 208462306a36Sopenharmony_ci "OutBCast", /* 0x7310 */ 208562306a36Sopenharmony_ci "OutPkts", /* 0x7320 */ 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* 0x7330-0x7360 RSRV */ 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci "OutVLAN", /* 0x7370 */ 209062306a36Sopenharmony_ci "InUCastOctects", /* 0x7380 */ 209162306a36Sopenharmony_ci "OutUCastOctects", /* 0x7390 */ 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* 0x73A0-0x73B0 RSRV */ 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci "InBCastOctects", /* 0x73C0 */ 209662306a36Sopenharmony_ci "OutBCastOctects", /* 0x73D0 */ 209762306a36Sopenharmony_ci "InOctects", /* 0x73E0 */ 209862306a36Sopenharmony_ci "OutOctects", /* 0x73F0 */ 209962306a36Sopenharmony_ci}; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci/* 210262306a36Sopenharmony_ci * bdx_get_link_ksettings - get device-specific settings 210362306a36Sopenharmony_ci * @netdev 210462306a36Sopenharmony_ci * @ecmd 210562306a36Sopenharmony_ci */ 210662306a36Sopenharmony_cistatic int bdx_get_link_ksettings(struct net_device *netdev, 210762306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, supported); 211062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, 211162306a36Sopenharmony_ci 10000baseT_Full); 211262306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, supported, FIBRE); 211362306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 211462306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, 211562306a36Sopenharmony_ci 10000baseT_Full); 211662306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ecmd, advertising, FIBRE); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci ecmd->base.speed = SPEED_10000; 211962306a36Sopenharmony_ci ecmd->base.duplex = DUPLEX_FULL; 212062306a36Sopenharmony_ci ecmd->base.port = PORT_FIBRE; 212162306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci return 0; 212462306a36Sopenharmony_ci} 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci/* 212762306a36Sopenharmony_ci * bdx_get_drvinfo - report driver information 212862306a36Sopenharmony_ci * @netdev 212962306a36Sopenharmony_ci * @drvinfo 213062306a36Sopenharmony_ci */ 213162306a36Sopenharmony_cistatic void 213262306a36Sopenharmony_cibdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) 213362306a36Sopenharmony_ci{ 213462306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci strscpy(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); 213762306a36Sopenharmony_ci strscpy(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); 213862306a36Sopenharmony_ci strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); 213962306a36Sopenharmony_ci strscpy(drvinfo->bus_info, pci_name(priv->pdev), 214062306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 214162306a36Sopenharmony_ci} 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci/* 214462306a36Sopenharmony_ci * bdx_get_coalesce - get interrupt coalescing parameters 214562306a36Sopenharmony_ci * @netdev 214662306a36Sopenharmony_ci * @ecoal 214762306a36Sopenharmony_ci */ 214862306a36Sopenharmony_cistatic int bdx_get_coalesce(struct net_device *netdev, 214962306a36Sopenharmony_ci struct ethtool_coalesce *ecoal, 215062306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 215162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci u32 rdintcm; 215462306a36Sopenharmony_ci u32 tdintcm; 215562306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci rdintcm = priv->rdintcm; 215862306a36Sopenharmony_ci tdintcm = priv->tdintcm; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci /* PCK_TH measures in multiples of FIFO bytes 216162306a36Sopenharmony_ci We translate to packets */ 216262306a36Sopenharmony_ci ecoal->rx_coalesce_usecs = GET_INT_COAL(rdintcm) * INT_COAL_MULT; 216362306a36Sopenharmony_ci ecoal->rx_max_coalesced_frames = 216462306a36Sopenharmony_ci ((GET_PCK_TH(rdintcm) * PCK_TH_MULT) / sizeof(struct rxf_desc)); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci ecoal->tx_coalesce_usecs = GET_INT_COAL(tdintcm) * INT_COAL_MULT; 216762306a36Sopenharmony_ci ecoal->tx_max_coalesced_frames = 216862306a36Sopenharmony_ci ((GET_PCK_TH(tdintcm) * PCK_TH_MULT) / BDX_TXF_DESC_SZ); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci /* adaptive parameters ignored */ 217162306a36Sopenharmony_ci return 0; 217262306a36Sopenharmony_ci} 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci/* 217562306a36Sopenharmony_ci * bdx_set_coalesce - set interrupt coalescing parameters 217662306a36Sopenharmony_ci * @netdev 217762306a36Sopenharmony_ci * @ecoal 217862306a36Sopenharmony_ci */ 217962306a36Sopenharmony_cistatic int bdx_set_coalesce(struct net_device *netdev, 218062306a36Sopenharmony_ci struct ethtool_coalesce *ecoal, 218162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 218262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 218362306a36Sopenharmony_ci{ 218462306a36Sopenharmony_ci u32 rdintcm; 218562306a36Sopenharmony_ci u32 tdintcm; 218662306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 218762306a36Sopenharmony_ci int rx_coal; 218862306a36Sopenharmony_ci int tx_coal; 218962306a36Sopenharmony_ci int rx_max_coal; 219062306a36Sopenharmony_ci int tx_max_coal; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci /* Check for valid input */ 219362306a36Sopenharmony_ci rx_coal = ecoal->rx_coalesce_usecs / INT_COAL_MULT; 219462306a36Sopenharmony_ci tx_coal = ecoal->tx_coalesce_usecs / INT_COAL_MULT; 219562306a36Sopenharmony_ci rx_max_coal = ecoal->rx_max_coalesced_frames; 219662306a36Sopenharmony_ci tx_max_coal = ecoal->tx_max_coalesced_frames; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci /* Translate from packets to multiples of FIFO bytes */ 219962306a36Sopenharmony_ci rx_max_coal = 220062306a36Sopenharmony_ci (((rx_max_coal * sizeof(struct rxf_desc)) + PCK_TH_MULT - 1) 220162306a36Sopenharmony_ci / PCK_TH_MULT); 220262306a36Sopenharmony_ci tx_max_coal = 220362306a36Sopenharmony_ci (((tx_max_coal * BDX_TXF_DESC_SZ) + PCK_TH_MULT - 1) 220462306a36Sopenharmony_ci / PCK_TH_MULT); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF) || 220762306a36Sopenharmony_ci (rx_max_coal > 0xF) || (tx_max_coal > 0xF)) 220862306a36Sopenharmony_ci return -EINVAL; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci rdintcm = INT_REG_VAL(rx_coal, GET_INT_COAL_RC(priv->rdintcm), 221162306a36Sopenharmony_ci GET_RXF_TH(priv->rdintcm), rx_max_coal); 221262306a36Sopenharmony_ci tdintcm = INT_REG_VAL(tx_coal, GET_INT_COAL_RC(priv->tdintcm), 0, 221362306a36Sopenharmony_ci tx_max_coal); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci priv->rdintcm = rdintcm; 221662306a36Sopenharmony_ci priv->tdintcm = tdintcm; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci WRITE_REG(priv, regRDINTCM0, rdintcm); 221962306a36Sopenharmony_ci WRITE_REG(priv, regTDINTCM0, tdintcm); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci return 0; 222262306a36Sopenharmony_ci} 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci/* Convert RX fifo size to number of pending packets */ 222562306a36Sopenharmony_cistatic inline int bdx_rx_fifo_size_to_packets(int rx_size) 222662306a36Sopenharmony_ci{ 222762306a36Sopenharmony_ci return (FIFO_SIZE * (1 << rx_size)) / sizeof(struct rxf_desc); 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci/* Convert TX fifo size to number of pending packets */ 223162306a36Sopenharmony_cistatic inline int bdx_tx_fifo_size_to_packets(int tx_size) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci return (FIFO_SIZE * (1 << tx_size)) / BDX_TXF_DESC_SZ; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci/* 223762306a36Sopenharmony_ci * bdx_get_ringparam - report ring sizes 223862306a36Sopenharmony_ci * @netdev 223962306a36Sopenharmony_ci * @ring 224062306a36Sopenharmony_ci * @kernel_ring 224162306a36Sopenharmony_ci * @extack 224262306a36Sopenharmony_ci */ 224362306a36Sopenharmony_cistatic void 224462306a36Sopenharmony_cibdx_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, 224562306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 224662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci /*max_pending - the maximum-sized FIFO we allow */ 225162306a36Sopenharmony_ci ring->rx_max_pending = bdx_rx_fifo_size_to_packets(3); 225262306a36Sopenharmony_ci ring->tx_max_pending = bdx_tx_fifo_size_to_packets(3); 225362306a36Sopenharmony_ci ring->rx_pending = bdx_rx_fifo_size_to_packets(priv->rxf_size); 225462306a36Sopenharmony_ci ring->tx_pending = bdx_tx_fifo_size_to_packets(priv->txd_size); 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci/* 225862306a36Sopenharmony_ci * bdx_set_ringparam - set ring sizes 225962306a36Sopenharmony_ci * @netdev 226062306a36Sopenharmony_ci * @ring 226162306a36Sopenharmony_ci * @kernel_ring 226262306a36Sopenharmony_ci * @extack 226362306a36Sopenharmony_ci */ 226462306a36Sopenharmony_cistatic int 226562306a36Sopenharmony_cibdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, 226662306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 226762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 227062306a36Sopenharmony_ci int rx_size = 0; 227162306a36Sopenharmony_ci int tx_size = 0; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci for (; rx_size < 4; rx_size++) { 227462306a36Sopenharmony_ci if (bdx_rx_fifo_size_to_packets(rx_size) >= ring->rx_pending) 227562306a36Sopenharmony_ci break; 227662306a36Sopenharmony_ci } 227762306a36Sopenharmony_ci if (rx_size == 4) 227862306a36Sopenharmony_ci rx_size = 3; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci for (; tx_size < 4; tx_size++) { 228162306a36Sopenharmony_ci if (bdx_tx_fifo_size_to_packets(tx_size) >= ring->tx_pending) 228262306a36Sopenharmony_ci break; 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci if (tx_size == 4) 228562306a36Sopenharmony_ci tx_size = 3; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci /*Is there anything to do? */ 228862306a36Sopenharmony_ci if ((rx_size == priv->rxf_size) && 228962306a36Sopenharmony_ci (tx_size == priv->txd_size)) 229062306a36Sopenharmony_ci return 0; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci priv->rxf_size = rx_size; 229362306a36Sopenharmony_ci if (rx_size > 1) 229462306a36Sopenharmony_ci priv->rxd_size = rx_size - 1; 229562306a36Sopenharmony_ci else 229662306a36Sopenharmony_ci priv->rxd_size = rx_size; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci priv->txf_size = priv->txd_size = tx_size; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci if (netif_running(netdev)) { 230162306a36Sopenharmony_ci bdx_close(netdev); 230262306a36Sopenharmony_ci bdx_open(netdev); 230362306a36Sopenharmony_ci } 230462306a36Sopenharmony_ci return 0; 230562306a36Sopenharmony_ci} 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci/* 230862306a36Sopenharmony_ci * bdx_get_strings - return a set of strings that describe the requested objects 230962306a36Sopenharmony_ci * @netdev 231062306a36Sopenharmony_ci * @data 231162306a36Sopenharmony_ci */ 231262306a36Sopenharmony_cistatic void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data) 231362306a36Sopenharmony_ci{ 231462306a36Sopenharmony_ci switch (stringset) { 231562306a36Sopenharmony_ci case ETH_SS_STATS: 231662306a36Sopenharmony_ci memcpy(data, *bdx_stat_names, sizeof(bdx_stat_names)); 231762306a36Sopenharmony_ci break; 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci/* 232262306a36Sopenharmony_ci * bdx_get_sset_count - return number of statistics or tests 232362306a36Sopenharmony_ci * @netdev 232462306a36Sopenharmony_ci */ 232562306a36Sopenharmony_cistatic int bdx_get_sset_count(struct net_device *netdev, int stringset) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci switch (stringset) { 233062306a36Sopenharmony_ci case ETH_SS_STATS: 233162306a36Sopenharmony_ci BDX_ASSERT(ARRAY_SIZE(bdx_stat_names) 233262306a36Sopenharmony_ci != sizeof(struct bdx_stats) / sizeof(u64)); 233362306a36Sopenharmony_ci return (priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci return -EINVAL; 233762306a36Sopenharmony_ci} 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci/* 234062306a36Sopenharmony_ci * bdx_get_ethtool_stats - return device's hardware L2 statistics 234162306a36Sopenharmony_ci * @netdev 234262306a36Sopenharmony_ci * @stats 234362306a36Sopenharmony_ci * @data 234462306a36Sopenharmony_ci */ 234562306a36Sopenharmony_cistatic void bdx_get_ethtool_stats(struct net_device *netdev, 234662306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci struct bdx_priv *priv = netdev_priv(netdev); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (priv->stats_flag) { 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci /* Update stats from HW */ 235362306a36Sopenharmony_ci bdx_update_stats(priv); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci /* Copy data to user buffer */ 235662306a36Sopenharmony_ci memcpy(data, &priv->hw_stats, sizeof(priv->hw_stats)); 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci/* 236162306a36Sopenharmony_ci * bdx_set_ethtool_ops - ethtool interface implementation 236262306a36Sopenharmony_ci * @netdev 236362306a36Sopenharmony_ci */ 236462306a36Sopenharmony_cistatic void bdx_set_ethtool_ops(struct net_device *netdev) 236562306a36Sopenharmony_ci{ 236662306a36Sopenharmony_ci static const struct ethtool_ops bdx_ethtool_ops = { 236762306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 236862306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 236962306a36Sopenharmony_ci .get_drvinfo = bdx_get_drvinfo, 237062306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 237162306a36Sopenharmony_ci .get_coalesce = bdx_get_coalesce, 237262306a36Sopenharmony_ci .set_coalesce = bdx_set_coalesce, 237362306a36Sopenharmony_ci .get_ringparam = bdx_get_ringparam, 237462306a36Sopenharmony_ci .set_ringparam = bdx_set_ringparam, 237562306a36Sopenharmony_ci .get_strings = bdx_get_strings, 237662306a36Sopenharmony_ci .get_sset_count = bdx_get_sset_count, 237762306a36Sopenharmony_ci .get_ethtool_stats = bdx_get_ethtool_stats, 237862306a36Sopenharmony_ci .get_link_ksettings = bdx_get_link_ksettings, 237962306a36Sopenharmony_ci }; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci netdev->ethtool_ops = &bdx_ethtool_ops; 238262306a36Sopenharmony_ci} 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci/** 238562306a36Sopenharmony_ci * bdx_remove - Device Removal Routine 238662306a36Sopenharmony_ci * @pdev: PCI device information struct 238762306a36Sopenharmony_ci * 238862306a36Sopenharmony_ci * bdx_remove is called by the PCI subsystem to alert the driver 238962306a36Sopenharmony_ci * that it should release a PCI device. The could be caused by a 239062306a36Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from 239162306a36Sopenharmony_ci * memory. 239262306a36Sopenharmony_ci **/ 239362306a36Sopenharmony_cistatic void bdx_remove(struct pci_dev *pdev) 239462306a36Sopenharmony_ci{ 239562306a36Sopenharmony_ci struct pci_nic *nic = pci_get_drvdata(pdev); 239662306a36Sopenharmony_ci struct net_device *ndev; 239762306a36Sopenharmony_ci int port; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci for (port = 0; port < nic->port_num; port++) { 240062306a36Sopenharmony_ci ndev = nic->priv[port]->ndev; 240162306a36Sopenharmony_ci unregister_netdev(ndev); 240262306a36Sopenharmony_ci free_netdev(ndev); 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /*bdx_hw_reset_direct(nic->regs); */ 240662306a36Sopenharmony_ci#ifdef BDX_MSI 240762306a36Sopenharmony_ci if (nic->irq_type == IRQ_MSI) 240862306a36Sopenharmony_ci pci_disable_msi(pdev); 240962306a36Sopenharmony_ci#endif 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci iounmap(nic->regs); 241262306a36Sopenharmony_ci pci_release_regions(pdev); 241362306a36Sopenharmony_ci pci_disable_device(pdev); 241462306a36Sopenharmony_ci vfree(nic); 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci RET(); 241762306a36Sopenharmony_ci} 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_cistatic struct pci_driver bdx_pci_driver = { 242062306a36Sopenharmony_ci .name = BDX_DRV_NAME, 242162306a36Sopenharmony_ci .id_table = bdx_pci_tbl, 242262306a36Sopenharmony_ci .probe = bdx_probe, 242362306a36Sopenharmony_ci .remove = bdx_remove, 242462306a36Sopenharmony_ci}; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci/* 242762306a36Sopenharmony_ci * print_driver_id - print parameters of the driver build 242862306a36Sopenharmony_ci */ 242962306a36Sopenharmony_cistatic void __init print_driver_id(void) 243062306a36Sopenharmony_ci{ 243162306a36Sopenharmony_ci pr_info("%s, %s\n", BDX_DRV_DESC, BDX_DRV_VERSION); 243262306a36Sopenharmony_ci pr_info("Options: hw_csum %s\n", BDX_MSI_STRING); 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_cistatic int __init bdx_module_init(void) 243662306a36Sopenharmony_ci{ 243762306a36Sopenharmony_ci ENTER; 243862306a36Sopenharmony_ci init_txd_sizes(); 243962306a36Sopenharmony_ci print_driver_id(); 244062306a36Sopenharmony_ci RET(pci_register_driver(&bdx_pci_driver)); 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_cimodule_init(bdx_module_init); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_cistatic void __exit bdx_module_exit(void) 244662306a36Sopenharmony_ci{ 244762306a36Sopenharmony_ci ENTER; 244862306a36Sopenharmony_ci pci_unregister_driver(&bdx_pci_driver); 244962306a36Sopenharmony_ci RET(); 245062306a36Sopenharmony_ci} 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cimodule_exit(bdx_module_exit); 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 245562306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 245662306a36Sopenharmony_ciMODULE_DESCRIPTION(BDX_DRV_DESC); 245762306a36Sopenharmony_ciMODULE_FIRMWARE("tehuti/bdx.bin"); 2458