2007-09-18 07:13:55 +07:00
|
|
|
/*
|
|
|
|
* ipg.c: Device Driver for the IP1000 Gigabit Ethernet Adapter
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003, 2007 IC Plus Corp
|
|
|
|
*
|
|
|
|
* Original Author:
|
|
|
|
*
|
|
|
|
* Craig Rich
|
|
|
|
* Sundance Technology, Inc.
|
|
|
|
* www.sundanceti.com
|
|
|
|
* craig_rich@sundanceti.com
|
|
|
|
*
|
|
|
|
* Current Maintainer:
|
|
|
|
*
|
|
|
|
* Sorbica Shieh.
|
|
|
|
* http://www.icplus.com.tw
|
|
|
|
* sorbica@icplus.com.tw
|
|
|
|
*
|
|
|
|
* Jesse Huang
|
|
|
|
* http://www.icplus.com.tw
|
|
|
|
* jesse@icplus.com.tw
|
|
|
|
*/
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include <linux/ethtool.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/gfp.h>
|
2007-09-18 07:13:55 +07:00
|
|
|
#include <linux/mii.h>
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
|
2007-10-03 04:04:38 +07:00
|
|
|
#include <asm/div64.h>
|
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
#define IPG_RX_RING_BYTES (sizeof(struct ipg_rx) * IPG_RFDLIST_LENGTH)
|
|
|
|
#define IPG_TX_RING_BYTES (sizeof(struct ipg_tx) * IPG_TFDLIST_LENGTH)
|
|
|
|
#define IPG_RESET_MASK \
|
|
|
|
(IPG_AC_GLOBAL_RESET | IPG_AC_RX_RESET | IPG_AC_TX_RESET | \
|
|
|
|
IPG_AC_DMA | IPG_AC_FIFO | IPG_AC_NETWORK | IPG_AC_HOST | \
|
|
|
|
IPG_AC_AUTO_INIT)
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
#define ipg_w32(val32, reg) iowrite32((val32), ioaddr + (reg))
|
|
|
|
#define ipg_w16(val16, reg) iowrite16((val16), ioaddr + (reg))
|
|
|
|
#define ipg_w8(val8, reg) iowrite8((val8), ioaddr + (reg))
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
#define ipg_r32(reg) ioread32(ioaddr + (reg))
|
|
|
|
#define ipg_r16(reg) ioread16(ioaddr + (reg))
|
|
|
|
#define ipg_r8(reg) ioread8(ioaddr + (reg))
|
|
|
|
|
|
|
|
enum {
|
|
|
|
netdev_io_size = 128
|
|
|
|
};
|
|
|
|
|
|
|
|
#include "ipg.h"
|
|
|
|
#define DRV_NAME "ipg"
|
|
|
|
|
|
|
|
MODULE_AUTHOR("IC Plus Corp. 2003");
|
2007-11-30 14:54:06 +07:00
|
|
|
MODULE_DESCRIPTION("IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver");
|
2007-09-18 07:13:55 +07:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2008-06-23 18:36:56 +07:00
|
|
|
/*
|
|
|
|
* Defaults
|
|
|
|
*/
|
|
|
|
#define IPG_MAX_RXFRAME_SIZE 0x0600
|
|
|
|
#define IPG_RXFRAG_SIZE 0x0600
|
|
|
|
#define IPG_RXSUPPORT_SIZE 0x0600
|
|
|
|
#define IPG_IS_JUMBO false
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/*
|
|
|
|
* Variable record -- index by leading revision/length
|
|
|
|
* Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
|
|
|
|
*/
|
2007-10-24 23:23:19 +07:00
|
|
|
static unsigned short DefaultPhyParam[] = {
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 11/12/03 IP1000A v1-3 rev=0x40 */
|
2007-10-24 23:23:19 +07:00
|
|
|
/*--------------------------------------------------------------------------
|
|
|
|
(0x4000|(15*4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 22, 0x85bd, 24, 0xfff2,
|
|
|
|
27, 0x0c10, 28, 0x0c10, 29, 0x2c10, 31, 0x0003, 23, 0x92f6,
|
|
|
|
31, 0x0000, 23, 0x003d, 30, 0x00de, 20, 0x20e7, 9, 0x0700,
|
|
|
|
--------------------------------------------------------------------------*/
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 12/17/03 IP1000A v1-4 rev=0x40 */
|
2007-10-24 23:23:19 +07:00
|
|
|
(0x4000 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
|
|
|
|
0x0000,
|
|
|
|
30, 0x005e, 9, 0x0700,
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 01/09/04 IP1000A v1-5 rev=0x41 */
|
2007-10-24 23:23:19 +07:00
|
|
|
(0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
|
|
|
|
0x0000,
|
|
|
|
30, 0x005e, 9, 0x0700,
|
|
|
|
0x0000
|
|
|
|
};
|
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
static const char *ipg_brand_name[] = {
|
|
|
|
"IC PLUS IP1000 1000/100/10 based NIC",
|
|
|
|
"Sundance Technology ST2021 based NIC",
|
|
|
|
"Tamarack Microelectronics TC9020/9021 based NIC",
|
|
|
|
"Tamarack Microelectronics TC9020/9021 based NIC",
|
|
|
|
"D-Link NIC IP1000A"
|
|
|
|
};
|
|
|
|
|
2010-01-07 18:58:11 +07:00
|
|
|
static DEFINE_PCI_DEVICE_TABLE(ipg_pci_tbl) = {
|
2007-09-18 07:13:55 +07:00
|
|
|
{ PCI_VDEVICE(SUNDANCE, 0x1023), 0 },
|
|
|
|
{ PCI_VDEVICE(SUNDANCE, 0x2021), 1 },
|
|
|
|
{ PCI_VDEVICE(SUNDANCE, 0x1021), 2 },
|
|
|
|
{ PCI_VDEVICE(DLINK, 0x9021), 3 },
|
2010-02-11 17:26:38 +07:00
|
|
|
{ PCI_VDEVICE(DLINK, 0x4020), 4 },
|
2007-09-18 07:13:55 +07:00
|
|
|
{ 0, }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, ipg_pci_tbl);
|
|
|
|
|
|
|
|
static inline void __iomem *ipg_ioaddr(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
return sp->ioaddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef IPG_DEBUG
|
|
|
|
static void ipg_dump_rfdlist(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int i;
|
|
|
|
u32 offset;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_dump_rfdlist\n");
|
|
|
|
|
|
|
|
printk(KERN_INFO "rx_current = %2.2x\n", sp->rx_current);
|
|
|
|
printk(KERN_INFO "rx_dirty = %2.2x\n", sp->rx_dirty);
|
|
|
|
printk(KERN_INFO "RFDList start address = %16.16lx\n",
|
|
|
|
(unsigned long) sp->rxd_map);
|
|
|
|
printk(KERN_INFO "RFDListPtr register = %8.8x%8.8x\n",
|
|
|
|
ipg_r32(IPG_RFDLISTPTR1), ipg_r32(IPG_RFDLISTPTR0));
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_RFDLIST_LENGTH; i++) {
|
|
|
|
offset = (u32) &sp->rxd[i].next_desc - (u32) sp->rxd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x RFDNextPtr = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->rxd[i].next_desc);
|
|
|
|
offset = (u32) &sp->rxd[i].rfs - (u32) sp->rxd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x RFS = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->rxd[i].rfs);
|
|
|
|
offset = (u32) &sp->rxd[i].frag_info - (u32) sp->rxd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->rxd[i].frag_info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_dump_tfdlist(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int i;
|
|
|
|
u32 offset;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_dump_tfdlist\n");
|
|
|
|
|
|
|
|
printk(KERN_INFO "tx_current = %2.2x\n", sp->tx_current);
|
|
|
|
printk(KERN_INFO "tx_dirty = %2.2x\n", sp->tx_dirty);
|
|
|
|
printk(KERN_INFO "TFDList start address = %16.16lx\n",
|
|
|
|
(unsigned long) sp->txd_map);
|
|
|
|
printk(KERN_INFO "TFDListPtr register = %8.8x%8.8x\n",
|
|
|
|
ipg_r32(IPG_TFDLISTPTR1), ipg_r32(IPG_TFDLISTPTR0));
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {
|
|
|
|
offset = (u32) &sp->txd[i].next_desc - (u32) sp->txd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x TFDNextPtr = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->txd[i].next_desc);
|
|
|
|
|
|
|
|
offset = (u32) &sp->txd[i].tfc - (u32) sp->txd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x TFC = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->txd[i].tfc);
|
|
|
|
offset = (u32) &sp->txd[i].frag_info - (u32) sp->txd;
|
|
|
|
printk(KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n", i,
|
|
|
|
offset, (unsigned long) sp->txd[i].frag_info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void ipg_write_phy_ctl(void __iomem *ioaddr, u8 data)
|
|
|
|
{
|
|
|
|
ipg_w8(IPG_PC_RSVD_MASK & data, PHY_CTRL);
|
|
|
|
ndelay(IPG_PC_PHYCTRLWAIT_NS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_drive_phy_ctl_low_high(void __iomem *ioaddr, u8 data)
|
|
|
|
{
|
|
|
|
ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_LO | data);
|
|
|
|
ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_HI | data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_three_state(void __iomem *ioaddr, u8 phyctrlpolarity)
|
|
|
|
{
|
|
|
|
phyctrlpolarity |= (IPG_PC_MGMTDATA & 0) | IPG_PC_MGMTDIR;
|
|
|
|
|
|
|
|
ipg_drive_phy_ctl_low_high(ioaddr, phyctrlpolarity);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_end(void __iomem *ioaddr, u8 phyctrlpolarity)
|
|
|
|
{
|
|
|
|
ipg_w8((IPG_PC_MGMTCLK_LO | (IPG_PC_MGMTDATA & 0) | IPG_PC_MGMTDIR |
|
|
|
|
phyctrlpolarity) & IPG_PC_RSVD_MASK, PHY_CTRL);
|
|
|
|
}
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
static u16 read_phy_bit(void __iomem *ioaddr, u8 phyctrlpolarity)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
u16 bit_data;
|
|
|
|
|
|
|
|
ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_LO | phyctrlpolarity);
|
|
|
|
|
|
|
|
bit_data = ((ipg_r8(PHY_CTRL) & IPG_PC_MGMTDATA) >> 1) & 1;
|
|
|
|
|
|
|
|
ipg_write_phy_ctl(ioaddr, IPG_PC_MGMTCLK_HI | phyctrlpolarity);
|
|
|
|
|
|
|
|
return bit_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a register from the Physical Layer device located
|
|
|
|
* on the IPG NIC, using the IPG PHYCTRL register.
|
|
|
|
*/
|
2007-11-30 14:55:43 +07:00
|
|
|
static int mdio_read(struct net_device *dev, int phy_id, int phy_reg)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
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 */
|
|
|
|
};
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if ((sp->led_mode & 0x03) > 1)
|
2007-09-18 07:13:55 +07:00
|
|
|
mode |= IPG_AC_LED_MODE_BIT_1; /* Write Asic Control Bit 29 */
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if ((sp->led_mode & 0x01) == 1)
|
2007-09-18 07:13:55 +07:00
|
|
|
mode |= IPG_AC_LED_MODE; /* Write Asic Control Bit 14 */
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if ((sp->led_mode & 0x08) == 8)
|
2007-09-18 07:13:55 +07:00
|
|
|
mode |= IPG_AC_LED_SPEED; /* Write Asic Control Bit 27 */
|
|
|
|
|
|
|
|
ipg_w32(mode, ASIC_CTRL);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2008-01-24 17:01:17 +07:00
|
|
|
physet |= ((sp->led_mode & 0x70) >> 4);
|
2007-09-18 07:13:55 +07:00
|
|
|
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.
|
|
|
|
*/
|
2007-11-30 14:54:51 +07:00
|
|
|
void __iomem *ioaddr = ipg_ioaddr(dev);
|
2007-09-18 07:13:55 +07:00
|
|
|
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;
|
|
|
|
}
|
2007-11-30 14:54:51 +07:00
|
|
|
/* Set LED Mode in Asic Control */
|
2007-09-18 07:13:55 +07:00
|
|
|
ipg_set_led_mode(dev);
|
|
|
|
|
2007-11-30 14:54:51 +07:00
|
|
|
/* Set PHYSet Register Value */
|
2007-09-18 07:13:55 +07:00
|
|
|
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;
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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");
|
|
|
|
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. */
|
2007-11-30 14:55:43 +07:00
|
|
|
printk(KERN_INFO "%s: setting half duplex, "
|
2007-09-18 07:13:55 +07:00
|
|
|
"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);
|
2010-04-02 04:22:57 +07:00
|
|
|
struct netdev_hw_addr *ha;
|
2007-09-18 07:13:55 +07:00
|
|
|
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) ||
|
2008-06-10 06:33:50 +07:00
|
|
|
((dev->flags & IFF_MULTICAST) &&
|
2010-02-08 11:30:35 +07:00
|
|
|
(netdev_mc_count(dev) > IPG_MULTICAST_HASHTABLE_SIZE))) {
|
2007-09-18 07:13:55 +07:00
|
|
|
/* NIC to be configured to receive all multicast
|
|
|
|
* frames. */
|
|
|
|
receivemode |= IPG_RM_RECEIVEMULTICAST;
|
2010-02-08 11:30:35 +07:00
|
|
|
} else if ((dev->flags & IFF_MULTICAST) && !netdev_mc_empty(dev)) {
|
2007-09-18 07:13:55 +07:00
|
|
|
/* 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. */
|
2010-04-02 04:22:57 +07:00
|
|
|
netdev_for_each_mc_addr(ha, dev) {
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Calculate CRC result for each multicast address. */
|
2010-04-02 04:22:57 +07:00
|
|
|
hashindex = crc32_le(0xffffffff, ha->addr,
|
2007-09-18 07:13:55 +07:00
|
|
|
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)
|
|
|
|
{
|
2008-06-23 18:36:35 +07:00
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
2007-09-18 07:13:55 +07:00
|
|
|
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);
|
|
|
|
|
2008-06-23 18:36:35 +07:00
|
|
|
ipg_w16(sp->max_rxframe_size, MAX_FRAME_SIZE);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
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");
|
|
|
|
|
2009-10-13 12:34:20 +07:00
|
|
|
skb = netdev_alloc_skb_ip_align(dev, sp->rxsupport_size);
|
2007-09-18 07:13:55 +07:00
|
|
|
if (!skb) {
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Associate the receive buffer with the IPG NIC. */
|
|
|
|
skb->dev = dev;
|
|
|
|
|
|
|
|
/* Save the address of the sk_buff structure. */
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = skb;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
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. */
|
2008-06-23 18:36:00 +07:00
|
|
|
rxfragsize = sp->rxfrag_size;
|
2007-09-18 07:13:55 +07:00
|
|
|
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;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[i]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->rx_buff[i]);
|
|
|
|
sp->rx_buff[i] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->tx_buff[i]) {
|
|
|
|
dev_kfree_skb_irq(sp->tx_buff[i]);
|
|
|
|
sp->tx_buff[i] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->reset_current_tfd = 1;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2008-01-11 05:40:59 +07:00
|
|
|
unsigned int released, pending, dirty;
|
2007-10-03 04:04:38 +07:00
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
IPG_DEBUG_MSG("_nic_txfree\n");
|
|
|
|
|
|
|
|
pending = sp->tx_current - sp->tx_dirty;
|
2008-01-11 05:40:59 +07:00
|
|
|
dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
for (released = 0; released < pending; released++) {
|
2008-01-24 17:01:17 +07:00
|
|
|
struct sk_buff *skb = sp->tx_buff[dirty];
|
2007-09-18 07:13:55 +07:00
|
|
|
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.
|
|
|
|
*/
|
2008-01-11 05:40:59 +07:00
|
|
|
if (!(txfd->tfc & cpu_to_le64(IPG_TFC_TFDDONE)))
|
|
|
|
break;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Free the transmit buffer. */
|
|
|
|
if (skb) {
|
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(txfd->frag_info) & ~IPG_TFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
skb->len, PCI_DMA_TODEVICE);
|
|
|
|
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(skb);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->tx_buff[dirty] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
2008-01-11 05:40:59 +07:00
|
|
|
dirty = (dirty + 1) % IPG_TFDLIST_LENGTH;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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. */
|
2007-10-24 23:23:19 +07:00
|
|
|
static struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
sp->stats.rx_packets += ipg_r32(IPG_FRAMESRCVDOK);
|
|
|
|
sp->stats.tx_packets += ipg_r32(IPG_FRAMESXMTDOK);
|
|
|
|
sp->stats.rx_bytes += ipg_r32(IPG_OCTETRCVOK);
|
|
|
|
sp->stats.tx_bytes += ipg_r32(IPG_OCTETXMTOK);
|
|
|
|
temp1 = ipg_r16(IPG_FRAMESLOSTRXERRORS);
|
|
|
|
sp->stats.rx_errors += temp1;
|
|
|
|
sp->stats.rx_missed_errors += temp1;
|
|
|
|
temp1 = ipg_r32(IPG_SINGLECOLFRAMES) + ipg_r32(IPG_MULTICOLFRAMES) +
|
|
|
|
ipg_r32(IPG_LATECOLLISIONS);
|
|
|
|
temp2 = ipg_r16(IPG_CARRIERSENSEERRORS);
|
|
|
|
sp->stats.collisions += temp1;
|
|
|
|
sp->stats.tx_dropped += ipg_r16(IPG_FRAMESABORTXSCOLLS);
|
|
|
|
sp->stats.tx_errors += ipg_r16(IPG_FRAMESWEXDEFERRAL) +
|
|
|
|
ipg_r32(IPG_FRAMESWDEFERREDXMT) + temp1 + temp2;
|
|
|
|
sp->stats.multicast += ipg_r32(IPG_MCSTOCTETRCVDOK);
|
|
|
|
|
|
|
|
/* detailed tx_errors */
|
|
|
|
sp->stats.tx_carrier_errors += temp2;
|
|
|
|
|
|
|
|
/* detailed rx_errors */
|
|
|
|
sp->stats.rx_length_errors += ipg_r16(IPG_INRANGELENGTHERRORS) +
|
|
|
|
ipg_r16(IPG_FRAMETOOLONGERRRORS);
|
|
|
|
sp->stats.rx_crc_errors += ipg_r16(IPG_FRAMECHECKSEQERRORS);
|
|
|
|
|
|
|
|
/* Unutilized IPG statistic registers. */
|
|
|
|
ipg_r32(IPG_MCSTFRAMESRCVDOK);
|
|
|
|
|
|
|
|
return &sp->stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Restore used receive buffers. */
|
|
|
|
static int ipg_nic_rxrestore(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
const unsigned int curr = sp->rx_current;
|
|
|
|
unsigned int dirty = sp->rx_dirty;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_rxrestore\n");
|
|
|
|
|
|
|
|
for (dirty = sp->rx_dirty; curr - dirty > 0; dirty++) {
|
|
|
|
unsigned int entry = dirty % IPG_RFDLIST_LENGTH;
|
|
|
|
|
|
|
|
/* rx_copybreak may poke hole here and there. */
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[entry])
|
2007-09-18 07:13:55 +07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Generate a new receive buffer to replace the
|
|
|
|
* current buffer (which will be released by the
|
|
|
|
* Linux system).
|
|
|
|
*/
|
|
|
|
if (ipg_get_rxbuff(dev, entry) < 0) {
|
|
|
|
IPG_DEBUG_MSG("Cannot allocate new Rx buffer.\n");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset the RFS field. */
|
|
|
|
sp->rxd[entry].rfs = 0x0000000000000000;
|
|
|
|
}
|
|
|
|
sp->rx_dirty = dirty;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use jumboindex and jumbosize to control jumbo frame status
|
2007-11-30 14:55:43 +07:00
|
|
|
* initial status is jumboindex=-1 and jumbosize=0
|
|
|
|
* 1. jumboindex = -1 and jumbosize=0 : previous jumbo frame has been done.
|
|
|
|
* 2. jumboindex != -1 and jumbosize != 0 : jumbo frame is not over size and receiving
|
|
|
|
* 3. jumboindex = -1 and jumbosize != 0 : jumbo frame is over size, already dump
|
|
|
|
* previous receiving and need to continue dumping the current one
|
|
|
|
*/
|
2007-09-18 07:13:55 +07:00
|
|
|
enum {
|
2008-01-24 17:01:17 +07:00
|
|
|
NORMAL_PACKET,
|
|
|
|
ERROR_PACKET
|
2007-09-18 07:13:55 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
2008-01-24 17:01:17 +07:00
|
|
|
FRAME_NO_START_NO_END = 0,
|
|
|
|
FRAME_WITH_START = 1,
|
|
|
|
FRAME_WITH_END = 10,
|
|
|
|
FRAME_WITH_START_WITH_END = 11
|
2007-09-18 07:13:55 +07:00
|
|
|
};
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
static void ipg_nic_rx_free_skb(struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
unsigned int entry = sp->rx_current % IPG_RFDLIST_LENGTH;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[entry]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
struct ipg_rx *rxfd = sp->rxd + entry;
|
|
|
|
|
|
|
|
pci_unmap_single(sp->pdev,
|
2008-11-20 06:54:38 +07:00
|
|
|
le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->rx_buff[entry]);
|
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
static int ipg_nic_rx_check_frame_type(struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
struct ipg_rx *rxfd = sp->rxd + (sp->rx_current % IPG_RFDLIST_LENGTH);
|
2008-01-24 17:01:17 +07:00
|
|
|
int type = FRAME_NO_START_NO_END;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMESTART)
|
2008-01-24 17:01:17 +07:00
|
|
|
type += FRAME_WITH_START;
|
2007-09-18 07:13:55 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMEEND)
|
2008-01-24 17:01:17 +07:00
|
|
|
type += FRAME_WITH_END;
|
2007-09-18 07:13:55 +07:00
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
static int ipg_nic_rx_check_error(struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
unsigned int entry = sp->rx_current % IPG_RFDLIST_LENGTH;
|
|
|
|
struct ipg_rx *rxfd = sp->rxd + entry;
|
|
|
|
|
|
|
|
if (IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu(rxfd->rfs) &
|
|
|
|
(IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |
|
|
|
|
IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |
|
|
|
|
IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR))) {
|
|
|
|
IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",
|
|
|
|
(unsigned long) rxfd->rfs);
|
|
|
|
|
|
|
|
/* Increment general receive error statistic. */
|
|
|
|
sp->stats.rx_errors++;
|
|
|
|
|
|
|
|
/* Increment detailed receive error statistics. */
|
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {
|
|
|
|
IPG_DEBUG_MSG("RX FIFO overrun occured.\n");
|
|
|
|
|
|
|
|
sp->stats.rx_fifo_errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {
|
|
|
|
IPG_DEBUG_MSG("RX runt occured.\n");
|
|
|
|
sp->stats.rx_length_errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do nothing for IPG_RFS_RXOVERSIZEDFRAME,
|
|
|
|
* error count handled by a IPG statistic register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {
|
|
|
|
IPG_DEBUG_MSG("RX alignment error occured.\n");
|
|
|
|
sp->stats.rx_frame_errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do nothing for IPG_RFS_RXFCSERROR, error count
|
|
|
|
* handled by a IPG statistic register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Free the memory associated with the RX
|
|
|
|
* buffer since it is erroneous and we will
|
|
|
|
* not pass it to higher layer processes.
|
|
|
|
*/
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[entry]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
pci_unmap_single(sp->pdev,
|
2008-11-20 06:54:38 +07:00
|
|
|
le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->rx_buff[entry]);
|
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
2008-01-24 17:01:17 +07:00
|
|
|
return ERROR_PACKET;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
2008-01-24 17:01:17 +07:00
|
|
|
return NORMAL_PACKET;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_nic_rx_with_start_and_end(struct net_device *dev,
|
|
|
|
struct ipg_nic_private *sp,
|
|
|
|
struct ipg_rx *rxfd, unsigned entry)
|
|
|
|
{
|
2008-01-24 17:01:17 +07:00
|
|
|
struct ipg_jumbo *jumbo = &sp->jumbo;
|
2007-09-18 07:13:55 +07:00
|
|
|
struct sk_buff *skb;
|
|
|
|
int framelen;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (jumbo->found_start) {
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(jumbo->skb);
|
2008-01-24 17:01:17 +07:00
|
|
|
jumbo->found_start = 0;
|
|
|
|
jumbo->current_size = 0;
|
2007-09-18 07:13:55 +07:00
|
|
|
jumbo->skb = NULL;
|
|
|
|
}
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 1: found error, 0 no error */
|
2008-01-24 17:01:17 +07:00
|
|
|
if (ipg_nic_rx_check_error(dev) != NORMAL_PACKET)
|
2007-09-18 07:13:55 +07:00
|
|
|
return;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
skb = sp->rx_buff[entry];
|
2007-09-18 07:13:55 +07:00
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* accept this frame and send to upper layer */
|
2007-09-18 07:13:55 +07:00
|
|
|
framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN;
|
2008-06-23 18:36:00 +07:00
|
|
|
if (framelen > sp->rxfrag_size)
|
|
|
|
framelen = sp->rxfrag_size;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
skb_put(skb, framelen);
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
netif_rx(skb);
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_nic_rx_with_start(struct net_device *dev,
|
|
|
|
struct ipg_nic_private *sp,
|
|
|
|
struct ipg_rx *rxfd, unsigned entry)
|
|
|
|
{
|
2008-01-24 17:01:17 +07:00
|
|
|
struct ipg_jumbo *jumbo = &sp->jumbo;
|
2007-09-18 07:13:55 +07:00
|
|
|
struct pci_dev *pdev = sp->pdev;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 1: found error, 0 no error */
|
2008-01-24 17:01:17 +07:00
|
|
|
if (ipg_nic_rx_check_error(dev) != NORMAL_PACKET)
|
2007-09-18 07:13:55 +07:00
|
|
|
return;
|
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* accept this frame and send to upper layer */
|
2008-01-24 17:01:17 +07:00
|
|
|
skb = sp->rx_buff[entry];
|
2007-09-18 07:13:55 +07:00
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (jumbo->found_start)
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(jumbo->skb);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2008-11-20 06:54:38 +07:00
|
|
|
pci_unmap_single(pdev, le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
|
|
|
|
2008-06-23 18:36:00 +07:00
|
|
|
skb_put(skb, sp->rxfrag_size);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
jumbo->found_start = 1;
|
2008-06-23 18:36:00 +07:00
|
|
|
jumbo->current_size = sp->rxfrag_size;
|
2007-09-18 07:13:55 +07:00
|
|
|
jumbo->skb = skb;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_nic_rx_with_end(struct net_device *dev,
|
|
|
|
struct ipg_nic_private *sp,
|
|
|
|
struct ipg_rx *rxfd, unsigned entry)
|
|
|
|
{
|
2008-01-24 17:01:17 +07:00
|
|
|
struct ipg_jumbo *jumbo = &sp->jumbo;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 1: found error, 0 no error */
|
2008-01-24 17:01:17 +07:00
|
|
|
if (ipg_nic_rx_check_error(dev) == NORMAL_PACKET) {
|
|
|
|
struct sk_buff *skb = sp->rx_buff[entry];
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (jumbo->found_start) {
|
2007-09-18 07:13:55 +07:00
|
|
|
int framelen, endframelen;
|
|
|
|
|
|
|
|
framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN;
|
|
|
|
|
2008-06-23 18:34:29 +07:00
|
|
|
endframelen = framelen - jumbo->current_size;
|
2008-06-23 18:36:18 +07:00
|
|
|
if (framelen > sp->rxsupport_size)
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(jumbo->skb);
|
2007-09-18 07:13:55 +07:00
|
|
|
else {
|
2008-06-23 18:34:29 +07:00
|
|
|
memcpy(skb_put(jumbo->skb, endframelen),
|
|
|
|
skb->data, endframelen);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
jumbo->skb->protocol =
|
|
|
|
eth_type_trans(jumbo->skb, dev);
|
|
|
|
|
|
|
|
jumbo->skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
netif_rx(jumbo->skb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
jumbo->found_start = 0;
|
|
|
|
jumbo->current_size = 0;
|
2007-09-18 07:13:55 +07:00
|
|
|
jumbo->skb = NULL;
|
|
|
|
|
|
|
|
ipg_nic_rx_free_skb(dev);
|
|
|
|
} else {
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(jumbo->skb);
|
2008-01-24 17:01:17 +07:00
|
|
|
jumbo->found_start = 0;
|
|
|
|
jumbo->current_size = 0;
|
2007-09-18 07:13:55 +07:00
|
|
|
jumbo->skb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_nic_rx_no_start_no_end(struct net_device *dev,
|
|
|
|
struct ipg_nic_private *sp,
|
|
|
|
struct ipg_rx *rxfd, unsigned entry)
|
|
|
|
{
|
2008-01-24 17:01:17 +07:00
|
|
|
struct ipg_jumbo *jumbo = &sp->jumbo;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2007-11-30 14:55:43 +07:00
|
|
|
/* 1: found error, 0 no error */
|
2008-01-24 17:01:17 +07:00
|
|
|
if (ipg_nic_rx_check_error(dev) == NORMAL_PACKET) {
|
|
|
|
struct sk_buff *skb = sp->rx_buff[entry];
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
if (skb) {
|
2008-01-24 17:01:17 +07:00
|
|
|
if (jumbo->found_start) {
|
2008-06-23 18:36:00 +07:00
|
|
|
jumbo->current_size += sp->rxfrag_size;
|
2008-06-23 18:36:18 +07:00
|
|
|
if (jumbo->current_size <= sp->rxsupport_size) {
|
2007-09-18 07:13:55 +07:00
|
|
|
memcpy(skb_put(jumbo->skb,
|
2008-06-23 18:36:00 +07:00
|
|
|
sp->rxfrag_size),
|
|
|
|
skb->data, sp->rxfrag_size);
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ipg_nic_rx_free_skb(dev);
|
|
|
|
}
|
|
|
|
} else {
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(jumbo->skb);
|
2008-01-24 17:01:17 +07:00
|
|
|
jumbo->found_start = 0;
|
|
|
|
jumbo->current_size = 0;
|
2007-09-18 07:13:55 +07:00
|
|
|
jumbo->skb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
static int ipg_nic_rx_jumbo(struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
unsigned int curr = sp->rx_current;
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_rx\n");
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_MAXRFDPROCESS_COUNT; i++, curr++) {
|
|
|
|
unsigned int entry = curr % IPG_RFDLIST_LENGTH;
|
|
|
|
struct ipg_rx *rxfd = sp->rxd + entry;
|
|
|
|
|
2008-11-20 06:54:38 +07:00
|
|
|
if (!(rxfd->rfs & cpu_to_le64(IPG_RFS_RFDDONE)))
|
2007-09-18 07:13:55 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
switch (ipg_nic_rx_check_frame_type(dev)) {
|
2008-01-24 17:01:17 +07:00
|
|
|
case FRAME_WITH_START_WITH_END:
|
2008-06-23 18:34:29 +07:00
|
|
|
ipg_nic_rx_with_start_and_end(dev, sp, rxfd, entry);
|
2007-09-18 07:13:55 +07:00
|
|
|
break;
|
2008-01-24 17:01:17 +07:00
|
|
|
case FRAME_WITH_START:
|
2008-06-23 18:34:29 +07:00
|
|
|
ipg_nic_rx_with_start(dev, sp, rxfd, entry);
|
2007-09-18 07:13:55 +07:00
|
|
|
break;
|
2008-01-24 17:01:17 +07:00
|
|
|
case FRAME_WITH_END:
|
2008-06-23 18:34:29 +07:00
|
|
|
ipg_nic_rx_with_end(dev, sp, rxfd, entry);
|
2007-09-18 07:13:55 +07:00
|
|
|
break;
|
2008-01-24 17:01:17 +07:00
|
|
|
case FRAME_NO_START_NO_END:
|
2008-06-23 18:34:29 +07:00
|
|
|
ipg_nic_rx_no_start_no_end(dev, sp, rxfd, entry);
|
2007-09-18 07:13:55 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sp->rx_current = curr;
|
|
|
|
|
|
|
|
if (i == IPG_MAXRFDPROCESS_COUNT) {
|
|
|
|
/* There are more RFDs to process, however the
|
|
|
|
* allocated amount of RFD processing time has
|
|
|
|
* expired. Assert Interrupt Requested to make
|
|
|
|
* sure we come back to process the remaining RFDs.
|
|
|
|
*/
|
|
|
|
ipg_w32(ipg_r32(ASIC_CTRL) | IPG_AC_INT_REQUEST, ASIC_CTRL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ipg_nic_rxrestore(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_nic_rx(struct net_device *dev)
|
|
|
|
{
|
|
|
|
/* Transfer received Ethernet frames to higher network layers. */
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
unsigned int curr = sp->rx_current;
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
struct ipg_rx *rxfd;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_rx\n");
|
|
|
|
|
|
|
|
#define __RFS_MASK \
|
|
|
|
cpu_to_le64(IPG_RFS_RFDDONE | IPG_RFS_FRAMESTART | IPG_RFS_FRAMEEND)
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_MAXRFDPROCESS_COUNT; i++, curr++) {
|
|
|
|
unsigned int entry = curr % IPG_RFDLIST_LENGTH;
|
2008-01-24 17:01:17 +07:00
|
|
|
struct sk_buff *skb = sp->rx_buff[entry];
|
2007-09-18 07:13:55 +07:00
|
|
|
unsigned int framelen;
|
|
|
|
|
|
|
|
rxfd = sp->rxd + entry;
|
|
|
|
|
|
|
|
if (((rxfd->rfs & __RFS_MASK) != __RFS_MASK) || !skb)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Get received frame length. */
|
|
|
|
framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN;
|
|
|
|
|
|
|
|
/* Check for jumbo frame arrival with too small
|
|
|
|
* RXFRAG_SIZE.
|
|
|
|
*/
|
2008-06-23 18:36:00 +07:00
|
|
|
if (framelen > sp->rxfrag_size) {
|
2007-09-18 07:13:55 +07:00
|
|
|
IPG_DEBUG_MSG
|
|
|
|
("RFS FrameLen > allocated fragment size.\n");
|
|
|
|
|
2008-06-23 18:36:00 +07:00
|
|
|
framelen = sp->rxfrag_size;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
2007-10-15 01:41:29 +07:00
|
|
|
if ((IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu(rxfd->rfs) &
|
2007-09-18 07:13:55 +07:00
|
|
|
(IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |
|
|
|
|
IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |
|
2007-10-15 01:41:29 +07:00
|
|
|
IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR)))) {
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",
|
|
|
|
(unsigned long int) rxfd->rfs);
|
|
|
|
|
|
|
|
/* Increment general receive error statistic. */
|
|
|
|
sp->stats.rx_errors++;
|
|
|
|
|
|
|
|
/* Increment detailed receive error statistics. */
|
2007-10-15 01:41:29 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {
|
2007-09-18 07:13:55 +07:00
|
|
|
IPG_DEBUG_MSG("RX FIFO overrun occured.\n");
|
|
|
|
sp->stats.rx_fifo_errors++;
|
|
|
|
}
|
|
|
|
|
2007-10-15 01:41:29 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {
|
2007-09-18 07:13:55 +07:00
|
|
|
IPG_DEBUG_MSG("RX runt occured.\n");
|
|
|
|
sp->stats.rx_length_errors++;
|
|
|
|
}
|
|
|
|
|
2007-10-15 01:41:29 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXOVERSIZEDFRAME) ;
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Do nothing, error count handled by a IPG
|
|
|
|
* statistic register.
|
|
|
|
*/
|
|
|
|
|
2007-10-15 01:41:29 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {
|
2007-09-18 07:13:55 +07:00
|
|
|
IPG_DEBUG_MSG("RX alignment error occured.\n");
|
|
|
|
sp->stats.rx_frame_errors++;
|
|
|
|
}
|
|
|
|
|
2007-10-15 01:41:29 +07:00
|
|
|
if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFCSERROR) ;
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Do nothing, error count handled by a IPG
|
|
|
|
* statistic register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Free the memory associated with the RX
|
|
|
|
* buffer since it is erroneous and we will
|
|
|
|
* not pass it to higher layer processes.
|
|
|
|
*/
|
|
|
|
if (skb) {
|
2007-10-15 01:41:29 +07:00
|
|
|
__le64 info = rxfd->frag_info;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
|
|
|
|
2007-11-30 14:53:51 +07:00
|
|
|
dev_kfree_skb_irq(skb);
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* Adjust the new buffer length to accomodate the size
|
|
|
|
* of the received frame.
|
|
|
|
*/
|
|
|
|
skb_put(skb, framelen);
|
|
|
|
|
|
|
|
/* Set the buffer's protocol field to Ethernet. */
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
|
|
|
2007-11-30 14:54:33 +07:00
|
|
|
/* 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.
|
2007-09-18 07:13:55 +07:00
|
|
|
*/
|
2007-11-30 14:54:33 +07:00
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Hand off frame for higher layer processing.
|
|
|
|
* The function netif_rx() releases the sk_buff
|
|
|
|
* when processing completes.
|
|
|
|
*/
|
|
|
|
netif_rx(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assure RX buffer is not reused by IPG. */
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2007-10-15 01:41:29 +07:00
|
|
|
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))) {
|
2007-09-18 07:13:55 +07:00
|
|
|
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.
|
|
|
|
*/
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[entry]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->rx_buff[entry]);
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assure RX buffer is not reused by IPG. */
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->rx_buff[entry] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_reset_after_host_error(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp =
|
|
|
|
container_of(work, struct ipg_nic_private, task.work);
|
|
|
|
struct net_device *dev = sp->dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Acknowledge HostError interrupt by resetting
|
|
|
|
* IPG DMA and HOST.
|
|
|
|
*/
|
|
|
|
ipg_reset(dev, IPG_AC_GLOBAL_RESET | IPG_AC_HOST | IPG_AC_DMA);
|
|
|
|
|
|
|
|
init_rfdlist(dev);
|
|
|
|
init_tfdlist(dev);
|
|
|
|
|
|
|
|
if (ipg_io_config(dev) < 0) {
|
|
|
|
printk(KERN_INFO "%s: Cannot recover from PCI error.\n",
|
|
|
|
dev->name);
|
|
|
|
schedule_delayed_work(&sp->task, HZ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
|
|
|
|
{
|
|
|
|
struct net_device *dev = dev_inst;
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int handled = 0;
|
|
|
|
u16 status;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_interrupt_handler\n");
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
if (sp->is_jumbo)
|
|
|
|
ipg_nic_rxrestore(dev);
|
|
|
|
|
2008-01-11 05:25:30 +07:00
|
|
|
spin_lock(&sp->lock);
|
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Get interrupt source information, and acknowledge
|
|
|
|
* some (i.e. TxDMAComplete, RxDMAComplete, RxEarly,
|
|
|
|
* IntRequested, MacControlFrame, LinkEvent) interrupts
|
|
|
|
* if issued. Also, all IPG interrupts are disabled by
|
|
|
|
* reading IntStatusAck.
|
|
|
|
*/
|
|
|
|
status = ipg_r16(INT_STATUS_ACK);
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("IntStatusAck = %4.4x\n", status);
|
|
|
|
|
|
|
|
/* Shared IRQ of remove event. */
|
|
|
|
if (!(status & IPG_IS_RSVD_MASK))
|
|
|
|
goto out_enable;
|
|
|
|
|
|
|
|
handled = 1;
|
|
|
|
|
|
|
|
if (unlikely(!netif_running(dev)))
|
2008-01-11 05:25:30 +07:00
|
|
|
goto out_unlock;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* If RFDListEnd interrupt, restore all used RFDs. */
|
|
|
|
if (status & IPG_IS_RFD_LIST_END) {
|
|
|
|
IPG_DEBUG_MSG("RFDListEnd Interrupt.\n");
|
|
|
|
|
|
|
|
/* The RFD list end indicates an RFD was encountered
|
|
|
|
* with a 0 NextPtr, or with an RFDDone bit set to 1
|
|
|
|
* (indicating the RFD is not read for use by the
|
|
|
|
* IPG.) Try to restore all RFDs.
|
|
|
|
*/
|
|
|
|
ipg_nic_rxrestore(dev);
|
|
|
|
|
|
|
|
#ifdef IPG_DEBUG
|
|
|
|
/* Increment the RFDlistendCount counter. */
|
|
|
|
sp->RFDlistendCount++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If RFDListEnd, RxDMAPriority, RxDMAComplete, or
|
|
|
|
* IntRequested interrupt, process received frames. */
|
|
|
|
if ((status & IPG_IS_RX_DMA_PRIORITY) ||
|
|
|
|
(status & IPG_IS_RFD_LIST_END) ||
|
|
|
|
(status & IPG_IS_RX_DMA_COMPLETE) ||
|
|
|
|
(status & IPG_IS_INT_REQUESTED)) {
|
|
|
|
#ifdef IPG_DEBUG
|
|
|
|
/* Increment the RFD list checked counter if interrupted
|
|
|
|
* only to check the RFD list. */
|
|
|
|
if (status & (~(IPG_IS_RX_DMA_PRIORITY | IPG_IS_RFD_LIST_END |
|
|
|
|
IPG_IS_RX_DMA_COMPLETE | IPG_IS_INT_REQUESTED) &
|
|
|
|
(IPG_IS_HOST_ERROR | IPG_IS_TX_DMA_COMPLETE |
|
|
|
|
IPG_IS_LINK_EVENT | IPG_IS_TX_COMPLETE |
|
|
|
|
IPG_IS_UPDATE_STATS)))
|
|
|
|
sp->RFDListCheckedCount++;
|
|
|
|
#endif
|
|
|
|
|
2008-06-23 18:35:16 +07:00
|
|
|
if (sp->is_jumbo)
|
|
|
|
ipg_nic_rx_jumbo(dev);
|
|
|
|
else
|
|
|
|
ipg_nic_rx(dev);
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If TxDMAComplete interrupt, free used TFDs. */
|
|
|
|
if (status & IPG_IS_TX_DMA_COMPLETE)
|
|
|
|
ipg_nic_txfree(dev);
|
|
|
|
|
|
|
|
/* TxComplete interrupts indicate one of numerous actions.
|
|
|
|
* Determine what action to take based on TXSTATUS register.
|
|
|
|
*/
|
|
|
|
if (status & IPG_IS_TX_COMPLETE)
|
|
|
|
ipg_nic_txcleanup(dev);
|
|
|
|
|
|
|
|
/* If UpdateStats interrupt, update Linux Ethernet statistics */
|
|
|
|
if (status & IPG_IS_UPDATE_STATS)
|
|
|
|
ipg_nic_get_stats(dev);
|
|
|
|
|
|
|
|
/* If HostError interrupt, reset IPG. */
|
|
|
|
if (status & IPG_IS_HOST_ERROR) {
|
|
|
|
IPG_DDEBUG_MSG("HostError Interrupt\n");
|
|
|
|
|
|
|
|
schedule_delayed_work(&sp->task, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If LinkEvent interrupt, resolve autonegotiation. */
|
|
|
|
if (status & IPG_IS_LINK_EVENT) {
|
|
|
|
if (ipg_config_autoneg(dev) < 0)
|
|
|
|
printk(KERN_INFO "%s: Auto-negotiation error.\n",
|
|
|
|
dev->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If MACCtrlFrame interrupt, do nothing. */
|
|
|
|
if (status & IPG_IS_MAC_CTRL_FRAME)
|
|
|
|
IPG_DEBUG_MSG("MACCtrlFrame interrupt.\n");
|
|
|
|
|
|
|
|
/* If RxComplete interrupt, do nothing. */
|
|
|
|
if (status & IPG_IS_RX_COMPLETE)
|
|
|
|
IPG_DEBUG_MSG("RxComplete interrupt.\n");
|
|
|
|
|
|
|
|
/* If RxEarly interrupt, do nothing. */
|
|
|
|
if (status & IPG_IS_RX_EARLY)
|
|
|
|
IPG_DEBUG_MSG("RxEarly interrupt.\n");
|
|
|
|
|
|
|
|
out_enable:
|
|
|
|
/* Re-enable IPG interrupts. */
|
|
|
|
ipg_w16(IPG_IE_TX_DMA_COMPLETE | IPG_IE_RX_DMA_COMPLETE |
|
|
|
|
IPG_IE_HOST_ERROR | IPG_IE_INT_REQUESTED | IPG_IE_TX_COMPLETE |
|
|
|
|
IPG_IE_LINK_EVENT | IPG_IE_UPDATE_STATS, INT_ENABLE);
|
2008-01-11 05:25:30 +07:00
|
|
|
out_unlock:
|
2007-09-18 07:13:55 +07:00
|
|
|
spin_unlock(&sp->lock);
|
2008-01-11 05:25:30 +07:00
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_rx_clear(struct ipg_nic_private *sp)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_RFDLIST_LENGTH; i++) {
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->rx_buff[i]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
struct ipg_rx *rxfd = sp->rxd + i;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->rx_buff[i]);
|
|
|
|
sp->rx_buff[i] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
|
2007-09-18 07:13:55 +07:00
|
|
|
sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_tx_clear(struct ipg_nic_private *sp)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < IPG_TFDLIST_LENGTH; i++) {
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->tx_buff[i]) {
|
2007-09-18 07:13:55 +07:00
|
|
|
struct ipg_tx *txfd = sp->txd + i;
|
|
|
|
|
|
|
|
pci_unmap_single(sp->pdev,
|
2007-10-15 01:41:29 +07:00
|
|
|
le64_to_cpu(txfd->frag_info) & ~IPG_TFI_FRAGLEN,
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->tx_buff[i]->len, PCI_DMA_TODEVICE);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
dev_kfree_skb_irq(sp->tx_buff[i]);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->tx_buff[i] = NULL;
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_nic_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
struct pci_dev *pdev = sp->pdev;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_open\n");
|
|
|
|
|
2008-06-23 18:36:18 +07:00
|
|
|
sp->rx_buf_sz = sp->rxsupport_size;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Check for interrupt line conflicts, and request interrupt
|
|
|
|
* line for IPG.
|
|
|
|
*
|
|
|
|
* IMPORTANT: Disable IPG interrupts prior to registering
|
|
|
|
* IRQ.
|
|
|
|
*/
|
|
|
|
ipg_w16(0x0000, INT_ENABLE);
|
|
|
|
|
|
|
|
/* Register the interrupt line to be used by the IPG within
|
|
|
|
* the Linux system.
|
|
|
|
*/
|
2009-11-18 15:21:45 +07:00
|
|
|
rc = request_irq(pdev->irq, ipg_interrupt_handler, IRQF_SHARED,
|
2007-09-18 07:13:55 +07:00
|
|
|
dev->name, dev);
|
|
|
|
if (rc < 0) {
|
|
|
|
printk(KERN_INFO "%s: Error when requesting interrupt.\n",
|
|
|
|
dev->name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->irq = pdev->irq;
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
|
|
|
|
sp->rxd = dma_alloc_coherent(&pdev->dev, IPG_RX_RING_BYTES,
|
|
|
|
&sp->rxd_map, GFP_KERNEL);
|
|
|
|
if (!sp->rxd)
|
|
|
|
goto err_free_irq_0;
|
|
|
|
|
|
|
|
sp->txd = dma_alloc_coherent(&pdev->dev, IPG_TX_RING_BYTES,
|
|
|
|
&sp->txd_map, GFP_KERNEL);
|
|
|
|
if (!sp->txd)
|
|
|
|
goto err_free_rx_1;
|
|
|
|
|
|
|
|
rc = init_rfdlist(dev);
|
|
|
|
if (rc < 0) {
|
|
|
|
printk(KERN_INFO "%s: Error during configuration.\n",
|
|
|
|
dev->name);
|
|
|
|
goto err_free_tx_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_tfdlist(dev);
|
|
|
|
|
|
|
|
rc = ipg_io_config(dev);
|
|
|
|
if (rc < 0) {
|
|
|
|
printk(KERN_INFO "%s: Error during configuration.\n",
|
|
|
|
dev->name);
|
|
|
|
goto err_release_tfdlist_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resolve autonegotiation. */
|
|
|
|
if (ipg_config_autoneg(dev) < 0)
|
|
|
|
printk(KERN_INFO "%s: Auto-negotiation error.\n", dev->name);
|
|
|
|
|
|
|
|
/* initialize JUMBO Frame control variable */
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->jumbo.found_start = 0;
|
|
|
|
sp->jumbo.current_size = 0;
|
2008-06-23 18:34:50 +07:00
|
|
|
sp->jumbo.skb = NULL;
|
2008-06-23 18:35:37 +07:00
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Enable transmit and receive operation of the IPG. */
|
|
|
|
ipg_w32((ipg_r32(MAC_CTRL) | IPG_MC_RX_ENABLE | IPG_MC_TX_ENABLE) &
|
|
|
|
IPG_MC_RSVD_MASK, MAC_CTRL);
|
|
|
|
|
|
|
|
netif_start_queue(dev);
|
|
|
|
out:
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
err_release_tfdlist_3:
|
|
|
|
ipg_tx_clear(sp);
|
|
|
|
ipg_rx_clear(sp);
|
|
|
|
err_free_tx_2:
|
|
|
|
dma_free_coherent(&pdev->dev, IPG_TX_RING_BYTES, sp->txd, sp->txd_map);
|
|
|
|
err_free_rx_1:
|
|
|
|
dma_free_coherent(&pdev->dev, IPG_RX_RING_BYTES, sp->rxd, sp->rxd_map);
|
|
|
|
err_free_irq_0:
|
|
|
|
free_irq(pdev->irq, dev);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_nic_stop(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
struct pci_dev *pdev = sp->pdev;
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_stop\n");
|
|
|
|
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
IPG_DUMPTFDLIST(dev);
|
|
|
|
|
|
|
|
do {
|
|
|
|
(void) ipg_r16(INT_STATUS_ACK);
|
|
|
|
|
|
|
|
ipg_reset(dev, IPG_AC_GLOBAL_RESET | IPG_AC_HOST | IPG_AC_DMA);
|
|
|
|
|
|
|
|
synchronize_irq(pdev->irq);
|
|
|
|
} while (ipg_r16(INT_ENABLE) & IPG_IE_RSVD_MASK);
|
|
|
|
|
|
|
|
ipg_rx_clear(sp);
|
|
|
|
|
|
|
|
ipg_tx_clear(sp);
|
|
|
|
|
|
|
|
pci_free_consistent(pdev, IPG_RX_RING_BYTES, sp->rxd, sp->rxd_map);
|
|
|
|
pci_free_consistent(pdev, IPG_TX_RING_BYTES, sp->txd, sp->txd_map);
|
|
|
|
|
|
|
|
free_irq(pdev->irq, dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-01 02:50:58 +07:00
|
|
|
static netdev_tx_t ipg_nic_hard_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int entry = sp->tx_current % IPG_TFDLIST_LENGTH;
|
|
|
|
unsigned long flags;
|
|
|
|
struct ipg_tx *txfd;
|
|
|
|
|
|
|
|
IPG_DDEBUG_MSG("_nic_hard_start_xmit\n");
|
|
|
|
|
|
|
|
/* If in 10Mbps mode, stop the transmit queue so
|
|
|
|
* no more transmit frames are accepted.
|
|
|
|
*/
|
|
|
|
if (sp->tenmbpsmode)
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
if (sp->reset_current_tfd) {
|
|
|
|
sp->reset_current_tfd = 0;
|
2007-09-18 07:13:55 +07:00
|
|
|
entry = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
txfd = sp->txd + entry;
|
|
|
|
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->tx_buff[entry] = skb;
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Clear all TFC fields, except TFDDONE. */
|
|
|
|
txfd->tfc = cpu_to_le64(IPG_TFC_TFDDONE);
|
|
|
|
|
|
|
|
/* Specify the TFC field within the TFD. */
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_WORDALIGNDISABLED |
|
2008-03-17 05:22:14 +07:00
|
|
|
(IPG_TFC_FRAMEID & sp->tx_current) |
|
2007-09-18 07:13:55 +07:00
|
|
|
(IPG_TFC_FRAGCOUNT & (1 << 24)));
|
2008-03-17 05:22:14 +07:00
|
|
|
/*
|
|
|
|
* 16--17 (WordAlign) <- 3 (disable),
|
|
|
|
* 0--15 (FrameId) <- sp->tx_current,
|
|
|
|
* 24--27 (FragCount) <- 1
|
|
|
|
*/
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Request TxComplete interrupts at an interval defined
|
|
|
|
* by the constant IPG_FRAMESBETWEENTXCOMPLETES.
|
|
|
|
* Request TxComplete interrupt for every frame
|
|
|
|
* if in 10Mbps mode to accomodate problem with 10Mbps
|
|
|
|
* processing.
|
|
|
|
*/
|
|
|
|
if (sp->tenmbpsmode)
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_TXINDICATE);
|
2008-01-11 05:53:15 +07:00
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_TXDMAINDICATE);
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Based on compilation option, determine if FCS is to be
|
|
|
|
* appended to transmit frame by IPG.
|
|
|
|
*/
|
|
|
|
if (!(IPG_APPEND_FCS_ON_TX))
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_FCSAPPENDDISABLE);
|
|
|
|
|
|
|
|
/* Based on compilation option, determine if IP, TCP and/or
|
|
|
|
* UDP checksums are to be added to transmit frame by IPG.
|
|
|
|
*/
|
|
|
|
if (IPG_ADD_IPCHECKSUM_ON_TX)
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_IPCHECKSUMENABLE);
|
|
|
|
|
|
|
|
if (IPG_ADD_TCPCHECKSUM_ON_TX)
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_TCPCHECKSUMENABLE);
|
|
|
|
|
|
|
|
if (IPG_ADD_UDPCHECKSUM_ON_TX)
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_UDPCHECKSUMENABLE);
|
|
|
|
|
|
|
|
/* Based on compilation option, determine if VLAN tag info is to be
|
|
|
|
* inserted into transmit frame by IPG.
|
|
|
|
*/
|
|
|
|
if (IPG_INSERT_MANUAL_VLAN_TAG) {
|
|
|
|
txfd->tfc |= cpu_to_le64(IPG_TFC_VLANTAGINSERT |
|
|
|
|
((u64) IPG_MANUAL_VLAN_VID << 32) |
|
|
|
|
((u64) IPG_MANUAL_VLAN_CFI << 44) |
|
|
|
|
((u64) IPG_MANUAL_VLAN_USERPRIORITY << 45));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The fragment start location within system memory is defined
|
|
|
|
* by the sk_buff structure's data field. The physical address
|
|
|
|
* of this location within the system's virtual memory space
|
|
|
|
* is determined using the IPG_HOST2BUS_MAP function.
|
|
|
|
*/
|
|
|
|
txfd->frag_info = cpu_to_le64(pci_map_single(sp->pdev, skb->data,
|
|
|
|
skb->len, PCI_DMA_TODEVICE));
|
|
|
|
|
|
|
|
/* The length of the fragment within system memory is defined by
|
|
|
|
* the sk_buff structure's len field.
|
|
|
|
*/
|
|
|
|
txfd->frag_info |= cpu_to_le64(IPG_TFI_FRAGLEN &
|
|
|
|
((u64) (skb->len & 0xffff) << 48));
|
|
|
|
|
|
|
|
/* Clear the TFDDone bit last to indicate the TFD is ready
|
|
|
|
* for transfer to the IPG.
|
|
|
|
*/
|
|
|
|
txfd->tfc &= cpu_to_le64(~IPG_TFC_TFDDONE);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sp->lock, flags);
|
|
|
|
|
|
|
|
sp->tx_current++;
|
|
|
|
|
|
|
|
mmiowb();
|
|
|
|
|
|
|
|
ipg_w32(IPG_DC_TX_DMA_POLL_NOW, DMA_CTRL);
|
|
|
|
|
|
|
|
if (sp->tx_current == (sp->tx_dirty + IPG_TFDLIST_LENGTH))
|
2008-01-11 05:45:05 +07:00
|
|
|
netif_stop_queue(dev);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sp->lock, flags);
|
|
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_set_phy_default_param(unsigned char rev,
|
|
|
|
struct net_device *dev, int phy_address)
|
|
|
|
{
|
|
|
|
unsigned short length;
|
|
|
|
unsigned char revision;
|
|
|
|
unsigned short *phy_param;
|
|
|
|
unsigned short address, value;
|
|
|
|
|
|
|
|
phy_param = &DefaultPhyParam[0];
|
|
|
|
length = *phy_param & 0x00FF;
|
|
|
|
revision = (unsigned char)((*phy_param) >> 8);
|
|
|
|
phy_param++;
|
|
|
|
while (length != 0) {
|
|
|
|
if (rev == revision) {
|
|
|
|
while (length > 1) {
|
|
|
|
address = *phy_param;
|
|
|
|
value = *(phy_param + 1);
|
|
|
|
phy_param += 2;
|
|
|
|
mdio_write(dev, phy_address, address, value);
|
|
|
|
length -= 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
phy_param += length / 2;
|
|
|
|
length = *phy_param & 0x00FF;
|
|
|
|
revision = (unsigned char)((*phy_param) >> 8);
|
|
|
|
phy_param++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_eeprom(struct net_device *dev, int eep_addr)
|
|
|
|
{
|
|
|
|
void __iomem *ioaddr = ipg_ioaddr(dev);
|
|
|
|
unsigned int i;
|
|
|
|
int ret = 0;
|
|
|
|
u16 value;
|
|
|
|
|
|
|
|
value = IPG_EC_EEPROM_READOPCODE | (eep_addr & 0xff);
|
|
|
|
ipg_w16(value, EEPROM_CTRL);
|
|
|
|
|
|
|
|
for (i = 0; i < 1000; i++) {
|
|
|
|
u16 data;
|
|
|
|
|
|
|
|
mdelay(10);
|
|
|
|
data = ipg_r16(EEPROM_CTRL);
|
|
|
|
if (!(data & IPG_EC_EEPROM_BUSY)) {
|
|
|
|
ret = ipg_r16(EEPROM_DATA);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipg_init_mii(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
struct mii_if_info *mii_if = &sp->mii_if;
|
|
|
|
int phyaddr;
|
|
|
|
|
|
|
|
mii_if->dev = dev;
|
|
|
|
mii_if->mdio_read = mdio_read;
|
|
|
|
mii_if->mdio_write = mdio_write;
|
|
|
|
mii_if->phy_id_mask = 0x1f;
|
|
|
|
mii_if->reg_num_mask = 0x1f;
|
|
|
|
|
|
|
|
mii_if->phy_id = phyaddr = ipg_find_phyaddr(dev);
|
|
|
|
|
|
|
|
if (phyaddr != 0x1f) {
|
|
|
|
u16 mii_phyctrl, mii_1000cr;
|
|
|
|
u8 revisionid = 0;
|
|
|
|
|
|
|
|
mii_1000cr = mdio_read(dev, phyaddr, MII_CTRL1000);
|
|
|
|
mii_1000cr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF |
|
|
|
|
GMII_PHY_1000BASETCONTROL_PreferMaster;
|
|
|
|
mdio_write(dev, phyaddr, MII_CTRL1000, mii_1000cr);
|
|
|
|
|
|
|
|
mii_phyctrl = mdio_read(dev, phyaddr, MII_BMCR);
|
|
|
|
|
|
|
|
/* Set default phyparam */
|
|
|
|
pci_read_config_byte(sp->pdev, PCI_REVISION_ID, &revisionid);
|
|
|
|
ipg_set_phy_default_param(revisionid, dev, phyaddr);
|
|
|
|
|
|
|
|
/* Reset PHY */
|
|
|
|
mii_phyctrl |= BMCR_RESET | BMCR_ANRESTART;
|
|
|
|
mdio_write(dev, phyaddr, MII_BMCR, mii_phyctrl);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_hw_init(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
void __iomem *ioaddr = sp->ioaddr;
|
|
|
|
unsigned int i;
|
|
|
|
int rc;
|
|
|
|
|
2007-11-30 14:54:51 +07:00
|
|
|
/* Read/Write and Reset EEPROM Value */
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Read LED Mode Configuration from EEPROM */
|
2008-01-24 17:01:17 +07:00
|
|
|
sp->led_mode = read_eeprom(dev, 6);
|
2007-09-18 07:13:55 +07:00
|
|
|
|
|
|
|
/* Reset all functions within the IPG. Do not assert
|
|
|
|
* RST_OUT as not compatible with some PHYs.
|
|
|
|
*/
|
|
|
|
rc = ipg_reset(dev, IPG_RESET_MASK);
|
|
|
|
if (rc < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ipg_init_mii(dev);
|
|
|
|
|
|
|
|
/* Read MAC Address from EEPROM */
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
sp->station_addr[i] = read_eeprom(dev, 16 + i);
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
ipg_w16(sp->station_addr[i], STATION_ADDRESS_0 + 2*i);
|
|
|
|
|
|
|
|
/* Set station address in ethernet_device structure. */
|
|
|
|
dev->dev_addr[0] = ipg_r16(STATION_ADDRESS_0) & 0x00ff;
|
|
|
|
dev->dev_addr[1] = (ipg_r16(STATION_ADDRESS_0) & 0xff00) >> 8;
|
|
|
|
dev->dev_addr[2] = ipg_r16(STATION_ADDRESS_1) & 0x00ff;
|
|
|
|
dev->dev_addr[3] = (ipg_r16(STATION_ADDRESS_1) & 0xff00) >> 8;
|
|
|
|
dev->dev_addr[4] = ipg_r16(STATION_ADDRESS_2) & 0x00ff;
|
|
|
|
dev->dev_addr[5] = (ipg_r16(STATION_ADDRESS_2) & 0xff00) >> 8;
|
|
|
|
out:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mutex_lock(&sp->mii_mutex);
|
|
|
|
rc = generic_mii_ioctl(&sp->mii_if, if_mii(ifr), cmd, NULL);
|
|
|
|
mutex_unlock(&sp->mii_mutex);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_nic_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
{
|
2008-06-23 18:36:35 +07:00
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
2008-06-23 18:36:56 +07:00
|
|
|
int err;
|
2008-06-23 18:36:35 +07:00
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Function to accomodate changes to Maximum Transfer Unit
|
|
|
|
* (or MTU) of IPG NIC. Cannot use default function since
|
|
|
|
* the default will not allow for MTU > 1500 bytes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_nic_change_mtu\n");
|
|
|
|
|
2008-06-23 18:36:56 +07:00
|
|
|
/*
|
|
|
|
* Check that the new MTU value is between 68 (14 byte header, 46 byte
|
|
|
|
* payload, 4 byte FCS) and 10 KB, which is the largest supported MTU.
|
2007-09-18 07:13:55 +07:00
|
|
|
*/
|
2008-06-23 18:36:56 +07:00
|
|
|
if (new_mtu < 68 || new_mtu > 10240)
|
2007-09-18 07:13:55 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-06-23 18:36:56 +07:00
|
|
|
err = ipg_nic_stop(dev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
dev->mtu = new_mtu;
|
|
|
|
|
2008-06-23 18:36:56 +07:00
|
|
|
sp->max_rxframe_size = new_mtu;
|
|
|
|
|
|
|
|
sp->rxfrag_size = new_mtu;
|
|
|
|
if (sp->rxfrag_size > 4088)
|
|
|
|
sp->rxfrag_size = 4088;
|
|
|
|
|
|
|
|
sp->rxsupport_size = sp->max_rxframe_size;
|
|
|
|
|
|
|
|
if (new_mtu > 0x0600)
|
|
|
|
sp->is_jumbo = true;
|
|
|
|
else
|
|
|
|
sp->is_jumbo = false;
|
|
|
|
|
|
|
|
return ipg_nic_open(dev);
|
2007-09-18 07:13:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mutex_lock(&sp->mii_mutex);
|
|
|
|
rc = mii_ethtool_gset(&sp->mii_if, cmd);
|
|
|
|
mutex_unlock(&sp->mii_mutex);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mutex_lock(&sp->mii_mutex);
|
|
|
|
rc = mii_ethtool_sset(&sp->mii_if, cmd);
|
|
|
|
mutex_unlock(&sp->mii_mutex);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipg_nway_reset(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mutex_lock(&sp->mii_mutex);
|
|
|
|
rc = mii_nway_restart(&sp->mii_if);
|
|
|
|
mutex_unlock(&sp->mii_mutex);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-09-02 15:03:33 +07:00
|
|
|
static const struct ethtool_ops ipg_ethtool_ops = {
|
2007-09-18 07:13:55 +07:00
|
|
|
.get_settings = ipg_get_settings,
|
|
|
|
.set_settings = ipg_set_settings,
|
|
|
|
.nway_reset = ipg_nway_reset,
|
|
|
|
};
|
|
|
|
|
2007-12-12 05:20:30 +07:00
|
|
|
static void __devexit ipg_remove(struct pci_dev *pdev)
|
2007-09-18 07:13:55 +07:00
|
|
|
{
|
|
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
|
|
struct ipg_nic_private *sp = netdev_priv(dev);
|
|
|
|
|
|
|
|
IPG_DEBUG_MSG("_remove\n");
|
|
|
|
|
|
|
|
/* Un-register Ethernet device. */
|
|
|
|
unregister_netdev(dev);
|
|
|
|
|
|
|
|
pci_iounmap(pdev, sp->ioaddr);
|
|
|
|
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
|
|
|
|
free_netdev(dev);
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
}
|
|
|
|
|
2009-01-08 08:26:14 +07:00
|
|
|
static const struct net_device_ops ipg_netdev_ops = {
|
|
|
|
.ndo_open = ipg_nic_open,
|
|
|
|
.ndo_stop = ipg_nic_stop,
|
|
|
|
.ndo_start_xmit = ipg_nic_hard_start_xmit,
|
|
|
|
.ndo_get_stats = ipg_nic_get_stats,
|
|
|
|
.ndo_set_multicast_list = ipg_nic_set_multicast_list,
|
|
|
|
.ndo_do_ioctl = ipg_ioctl,
|
|
|
|
.ndo_tx_timeout = ipg_tx_timeout,
|
|
|
|
.ndo_change_mtu = ipg_nic_change_mtu,
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
};
|
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
static int __devinit ipg_probe(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *id)
|
|
|
|
{
|
|
|
|
unsigned int i = id->driver_data;
|
|
|
|
struct ipg_nic_private *sp;
|
|
|
|
struct net_device *dev;
|
|
|
|
void __iomem *ioaddr;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = pci_enable_device(pdev);
|
|
|
|
if (rc < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: %s\n", pci_name(pdev), ipg_brand_name[i]);
|
|
|
|
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
2009-04-07 09:01:14 +07:00
|
|
|
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(40));
|
2007-09-18 07:13:55 +07:00
|
|
|
if (rc < 0) {
|
2009-04-07 09:01:15 +07:00
|
|
|
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
2007-09-18 07:13:55 +07:00
|
|
|
if (rc < 0) {
|
|
|
|
printk(KERN_ERR "%s: DMA config failed.\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto err_disable_0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize net device.
|
|
|
|
*/
|
|
|
|
dev = alloc_etherdev(sizeof(struct ipg_nic_private));
|
|
|
|
if (!dev) {
|
|
|
|
printk(KERN_ERR "%s: alloc_etherdev failed\n", pci_name(pdev));
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err_disable_0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sp = netdev_priv(dev);
|
|
|
|
spin_lock_init(&sp->lock);
|
|
|
|
mutex_init(&sp->mii_mutex);
|
|
|
|
|
2008-06-23 18:36:56 +07:00
|
|
|
sp->is_jumbo = IPG_IS_JUMBO;
|
2008-06-23 18:36:00 +07:00
|
|
|
sp->rxfrag_size = IPG_RXFRAG_SIZE;
|
2008-06-23 18:36:18 +07:00
|
|
|
sp->rxsupport_size = IPG_RXSUPPORT_SIZE;
|
2008-06-23 18:36:35 +07:00
|
|
|
sp->max_rxframe_size = IPG_MAX_RXFRAME_SIZE;
|
2008-06-23 18:35:16 +07:00
|
|
|
|
2007-09-18 07:13:55 +07:00
|
|
|
/* Declare IPG NIC functions for Ethernet device methods.
|
|
|
|
*/
|
2009-01-08 08:26:14 +07:00
|
|
|
dev->netdev_ops = &ipg_netdev_ops;
|
2007-09-18 07:13:55 +07:00
|
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
|
|
SET_ETHTOOL_OPS(dev, &ipg_ethtool_ops);
|
|
|
|
|
|
|
|
rc = pci_request_regions(pdev, DRV_NAME);
|
|
|
|
if (rc)
|
|
|
|
goto err_free_dev_1;
|
|
|
|
|
|
|
|
ioaddr = pci_iomap(pdev, 1, pci_resource_len(pdev, 1));
|
|
|
|
if (!ioaddr) {
|
|
|
|
printk(KERN_ERR "%s cannot map MMIO\n", pci_name(pdev));
|
|
|
|
rc = -EIO;
|
|
|
|
goto err_release_regions_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the pointer to the PCI device information. */
|
|
|
|
sp->ioaddr = ioaddr;
|
|
|
|
sp->pdev = pdev;
|
|
|
|
sp->dev = dev;
|
|
|
|
|
|
|
|
INIT_DELAYED_WORK(&sp->task, ipg_reset_after_host_error);
|
|
|
|
|
|
|
|
pci_set_drvdata(pdev, dev);
|
|
|
|
|
|
|
|
rc = ipg_hw_init(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err_unmap_3;
|
|
|
|
|
|
|
|
rc = register_netdev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err_unmap_3;
|
|
|
|
|
|
|
|
printk(KERN_INFO "Ethernet device registered as: %s\n", dev->name);
|
|
|
|
out:
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
err_unmap_3:
|
|
|
|
pci_iounmap(pdev, ioaddr);
|
|
|
|
err_release_regions_2:
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
err_free_dev_1:
|
|
|
|
free_netdev(dev);
|
|
|
|
err_disable_0:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_driver ipg_pci_driver = {
|
|
|
|
.name = IPG_DRIVER_NAME,
|
|
|
|
.id_table = ipg_pci_tbl,
|
|
|
|
.probe = ipg_probe,
|
|
|
|
.remove = __devexit_p(ipg_remove),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init ipg_init_module(void)
|
|
|
|
{
|
|
|
|
return pci_register_driver(&ipg_pci_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ipg_exit_module(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&ipg_pci_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ipg_init_module);
|
|
|
|
module_exit(ipg_exit_module);
|