lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20070911164144.2853bcff@oldman>
Date:	Tue, 11 Sep 2007 16:41:44 +0200
From:	Stephen Hemminger <shemminger@...ux-foundation.org>
To:	Jesse Huang <jesse@...lus.com.tw>
Cc:	jeff@...zik.org, akpm@...ux-foundation.org, netdev@...r.kernel.org,
	jesse@...lus.com.tw
Subject: Re: [PATCH] Add IP1000A Driver

On Tue, 11 Sep 2007 11:30:38 -0400
Jesse Huang <jesse@...lus.com.tw> wrote:

> From: Jesse Huang <jesse@...lus.com.tw>
> 
> Change Logs: Add IP1000A Driver to kernel tree.
> 
> Signed-off-by: Jesse Huang <jesse@...lus.com.tw>

Who will be listed as maintainer of this device?
A good way to show that is to add an entry to MAINTAINERS file.


>  drivers/net/ipg.c | 2331 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/ipg.h |  856 +++++++++++++++++++
>  2 files changed, 3187 insertions(+), 0 deletions(-)
>  create mode 100755 drivers/net/ipg.c
>  create mode 100755 drivers/net/ipg.h
> 
> e804d1c265bf1d843f845457f925a1728bbfdff7
> diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
> new file mode 100755
> index 0000000..bdc2b8d
> --- /dev/null
> +++ b/drivers/net/ipg.c
> @@ -0,0 +1,2331 @@
> +/*
> + * ipg.c: Device Driver for the IP1000 Gigabit Ethernet Adapter
> + *
> + * Copyright (C) 2003, 2006  IC Plus Corp.
> + *
> + * Original Author:
> + *
> + *   Craig Rich
> + *   Sundance Technology, Inc.
> + *   1485 Saratoga Avenue
> + *   Suite 200
> + *   San Jose, CA 95129
> + *   408 873 4117
> + *   www.sundanceti.com
> + *   craig_rich@...danceti.com
> + *
> + * Current Maintainer:
> + *
> + *   Sorbica Shieh.
> + *   10F, No.47, Lane 2, Kwang-Fu RD.
> + *   Sec. 2, Hsin-Chu, Taiwan, R.O.C.
> + *   http://www.icplus.com.tw
> + *   sorbica@...lus.com.tw
> + */

Names only, no physical addresses please.

> +/*
> + * Read a register from the Physical Layer device located
> + * on the IPG NIC, using the IPG PHYCTRL register.
> + */
> +static int mdio_read(struct net_device * dev, int phy_id, int phy_reg)
> +{
> +	void __iomem *ioaddr = ipg_ioaddr(dev);
> +	/*
> +	 * The GMII mangement frame structure for a read is as follows:
> +	 *
> +	 * |Preamble|st|op|phyad|regad|ta|      data      |idle|
> +	 * |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z   |
> +	 *
> +	 * <32 1s> = 32 consecutive logic 1 values
> +	 * A = bit of Physical Layer device address (MSB first)
> +	 * R = bit of register address (MSB first)
> +	 * z = High impedance state
> +	 * D = bit of read data (MSB first)
> +	 *
> +	 * Transmission order is 'Preamble' field first, bits transmitted
> +	 * left to right (first to last).
> +	 */
> +	struct {
> +		u32 field;
> +		unsigned int len;
> +	} p[] = {
> +		{ GMII_PREAMBLE,	32 },	/* Preamble */
> +		{ GMII_ST,		2  },	/* ST */
> +		{ GMII_READ,		2  },	/* OP */
> +		{ phy_id,		5  },	/* PHYAD */
> +		{ phy_reg,		5  },	/* REGAD */
> +		{ 0x0000,		2  },	/* TA */
> +		{ 0x0000,		16 },	/* DATA */
> +		{ 0x0000,		1  }	/* IDLE */
> +	};

This could be declared static const, since it doesn't change.

> +	unsigned int i, j;
> +	u8 polarity, data;
> +
> +	polarity  = ipg_r8(PHY_CTRL);
> +	polarity &= (IPG_PC_DUPLEX_POLARITY | IPG_PC_LINK_POLARITY);
> +
> +	/* Create the Preamble, ST, OP, PHYAD, and REGAD field. */
> +	for (j = 0; j < 5; j++) {
> +		for (i = 0; i < p[j].len; i++) {
> +			/* For each variable length field, the MSB must be
> +			 * transmitted first. Rotate through the field bits,
> +			 * starting with the MSB, and move each bit into the
> +			 * the 1st (2^1) bit position (this is the bit position
> +			 * corresponding to the MgmtData bit of the PhyCtrl
> +			 * register for the IPG).
> +			 *
> +			 * Example: ST = 01;
> +			 *
> +			 *          First write a '0' to bit 1 of the PhyCtrl
> +			 *          register, then write a '1' to bit 1 of the
> +			 *          PhyCtrl register.
> +			 *
> +			 * To do this, right shift the MSB of ST by the value:
> +			 * [field length - 1 - #ST bits already written]
> +			 * then left shift this result by 1.
> +			 */
> +			data  = (p[j].field >> (p[j].len - 1 - i)) << 1;
> +			data &= IPG_PC_MGMTDATA;
> +			data |= polarity | IPG_PC_MGMTDIR;
> +
> +			ipg_drive_phy_ctl_low_high(ioaddr, data);
> +		}
> +	}
> +
> +	send_three_state(ioaddr, polarity);
> +
> +	read_phy_bit(ioaddr, polarity);
> +
> +	/*
> +	 * For a read cycle, the bits for the next two fields (TA and
> +	 * DATA) are driven by the PHY (the IPG reads these bits).
> +	 */
> +	for (i = 0; i < p[6].len; i++) {
> +		p[6].field |=
> +		    (read_phy_bit(ioaddr, polarity) << (p[6].len - 1 - i));
> +	}
> +
> +	send_three_state(ioaddr, polarity);
> +	send_three_state(ioaddr, polarity);
> +	send_three_state(ioaddr, polarity);
> +	send_end(ioaddr, polarity);
> +
> +	/* Return the value of the DATA field. */
> +	return p[6].field;
> +}
> +
> +/*
> + * Write to a register from the Physical Layer device located
> + * on the IPG NIC, using the IPG PHYCTRL register.
> + */
> +static void mdio_write(struct net_device *dev, int phy_id, int phy_reg, int val)
> +{
> +	void __iomem *ioaddr = ipg_ioaddr(dev);
> +	/*
> +	 * The GMII mangement frame structure for a read is as follows:
> +	 *
> +	 * |Preamble|st|op|phyad|regad|ta|      data      |idle|
> +	 * |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z   |
> +	 *
> +	 * <32 1s> = 32 consecutive logic 1 values
> +	 * A = bit of Physical Layer device address (MSB first)
> +	 * R = bit of register address (MSB first)
> +	 * z = High impedance state
> +	 * D = bit of write data (MSB first)
> +	 *
> +	 * Transmission order is 'Preamble' field first, bits transmitted
> +	 * left to right (first to last).
> +	 */
> +	struct {
> +		u32 field;
> +		unsigned int len;
> +	} p[] = {
> +		{ GMII_PREAMBLE,	32 },	/* Preamble */
> +		{ GMII_ST,		2  },	/* ST */
> +		{ GMII_WRITE,		2  },	/* OP */
> +		{ phy_id,		5  },	/* PHYAD */
> +		{ phy_reg,		5  },	/* REGAD */
> +		{ 0x0002,		2  },	/* TA */
> +		{ val & 0xffff,		16 },	/* DATA */
> +		{ 0x0000,		1  }	/* IDLE */
> +	};
> +	unsigned int i, j;
> +	u8 polarity, data;
> +
> +	polarity  = ipg_r8(PHY_CTRL);
> +	polarity &= (IPG_PC_DUPLEX_POLARITY | IPG_PC_LINK_POLARITY);
> +
> +	/* Create the Preamble, ST, OP, PHYAD, and REGAD field. */
> +	for (j = 0; j < 7; j++) {
> +		for (i = 0; i < p[j].len; i++) {
> +			/* For each variable length field, the MSB must be
> +			 * transmitted first. Rotate through the field bits,
> +			 * starting with the MSB, and move each bit into the
> +			 * the 1st (2^1) bit position (this is the bit position
> +			 * corresponding to the MgmtData bit of the PhyCtrl
> +			 * register for the IPG).
> +			 *
> +			 * Example: ST = 01;
> +			 *
> +			 *          First write a '0' to bit 1 of the PhyCtrl
> +			 *          register, then write a '1' to bit 1 of the
> +			 *          PhyCtrl register.
> +			 *
> +			 * To do this, right shift the MSB of ST by the value:
> +			 * [field length - 1 - #ST bits already written]
> +			 * then left shift this result by 1.
> +			 */
> +			data  = (p[j].field >> (p[j].len - 1 - i)) << 1;
> +			data &= IPG_PC_MGMTDATA;
> +			data |= polarity | IPG_PC_MGMTDIR;
> +
> +			ipg_drive_phy_ctl_low_high(ioaddr, data);
> +		}
> +	}
> +
> +	/* The last cycle is a tri-state, so read from the PHY. */
> +	for (j = 7; j < 8; j++) {
> +		for (i = 0; i < p[j].len; i++) {
> +			ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_LO | polarity);
> +
> +			p[j].field |= ((ipg_r8(PHY_CTRL) &
> +				IPG_PC_MGMTDATA) >> 1) << (p[j].len - 1 - i);
> +
> +			ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_HI | polarity);
> +		}
> +	}
> +}
> +
> +/* Set LED_Mode JES20040127EEPROM */
> +static void ipg_set_led_mode(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	u32 mode;
> +
> +	mode = ipg_r32(ASIC_CTRL);
> +	mode &= ~(IPG_AC_LED_MODE_BIT_1 | IPG_AC_LED_MODE | IPG_AC_LED_SPEED);
> +
> +	if ((sp->LED_Mode & 0x03) > 1)
> +		mode |= IPG_AC_LED_MODE_BIT_1;	/* Write Asic Control Bit 29 */
> +
> +	if ((sp->LED_Mode & 0x01) == 1)
> +		mode |= IPG_AC_LED_MODE;	/* Write Asic Control Bit 14 */
> +
> +	if ((sp->LED_Mode & 0x08) == 8)
> +		mode |= IPG_AC_LED_SPEED;	/* Write Asic Control Bit 27 */
> +
> +	ipg_w32(mode, ASIC_CTRL);
> +}
> +
> +/* Set PHYSet JES20040127EEPROM */
> +static void ipg_set_phy_set(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	int physet;
> +
> +	physet = ipg_r8(PHY_SET);
> +	physet &= ~(IPG_PS_MEM_LENB9B | IPG_PS_MEM_LEN9 | IPG_PS_NON_COMPDET);
> +	physet |= ((sp->LED_Mode & 0x70) >> 4);
> +	ipg_w8(physet, PHY_SET);
> +}
> +
> +static int ipg_reset(struct net_device *dev, u32 resetflags)
> +{
> +	/* Assert functional resets via the IPG AsicCtrl
> +	 * register as specified by the 'resetflags' input
> +	 * parameter.
> +	 */
> +	void __iomem *ioaddr = ipg_ioaddr(dev);	//JES20040127EEPROM:
> +	unsigned int timeout_count = 0;
> +
> +	IPG_DEBUG_MSG("_reset\n");
> +
> +	ipg_w32(ipg_r32(ASIC_CTRL) | resetflags, ASIC_CTRL);
> +
> +	/* Delay added to account for problem with 10Mbps reset. */
> +	mdelay(IPG_AC_RESETWAIT);
> +
> +	while (IPG_AC_RESET_BUSY & ipg_r32(ASIC_CTRL)) {
> +		mdelay(IPG_AC_RESETWAIT);
> +		if (++timeout_count > IPG_AC_RESET_TIMEOUT)
> +			return -ETIME;
> +	}
> +	/* Set LED Mode in Asic Control JES20040127EEPROM */
> +	ipg_set_led_mode(dev);
> +
> +	/* Set PHYSet Register Value JES20040127EEPROM */
> +	ipg_set_phy_set(dev);
> +	return 0;
> +}
> +
> +/* Find the GMII PHY address. */
> +static int ipg_find_phyaddr(struct net_device *dev)
> +{
> +	unsigned int phyaddr, i;
> +
> +	for (i = 0; i < 32; i++) {
> +		u32 status;
> +
> +		/* Search for the correct PHY address among 32 possible. */
> +		phyaddr = (IPG_NIC_PHY_ADDRESS + i) % 32;
> +
> +		/* 10/22/03 Grace change verify from GMII_PHY_STATUS to
> +		   GMII_PHY_ID1
> +		 */
> +
> +		status = mdio_read(dev, phyaddr, MII_BMSR);
> +
> +		if ((status != 0xFFFF) && (status != 0))
> +			return phyaddr;
> +	}
> +
> +	return 0x1f;
> +}
> +
> +/*
> + * Configure IPG based on result of IEEE 802.3 PHY
> + * auto-negotiation.
> + */
> +static int ipg_config_autoneg(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	unsigned int txflowcontrol;
> +	unsigned int rxflowcontrol;
> +	unsigned int fullduplex;
> +	unsigned int gig;
> +	u32 mac_ctrl_val;
> +	u32 asicctrl;
> +	u8 phyctrl;
> +
> +	IPG_DEBUG_MSG("_config_autoneg\n");
> +
> +	asicctrl = ipg_r32(ASIC_CTRL);
> +	phyctrl = ipg_r8(PHY_CTRL);
> +	mac_ctrl_val = ipg_r32(MAC_CTRL);
> +
> +	/* Set flags for use in resolving auto-negotation, assuming
> +	 * non-1000Mbps, half duplex, no flow control.
> +	 */
> +	fullduplex = 0;
> +	txflowcontrol = 0;
> +	rxflowcontrol = 0;
> +	gig = 0;
> +
> +	/* To accomodate a problem in 10Mbps operation,
> +	 * set a global flag if PHY running in 10Mbps mode.
> +	 */
> +	sp->tenmbpsmode = 0;
> +
> +	printk(KERN_INFO "%s: Link speed = ", dev->name);
> +
> +	/* Determine actual speed of operation. */
> +	switch (phyctrl & IPG_PC_LINK_SPEED) {
> +	case IPG_PC_LINK_SPEED_10MBPS:
> +		printk("10Mbps.\n");
> +		printk(KERN_INFO "%s: 10Mbps operational mode enabled.\n",
> +		       dev->name);
> +		sp->tenmbpsmode = 1;
> +		break;
> +	case IPG_PC_LINK_SPEED_100MBPS:
> +		printk("100Mbps.\n");
> +		break;
> +	case IPG_PC_LINK_SPEED_1000MBPS:
> +		printk("1000Mbps.\n");
> +		gig = 1;
> +		break;
> +	default:
> +		printk("undefined!\n");
> +		return 0;
> +	}
> +
> +	if (phyctrl & IPG_PC_DUPLEX_STATUS) {
> +		fullduplex = 1;
> +		txflowcontrol = 1;
> +		rxflowcontrol = 1;
> +	}
> +
> +	/* Configure full duplex, and flow control. */
> +	if (fullduplex == 1) {
> +		/* Configure IPG for full duplex operation. */
> +		printk(KERN_INFO "%s: setting full duplex, ", dev->name);
> +
> +		mac_ctrl_val |= IPG_MC_DUPLEX_SELECT_FD;
> +
> +		if (txflowcontrol == 1) {
> +			printk("TX flow control");
> +			mac_ctrl_val |= IPG_MC_TX_FLOW_CONTROL_ENABLE;
> +		} else {
> +			printk("no TX flow control");
> +			mac_ctrl_val &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
> +		}
> +
> +		if (rxflowcontrol == 1) {
> +			printk(", RX flow control.");
> +			mac_ctrl_val |= IPG_MC_RX_FLOW_CONTROL_ENABLE;
> +		} else {
> +			printk(", no RX flow control.");
> +			mac_ctrl_val &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
> +		}
> +
> +		printk("\n");
> +	} else {
> +		/* Configure IPG for half duplex operation. */
> +	        printk(KERN_INFO "%s: setting half duplex, "
> +		       "no TX flow control, no RX flow control.\n", dev->name);
> +
> +		mac_ctrl_val &= ~IPG_MC_DUPLEX_SELECT_FD &
> +			~IPG_MC_TX_FLOW_CONTROL_ENABLE &
> +			~IPG_MC_RX_FLOW_CONTROL_ENABLE;
> +	}
> +	ipg_w32(mac_ctrl_val, MAC_CTRL);
> +	return 0;
> +}
> +
> +/* Determine and configure multicast operation and set
> + * receive mode for IPG.
> + */
> +static void ipg_nic_set_multicast_list(struct net_device *dev)
> +{
> +	void __iomem *ioaddr = ipg_ioaddr(dev);
> +	struct dev_mc_list *mc_list_ptr;
> +	unsigned int hashindex;
> +	u32 hashtable[2];
> +	u8 receivemode;
> +
> +	IPG_DEBUG_MSG("_nic_set_multicast_list\n");
> +
> +	receivemode = IPG_RM_RECEIVEUNICAST | IPG_RM_RECEIVEBROADCAST;
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		/* NIC to be configured in promiscuous mode. */
> +		receivemode = IPG_RM_RECEIVEALLFRAMES;
> +	} else if ((dev->flags & IFF_ALLMULTI) ||
> +		   (dev->flags & IFF_MULTICAST &
> +		    (dev->mc_count > IPG_MULTICAST_HASHTABLE_SIZE))) {
> +		/* NIC to be configured to receive all multicast
> +		 * frames. */
> +		receivemode |= IPG_RM_RECEIVEMULTICAST;
> +	} else if (dev->flags & IFF_MULTICAST & (dev->mc_count > 0)) {
> +		/* NIC to be configured to receive selected
> +		 * multicast addresses. */
> +		receivemode |= IPG_RM_RECEIVEMULTICASTHASH;
> +	}
> +
> +	/* Calculate the bits to set for the 64 bit, IPG HASHTABLE.
> +	 * The IPG applies a cyclic-redundancy-check (the same CRC
> +	 * used to calculate the frame data FCS) to the destination
> +	 * address all incoming multicast frames whose destination
> +	 * address has the multicast bit set. The least significant
> +	 * 6 bits of the CRC result are used as an addressing index
> +	 * into the hash table. If the value of the bit addressed by
> +	 * this index is a 1, the frame is passed to the host system.
> +	 */
> +
> +	/* Clear hashtable. */
> +	hashtable[0] = 0x00000000;
> +	hashtable[1] = 0x00000000;
> +
> +	/* Cycle through all multicast addresses to filter. */
> +	for (mc_list_ptr = dev->mc_list;
> +	     mc_list_ptr != NULL; mc_list_ptr = mc_list_ptr->next) {
> +		/* Calculate CRC result for each multicast address. */
> +		hashindex = crc32_le(0xffffffff, mc_list_ptr->dmi_addr,
> +				     ETH_ALEN);
> +
> +		/* Use only the least significant 6 bits. */
> +		hashindex = hashindex & 0x3F;
> +
> +		/* Within "hashtable", set bit number "hashindex"
> +		 * to a logic 1.
> +		 */
> +		set_bit(hashindex, (void *)hashtable);
> +	}
> +
> +	/* Write the value of the hashtable, to the 4, 16 bit
> +	 * HASHTABLE IPG registers.
> +	 */
> +	ipg_w32(hashtable[0], HASHTABLE_0);
> +	ipg_w32(hashtable[1], HASHTABLE_1);
> +
> +	ipg_w8(IPG_RM_RSVD_MASK & receivemode, RECEIVE_MODE);
> +
> +	IPG_DEBUG_MSG("ReceiveMode = %x\n", ipg_r8(RECEIVE_MODE));
> +}
> +
> +static int ipg_io_config(struct net_device *dev)
> +{
> +	void __iomem *ioaddr = ipg_ioaddr(dev);
> +	u32 origmacctrl;
> +	u32 restoremacctrl;
> +
> +	IPG_DEBUG_MSG("_io_config\n");
> +
> +	origmacctrl = ipg_r32(MAC_CTRL);
> +
> +	restoremacctrl = origmacctrl | IPG_MC_STATISTICS_ENABLE;
> +
> +	/* Based on compilation option, determine if FCS is to be
> +	 * stripped on receive frames by IPG.
> +	 */
> +	if (!IPG_STRIP_FCS_ON_RX)
> +		restoremacctrl |= IPG_MC_RCV_FCS;
> +
> +	/* Determine if transmitter and/or receiver are
> +	 * enabled so we may restore MACCTRL correctly.
> +	 */
> +	if (origmacctrl & IPG_MC_TX_ENABLED)
> +		restoremacctrl |= IPG_MC_TX_ENABLE;
> +
> +	if (origmacctrl & IPG_MC_RX_ENABLED)
> +		restoremacctrl |= IPG_MC_RX_ENABLE;
> +
> +	/* Transmitter and receiver must be disabled before setting
> +	 * IFSSelect.
> +	 */
> +	ipg_w32((origmacctrl & (IPG_MC_RX_DISABLE | IPG_MC_TX_DISABLE)) &
> +		IPG_MC_RSVD_MASK, MAC_CTRL);
> +
> +	/* Now that transmitter and receiver are disabled, write
> +	 * to IFSSelect.
> +	 */
> +	ipg_w32((origmacctrl & IPG_MC_IFS_96BIT) & IPG_MC_RSVD_MASK, MAC_CTRL);
> +
> +	/* Set RECEIVEMODE register. */
> +	ipg_nic_set_multicast_list(dev);
> +
> +	ipg_w16(IPG_MAX_RXFRAME_SIZE, MAX_FRAME_SIZE);
> +
> +	ipg_w8(IPG_RXDMAPOLLPERIOD_VALUE,   RX_DMA_POLL_PERIOD);
> +	ipg_w8(IPG_RXDMAURGENTTHRESH_VALUE, RX_DMA_URGENT_THRESH);
> +	ipg_w8(IPG_RXDMABURSTTHRESH_VALUE,  RX_DMA_BURST_THRESH);
> +	ipg_w8(IPG_TXDMAPOLLPERIOD_VALUE,   TX_DMA_POLL_PERIOD);
> +	ipg_w8(IPG_TXDMAURGENTTHRESH_VALUE, TX_DMA_URGENT_THRESH);
> +	ipg_w8(IPG_TXDMABURSTTHRESH_VALUE,  TX_DMA_BURST_THRESH);
> +	ipg_w16((IPG_IE_HOST_ERROR | IPG_IE_TX_DMA_COMPLETE |
> +		 IPG_IE_TX_COMPLETE | IPG_IE_INT_REQUESTED |
> +		 IPG_IE_UPDATE_STATS | IPG_IE_LINK_EVENT |
> +		 IPG_IE_RX_DMA_COMPLETE | IPG_IE_RX_DMA_PRIORITY), INT_ENABLE);
> +	ipg_w16(IPG_FLOWONTHRESH_VALUE,  FLOW_ON_THRESH);
> +	ipg_w16(IPG_FLOWOFFTHRESH_VALUE, FLOW_OFF_THRESH);
> +
> +	/* IPG multi-frag frame bug workaround.
> +	 * Per silicon revision B3 eratta.
> +	 */
> +	ipg_w16(ipg_r16(DEBUG_CTRL) | 0x0200, DEBUG_CTRL);
> +
> +	/* IPG TX poll now bug workaround.
> +	 * Per silicon revision B3 eratta.
> +	 */
> +	ipg_w16(ipg_r16(DEBUG_CTRL) | 0x0010, DEBUG_CTRL);
> +
> +	/* IPG RX poll now bug workaround.
> +	 * Per silicon revision B3 eratta.
> +	 */
> +	ipg_w16(ipg_r16(DEBUG_CTRL) | 0x0020, DEBUG_CTRL);
> +
> +	/* Now restore MACCTRL to original setting. */
> +	ipg_w32(IPG_MC_RSVD_MASK & restoremacctrl, MAC_CTRL);
> +
> +	/* Disable unused RMON statistics. */
> +	ipg_w32(IPG_RZ_ALL, RMON_STATISTICS_MASK);
> +
> +	/* Disable unused MIB statistics. */
> +	ipg_w32(IPG_SM_MACCONTROLFRAMESXMTD | IPG_SM_MACCONTROLFRAMESRCVD |
> +		IPG_SM_BCSTOCTETXMTOK_BCSTFRAMESXMTDOK | IPG_SM_TXJUMBOFRAMES |
> +		IPG_SM_MCSTOCTETXMTOK_MCSTFRAMESXMTDOK | IPG_SM_RXJUMBOFRAMES |
> +		IPG_SM_BCSTOCTETRCVDOK_BCSTFRAMESRCVDOK |
> +		IPG_SM_UDPCHECKSUMERRORS | IPG_SM_TCPCHECKSUMERRORS |
> +		IPG_SM_IPCHECKSUMERRORS, STATISTICS_MASK);
> +
> +	return 0;
> +}
> +
> +/*
> + * Create a receive buffer within system memory and update
> + * NIC private structure appropriately.
> + */
> +static int ipg_get_rxbuff(struct net_device *dev, int entry)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	struct ipg_rx *rxfd = sp->rxd + entry;
> +	struct sk_buff *skb;
> +	u64 rxfragsize;
> +
> +	IPG_DEBUG_MSG("_get_rxbuff\n");
> +
> +	skb = netdev_alloc_skb(dev, IPG_RXSUPPORT_SIZE + NET_IP_ALIGN);
> +	if (!skb) {
> +		sp->RxBuff[entry] = NULL;
> +		return -ENOMEM;
> +	}
> +
> +	/* Adjust the data start location within the buffer to
> +	 * align IP address field to a 16 byte boundary.
> +	 */
> +	skb_reserve(skb, NET_IP_ALIGN);
> +
> +	/* Associate the receive buffer with the IPG NIC. */
> +	skb->dev = dev;
> +
> +	/* Save the address of the sk_buff structure. */
> +	sp->RxBuff[entry] = skb;
> +
> +	rxfd->frag_info = cpu_to_le64(pci_map_single(sp->pdev, skb->data,
> +		sp->rx_buf_sz, PCI_DMA_FROMDEVICE));
> +
> +	/* Set the RFD fragment length. */
> +	rxfragsize = IPG_RXFRAG_SIZE;
> +	rxfd->frag_info |= cpu_to_le64((rxfragsize << 48) & IPG_RFI_FRAGLEN);
> +
> +	return 0;
> +}
> +
> +static int init_rfdlist(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	unsigned int i;
> +
> +	IPG_DEBUG_MSG("_init_rfdlist\n");
> +
> +	for (i = 0; i < IPG_RFDLIST_LENGTH; i++) {
> +		struct ipg_rx *rxfd = sp->rxd + i;
> +
> +		if (sp->RxBuff[i]) {
> +			pci_unmap_single(sp->pdev,
> +				le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN),
> +				sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> +			IPG_DEV_KFREE_SKB(sp->RxBuff[i]);
> +			sp->RxBuff[i] = NULL;
> +		}
> +
> +		/* Clear out the RFS field. */
> +		rxfd->rfs = 0x0000000000000000;
> +
> +		if (ipg_get_rxbuff(dev, i) < 0) {
> +			/*
> +			 * A receive buffer was not ready, break the
> +			 * RFD list here.
> +			 */
> +			IPG_DEBUG_MSG("Cannot allocate Rx buffer.\n");
> +
> +			/* Just in case we cannot allocate a single RFD.
> +			 * Should not occur.
> +			 */
> +			if (i == 0) {
> +				printk(KERN_ERR "%s: No memory available"
> +					" for RFD list.\n", dev->name);
> +				return -ENOMEM;
> +			}
> +		}
> +
> +		rxfd->next_desc = cpu_to_le64(sp->rxd_map +
> +			sizeof(struct ipg_rx)*(i + 1));
> +	}
> +	sp->rxd[i - 1].next_desc = cpu_to_le64(sp->rxd_map);
> +
> +	sp->rx_current = 0;
> +	sp->rx_dirty = 0;
> +
> +	/* Write the location of the RFDList to the IPG. */
> +	ipg_w32((u32) sp->rxd_map, RFD_LIST_PTR_0);
> +	ipg_w32(0x00000000, RFD_LIST_PTR_1);
> +
> +	return 0;
> +}
> +
> +static void init_tfdlist(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	unsigned int i;
> +
> +	IPG_DEBUG_MSG("_init_tfdlist\n");
> +
> +	for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {
> +		struct ipg_tx *txfd = sp->txd + i;
> +
> +		txfd->tfc = cpu_to_le64(IPG_TFC_TFDDONE);
> +
> +		if (sp->TxBuff[i]) {
> +			IPG_DEV_KFREE_SKB(sp->TxBuff[i]);
> +			sp->TxBuff[i] = NULL;
> +		}
> +
> +		txfd->next_desc = cpu_to_le64(sp->txd_map +
> +			sizeof(struct ipg_tx)*(i + 1));
> +	}
> +	sp->txd[i - 1].next_desc = cpu_to_le64(sp->txd_map);
> +
> +	sp->tx_current = 0;
> +	sp->tx_dirty = 0;
> +
> +	/* Write the location of the TFDList to the IPG. */
> +	IPG_DDEBUG_MSG("Starting TFDListPtr = %8.8x\n",
> +		       (u32) sp->txd_map);
> +	ipg_w32((u32) sp->txd_map, TFD_LIST_PTR_0);
> +	ipg_w32(0x00000000, TFD_LIST_PTR_1);
> +
> +	sp->ResetCurrentTFD = 1;
> +}
> +
> +/*
> + * Free all transmit buffers which have already been transfered
> + * via DMA to the IPG.
> + */
> +static void ipg_nic_txfree(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	const unsigned int curr = ipg_r32(TFD_LIST_PTR_0) -
> +		(sp->txd_map / sizeof(struct ipg_tx)) - 1;
> +	unsigned int released, pending;
> +
> +	IPG_DEBUG_MSG("_nic_txfree\n");
> +
> +	pending = sp->tx_current - sp->tx_dirty;
> +
> +	for (released = 0; released < pending; released++) {
> +		unsigned int dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
> +		struct sk_buff *skb = sp->TxBuff[dirty];
> +		struct ipg_tx *txfd = sp->txd + dirty;
> +
> +		IPG_DEBUG_MSG("TFC = %16.16lx\n", (unsigned long) txfd->tfc);
> +
> +		/* Look at each TFD's TFC field beginning
> +		 * at the last freed TFD up to the current TFD.
> +		 * If the TFDDone bit is set, free the associated
> +		 * buffer.
> +		 */
> +		if (dirty == curr)
> +			break;
> +
> +		/* Setup TFDDONE for compatible issue. */
> +		txfd->tfc |= cpu_to_le64(IPG_TFC_TFDDONE);
> +
> +		/* Free the transmit buffer. */
> +		if (skb) {
> +			pci_unmap_single(sp->pdev,
> +				le64_to_cpu(txfd->frag_info & ~IPG_TFI_FRAGLEN),
> +				skb->len, PCI_DMA_TODEVICE);
> +
> +			IPG_DEV_KFREE_SKB(skb);
> +
> +			sp->TxBuff[dirty] = NULL;
> +		}
> +	}
> +
> +	sp->tx_dirty += released;
> +
> +	if (netif_queue_stopped(dev) &&
> +	    (sp->tx_current != (sp->tx_dirty + IPG_TFDLIST_LENGTH))) {
> +		netif_wake_queue(dev);
> +	}
> +}
> +
> +static void ipg_tx_timeout(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +
> +	ipg_reset(dev, IPG_AC_TX_RESET | IPG_AC_DMA | IPG_AC_NETWORK |
> +		  IPG_AC_FIFO);
> +
> +	spin_lock_irq(&sp->lock);
> +
> +	/* Re-configure after DMA reset. */
> +	if (ipg_io_config(dev) < 0) {
> +		printk(KERN_INFO "%s: Error during re-configuration.\n",
> +		       dev->name);
> +	}
> +
> +	init_tfdlist(dev);
> +
> +	spin_unlock_irq(&sp->lock);
> +
> +	ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) & IPG_MC_RSVD_MASK,
> +		MAC_CTRL);
> +}
> +
> +/*
> + * For TxComplete interrupts, free all transmit
> + * buffers which have already been transfered via DMA
> + * to the IPG.
> + */
> +static void ipg_nic_txcleanup(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	unsigned int i;
> +
> +	IPG_DEBUG_MSG("_nic_txcleanup\n");
> +
> +	for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {
> +		/* Reading the TXSTATUS register clears the
> +		 * TX_COMPLETE interrupt.
> +		 */
> +		u32 txstatusdword = ipg_r32(TX_STATUS);
> +
> +		IPG_DEBUG_MSG("TxStatus = %8.8x\n", txstatusdword);
> +
> +		/* Check for Transmit errors. Error bits only valid if
> +		 * TX_COMPLETE bit in the TXSTATUS register is a 1.
> +		 */
> +		if (!(txstatusdword & IPG_TS_TX_COMPLETE))
> +			break;
> +
> +		/* If in 10Mbps mode, indicate transmit is ready. */
> +		if (sp->tenmbpsmode) {
> +			netif_wake_queue(dev);
> +		}
> +
> +		/* Transmit error, increment stat counters. */
> +		if (txstatusdword & IPG_TS_TX_ERROR) {
> +			IPG_DEBUG_MSG("Transmit error.\n");
> +			sp->stats.tx_errors++;
> +		}
> +
> +		/* Late collision, re-enable transmitter. */
> +		if (txstatusdword & IPG_TS_LATE_COLLISION) {
> +			IPG_DEBUG_MSG("Late collision on transmit.\n");
> +			ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) &
> +				IPG_MC_RSVD_MASK, MAC_CTRL);
> +		}
> +
> +		/* Maximum collisions, re-enable transmitter. */
> +		if (txstatusdword & IPG_TS_TX_MAX_COLL) {
> +			IPG_DEBUG_MSG("Maximum collisions on transmit.\n");
> +			ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) &
> +				IPG_MC_RSVD_MASK, MAC_CTRL);
> +		}
> +
> +		/* Transmit underrun, reset and re-enable
> +		 * transmitter.
> +		 */
> +		if (txstatusdword & IPG_TS_TX_UNDERRUN) {
> +			IPG_DEBUG_MSG("Transmitter underrun.\n");
> +			sp->stats.tx_fifo_errors++;
> +			ipg_reset(dev, IPG_AC_TX_RESET | IPG_AC_DMA |
> +				  IPG_AC_NETWORK | IPG_AC_FIFO);
> +
> +			/* Re-configure after DMA reset. */
> +			if (ipg_io_config(dev) < 0) {
> +				printk(KERN_INFO
> +				       "%s: Error during re-configuration.\n",
> +				       dev->name);
> +			}
> +			init_tfdlist(dev);
> +
> +			ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_TX_ENABLE) &
> +				IPG_MC_RSVD_MASK, MAC_CTRL);
> +		}
> +	}
> +
> +	ipg_nic_txfree(dev);
> +}
> +
> +/* Provides statistical information about the IPG NIC. */
> +struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
> +{
> +	struct ipg_nic_private *sp = netdev_priv(dev);
> +	void __iomem *ioaddr = sp->ioaddr;
> +	u16 temp1;
> +	u16 temp2;
> +
> +	IPG_DEBUG_MSG("_nic_get_stats\n");
> +
> +	/* Check to see if the NIC has been initialized via nic_open,
> +	 * before trying to read statistic registers.
> +	 */
> +	if (!test_bit(__LINK_STATE_START, &dev->state))
> +		return &sp->stats;

The latest kernel has a statistics struct inside the netdevice that
can be used instead of having your own.


...

> +			/* If the frame contains an IP/TCP/UDP frame,
> +			 * determine if upper layer must check IP/TCP/UDP
> +			 * checksums.
> +			 *
> +			 * NOTE: DO NOT RELY ON THE TCP/UDP CHECKSUM
> +			 *       VERIFICATION FOR SILICON REVISIONS B3
> +			 *       AND EARLIER!
> +			 *
> +			 if ((le64_to_cpu(rxfd->rfs &
> +			 (IPG_RFS_TCPDETECTED | IPG_RFS_UDPDETECTED |
> +			 IPG_RFS_IPDETECTED))) &&
> +			 !(le64_to_cpu(rxfd->rfs &
> +			 (IPG_RFS_TCPERROR | IPG_RFS_UDPERROR |
> +			 IPG_RFS_IPERROR))))
> +			 {
> +			 * Indicate IP checksums were performed
> +			 * by the IPG.
> +			 *
> +			 skb->ip_summed = CHECKSUM_UNNECESSARY;
> +			 }

Sudden loss of proper indentation style

> +			 else
> +			 */
> +			if (1 == 1) {
> +				/* The IPG encountered an error with (or
> +				 * there were no) IP/TCP/UDP checksums.
> +				 * This may or may not indicate an invalid
> +				 * IP/TCP/UDP frame was received. Let the
> +				 * upper layer decide.
> +				 */
> +				skb->ip_summed = CHECKSUM_NONE;
> +			}
> +
> +			/* Hand off frame for higher layer processing.
> +			 * The function netif_rx() releases the sk_buff
> +			 * when processing completes.
> +			 */
> +			netif_rx(skb);
> +
> +			/* Record frame receive time (jiffies = Linux
> +			 * kernel current time stamp).
> +			 */
> +			dev->last_rx = jiffies;
> +		}
> +
> +		/* Assure RX buffer is not reused by IPG. */
> +		sp->RxBuff[entry] = NULL;
> +	}
> +
> +	/*
> +	 * If there are more RFDs to proces and the allocated amount of RFD
> +	 * processing time has expired, assert Interrupt Requested to make
> +	 * sure we come back to process the remaining RFDs.
> +	 */
> +	if (i == IPG_MAXRFDPROCESS_COUNT)
> +		ipg_w32(ipg_r32(ASIC_CTRL) | IPG_AC_INT_REQUEST, ASIC_CTRL);
> +
> +#ifdef IPG_DEBUG
> +	/* Check if the RFD list contained no receive frame data. */
> +	if (!i)
> +		sp->EmptyRFDListCount++;
> +#endif
> +	while ((le64_to_cpu(rxfd->rfs & IPG_RFS_RFDDONE)) &&
> +	       !((le64_to_cpu(rxfd->rfs & IPG_RFS_FRAMESTART)) &&
> +		 (le64_to_cpu(rxfd->rfs & IPG_RFS_FRAMEEND)))) {
> +		unsigned int entry = curr++ % IPG_RFDLIST_LENGTH;
> +
> +		rxfd = sp->rxd + entry;
> +
> +		IPG_DEBUG_MSG("Frame requires multiple RFDs.\n");
> +
> +		/* An unexpected event, additional code needed to handle
> +		 * properly. So for the time being, just disregard the
> +		 * frame.
> +		 */
> +
> +		/* Free the memory associated with the RX
> +		 * buffer since it is erroneous and we will
> +		 * not pass it to higher layer processes.
> +		 */
> +		if (sp->RxBuff[entry]) {
> +			pci_unmap_single(sp->pdev,
> +				le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN),
> +				sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> +			IPG_DEV_KFREE_SKB(sp->RxBuff[entry]);
> +		}
> +
> +		/* Assure RX buffer is not reused by IPG. */
> +		sp->RxBuff[entry] = NULL;
> +	}
> +
> +	sp->rx_current = curr;
> +
> +	/* Check to see if there are a minimum number of used
> +	 * RFDs before restoring any (should improve performance.)
> +	 */
> +	if ((curr - sp->rx_dirty) >= IPG_MINUSEDRFDSTOFREE)
> +		ipg_nic_rxrestore(dev);
> +
> +	return 0;
> +}
> +#endif
>
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ