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 + -
显示快捷键?