tehuti.c
来自「linux 内核源代码」· C语言 代码 · 共 2,340 行 · 第 1/5 页
C
2,340 行
/* * Tehuti Networks(R) Network Driver * ethtool interface implementation * Copyright (C) 2007 Tehuti Networks Ltd. All rights reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *//* * RX HW/SW interaction overview * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * There are 2 types of RX communication channels betwean driver and NIC. * 1) RX Free Fifo - RXF - holds descriptors of empty buffers to accept incoming * traffic. This Fifo is filled by SW and is readen by HW. Each descriptor holds * info about buffer's location, size and ID. An ID field is used to identify a * buffer when it's returned with data via RXD Fifo (see below) * 2) RX Data Fifo - RXD - holds descriptors of full buffers. This Fifo is * filled by HW and is readen by SW. Each descriptor holds status and ID. * HW pops descriptor from RXF Fifo, stores ID, fills buffer with incoming data, * via dma moves it into host memory, builds new RXD descriptor with same ID, * pushes it into RXD Fifo and raises interrupt to indicate new RX data. * * Current NIC configuration (registers + firmware) makes NIC use 2 RXF Fifos. * One holds 1.5K packets and another - 26K packets. Depending on incoming * packet size, HW desides on a RXF Fifo to pop buffer from. When packet is * filled with data, HW builds new RXD descriptor for it and push it into single * RXD Fifo. * * RX SW Data Structures * ~~~~~~~~~~~~~~~~~~~~~ * skb db - used to keep track of all skbs owned by SW and their dma addresses. * For RX case, ownership lasts from allocating new empty skb for RXF until * accepting full skb from RXD and passing it to OS. Each RXF Fifo has its own * skb db. Implemented as array with bitmask. * fifo - keeps info about fifo's size and location, relevant HW registers, * usage and skb db. Each RXD and RXF Fifo has its own fifo structure. * Implemented as simple struct. * * RX SW Execution Flow * ~~~~~~~~~~~~~~~~~~~~ * Upon initialization (ifconfig up) driver creates RX fifos and initializes * relevant registers. At the end of init phase, driver enables interrupts. * NIC sees that there is no RXF buffers and raises * RD_INTR interrupt, isr fills skbs and Rx begins. * Driver has two receive operation modes: * NAPI - interrupt-driven mixed with polling * interrupt-driven only * * Interrupt-driven only flow is following. When buffer is ready, HW raises * interrupt and isr is called. isr collects all available packets * (bdx_rx_receive), refills skbs (bdx_rx_alloc_skbs) and exit. * Rx buffer allocation note * ~~~~~~~~~~~~~~~~~~~~~~~~~ * Driver cares to feed such amount of RxF descriptors that respective amount of * RxD descriptors can not fill entire RxD fifo. The main reason is lack of * overflow check in Bordeaux for RxD fifo free/used size. * FIXME: this is NOT fully implemented, more work should be done * */#include "tehuti.h"#include "tehuti_fw.h"static struct pci_device_id __devinitdata bdx_pci_tbl[] = { {0x1FC9, 0x3009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0x1FC9, 0x3010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0x1FC9, 0x3014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0}};MODULE_DEVICE_TABLE(pci, bdx_pci_tbl);/* Definitions needed by ISR or NAPI functions */static void bdx_rx_alloc_skbs(struct bdx_priv *priv, struct rxf_fifo *f);static void bdx_tx_cleanup(struct bdx_priv *priv);static int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget);/* Definitions needed by FW loading */static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size);/* Definitions needed by hw_start */static int bdx_tx_init(struct bdx_priv *priv);static int bdx_rx_init(struct bdx_priv *priv);/* Definitions needed by bdx_close */static void bdx_rx_free(struct bdx_priv *priv);static void bdx_tx_free(struct bdx_priv *priv);/* Definitions needed by bdx_probe */static void bdx_ethtool_ops(struct net_device *netdev);/************************************************************************* * Print Info * *************************************************************************/static void print_hw_id(struct pci_dev *pdev){ struct pci_nic *nic = pci_get_drvdata(pdev); u16 pci_link_status = 0; u16 pci_ctrl = 0; pci_read_config_word(pdev, PCI_LINK_STATUS_REG, &pci_link_status); pci_read_config_word(pdev, PCI_DEV_CTRL_REG, &pci_ctrl); printk(KERN_INFO "tehuti: %s%s\n", BDX_NIC_NAME, nic->port_num == 1 ? "" : ", 2-Port"); printk(KERN_INFO "tehuti: srom 0x%x fpga %d build %u lane# %d" " max_pl 0x%x mrrs 0x%x\n", readl(nic->regs + SROM_VER), readl(nic->regs + FPGA_VER) & 0xFFF, readl(nic->regs + FPGA_SEED), GET_LINK_STATUS_LANES(pci_link_status), GET_DEV_CTRL_MAXPL(pci_ctrl), GET_DEV_CTRL_MRRS(pci_ctrl));}static void print_fw_id(struct pci_nic *nic){ printk(KERN_INFO "tehuti: fw 0x%x\n", readl(nic->regs + FW_VER));}static void print_eth_id(struct net_device *ndev){ printk(KERN_INFO "%s: %s, Port %c\n", ndev->name, BDX_NIC_NAME, (ndev->if_port == 0) ? 'A' : 'B');}/************************************************************************* * Code * *************************************************************************/#define bdx_enable_interrupts(priv) \ do { WRITE_REG(priv, regIMR, IR_RUN); } while (0)#define bdx_disable_interrupts(priv) \ do { WRITE_REG(priv, regIMR, 0); } while (0)/* bdx_fifo_init * create TX/RX descriptor fifo for host-NIC communication. * 1K extra space is allocated at the end of the fifo to simplify * processing of descriptors that wraps around fifo's end * @priv - NIC private structure * @f - fifo to initialize * @fsz_type - fifo size type: 0-4KB, 1-8KB, 2-16KB, 3-32KB * @reg_XXX - offsets of registers relative to base address * * Returns 0 on success, negative value on failure * */static intbdx_fifo_init(struct bdx_priv *priv, struct fifo *f, int fsz_type, u16 reg_CFG0, u16 reg_CFG1, u16 reg_RPTR, u16 reg_WPTR){ u16 memsz = FIFO_SIZE * (1 << fsz_type); memset(f, 0, sizeof(struct fifo)); /* pci_alloc_consistent gives us 4k-aligned memory */ f->va = pci_alloc_consistent(priv->pdev, memsz + FIFO_EXTRA_SPACE, &f->da); if (!f->va) { ERR("pci_alloc_consistent failed\n"); RET(-ENOMEM); } f->reg_CFG0 = reg_CFG0; f->reg_CFG1 = reg_CFG1; f->reg_RPTR = reg_RPTR; f->reg_WPTR = reg_WPTR; f->rptr = 0; f->wptr = 0; f->memsz = memsz; f->size_mask = memsz - 1; WRITE_REG(priv, reg_CFG0, (u32) ((f->da & TX_RX_CFG0_BASE) | fsz_type)); WRITE_REG(priv, reg_CFG1, H32_64(f->da)); RET(0);}/* bdx_fifo_free - free all resources used by fifo * @priv - NIC private structure * @f - fifo to release */static void bdx_fifo_free(struct bdx_priv *priv, struct fifo *f){ ENTER; if (f->va) { pci_free_consistent(priv->pdev, f->memsz + FIFO_EXTRA_SPACE, f->va, f->da); f->va = NULL; } RET();}/* * bdx_link_changed - notifies OS about hw link state. * @bdx_priv - hw adapter structure */static void bdx_link_changed(struct bdx_priv *priv){ u32 link = READ_REG(priv, regMAC_LNK_STAT) & MAC_LINK_STAT; if (!link) { if (netif_carrier_ok(priv->ndev)) { netif_stop_queue(priv->ndev); netif_carrier_off(priv->ndev); ERR("%s: Link Down\n", priv->ndev->name); } } else { if (!netif_carrier_ok(priv->ndev)) { netif_wake_queue(priv->ndev); netif_carrier_on(priv->ndev); ERR("%s: Link Up\n", priv->ndev->name); } }}static void bdx_isr_extra(struct bdx_priv *priv, u32 isr){ if (isr & IR_RX_FREE_0) { bdx_rx_alloc_skbs(priv, &priv->rxf_fifo0); DBG("RX_FREE_0\n"); } if (isr & IR_LNKCHG0) bdx_link_changed(priv); if (isr & IR_PCIE_LINK) ERR("%s: PCI-E Link Fault\n", priv->ndev->name); if (isr & IR_PCIE_TOUT) ERR("%s: PCI-E Time Out\n", priv->ndev->name);}/* bdx_isr - Interrupt Service Routine for Bordeaux NIC * @irq - interrupt number * @ndev - network device * @regs - CPU registers * * Return IRQ_NONE if it was not our interrupt, IRQ_HANDLED - otherwise * * It reads ISR register to know interrupt reasons, and proceed them one by one. * Reasons of interest are: * RX_DESC - new packet has arrived and RXD fifo holds its descriptor * RX_FREE - number of free Rx buffers in RXF fifo gets low * TX_FREE - packet was transmited and RXF fifo holds its descriptor */static irqreturn_t bdx_isr_napi(int irq, void *dev){ struct net_device *ndev = dev; struct bdx_priv *priv = ndev->priv; u32 isr; ENTER; isr = (READ_REG(priv, regISR) & IR_RUN); if (unlikely(!isr)) { bdx_enable_interrupts(priv); return IRQ_NONE; /* Not our interrupt */ } if (isr & IR_EXTRA) bdx_isr_extra(priv, isr); if (isr & (IR_RX_DESC_0 | IR_TX_FREE_0)) { if (likely(netif_rx_schedule_prep(ndev, &priv->napi))) { __netif_rx_schedule(ndev, &priv->napi); RET(IRQ_HANDLED); } else { /* NOTE: we get here if intr has slipped into window * between these lines in bdx_poll: * bdx_enable_interrupts(priv); * return 0; * currently intrs are disabled (since we read ISR), * and we have failed to register next poll. * so we read the regs to trigger chip * and allow further interupts. */ READ_REG(priv, regTXF_WPTR_0); READ_REG(priv, regRXD_WPTR_0); } } bdx_enable_interrupts(priv); RET(IRQ_HANDLED);}static int bdx_poll(struct napi_struct *napi, int budget){ struct bdx_priv *priv = container_of(napi, struct bdx_priv, napi); struct net_device *dev = priv->ndev; int work_done; ENTER; bdx_tx_cleanup(priv); work_done = bdx_rx_receive(priv, &priv->rxd_fifo0, budget); if ((work_done < budget) || (priv->napi_stop++ >= 30)) { DBG("rx poll is done. backing to isr-driven\n"); /* from time to time we exit to let NAPI layer release * device lock and allow waiting tasks (eg rmmod) to advance) */ priv->napi_stop = 0; netif_rx_complete(dev, napi); bdx_enable_interrupts(priv); } return work_done;}/* bdx_fw_load - loads firmware to NIC * @priv - NIC private structure * Firmware is loaded via TXD fifo, so it must be initialized first. * Firware must be loaded once per NIC not per PCI device provided by NIC (NIC * can have few of them). So all drivers use semaphore register to choose one * that will actually load FW to NIC. */static int bdx_fw_load(struct bdx_priv *priv){ int master, i; ENTER; master = READ_REG(priv, regINIT_SEMAPHORE); if (!READ_REG(priv, regINIT_STATUS) && master) { bdx_tx_push_desc_safe(priv, s_firmLoad, sizeof(s_firmLoad)); mdelay(100); } for (i = 0; i < 200; i++) { if (READ_REG(priv, regINIT_STATUS)) break; mdelay(2); } if (master) WRITE_REG(priv, regINIT_SEMAPHORE, 1); if (i == 200) { ERR("%s: firmware loading failed\n", priv->ndev->name); DBG("VPC = 0x%x VIC = 0x%x INIT_STATUS = 0x%x i=%d\n", READ_REG(priv, regVPC), READ_REG(priv, regVIC), READ_REG(priv, regINIT_STATUS), i); RET(-EIO); } else { DBG("%s: firmware loading success\n", priv->ndev->name); RET(0); }}static void bdx_restore_mac(struct net_device *ndev, struct bdx_priv *priv){ u32 val; ENTER; DBG("mac0=%x mac1=%x mac2=%x\n", READ_REG(priv, regUNC_MAC0_A), READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); val = (ndev->dev_addr[0] << 8) | (ndev->dev_addr[1]); WRITE_REG(priv, regUNC_MAC2_A, val); val = (ndev->dev_addr[2] << 8) | (ndev->dev_addr[3]); WRITE_REG(priv, regUNC_MAC1_A, val); val = (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5]); WRITE_REG(priv, regUNC_MAC0_A, val); DBG("mac0=%x mac1=%x mac2=%x\n", READ_REG(priv, regUNC_MAC0_A), READ_REG(priv, regUNC_MAC1_A), READ_REG(priv, regUNC_MAC2_A)); RET();}/* bdx_hw_start - inits registers and starts HW's Rx and Tx engines * @priv - NIC private structure */static int bdx_hw_start(struct bdx_priv *priv){ int rc = -EIO; struct net_device *ndev = priv->ndev; ENTER; bdx_link_changed(priv); /* 10G overall max length (vlan, eth&ip header, ip payload, crc) */ WRITE_REG(priv, regFRM_LENGTH, 0X3FE0); WRITE_REG(priv, regPAUSE_QUANT, 0x96); WRITE_REG(priv, regRX_FIFO_SECTION, 0x800010); WRITE_REG(priv, regTX_FIFO_SECTION, 0xE00010); WRITE_REG(priv, regRX_FULLNESS, 0); WRITE_REG(priv, regTX_FULLNESS, 0); WRITE_REG(priv, regCTRLST, regCTRLST_BASE | regCTRLST_RX_ENA | regCTRLST_TX_ENA); WRITE_REG(priv, regVGLB, 0); WRITE_REG(priv, regMAX_FRAME_A, priv->rxf_fifo0.m.pktsz & MAX_FRAME_AB_VAL); DBG("RDINTCM=%08x\n", priv->rdintcm); /*NOTE: test script uses this */ WRITE_REG(priv, regRDINTCM0, priv->rdintcm); WRITE_REG(priv, regRDINTCM2, 0); /*cpu_to_le32(rcm.val)); */ DBG("TDINTCM=%08x\n", priv->tdintcm); /*NOTE: test script uses this */ WRITE_REG(priv, regTDINTCM0, priv->tdintcm); /* old val = 0x300064 */ /* Enable timer interrupt once in 2 secs. */ /*WRITE_REG(priv, regGTMR0, ((GTMR_SEC * 2) & GTMR_DATA)); */ bdx_restore_mac(priv->ndev, priv); WRITE_REG(priv, regGMAC_RXF_A, GMAC_RX_FILTER_OSEN | GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB);#define BDX_IRQ_TYPE ((priv->nic->irq_type == IRQ_MSI)?0:IRQF_SHARED) if ((rc = request_irq(priv->pdev->irq, &bdx_isr_napi, BDX_IRQ_TYPE, ndev->name, ndev))) goto err_irq; bdx_enable_interrupts(priv); RET(0);err_irq: RET(rc);}static void bdx_hw_stop(struct bdx_priv *priv){ ENTER; bdx_disable_interrupts(priv); free_irq(priv->pdev->irq, priv->ndev); netif_carrier_off(priv->ndev); netif_stop_queue(priv->ndev); RET();}static int bdx_hw_reset_direct(void __iomem *regs){ u32 val, i; ENTER; /* reset sequences: read, write 1, read, write 0 */ val = readl(regs + regCLKPLL); writel((val | CLKPLL_SFTRST) + 0x8, regs + regCLKPLL); udelay(50); val = readl(regs + regCLKPLL); writel(val & ~CLKPLL_SFTRST, regs + regCLKPLL); /* check that the PLLs are locked and reset ended */ for (i = 0; i < 70; i++, mdelay(10)) if ((readl(regs + regCLKPLL) & CLKPLL_LKD) == CLKPLL_LKD) { /* do any PCI-E read transaction */ readl(regs + regRXD_CFG0_0); return 0; } ERR("tehuti: HW reset failed\n"); return 1; /* failure */}static int bdx_hw_reset(struct bdx_priv *priv){ u32 val, i; ENTER; if (priv->port == 0) { /* reset sequences: read, write 1, read, write 0 */ val = READ_REG(priv, regCLKPLL); WRITE_REG(priv, regCLKPLL, (val | CLKPLL_SFTRST) + 0x8); udelay(50); val = READ_REG(priv, regCLKPLL);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?