2005-04-17 05:20:36 +07:00
|
|
|
/* $Id: sungem.c,v 1.44.2.22 2002/03/13 01:18:12 davem Exp $
|
|
|
|
* sungem.c: Sun GEM ethernet driver.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000, 2001, 2002, 2003 David S. Miller (davem@redhat.com)
|
2006-09-14 00:24:59 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Support for Apple GMAC and assorted PHYs, WOL, Power Management
|
|
|
|
* (C) 2001,2002,2003 Benjamin Herrenscmidt (benh@kernel.crashing.org)
|
|
|
|
* (C) 2004,2005 Benjamin Herrenscmidt, IBM Corp.
|
|
|
|
*
|
|
|
|
* NAPI and NETPOLL support
|
|
|
|
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
|
2006-09-14 00:24:59 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/in.h>
|
2009-10-07 20:09:06 +07:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/pci.h>
|
2005-06-27 05:22:14 +07:00
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/mii.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/bitops.h>
|
2006-12-04 11:15:30 +07:00
|
|
|
#include <linux/mm.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>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
|
2007-03-01 06:42:50 +07:00
|
|
|
#ifdef CONFIG_SPARC
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/idprom.h>
|
2011-07-29 22:31:16 +07:00
|
|
|
#include <asm/prom.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_PMAC
|
|
|
|
#include <asm/pci-bridge.h>
|
2011-07-29 22:31:16 +07:00
|
|
|
#include <asm/prom.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/machdep.h>
|
|
|
|
#include <asm/pmac_feature.h>
|
|
|
|
#endif
|
|
|
|
|
2011-08-15 12:52:04 +07:00
|
|
|
#include <linux/sungem_phy.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "sungem.h"
|
|
|
|
|
|
|
|
/* Stripping FCS is causing problems, disabled for now */
|
|
|
|
#undef STRIP_FCS
|
|
|
|
|
|
|
|
#define DEFAULT_MSG (NETIF_MSG_DRV | \
|
|
|
|
NETIF_MSG_PROBE | \
|
|
|
|
NETIF_MSG_LINK)
|
|
|
|
|
|
|
|
#define ADVERTISE_MASK (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
|
|
|
|
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
|
2007-01-04 09:54:43 +07:00
|
|
|
SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | \
|
|
|
|
SUPPORTED_Pause | SUPPORTED_Autoneg)
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#define DRV_NAME "sungem"
|
2011-06-01 14:17:10 +07:00
|
|
|
#define DRV_VERSION "1.0"
|
|
|
|
#define DRV_AUTHOR "David S. Miller <davem@redhat.com>"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-12-03 21:24:02 +07:00
|
|
|
static char version[] =
|
2011-06-01 14:17:10 +07:00
|
|
|
DRV_NAME ".c:v" DRV_VERSION " " DRV_AUTHOR "\n";
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
MODULE_AUTHOR(DRV_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
#define GEM_MODULE_NAME "gem"
|
|
|
|
|
2014-08-08 20:56:03 +07:00
|
|
|
static const struct pci_device_id gem_pci_tbl[] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_GEM,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
|
|
|
|
/* These models only differ from the original GEM in
|
|
|
|
* that their tx/rx fifos are of a different size and
|
|
|
|
* they only support 10/100 speeds. -DaveM
|
2006-09-14 00:24:59 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Apple's GMAC does support gigabit on machines with
|
|
|
|
* the BCM54xx PHYs. -BenH
|
|
|
|
*/
|
|
|
|
{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_GEM,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
2005-11-14 07:06:48 +07:00
|
|
|
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
|
2005-04-17 05:20:36 +07:00
|
|
|
{0, }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, gem_pci_tbl);
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static u16 __sungem_phy_read(struct gem *gp, int phy_addr, int reg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
u32 cmd;
|
|
|
|
int limit = 10000;
|
|
|
|
|
|
|
|
cmd = (1 << 30);
|
|
|
|
cmd |= (2 << 28);
|
|
|
|
cmd |= (phy_addr << 23) & MIF_FRAME_PHYAD;
|
|
|
|
cmd |= (reg << 18) & MIF_FRAME_REGAD;
|
|
|
|
cmd |= (MIF_FRAME_TAMSB);
|
|
|
|
writel(cmd, gp->regs + MIF_FRAME);
|
|
|
|
|
2009-02-03 12:39:02 +07:00
|
|
|
while (--limit) {
|
2005-04-17 05:20:36 +07:00
|
|
|
cmd = readl(gp->regs + MIF_FRAME);
|
|
|
|
if (cmd & MIF_FRAME_TALSB)
|
|
|
|
break;
|
|
|
|
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!limit)
|
|
|
|
cmd = 0xffff;
|
|
|
|
|
|
|
|
return cmd & MIF_FRAME_DATA;
|
|
|
|
}
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static inline int _sungem_phy_read(struct net_device *dev, int mii_id, int reg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2014-08-28 12:59:26 +07:00
|
|
|
return __sungem_phy_read(gp, mii_id, reg);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static inline u16 sungem_phy_read(struct gem *gp, int reg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2014-08-28 12:59:26 +07:00
|
|
|
return __sungem_phy_read(gp, gp->mii_phy_addr, reg);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static void __sungem_phy_write(struct gem *gp, int phy_addr, int reg, u16 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
u32 cmd;
|
|
|
|
int limit = 10000;
|
|
|
|
|
|
|
|
cmd = (1 << 30);
|
|
|
|
cmd |= (1 << 28);
|
|
|
|
cmd |= (phy_addr << 23) & MIF_FRAME_PHYAD;
|
|
|
|
cmd |= (reg << 18) & MIF_FRAME_REGAD;
|
|
|
|
cmd |= (MIF_FRAME_TAMSB);
|
|
|
|
cmd |= (val & MIF_FRAME_DATA);
|
|
|
|
writel(cmd, gp->regs + MIF_FRAME);
|
|
|
|
|
|
|
|
while (limit--) {
|
|
|
|
cmd = readl(gp->regs + MIF_FRAME);
|
|
|
|
if (cmd & MIF_FRAME_TALSB)
|
|
|
|
break;
|
|
|
|
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static inline void _sungem_phy_write(struct net_device *dev, int mii_id, int reg, int val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2014-08-28 12:59:26 +07:00
|
|
|
__sungem_phy_write(gp, mii_id, reg, val & 0xffff);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2014-08-28 12:59:26 +07:00
|
|
|
static inline void sungem_phy_write(struct gem *gp, int reg, u16 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2014-08-28 12:59:26 +07:00
|
|
|
__sungem_phy_write(gp, gp->mii_phy_addr, reg, val);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void gem_enable_ints(struct gem *gp)
|
|
|
|
{
|
|
|
|
/* Enable all interrupts but TXDONE */
|
|
|
|
writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void gem_disable_ints(struct gem *gp)
|
|
|
|
{
|
|
|
|
/* Disable all interrupts, including TXDONE */
|
|
|
|
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
|
2011-06-01 14:17:10 +07:00
|
|
|
(void)readl(gp->regs + GREG_IMASK); /* write posting */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_get_cell(struct gem *gp)
|
|
|
|
{
|
|
|
|
BUG_ON(gp->cell_enabled < 0);
|
|
|
|
gp->cell_enabled++;
|
|
|
|
#ifdef CONFIG_PPC_PMAC
|
|
|
|
if (gp->cell_enabled == 1) {
|
|
|
|
mb();
|
|
|
|
pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PPC_PMAC */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn off the chip's clock */
|
|
|
|
static void gem_put_cell(struct gem *gp)
|
|
|
|
{
|
|
|
|
BUG_ON(gp->cell_enabled <= 0);
|
|
|
|
gp->cell_enabled--;
|
|
|
|
#ifdef CONFIG_PPC_PMAC
|
|
|
|
if (gp->cell_enabled == 0) {
|
|
|
|
mb();
|
|
|
|
pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PPC_PMAC */
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
static inline void gem_netif_stop(struct gem *gp)
|
|
|
|
{
|
|
|
|
gp->dev->trans_start = jiffies; /* prevent tx timeout */
|
|
|
|
napi_disable(&gp->napi);
|
|
|
|
netif_tx_disable(gp->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void gem_netif_start(struct gem *gp)
|
|
|
|
{
|
|
|
|
/* NOTE: unconditional netif_wake_queue is only
|
|
|
|
* appropriate so long as all callers are assured to
|
|
|
|
* have free tx slots.
|
|
|
|
*/
|
|
|
|
netif_wake_queue(gp->dev);
|
|
|
|
napi_enable(&gp->napi);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_schedule_reset(struct gem *gp)
|
|
|
|
{
|
|
|
|
gp->reset_task_pending = 1;
|
|
|
|
schedule_work(&gp->reset_task);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
|
|
|
|
{
|
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: mif interrupt\n", gp->dev->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_pcs_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 pcs_istat = readl(gp->regs + PCS_ISTAT);
|
|
|
|
u32 pcs_miistat;
|
|
|
|
|
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: pcs interrupt, pcs_istat: 0x%x\n",
|
|
|
|
gp->dev->name, pcs_istat);
|
|
|
|
|
|
|
|
if (!(pcs_istat & PCS_ISTAT_LSC)) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCS irq but no link status change???\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The link status bit latches on zero, so you must
|
|
|
|
* read it twice in such a case to see a transition
|
|
|
|
* to the link being up.
|
|
|
|
*/
|
|
|
|
pcs_miistat = readl(gp->regs + PCS_MIISTAT);
|
|
|
|
if (!(pcs_miistat & PCS_MIISTAT_LS))
|
|
|
|
pcs_miistat |=
|
|
|
|
(readl(gp->regs + PCS_MIISTAT) &
|
|
|
|
PCS_MIISTAT_LS);
|
|
|
|
|
|
|
|
if (pcs_miistat & PCS_MIISTAT_ANC) {
|
|
|
|
/* The remote-fault indication is only valid
|
|
|
|
* when autoneg has completed.
|
|
|
|
*/
|
|
|
|
if (pcs_miistat & PCS_MIISTAT_RF)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(dev, "PCS AutoNEG complete, RemoteFault\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(dev, "PCS AutoNEG complete\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pcs_miistat & PCS_MIISTAT_LS) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(dev, "PCS link is now up\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
netif_carrier_on(gp->dev);
|
|
|
|
} else {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(dev, "PCS link is now down\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
netif_carrier_off(gp->dev);
|
|
|
|
/* If this happens and the link timer is not running,
|
|
|
|
* reset so we re-negotiate.
|
|
|
|
*/
|
|
|
|
if (!timer_pending(&gp->link_timer))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_txmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 txmac_stat = readl(gp->regs + MAC_TXSTAT);
|
|
|
|
|
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: txmac interrupt, txmac_stat: 0x%x\n",
|
|
|
|
gp->dev->name, txmac_stat);
|
|
|
|
|
|
|
|
/* Defer timer expiration is quite normal,
|
|
|
|
* don't even log the event.
|
|
|
|
*/
|
|
|
|
if ((txmac_stat & MAC_TXSTAT_DTE) &&
|
|
|
|
!(txmac_stat & ~MAC_TXSTAT_DTE))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (txmac_stat & MAC_TXSTAT_URUN) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "TX MAC xmit underrun\n");
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_fifo_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (txmac_stat & MAC_TXSTAT_MPE) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "TX MAC max packet size error\n");
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The rest are all cases of one of the 16-bit TX
|
|
|
|
* counters expiring.
|
|
|
|
*/
|
|
|
|
if (txmac_stat & MAC_TXSTAT_NCE)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.collisions += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (txmac_stat & MAC_TXSTAT_ECE) {
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_aborted_errors += 0x10000;
|
|
|
|
dev->stats.collisions += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (txmac_stat & MAC_TXSTAT_LCE) {
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_aborted_errors += 0x10000;
|
|
|
|
dev->stats.collisions += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We do not keep track of MAC_TXSTAT_FCE and
|
|
|
|
* MAC_TXSTAT_PCE events.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When we get a RX fifo overflow, the RX unit in GEM is probably hung
|
|
|
|
* so we do the following.
|
|
|
|
*
|
|
|
|
* If any part of the reset goes wrong, we return 1 and that causes the
|
|
|
|
* whole chip to be reset.
|
|
|
|
*/
|
|
|
|
static int gem_rxmac_reset(struct gem *gp)
|
|
|
|
{
|
|
|
|
struct net_device *dev = gp->dev;
|
|
|
|
int limit, i;
|
|
|
|
u64 desc_dma;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* First, reset & disable MAC RX. */
|
|
|
|
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
|
|
|
|
for (limit = 0; limit < 5000; limit++) {
|
|
|
|
if (!(readl(gp->regs + MAC_RXRST) & MAC_RXRST_CMD))
|
|
|
|
break;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
if (limit == 5000) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "RX MAC will not reset, resetting whole chip\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(gp->mac_rx_cfg & ~MAC_RXCFG_ENAB,
|
|
|
|
gp->regs + MAC_RXCFG);
|
|
|
|
for (limit = 0; limit < 5000; limit++) {
|
|
|
|
if (!(readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB))
|
|
|
|
break;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
if (limit == 5000) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "RX MAC will not disable, resetting whole chip\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Second, disable RX DMA. */
|
|
|
|
writel(0, gp->regs + RXDMA_CFG);
|
|
|
|
for (limit = 0; limit < 5000; limit++) {
|
|
|
|
if (!(readl(gp->regs + RXDMA_CFG) & RXDMA_CFG_ENABLE))
|
|
|
|
break;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
if (limit == 5000) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "RX DMA will not disable, resetting whole chip\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-04-20 17:56:15 +07:00
|
|
|
mdelay(5);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Execute RX reset command. */
|
|
|
|
writel(gp->swrst_base | GREG_SWRST_RXRST,
|
|
|
|
gp->regs + GREG_SWRST);
|
|
|
|
for (limit = 0; limit < 5000; limit++) {
|
|
|
|
if (!(readl(gp->regs + GREG_SWRST) & GREG_SWRST_RXRST))
|
|
|
|
break;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
if (limit == 5000) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "RX reset command will not execute, resetting whole chip\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Refresh the RX ring. */
|
|
|
|
for (i = 0; i < RX_RING_SIZE; i++) {
|
|
|
|
struct gem_rxd *rxd = &gp->init_block->rxd[i];
|
|
|
|
|
|
|
|
if (gp->rx_skbs[i] == NULL) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "Parts of RX ring empty, resetting whole chip\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
|
|
|
|
}
|
|
|
|
gp->rx_new = gp->rx_old = 0;
|
|
|
|
|
|
|
|
/* Now we must reprogram the rest of RX unit. */
|
|
|
|
desc_dma = (u64) gp->gblock_dvma;
|
|
|
|
desc_dma += (INIT_BLOCK_TX_RING_SIZE * sizeof(struct gem_txd));
|
|
|
|
writel(desc_dma >> 32, gp->regs + RXDMA_DBHI);
|
|
|
|
writel(desc_dma & 0xffffffff, gp->regs + RXDMA_DBLOW);
|
|
|
|
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
|
|
|
|
val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) |
|
|
|
|
((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128);
|
|
|
|
writel(val, gp->regs + RXDMA_CFG);
|
|
|
|
if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN)
|
|
|
|
writel(((5 & RXDMA_BLANK_IPKTS) |
|
|
|
|
((8 << 12) & RXDMA_BLANK_ITIME)),
|
|
|
|
gp->regs + RXDMA_BLANK);
|
|
|
|
else
|
|
|
|
writel(((5 & RXDMA_BLANK_IPKTS) |
|
|
|
|
((4 << 12) & RXDMA_BLANK_ITIME)),
|
|
|
|
gp->regs + RXDMA_BLANK);
|
|
|
|
val = (((gp->rx_pause_off / 64) << 0) & RXDMA_PTHRESH_OFF);
|
|
|
|
val |= (((gp->rx_pause_on / 64) << 12) & RXDMA_PTHRESH_ON);
|
|
|
|
writel(val, gp->regs + RXDMA_PTHRESH);
|
|
|
|
val = readl(gp->regs + RXDMA_CFG);
|
|
|
|
writel(val | RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
|
|
|
|
writel(MAC_RXSTAT_RCV, gp->regs + MAC_RXMASK);
|
|
|
|
val = readl(gp->regs + MAC_RXCFG);
|
|
|
|
writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_rxmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 rxmac_stat = readl(gp->regs + MAC_RXSTAT);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: rxmac interrupt, rxmac_stat: 0x%x\n",
|
|
|
|
gp->dev->name, rxmac_stat);
|
|
|
|
|
|
|
|
if (rxmac_stat & MAC_RXSTAT_OFLW) {
|
|
|
|
u32 smac = readl(gp->regs + MAC_SMACHINE);
|
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "RX MAC fifo overflow smac[%08x]\n", smac);
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_over_errors++;
|
|
|
|
dev->stats.rx_fifo_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ret = gem_rxmac_reset(gp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rxmac_stat & MAC_RXSTAT_ACE)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_frame_errors += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (rxmac_stat & MAC_RXSTAT_CCE)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_crc_errors += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (rxmac_stat & MAC_RXSTAT_LCE)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_length_errors += 0x10000;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* We do not track MAC_RXSTAT_FCE and MAC_RXSTAT_VCE
|
|
|
|
* events.
|
|
|
|
*/
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_mac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 mac_cstat = readl(gp->regs + MAC_CSTAT);
|
|
|
|
|
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: mac interrupt, mac_cstat: 0x%x\n",
|
|
|
|
gp->dev->name, mac_cstat);
|
|
|
|
|
|
|
|
/* This interrupt is just for pause frame and pause
|
|
|
|
* tracking. It is useful for diagnostics and debug
|
|
|
|
* but probably by default we will mask these events.
|
|
|
|
*/
|
|
|
|
if (mac_cstat & MAC_CSTAT_PS)
|
|
|
|
gp->pause_entered++;
|
|
|
|
|
|
|
|
if (mac_cstat & MAC_CSTAT_PRCV)
|
|
|
|
gp->pause_last_time_recvd = (mac_cstat >> 16);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_mif_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 mif_status = readl(gp->regs + MIF_STATUS);
|
|
|
|
u32 reg_val, changed_bits;
|
|
|
|
|
|
|
|
reg_val = (mif_status & MIF_STATUS_DATA) >> 16;
|
|
|
|
changed_bits = (mif_status & MIF_STATUS_STAT);
|
|
|
|
|
|
|
|
gem_handle_mif_event(gp, reg_val, changed_bits);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_pci_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
u32 pci_estat = readl(gp->regs + GREG_PCIESTAT);
|
|
|
|
|
|
|
|
if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
|
|
|
|
gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI error [%04x]", pci_estat);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (pci_estat & GREG_PCIESTAT_BADACK)
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_cont(" <No ACK64# during ABS64 cycle>");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_estat & GREG_PCIESTAT_DTRTO)
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_cont(" <Delayed transaction timeout>");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_estat & GREG_PCIESTAT_OTHER)
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_cont(" <other>");
|
|
|
|
pr_cont("\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
} else {
|
|
|
|
pci_estat |= GREG_PCIESTAT_OTHER;
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI error\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pci_estat & GREG_PCIESTAT_OTHER) {
|
|
|
|
u16 pci_cfg_stat;
|
|
|
|
|
|
|
|
/* Interrogate PCI config space for the
|
|
|
|
* true cause.
|
|
|
|
*/
|
|
|
|
pci_read_config_word(gp->pdev, PCI_STATUS,
|
|
|
|
&pci_cfg_stat);
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "Read PCI cfg space status [%04x]\n",
|
|
|
|
pci_cfg_stat);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_PARITY)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI parity error detected\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_SIG_TARGET_ABORT)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI target abort\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_REC_TARGET_ABORT)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI master acks target abort\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_REC_MASTER_ABORT)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI master abort\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_SIG_SYSTEM_ERROR)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI system error SERR#\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (pci_cfg_stat & PCI_STATUS_DETECTED_PARITY)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "PCI parity error\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Write the error bits back to clear them. */
|
|
|
|
pci_cfg_stat &= (PCI_STATUS_PARITY |
|
|
|
|
PCI_STATUS_SIG_TARGET_ABORT |
|
|
|
|
PCI_STATUS_REC_TARGET_ABORT |
|
|
|
|
PCI_STATUS_REC_MASTER_ABORT |
|
|
|
|
PCI_STATUS_SIG_SYSTEM_ERROR |
|
|
|
|
PCI_STATUS_DETECTED_PARITY);
|
|
|
|
pci_write_config_word(gp->pdev,
|
|
|
|
PCI_STATUS, pci_cfg_stat);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For all PCI errors, we should reset the chip. */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All non-normal interrupt conditions get serviced here.
|
|
|
|
* Returns non-zero if we should just exit the interrupt
|
|
|
|
* handler right now (ie. if we reset the card which invalidates
|
|
|
|
* all of the other original irq status bits).
|
|
|
|
*/
|
|
|
|
static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
if (gem_status & GREG_STAT_RXNOBUF) {
|
|
|
|
/* Frame arrived, no free RX buffers available. */
|
|
|
|
if (netif_msg_rx_err(gp))
|
|
|
|
printk(KERN_DEBUG "%s: no buffer for rx frame\n",
|
|
|
|
gp->dev->name);
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_dropped++;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_RXTAGERR) {
|
|
|
|
/* corrupt RX tag framing */
|
|
|
|
if (netif_msg_rx_err(gp))
|
|
|
|
printk(KERN_DEBUG "%s: corrupt rx tag framing\n",
|
|
|
|
gp->dev->name);
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_PCS) {
|
|
|
|
if (gem_pcs_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_TXMAC) {
|
|
|
|
if (gem_txmac_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_RXMAC) {
|
|
|
|
if (gem_rxmac_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_MAC) {
|
|
|
|
if (gem_mac_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_MIF) {
|
|
|
|
if (gem_mif_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gem_status & GREG_STAT_PCIERR) {
|
|
|
|
if (gem_pci_interrupt(dev, gp, gem_status))
|
2011-06-01 14:17:10 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
|
|
{
|
|
|
|
int entry, limit;
|
|
|
|
|
|
|
|
entry = gp->tx_old;
|
|
|
|
limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT);
|
|
|
|
while (entry != limit) {
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct gem_txd *txd;
|
|
|
|
dma_addr_t dma_addr;
|
|
|
|
u32 dma_len;
|
|
|
|
int frag;
|
|
|
|
|
|
|
|
if (netif_msg_tx_done(gp))
|
|
|
|
printk(KERN_DEBUG "%s: tx done, slot %d\n",
|
|
|
|
gp->dev->name, entry);
|
|
|
|
skb = gp->tx_skbs[entry];
|
|
|
|
if (skb_shinfo(skb)->nr_frags) {
|
|
|
|
int last = entry + skb_shinfo(skb)->nr_frags;
|
|
|
|
int walk = entry;
|
|
|
|
int incomplete = 0;
|
|
|
|
|
|
|
|
last &= (TX_RING_SIZE - 1);
|
|
|
|
for (;;) {
|
|
|
|
walk = NEXT_TX(walk);
|
|
|
|
if (walk == limit)
|
|
|
|
incomplete = 1;
|
|
|
|
if (walk == last)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (incomplete)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gp->tx_skbs[entry] = NULL;
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_bytes += skb->len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
|
|
|
|
txd = &gp->init_block->txd[entry];
|
|
|
|
|
|
|
|
dma_addr = le64_to_cpu(txd->buffer);
|
|
|
|
dma_len = le64_to_cpu(txd->control_word) & TXDCTRL_BUFSZ;
|
|
|
|
|
|
|
|
pci_unmap_page(gp->pdev, dma_addr, dma_len, PCI_DMA_TODEVICE);
|
|
|
|
entry = NEXT_TX(entry);
|
|
|
|
}
|
|
|
|
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.tx_packets++;
|
2014-03-16 08:13:13 +07:00
|
|
|
dev_consume_skb_any(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
gp->tx_old = entry;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Need to make the tx_old update visible to gem_start_xmit()
|
|
|
|
* before checking for netif_queue_stopped(). Without the
|
|
|
|
* memory barrier, there is a small possibility that gem_start_xmit()
|
|
|
|
* will miss it and cause the queue to be stopped forever.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
|
|
|
|
if (unlikely(netif_queue_stopped(dev) &&
|
|
|
|
TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))) {
|
|
|
|
struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
|
|
|
|
|
|
|
|
__netif_tx_lock(txq, smp_processor_id());
|
|
|
|
if (netif_queue_stopped(dev) &&
|
|
|
|
TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
__netif_tx_unlock(txq);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static __inline__ void gem_post_rxds(struct gem *gp, int limit)
|
|
|
|
{
|
|
|
|
int cluster_start, curr, count, kick;
|
|
|
|
|
|
|
|
cluster_start = curr = (gp->rx_new & ~(4 - 1));
|
|
|
|
count = 0;
|
|
|
|
kick = -1;
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
while (curr != limit) {
|
|
|
|
curr = NEXT_RX(curr);
|
|
|
|
if (++count == 4) {
|
|
|
|
struct gem_rxd *rxd =
|
|
|
|
&gp->init_block->rxd[cluster_start];
|
|
|
|
for (;;) {
|
|
|
|
rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
|
|
|
|
rxd++;
|
|
|
|
cluster_start = NEXT_RX(cluster_start);
|
|
|
|
if (cluster_start == curr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
kick = curr;
|
|
|
|
count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (kick >= 0) {
|
|
|
|
mb();
|
|
|
|
writel(kick, gp->regs + RXDMA_KICK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
#define ALIGNED_RX_SKB_ADDR(addr) \
|
|
|
|
((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
|
|
|
|
static __inline__ struct sk_buff *gem_alloc_skb(struct net_device *dev, int size,
|
|
|
|
gfp_t gfp_flags)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb = alloc_skb(size + 64, gfp_flags);
|
|
|
|
|
|
|
|
if (likely(skb)) {
|
|
|
|
unsigned long offset = ALIGNED_RX_SKB_ADDR(skb->data);
|
|
|
|
skb_reserve(skb, offset);
|
|
|
|
}
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int gem_rx(struct gem *gp, int work_to_do)
|
|
|
|
{
|
2011-01-27 16:54:12 +07:00
|
|
|
struct net_device *dev = gp->dev;
|
2005-04-17 05:20:36 +07:00
|
|
|
int entry, drops, work_done = 0;
|
|
|
|
u32 done;
|
2007-12-17 13:48:04 +07:00
|
|
|
__sum16 csum;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (netif_msg_rx_status(gp))
|
|
|
|
printk(KERN_DEBUG "%s: rx interrupt, done: %d, rx_new: %d\n",
|
|
|
|
gp->dev->name, readl(gp->regs + RXDMA_DONE), gp->rx_new);
|
|
|
|
|
|
|
|
entry = gp->rx_new;
|
|
|
|
drops = 0;
|
|
|
|
done = readl(gp->regs + RXDMA_DONE);
|
|
|
|
for (;;) {
|
|
|
|
struct gem_rxd *rxd = &gp->init_block->rxd[entry];
|
|
|
|
struct sk_buff *skb;
|
2007-12-17 13:48:04 +07:00
|
|
|
u64 status = le64_to_cpu(rxd->status_word);
|
2005-04-17 05:20:36 +07:00
|
|
|
dma_addr_t dma_addr;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if ((status & RXDCTRL_OWN) != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (work_done >= RX_RING_SIZE || work_done >= work_to_do)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* When writing back RX descriptor, GEM writes status
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
* then buffer address, possibly in separate transactions.
|
2005-04-17 05:20:36 +07:00
|
|
|
* If we don't wait for the chip to write both, we could
|
|
|
|
* post a new buffer to this descriptor then have GEM spam
|
|
|
|
* on the buffer address. We sync on the RX completion
|
|
|
|
* register to prevent this from happening.
|
|
|
|
*/
|
|
|
|
if (entry == done) {
|
|
|
|
done = readl(gp->regs + RXDMA_DONE);
|
|
|
|
if (entry == done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We can now account for the work we're about to do */
|
|
|
|
work_done++;
|
|
|
|
|
|
|
|
skb = gp->rx_skbs[entry];
|
|
|
|
|
|
|
|
len = (status & RXDCTRL_BUFSZ) >> 16;
|
|
|
|
if ((len < ETH_ZLEN) || (status & RXDCTRL_BAD)) {
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (len < ETH_ZLEN)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_length_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (len & RXDCTRL_BAD)
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_crc_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* We'll just return it to GEM. */
|
|
|
|
drop_it:
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_dropped++;
|
2005-04-17 05:20:36 +07:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
2007-12-17 13:48:04 +07:00
|
|
|
dma_addr = le64_to_cpu(rxd->buffer);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (len > RX_COPY_THRESHOLD) {
|
|
|
|
struct sk_buff *new_skb;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
new_skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (new_skb == NULL) {
|
|
|
|
drops++;
|
|
|
|
goto drop_it;
|
|
|
|
}
|
|
|
|
pci_unmap_page(gp->pdev, dma_addr,
|
|
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
gp->rx_skbs[entry] = new_skb;
|
|
|
|
skb_put(new_skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
|
|
rxd->buffer = cpu_to_le64(pci_map_page(gp->pdev,
|
|
|
|
virt_to_page(new_skb->data),
|
|
|
|
offset_in_page(new_skb->data),
|
|
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
|
|
PCI_DMA_FROMDEVICE));
|
|
|
|
skb_reserve(new_skb, RX_OFFSET);
|
|
|
|
|
|
|
|
/* Trim the original skb for the netif. */
|
|
|
|
skb_trim(skb, len);
|
|
|
|
} else {
|
2011-06-01 14:17:10 +07:00
|
|
|
struct sk_buff *copy_skb = netdev_alloc_skb(dev, len + 2);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (copy_skb == NULL) {
|
|
|
|
drops++;
|
|
|
|
goto drop_it;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_reserve(copy_skb, 2);
|
|
|
|
skb_put(copy_skb, len);
|
|
|
|
pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
|
2007-03-28 04:55:52 +07:00
|
|
|
skb_copy_from_linear_data(skb, copy_skb->data, len);
|
2005-04-17 05:20:36 +07:00
|
|
|
pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
|
|
|
|
|
|
|
|
/* We'll reuse the original ring buffer. */
|
|
|
|
skb = copy_skb;
|
|
|
|
}
|
|
|
|
|
2007-12-17 13:48:04 +07:00
|
|
|
csum = (__force __sum16)htons((status & RXDCTRL_TCPCSUM) ^ 0xffff);
|
|
|
|
skb->csum = csum_unfold(csum);
|
2006-08-30 06:44:56 +07:00
|
|
|
skb->ip_summed = CHECKSUM_COMPLETE;
|
2005-04-17 05:20:36 +07:00
|
|
|
skb->protocol = eth_type_trans(skb, gp->dev);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
napi_gro_receive(&gp->napi, skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-01-27 16:54:12 +07:00
|
|
|
dev->stats.rx_packets++;
|
|
|
|
dev->stats.rx_bytes += len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
next:
|
|
|
|
entry = NEXT_RX(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
gem_post_rxds(gp, entry);
|
|
|
|
|
|
|
|
gp->rx_new = entry;
|
|
|
|
|
|
|
|
if (drops)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(gp->dev, "Memory squeeze, deferring packet\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return work_done;
|
|
|
|
}
|
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
static int gem_poll(struct napi_struct *napi, int budget)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
struct gem *gp = container_of(napi, struct gem, napi);
|
|
|
|
struct net_device *dev = gp->dev;
|
|
|
|
int work_done;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
work_done = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
/* Handle anomalies */
|
2011-06-01 14:17:10 +07:00
|
|
|
if (unlikely(gp->status & GREG_STAT_ABNORMAL)) {
|
|
|
|
struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
|
|
|
|
int reset;
|
|
|
|
|
|
|
|
/* We run the abnormal interrupt handling code with
|
|
|
|
* the Tx lock. It only resets the Rx portion of the
|
|
|
|
* chip, but we need to guard it against DMA being
|
|
|
|
* restarted by the link poll timer
|
|
|
|
*/
|
|
|
|
__netif_tx_lock(txq, smp_processor_id());
|
|
|
|
reset = gem_abnormal_irq(dev, gp, gp->status);
|
|
|
|
__netif_tx_unlock(txq);
|
|
|
|
if (reset) {
|
|
|
|
gem_schedule_reset(gp);
|
|
|
|
napi_complete(napi);
|
|
|
|
return work_done;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Run TX completion thread */
|
|
|
|
gem_tx(dev, gp, gp->status);
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
/* Run RX thread. We don't use any locking here,
|
|
|
|
* code willing to do bad things - like cleaning the
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
* rx ring - must call napi_disable(), which
|
2005-04-17 05:20:36 +07:00
|
|
|
* schedule_timeout()'s if polling is already disabled.
|
|
|
|
*/
|
2008-03-23 17:35:12 +07:00
|
|
|
work_done += gem_rx(gp, budget - work_done);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
if (work_done >= budget)
|
|
|
|
return work_done;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
gp->status = readl(gp->regs + GREG_STAT);
|
|
|
|
} while (gp->status & GREG_STAT_NAPI);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
napi_complete(napi);
|
2005-04-17 05:20:36 +07:00
|
|
|
gem_enable_ints(gp);
|
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
return work_done;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 20:55:46 +07:00
|
|
|
static irqreturn_t gem_interrupt(int irq, void *dev_id)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct net_device *dev = dev_id;
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2009-01-20 07:43:59 +07:00
|
|
|
if (napi_schedule_prep(&gp->napi)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 gem_status = readl(gp->regs + GREG_STAT);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
if (unlikely(gem_status == 0)) {
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
napi_enable(&gp->napi);
|
2005-04-17 05:20:36 +07:00
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
2011-06-01 14:17:10 +07:00
|
|
|
if (netif_msg_intr(gp))
|
|
|
|
printk(KERN_DEBUG "%s: gem_interrupt() gem_status: 0x%x\n",
|
|
|
|
gp->dev->name, gem_status);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->status = gem_status;
|
|
|
|
gem_disable_ints(gp);
|
2009-01-20 07:43:59 +07:00
|
|
|
__napi_schedule(&gp->napi);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If polling was disabled at the time we received that
|
2006-09-14 00:24:59 +07:00
|
|
|
* interrupt, we may return IRQ_HANDLED here while we
|
2005-04-17 05:20:36 +07:00
|
|
|
* should return IRQ_NONE. No big deal...
|
|
|
|
*/
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
static void gem_poll_controller(struct net_device *dev)
|
|
|
|
{
|
2011-06-01 14:17:10 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
|
|
|
|
|
|
|
disable_irq(gp->pdev->irq);
|
|
|
|
gem_interrupt(gp->pdev->irq, dev);
|
|
|
|
enable_irq(gp->pdev->irq);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void gem_tx_timeout(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "transmit timed out, resetting\n");
|
2011-06-01 14:17:10 +07:00
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "TX_STATE[%08x:%08x:%08x]\n",
|
|
|
|
readl(gp->regs + TXDMA_CFG),
|
|
|
|
readl(gp->regs + MAC_TXSTAT),
|
|
|
|
readl(gp->regs + MAC_TXCFG));
|
|
|
|
netdev_err(dev, "RX_STATE[%08x:%08x:%08x]\n",
|
|
|
|
readl(gp->regs + RXDMA_CFG),
|
|
|
|
readl(gp->regs + MAC_RXSTAT),
|
|
|
|
readl(gp->regs + MAC_RXCFG));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
gem_schedule_reset(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static __inline__ int gem_intme(int entry)
|
|
|
|
{
|
|
|
|
/* Algorithm: IRQ every 1/2 of descriptors. */
|
|
|
|
if (!(entry & ((TX_RING_SIZE>>1)-1)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-01 02:50:58 +07:00
|
|
|
static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
int entry;
|
|
|
|
u64 ctrl;
|
|
|
|
|
|
|
|
ctrl = 0;
|
2006-08-30 06:44:56 +07:00
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
2010-12-14 22:24:08 +07:00
|
|
|
const u64 csum_start_off = skb_checksum_start_offset(skb);
|
2007-04-26 07:55:53 +07:00
|
|
|
const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ctrl = (TXDCTRL_CENAB |
|
|
|
|
(csum_start_off << 15) |
|
|
|
|
(csum_stuff_off << 21));
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
if (unlikely(TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1))) {
|
|
|
|
/* This is a hard error, log it. */
|
|
|
|
if (!netif_queue_stopped(dev)) {
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = gp->tx_new;
|
|
|
|
gp->tx_skbs[entry] = skb;
|
|
|
|
|
|
|
|
if (skb_shinfo(skb)->nr_frags == 0) {
|
|
|
|
struct gem_txd *txd = &gp->init_block->txd[entry];
|
|
|
|
dma_addr_t mapping;
|
|
|
|
u32 len;
|
|
|
|
|
|
|
|
len = skb->len;
|
|
|
|
mapping = pci_map_page(gp->pdev,
|
|
|
|
virt_to_page(skb->data),
|
|
|
|
offset_in_page(skb->data),
|
|
|
|
len, PCI_DMA_TODEVICE);
|
|
|
|
ctrl |= TXDCTRL_SOF | TXDCTRL_EOF | len;
|
|
|
|
if (gem_intme(entry))
|
|
|
|
ctrl |= TXDCTRL_INTME;
|
|
|
|
txd->buffer = cpu_to_le64(mapping);
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
txd->control_word = cpu_to_le64(ctrl);
|
|
|
|
entry = NEXT_TX(entry);
|
|
|
|
} else {
|
|
|
|
struct gem_txd *txd;
|
|
|
|
u32 first_len;
|
|
|
|
u64 intme;
|
|
|
|
dma_addr_t first_mapping;
|
|
|
|
int frag, first_entry = entry;
|
|
|
|
|
|
|
|
intme = 0;
|
|
|
|
if (gem_intme(entry))
|
|
|
|
intme |= TXDCTRL_INTME;
|
|
|
|
|
|
|
|
/* We must give this initial chunk to the device last.
|
|
|
|
* Otherwise we could race with the device.
|
|
|
|
*/
|
|
|
|
first_len = skb_headlen(skb);
|
|
|
|
first_mapping = pci_map_page(gp->pdev, virt_to_page(skb->data),
|
|
|
|
offset_in_page(skb->data),
|
|
|
|
first_len, PCI_DMA_TODEVICE);
|
|
|
|
entry = NEXT_TX(entry);
|
|
|
|
|
|
|
|
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
|
2011-10-19 04:00:24 +07:00
|
|
|
const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 len;
|
|
|
|
dma_addr_t mapping;
|
|
|
|
u64 this_ctrl;
|
|
|
|
|
2011-10-19 04:00:24 +07:00
|
|
|
len = skb_frag_size(this_frag);
|
2011-09-22 04:53:21 +07:00
|
|
|
mapping = skb_frag_dma_map(&gp->pdev->dev, this_frag,
|
2011-10-06 17:10:48 +07:00
|
|
|
0, len, DMA_TO_DEVICE);
|
2005-04-17 05:20:36 +07:00
|
|
|
this_ctrl = ctrl;
|
|
|
|
if (frag == skb_shinfo(skb)->nr_frags - 1)
|
|
|
|
this_ctrl |= TXDCTRL_EOF;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
txd = &gp->init_block->txd[entry];
|
|
|
|
txd->buffer = cpu_to_le64(mapping);
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
txd->control_word = cpu_to_le64(this_ctrl | len);
|
|
|
|
|
|
|
|
if (gem_intme(entry))
|
|
|
|
intme |= TXDCTRL_INTME;
|
|
|
|
|
|
|
|
entry = NEXT_TX(entry);
|
|
|
|
}
|
|
|
|
txd = &gp->init_block->txd[first_entry];
|
|
|
|
txd->buffer = cpu_to_le64(first_mapping);
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
txd->control_word =
|
|
|
|
cpu_to_le64(ctrl | TXDCTRL_SOF | intme | first_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
gp->tx_new = entry;
|
2011-06-01 14:17:10 +07:00
|
|
|
if (unlikely(TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* netif_stop_queue() must be done before checking
|
|
|
|
* checking tx index in TX_BUFFS_AVAIL() below, because
|
|
|
|
* in gem_tx(), we update tx_old before checking for
|
|
|
|
* netif_queue_stopped().
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
if (TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
if (netif_msg_tx_queued(gp))
|
|
|
|
printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
|
|
|
|
dev->name, entry, skb->len);
|
|
|
|
mb();
|
|
|
|
writel(gp->tx_new, gp->regs + TXDMA_KICK);
|
|
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2008-12-09 15:10:11 +07:00
|
|
|
static void gem_pcs_reset(struct gem *gp)
|
|
|
|
{
|
|
|
|
int limit;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Reset PCS unit. */
|
|
|
|
val = readl(gp->regs + PCS_MIICTRL);
|
|
|
|
val |= PCS_MIICTRL_RST;
|
|
|
|
writel(val, gp->regs + PCS_MIICTRL);
|
|
|
|
|
|
|
|
limit = 32;
|
|
|
|
while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) {
|
|
|
|
udelay(100);
|
|
|
|
if (limit-- <= 0)
|
|
|
|
break;
|
|
|
|
}
|
2009-02-16 11:02:04 +07:00
|
|
|
if (limit < 0)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_warn(gp->dev, "PCS reset bit would not clear\n");
|
2008-12-09 15:10:11 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_pcs_reinit_adv(struct gem *gp)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Make sure PCS is disabled while changing advertisement
|
|
|
|
* configuration.
|
|
|
|
*/
|
|
|
|
val = readl(gp->regs + PCS_CFG);
|
|
|
|
val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO);
|
|
|
|
writel(val, gp->regs + PCS_CFG);
|
|
|
|
|
2011-03-31 08:57:33 +07:00
|
|
|
/* Advertise all capabilities except asymmetric
|
2008-12-09 15:10:11 +07:00
|
|
|
* pause.
|
|
|
|
*/
|
|
|
|
val = readl(gp->regs + PCS_MIIADV);
|
|
|
|
val |= (PCS_MIIADV_FD | PCS_MIIADV_HD |
|
|
|
|
PCS_MIIADV_SP | PCS_MIIADV_AP);
|
|
|
|
writel(val, gp->regs + PCS_MIIADV);
|
|
|
|
|
|
|
|
/* Enable and restart auto-negotiation, disable wrapback/loopback,
|
|
|
|
* and re-enable PCS.
|
|
|
|
*/
|
|
|
|
val = readl(gp->regs + PCS_MIICTRL);
|
|
|
|
val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE);
|
|
|
|
val &= ~PCS_MIICTRL_WB;
|
|
|
|
writel(val, gp->regs + PCS_MIICTRL);
|
|
|
|
|
|
|
|
val = readl(gp->regs + PCS_CFG);
|
|
|
|
val |= PCS_CFG_ENABLE;
|
|
|
|
writel(val, gp->regs + PCS_CFG);
|
|
|
|
|
|
|
|
/* Make sure serialink loopback is off. The meaning
|
|
|
|
* of this bit is logically inverted based upon whether
|
|
|
|
* you are in Serialink or SERDES mode.
|
|
|
|
*/
|
|
|
|
val = readl(gp->regs + PCS_SCTRL);
|
|
|
|
if (gp->phy_type == phy_serialink)
|
|
|
|
val &= ~PCS_SCTRL_LOOP;
|
|
|
|
else
|
|
|
|
val |= PCS_SCTRL_LOOP;
|
|
|
|
writel(val, gp->regs + PCS_SCTRL);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define STOP_TRIES 32
|
|
|
|
|
|
|
|
static void gem_reset(struct gem *gp)
|
|
|
|
{
|
|
|
|
int limit;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Make sure we won't get any more interrupts */
|
|
|
|
writel(0xffffffff, gp->regs + GREG_IMASK);
|
|
|
|
|
|
|
|
/* Reset the chip */
|
|
|
|
writel(gp->swrst_base | GREG_SWRST_TXRST | GREG_SWRST_RXRST,
|
|
|
|
gp->regs + GREG_SWRST);
|
|
|
|
|
|
|
|
limit = STOP_TRIES;
|
|
|
|
|
|
|
|
do {
|
|
|
|
udelay(20);
|
|
|
|
val = readl(gp->regs + GREG_SWRST);
|
|
|
|
if (limit-- <= 0)
|
|
|
|
break;
|
|
|
|
} while (val & (GREG_SWRST_TXRST | GREG_SWRST_RXRST));
|
|
|
|
|
2009-03-04 15:08:39 +07:00
|
|
|
if (limit < 0)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(gp->dev, "SW reset is ghetto\n");
|
2008-12-09 15:10:11 +07:00
|
|
|
|
|
|
|
if (gp->phy_type == phy_serialink || gp->phy_type == phy_serdes)
|
|
|
|
gem_pcs_reinit_adv(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_start_dma(struct gem *gp)
|
|
|
|
{
|
|
|
|
u32 val;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* We are ready to rock, turn everything on. */
|
|
|
|
val = readl(gp->regs + TXDMA_CFG);
|
|
|
|
writel(val | TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG);
|
|
|
|
val = readl(gp->regs + RXDMA_CFG);
|
|
|
|
writel(val | RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
|
|
|
|
val = readl(gp->regs + MAC_TXCFG);
|
|
|
|
writel(val | MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG);
|
|
|
|
val = readl(gp->regs + MAC_RXCFG);
|
|
|
|
writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
|
|
|
|
|
|
|
|
(void) readl(gp->regs + MAC_RXCFG);
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
gem_enable_ints(gp);
|
|
|
|
|
|
|
|
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* DMA won't be actually stopped before about 4ms tho ...
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
static void gem_stop_dma(struct gem *gp)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* We are done rocking, turn everything off. */
|
|
|
|
val = readl(gp->regs + TXDMA_CFG);
|
|
|
|
writel(val & ~TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG);
|
|
|
|
val = readl(gp->regs + RXDMA_CFG);
|
|
|
|
writel(val & ~RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
|
|
|
|
val = readl(gp->regs + MAC_TXCFG);
|
|
|
|
writel(val & ~MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG);
|
|
|
|
val = readl(gp->regs + MAC_RXCFG);
|
|
|
|
writel(val & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
|
|
|
|
|
|
|
|
(void) readl(gp->regs + MAC_RXCFG);
|
|
|
|
|
|
|
|
/* Need to wait a bit ... done by the caller */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// XXX dbl check what that function should do when called on PCS PHY
|
|
|
|
static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
|
|
|
|
{
|
|
|
|
u32 advertise, features;
|
|
|
|
int autoneg;
|
|
|
|
int speed;
|
|
|
|
int duplex;
|
|
|
|
|
|
|
|
if (gp->phy_type != phy_mii_mdio0 &&
|
|
|
|
gp->phy_type != phy_mii_mdio1)
|
|
|
|
goto non_mii;
|
|
|
|
|
|
|
|
/* Setup advertise */
|
|
|
|
if (found_mii_phy(gp))
|
|
|
|
features = gp->phy_mii.def->features;
|
|
|
|
else
|
|
|
|
features = 0;
|
|
|
|
|
|
|
|
advertise = features & ADVERTISE_MASK;
|
|
|
|
if (gp->phy_mii.advertising != 0)
|
|
|
|
advertise &= gp->phy_mii.advertising;
|
|
|
|
|
|
|
|
autoneg = gp->want_autoneg;
|
|
|
|
speed = gp->phy_mii.speed;
|
|
|
|
duplex = gp->phy_mii.duplex;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Setup link parameters */
|
|
|
|
if (!ep)
|
|
|
|
goto start_aneg;
|
|
|
|
if (ep->autoneg == AUTONEG_ENABLE) {
|
|
|
|
advertise = ep->advertising;
|
|
|
|
autoneg = 1;
|
|
|
|
} else {
|
|
|
|
autoneg = 0;
|
2011-04-28 01:32:39 +07:00
|
|
|
speed = ethtool_cmd_speed(ep);
|
2005-04-17 05:20:36 +07:00
|
|
|
duplex = ep->duplex;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_aneg:
|
|
|
|
/* Sanitize settings based on PHY capabilities */
|
|
|
|
if ((features & SUPPORTED_Autoneg) == 0)
|
|
|
|
autoneg = 0;
|
|
|
|
if (speed == SPEED_1000 &&
|
|
|
|
!(features & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)))
|
|
|
|
speed = SPEED_100;
|
|
|
|
if (speed == SPEED_100 &&
|
|
|
|
!(features & (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full)))
|
|
|
|
speed = SPEED_10;
|
|
|
|
if (duplex == DUPLEX_FULL &&
|
|
|
|
!(features & (SUPPORTED_1000baseT_Full |
|
|
|
|
SUPPORTED_100baseT_Full |
|
|
|
|
SUPPORTED_10baseT_Full)))
|
|
|
|
duplex = DUPLEX_HALF;
|
|
|
|
if (speed == 0)
|
|
|
|
speed = SPEED_10;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* If we are asleep, we don't try to actually setup the PHY, we
|
|
|
|
* just store the settings
|
|
|
|
*/
|
2011-06-01 14:17:10 +07:00
|
|
|
if (!netif_device_present(gp->dev)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
|
|
|
|
gp->phy_mii.speed = speed;
|
|
|
|
gp->phy_mii.duplex = duplex;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure PHY & start aneg */
|
|
|
|
gp->want_autoneg = autoneg;
|
|
|
|
if (autoneg) {
|
|
|
|
if (found_mii_phy(gp))
|
|
|
|
gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, advertise);
|
|
|
|
gp->lstate = link_aneg;
|
|
|
|
} else {
|
|
|
|
if (found_mii_phy(gp))
|
|
|
|
gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, speed, duplex);
|
|
|
|
gp->lstate = link_force_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
non_mii:
|
|
|
|
gp->timer_ticks = 0;
|
|
|
|
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A link-up condition has occurred, initialize and enable the
|
|
|
|
* rest of the chip.
|
|
|
|
*/
|
|
|
|
static int gem_set_link_modes(struct gem *gp)
|
|
|
|
{
|
2011-06-01 14:17:10 +07:00
|
|
|
struct netdev_queue *txq = netdev_get_tx_queue(gp->dev, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
int full_duplex, speed, pause;
|
2011-06-01 14:17:10 +07:00
|
|
|
u32 val;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
full_duplex = 0;
|
|
|
|
speed = SPEED_10;
|
|
|
|
pause = 0;
|
|
|
|
|
|
|
|
if (found_mii_phy(gp)) {
|
|
|
|
if (gp->phy_mii.def->ops->read_link(&gp->phy_mii))
|
|
|
|
return 1;
|
|
|
|
full_duplex = (gp->phy_mii.duplex == DUPLEX_FULL);
|
|
|
|
speed = gp->phy_mii.speed;
|
|
|
|
pause = gp->phy_mii.pause;
|
|
|
|
} else if (gp->phy_type == phy_serialink ||
|
|
|
|
gp->phy_type == phy_serdes) {
|
|
|
|
u32 pcs_lpa = readl(gp->regs + PCS_MIILP);
|
|
|
|
|
2008-12-09 15:10:11 +07:00
|
|
|
if ((pcs_lpa & PCS_MIIADV_FD) || gp->phy_type == phy_serdes)
|
2005-04-17 05:20:36 +07:00
|
|
|
full_duplex = 1;
|
|
|
|
speed = SPEED_1000;
|
|
|
|
}
|
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
netif_info(gp, link, gp->dev, "Link is up at %d Mbps, %s-duplex\n",
|
|
|
|
speed, (full_duplex ? "full" : "half"));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
|
|
|
|
/* We take the tx queue lock to avoid collisions between
|
|
|
|
* this code, the tx path and the NAPI-driven error path
|
|
|
|
*/
|
|
|
|
__netif_tx_lock(txq, smp_processor_id());
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
|
|
|
|
if (full_duplex) {
|
|
|
|
val |= (MAC_TXCFG_ICS | MAC_TXCFG_ICOLL);
|
|
|
|
} else {
|
|
|
|
/* MAC_TXCFG_NBO must be zero. */
|
2006-09-14 00:24:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
writel(val, gp->regs + MAC_TXCFG);
|
|
|
|
|
|
|
|
val = (MAC_XIFCFG_OE | MAC_XIFCFG_LLED);
|
|
|
|
if (!full_duplex &&
|
|
|
|
(gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
gp->phy_type == phy_mii_mdio1)) {
|
|
|
|
val |= MAC_XIFCFG_DISE;
|
|
|
|
} else if (full_duplex) {
|
|
|
|
val |= MAC_XIFCFG_FLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (speed == SPEED_1000)
|
|
|
|
val |= (MAC_XIFCFG_GMII);
|
|
|
|
|
|
|
|
writel(val, gp->regs + MAC_XIFCFG);
|
|
|
|
|
|
|
|
/* If gigabit and half-duplex, enable carrier extension
|
|
|
|
* mode. Else, disable it.
|
|
|
|
*/
|
|
|
|
if (speed == SPEED_1000 && !full_duplex) {
|
|
|
|
val = readl(gp->regs + MAC_TXCFG);
|
|
|
|
writel(val | MAC_TXCFG_TCE, gp->regs + MAC_TXCFG);
|
|
|
|
|
|
|
|
val = readl(gp->regs + MAC_RXCFG);
|
|
|
|
writel(val | MAC_RXCFG_RCE, gp->regs + MAC_RXCFG);
|
|
|
|
} else {
|
|
|
|
val = readl(gp->regs + MAC_TXCFG);
|
|
|
|
writel(val & ~MAC_TXCFG_TCE, gp->regs + MAC_TXCFG);
|
|
|
|
|
|
|
|
val = readl(gp->regs + MAC_RXCFG);
|
|
|
|
writel(val & ~MAC_RXCFG_RCE, gp->regs + MAC_RXCFG);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gp->phy_type == phy_serialink ||
|
|
|
|
gp->phy_type == phy_serdes) {
|
|
|
|
u32 pcs_lpa = readl(gp->regs + PCS_MIILP);
|
|
|
|
|
|
|
|
if (pcs_lpa & (PCS_MIIADV_SP | PCS_MIIADV_AP))
|
|
|
|
pause = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!full_duplex)
|
|
|
|
writel(512, gp->regs + MAC_STIME);
|
|
|
|
else
|
|
|
|
writel(64, gp->regs + MAC_STIME);
|
|
|
|
val = readl(gp->regs + MAC_MCCFG);
|
|
|
|
if (pause)
|
|
|
|
val |= (MAC_MCCFG_SPE | MAC_MCCFG_RPE);
|
|
|
|
else
|
|
|
|
val &= ~(MAC_MCCFG_SPE | MAC_MCCFG_RPE);
|
|
|
|
writel(val, gp->regs + MAC_MCCFG);
|
|
|
|
|
|
|
|
gem_start_dma(gp);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
__netif_tx_unlock(txq);
|
|
|
|
|
|
|
|
if (netif_msg_link(gp)) {
|
|
|
|
if (pause) {
|
|
|
|
netdev_info(gp->dev,
|
|
|
|
"Pause is enabled (rxfifo: %d off: %d on: %d)\n",
|
|
|
|
gp->rx_fifo_sz,
|
|
|
|
gp->rx_pause_off,
|
|
|
|
gp->rx_pause_on);
|
|
|
|
} else {
|
|
|
|
netdev_info(gp->dev, "Pause is disabled\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_mdio_link_not_up(struct gem *gp)
|
|
|
|
{
|
|
|
|
switch (gp->lstate) {
|
|
|
|
case link_force_ret:
|
2010-08-17 14:55:04 +07:00
|
|
|
netif_info(gp, link, gp->dev,
|
|
|
|
"Autoneg failed again, keeping forced mode\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->phy_mii.def->ops->setup_forced(&gp->phy_mii,
|
|
|
|
gp->last_forced_speed, DUPLEX_HALF);
|
|
|
|
gp->timer_ticks = 5;
|
|
|
|
gp->lstate = link_force_ok;
|
|
|
|
return 0;
|
|
|
|
case link_aneg:
|
|
|
|
/* We try forced modes after a failed aneg only on PHYs that don't
|
|
|
|
* have "magic_aneg" bit set, which means they internally do the
|
|
|
|
* while forced-mode thingy. On these, we just restart aneg
|
|
|
|
*/
|
|
|
|
if (gp->phy_mii.def->magic_aneg)
|
|
|
|
return 1;
|
2010-08-17 14:55:04 +07:00
|
|
|
netif_info(gp, link, gp->dev, "switching to forced 100bt\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Try forced modes. */
|
|
|
|
gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_100,
|
|
|
|
DUPLEX_HALF);
|
|
|
|
gp->timer_ticks = 5;
|
|
|
|
gp->lstate = link_force_try;
|
|
|
|
return 0;
|
|
|
|
case link_force_try:
|
|
|
|
/* Downgrade from 100 to 10 Mbps if necessary.
|
|
|
|
* If already at 10Mbps, warn user about the
|
|
|
|
* situation every 10 ticks.
|
|
|
|
*/
|
|
|
|
if (gp->phy_mii.speed == SPEED_100) {
|
|
|
|
gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_10,
|
|
|
|
DUPLEX_HALF);
|
|
|
|
gp->timer_ticks = 5;
|
2010-08-17 14:55:04 +07:00
|
|
|
netif_info(gp, link, gp->dev,
|
|
|
|
"switching to forced 10bt\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_link_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct gem *gp = (struct gem *) data;
|
2011-06-01 14:17:10 +07:00
|
|
|
struct net_device *dev = gp->dev;
|
2005-04-17 05:20:36 +07:00
|
|
|
int restart_aneg = 0;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* There's no point doing anything if we're going to be reset */
|
2005-04-17 05:20:36 +07:00
|
|
|
if (gp->reset_task_pending)
|
2011-06-01 14:17:10 +07:00
|
|
|
return;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (gp->phy_type == phy_serialink ||
|
|
|
|
gp->phy_type == phy_serdes) {
|
|
|
|
u32 val = readl(gp->regs + PCS_MIISTAT);
|
|
|
|
|
|
|
|
if (!(val & PCS_MIISTAT_LS))
|
|
|
|
val = readl(gp->regs + PCS_MIISTAT);
|
|
|
|
|
|
|
|
if ((val & PCS_MIISTAT_LS) != 0) {
|
2008-12-09 15:10:11 +07:00
|
|
|
if (gp->lstate == link_up)
|
|
|
|
goto restart;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->lstate = link_up;
|
2011-06-01 14:17:10 +07:00
|
|
|
netif_carrier_on(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
(void)gem_set_link_modes(gp);
|
|
|
|
}
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
if (found_mii_phy(gp) && gp->phy_mii.def->ops->poll_link(&gp->phy_mii)) {
|
|
|
|
/* Ok, here we got a link. If we had it due to a forced
|
|
|
|
* fallback, and we were configured for autoneg, we do
|
|
|
|
* retry a short autoneg pass. If you know your hub is
|
|
|
|
* broken, use ethtool ;)
|
|
|
|
*/
|
|
|
|
if (gp->lstate == link_force_try && gp->want_autoneg) {
|
|
|
|
gp->lstate = link_force_ret;
|
|
|
|
gp->last_forced_speed = gp->phy_mii.speed;
|
|
|
|
gp->timer_ticks = 5;
|
|
|
|
if (netif_msg_link(gp))
|
2011-06-01 14:17:10 +07:00
|
|
|
netdev_info(dev,
|
2010-08-17 14:55:04 +07:00
|
|
|
"Got link after fallback, retrying autoneg once...\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising);
|
|
|
|
} else if (gp->lstate != link_up) {
|
|
|
|
gp->lstate = link_up;
|
2011-06-01 14:17:10 +07:00
|
|
|
netif_carrier_on(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (gem_set_link_modes(gp))
|
|
|
|
restart_aneg = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* If the link was previously up, we restart the
|
|
|
|
* whole process
|
|
|
|
*/
|
|
|
|
if (gp->lstate == link_up) {
|
|
|
|
gp->lstate = link_down;
|
2011-06-01 14:17:10 +07:00
|
|
|
netif_info(gp, link, dev, "Link down\n");
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
gem_schedule_reset(gp);
|
|
|
|
/* The reset task will restart the timer */
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else if (++gp->timer_ticks > 10) {
|
|
|
|
if (found_mii_phy(gp))
|
|
|
|
restart_aneg = gem_mdio_link_not_up(gp);
|
|
|
|
else
|
|
|
|
restart_aneg = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (restart_aneg) {
|
|
|
|
gem_begin_auto_negotiation(gp, NULL);
|
2011-06-01 14:17:10 +07:00
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
restart:
|
|
|
|
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_clean_rings(struct gem *gp)
|
|
|
|
{
|
|
|
|
struct gem_init_block *gb = gp->init_block;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int i;
|
|
|
|
dma_addr_t dma_addr;
|
|
|
|
|
|
|
|
for (i = 0; i < RX_RING_SIZE; i++) {
|
|
|
|
struct gem_rxd *rxd;
|
|
|
|
|
|
|
|
rxd = &gb->rxd[i];
|
|
|
|
if (gp->rx_skbs[i] != NULL) {
|
|
|
|
skb = gp->rx_skbs[i];
|
|
|
|
dma_addr = le64_to_cpu(rxd->buffer);
|
|
|
|
pci_unmap_page(gp->pdev, dma_addr,
|
|
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
gp->rx_skbs[i] = NULL;
|
|
|
|
}
|
|
|
|
rxd->status_word = 0;
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
rxd->buffer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < TX_RING_SIZE; i++) {
|
|
|
|
if (gp->tx_skbs[i] != NULL) {
|
|
|
|
struct gem_txd *txd;
|
|
|
|
int frag;
|
|
|
|
|
|
|
|
skb = gp->tx_skbs[i];
|
|
|
|
gp->tx_skbs[i] = NULL;
|
|
|
|
|
|
|
|
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
|
|
|
|
int ent = i & (TX_RING_SIZE - 1);
|
|
|
|
|
|
|
|
txd = &gb->txd[ent];
|
|
|
|
dma_addr = le64_to_cpu(txd->buffer);
|
|
|
|
pci_unmap_page(gp->pdev, dma_addr,
|
|
|
|
le64_to_cpu(txd->control_word) &
|
|
|
|
TXDCTRL_BUFSZ, PCI_DMA_TODEVICE);
|
|
|
|
|
|
|
|
if (frag != skb_shinfo(skb)->nr_frags)
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_init_rings(struct gem *gp)
|
|
|
|
{
|
|
|
|
struct gem_init_block *gb = gp->init_block;
|
|
|
|
struct net_device *dev = gp->dev;
|
|
|
|
int i;
|
|
|
|
dma_addr_t dma_addr;
|
|
|
|
|
|
|
|
gp->rx_new = gp->rx_old = gp->tx_new = gp->tx_old = 0;
|
|
|
|
|
|
|
|
gem_clean_rings(gp);
|
|
|
|
|
|
|
|
gp->rx_buf_sz = max(dev->mtu + ETH_HLEN + VLAN_HLEN,
|
|
|
|
(unsigned)VLAN_ETH_FRAME_LEN);
|
|
|
|
|
|
|
|
for (i = 0; i < RX_RING_SIZE; i++) {
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct gem_rxd *rxd = &gb->rxd[i];
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!skb) {
|
|
|
|
rxd->buffer = 0;
|
|
|
|
rxd->status_word = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
gp->rx_skbs[i] = skb;
|
|
|
|
skb_put(skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
|
|
dma_addr = pci_map_page(gp->pdev,
|
|
|
|
virt_to_page(skb->data),
|
|
|
|
offset_in_page(skb->data),
|
|
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
rxd->buffer = cpu_to_le64(dma_addr);
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
|
|
|
|
skb_reserve(skb, RX_OFFSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < TX_RING_SIZE; i++) {
|
|
|
|
struct gem_txd *txd = &gb->txd[i];
|
|
|
|
|
|
|
|
txd->control_word = 0;
|
2015-04-08 06:55:14 +07:00
|
|
|
dma_wmb();
|
2005-04-17 05:20:36 +07:00
|
|
|
txd->buffer = 0;
|
|
|
|
}
|
|
|
|
wmb();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init PHY interface and start link poll state machine */
|
|
|
|
static void gem_init_phy(struct gem *gp)
|
|
|
|
{
|
2006-02-01 08:09:20 +07:00
|
|
|
u32 mifcfg;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Revert MIF CFG setting done on stop_phy */
|
2006-02-01 08:09:20 +07:00
|
|
|
mifcfg = readl(gp->regs + MIF_CFG);
|
|
|
|
mifcfg &= ~MIF_CFG_BBMODE;
|
|
|
|
writel(mifcfg, gp->regs + MIF_CFG);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
|
|
|
|
int i;
|
|
|
|
|
2006-02-01 08:09:20 +07:00
|
|
|
/* Those delay sucks, the HW seem to love them though, I'll
|
|
|
|
* serisouly consider breaking some locks here to be able
|
|
|
|
* to schedule instead
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 3; i++) {
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef CONFIG_PPC_PMAC
|
2006-02-01 08:09:20 +07:00
|
|
|
pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
|
|
|
|
msleep(20);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
2006-02-01 08:09:20 +07:00
|
|
|
/* Some PHYs used by apple have problem getting back to us,
|
|
|
|
* we do an additional reset here
|
|
|
|
*/
|
2014-08-28 12:59:26 +07:00
|
|
|
sungem_phy_write(gp, MII_BMCR, BMCR_RESET);
|
2006-02-01 08:09:20 +07:00
|
|
|
msleep(20);
|
2014-08-28 12:59:26 +07:00
|
|
|
if (sungem_phy_read(gp, MII_BMCR) != 0xffff)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2006-02-01 08:09:20 +07:00
|
|
|
if (i == 2)
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_warn(gp->dev, "GMAC PHY not responding !\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
|
|
|
|
gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Init datapath mode register. */
|
|
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
gp->phy_type == phy_mii_mdio1) {
|
|
|
|
val = PCS_DMODE_MGM;
|
|
|
|
} else if (gp->phy_type == phy_serialink) {
|
|
|
|
val = PCS_DMODE_SM | PCS_DMODE_GMOE;
|
|
|
|
} else {
|
|
|
|
val = PCS_DMODE_ESM;
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(val, gp->regs + PCS_DMODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
gp->phy_type == phy_mii_mdio1) {
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Reset and detect MII PHY */
|
2011-08-16 13:10:39 +07:00
|
|
|
sungem_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Init PHY */
|
|
|
|
if (gp->phy_mii.def && gp->phy_mii.def->ops->init)
|
|
|
|
gp->phy_mii.def->ops->init(&gp->phy_mii);
|
|
|
|
} else {
|
2008-12-09 15:10:11 +07:00
|
|
|
gem_pcs_reset(gp);
|
|
|
|
gem_pcs_reinit_adv(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Default aneg parameters */
|
|
|
|
gp->timer_ticks = 0;
|
|
|
|
gp->lstate = link_down;
|
|
|
|
netif_carrier_off(gp->dev);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Print things out */
|
|
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
gp->phy_type == phy_mii_mdio1)
|
|
|
|
netdev_info(gp->dev, "Found %s PHY\n",
|
|
|
|
gp->phy_mii.def ? gp->phy_mii.def->name : "no");
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_init_dma(struct gem *gp)
|
|
|
|
{
|
|
|
|
u64 desc_dma = (u64) gp->gblock_dvma;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = (TXDMA_CFG_BASE | (0x7ff << 10) | TXDMA_CFG_PMODE);
|
|
|
|
writel(val, gp->regs + TXDMA_CFG);
|
|
|
|
|
|
|
|
writel(desc_dma >> 32, gp->regs + TXDMA_DBHI);
|
|
|
|
writel(desc_dma & 0xffffffff, gp->regs + TXDMA_DBLOW);
|
|
|
|
desc_dma += (INIT_BLOCK_TX_RING_SIZE * sizeof(struct gem_txd));
|
|
|
|
|
|
|
|
writel(0, gp->regs + TXDMA_KICK);
|
|
|
|
|
|
|
|
val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) |
|
|
|
|
((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128);
|
|
|
|
writel(val, gp->regs + RXDMA_CFG);
|
|
|
|
|
|
|
|
writel(desc_dma >> 32, gp->regs + RXDMA_DBHI);
|
|
|
|
writel(desc_dma & 0xffffffff, gp->regs + RXDMA_DBLOW);
|
|
|
|
|
|
|
|
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
|
|
|
|
|
|
|
|
val = (((gp->rx_pause_off / 64) << 0) & RXDMA_PTHRESH_OFF);
|
|
|
|
val |= (((gp->rx_pause_on / 64) << 12) & RXDMA_PTHRESH_ON);
|
|
|
|
writel(val, gp->regs + RXDMA_PTHRESH);
|
|
|
|
|
|
|
|
if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN)
|
|
|
|
writel(((5 & RXDMA_BLANK_IPKTS) |
|
|
|
|
((8 << 12) & RXDMA_BLANK_ITIME)),
|
|
|
|
gp->regs + RXDMA_BLANK);
|
|
|
|
else
|
|
|
|
writel(((5 & RXDMA_BLANK_IPKTS) |
|
|
|
|
((4 << 12) & RXDMA_BLANK_ITIME)),
|
|
|
|
gp->regs + RXDMA_BLANK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 gem_setup_multicast(struct gem *gp)
|
|
|
|
{
|
|
|
|
u32 rxcfg = 0;
|
|
|
|
int i;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((gp->dev->flags & IFF_ALLMULTI) ||
|
2010-02-08 11:30:35 +07:00
|
|
|
(netdev_mc_count(gp->dev) > 256)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i=0; i<16; i++)
|
|
|
|
writel(0xffff, gp->regs + MAC_HASH0 + (i << 2));
|
|
|
|
rxcfg |= MAC_RXCFG_HFE;
|
|
|
|
} else if (gp->dev->flags & IFF_PROMISC) {
|
|
|
|
rxcfg |= MAC_RXCFG_PROM;
|
|
|
|
} else {
|
|
|
|
u16 hash_table[16];
|
|
|
|
u32 crc;
|
2010-04-02 04:22:57 +07:00
|
|
|
struct netdev_hw_addr *ha;
|
2005-04-17 05:20:36 +07:00
|
|
|
int i;
|
|
|
|
|
2010-02-18 07:42:54 +07:00
|
|
|
memset(hash_table, 0, sizeof(hash_table));
|
2010-04-02 04:22:57 +07:00
|
|
|
netdev_for_each_mc_addr(ha, gp->dev) {
|
2011-07-08 05:06:26 +07:00
|
|
|
crc = ether_crc_le(6, ha->addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
crc >>= 24;
|
|
|
|
hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
|
|
|
|
}
|
|
|
|
for (i=0; i<16; i++)
|
|
|
|
writel(hash_table[i], gp->regs + MAC_HASH0 + (i << 2));
|
|
|
|
rxcfg |= MAC_RXCFG_HFE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rxcfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_init_mac(struct gem *gp)
|
|
|
|
{
|
|
|
|
unsigned char *e = &gp->dev->dev_addr[0];
|
|
|
|
|
|
|
|
writel(0x1bf0, gp->regs + MAC_SNDPAUSE);
|
|
|
|
|
|
|
|
writel(0x00, gp->regs + MAC_IPG0);
|
|
|
|
writel(0x08, gp->regs + MAC_IPG1);
|
|
|
|
writel(0x04, gp->regs + MAC_IPG2);
|
|
|
|
writel(0x40, gp->regs + MAC_STIME);
|
|
|
|
writel(0x40, gp->regs + MAC_MINFSZ);
|
|
|
|
|
|
|
|
/* Ethernet payload + header + FCS + optional VLAN tag. */
|
|
|
|
writel(0x20000000 | (gp->rx_buf_sz + 4), gp->regs + MAC_MAXFSZ);
|
|
|
|
|
|
|
|
writel(0x07, gp->regs + MAC_PASIZE);
|
|
|
|
writel(0x04, gp->regs + MAC_JAMSIZE);
|
|
|
|
writel(0x10, gp->regs + MAC_ATTLIM);
|
|
|
|
writel(0x8808, gp->regs + MAC_MCTYPE);
|
|
|
|
|
|
|
|
writel((e[5] | (e[4] << 8)) & 0x3ff, gp->regs + MAC_RANDSEED);
|
|
|
|
|
|
|
|
writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
|
|
|
|
writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
|
|
|
|
writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
|
|
|
|
|
|
|
|
writel(0, gp->regs + MAC_ADDR3);
|
|
|
|
writel(0, gp->regs + MAC_ADDR4);
|
|
|
|
writel(0, gp->regs + MAC_ADDR5);
|
|
|
|
|
|
|
|
writel(0x0001, gp->regs + MAC_ADDR6);
|
|
|
|
writel(0xc200, gp->regs + MAC_ADDR7);
|
|
|
|
writel(0x0180, gp->regs + MAC_ADDR8);
|
|
|
|
|
|
|
|
writel(0, gp->regs + MAC_AFILT0);
|
|
|
|
writel(0, gp->regs + MAC_AFILT1);
|
|
|
|
writel(0, gp->regs + MAC_AFILT2);
|
|
|
|
writel(0, gp->regs + MAC_AF21MSK);
|
|
|
|
writel(0, gp->regs + MAC_AF0MSK);
|
|
|
|
|
|
|
|
gp->mac_rx_cfg = gem_setup_multicast(gp);
|
|
|
|
#ifdef STRIP_FCS
|
|
|
|
gp->mac_rx_cfg |= MAC_RXCFG_SFCS;
|
|
|
|
#endif
|
|
|
|
writel(0, gp->regs + MAC_NCOLL);
|
|
|
|
writel(0, gp->regs + MAC_FASUCC);
|
|
|
|
writel(0, gp->regs + MAC_ECOLL);
|
|
|
|
writel(0, gp->regs + MAC_LCOLL);
|
|
|
|
writel(0, gp->regs + MAC_DTIMER);
|
|
|
|
writel(0, gp->regs + MAC_PATMPS);
|
|
|
|
writel(0, gp->regs + MAC_RFCTR);
|
|
|
|
writel(0, gp->regs + MAC_LERR);
|
|
|
|
writel(0, gp->regs + MAC_AERR);
|
|
|
|
writel(0, gp->regs + MAC_FCSERR);
|
|
|
|
writel(0, gp->regs + MAC_RXCVERR);
|
|
|
|
|
|
|
|
/* Clear RX/TX/MAC/XIF config, we will set these up and enable
|
|
|
|
* them once a link is established.
|
|
|
|
*/
|
|
|
|
writel(0, gp->regs + MAC_TXCFG);
|
|
|
|
writel(gp->mac_rx_cfg, gp->regs + MAC_RXCFG);
|
|
|
|
writel(0, gp->regs + MAC_MCCFG);
|
|
|
|
writel(0, gp->regs + MAC_XIFCFG);
|
|
|
|
|
|
|
|
/* Setup MAC interrupts. We want to get all of the interesting
|
|
|
|
* counter expiration events, but we do not want to hear about
|
|
|
|
* normal rx/tx as the DMA engine tells us that.
|
|
|
|
*/
|
|
|
|
writel(MAC_TXSTAT_XMIT, gp->regs + MAC_TXMASK);
|
|
|
|
writel(MAC_RXSTAT_RCV, gp->regs + MAC_RXMASK);
|
|
|
|
|
|
|
|
/* Don't enable even the PAUSE interrupts for now, we
|
|
|
|
* make no use of those events other than to record them.
|
|
|
|
*/
|
|
|
|
writel(0xffffffff, gp->regs + MAC_MCMASK);
|
|
|
|
|
|
|
|
/* Don't enable GEM's WOL in normal operations
|
|
|
|
*/
|
|
|
|
if (gp->has_wol)
|
|
|
|
writel(0, gp->regs + WOL_WAKECSR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_init_pause_thresholds(struct gem *gp)
|
|
|
|
{
|
|
|
|
u32 cfg;
|
|
|
|
|
|
|
|
/* Calculate pause thresholds. Setting the OFF threshold to the
|
|
|
|
* full RX fifo size effectively disables PAUSE generation which
|
|
|
|
* is what we do for 10/100 only GEMs which have FIFOs too small
|
|
|
|
* to make real gains from PAUSE.
|
|
|
|
*/
|
|
|
|
if (gp->rx_fifo_sz <= (2 * 1024)) {
|
|
|
|
gp->rx_pause_off = gp->rx_pause_on = gp->rx_fifo_sz;
|
|
|
|
} else {
|
|
|
|
int max_frame = (gp->rx_buf_sz + 4 + 64) & ~63;
|
|
|
|
int off = (gp->rx_fifo_sz - (max_frame * 2));
|
|
|
|
int on = off - max_frame;
|
|
|
|
|
|
|
|
gp->rx_pause_off = off;
|
|
|
|
gp->rx_pause_on = on;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Configure the chip "burst" DMA mode & enable some
|
|
|
|
* HW bug fixes on Apple version
|
|
|
|
*/
|
|
|
|
cfg = 0;
|
|
|
|
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
|
|
|
|
cfg |= GREG_CFG_RONPAULBIT | GREG_CFG_ENBUG2FIX;
|
|
|
|
#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
|
|
|
|
cfg |= GREG_CFG_IBURST;
|
|
|
|
#endif
|
|
|
|
cfg |= ((31 << 1) & GREG_CFG_TXDMALIM);
|
|
|
|
cfg |= ((31 << 6) & GREG_CFG_RXDMALIM);
|
|
|
|
writel(cfg, gp->regs + GREG_CFG);
|
|
|
|
|
|
|
|
/* If Infinite Burst didn't stick, then use different
|
|
|
|
* thresholds (and Apple bug fixes don't exist)
|
|
|
|
*/
|
|
|
|
if (!(readl(gp->regs + GREG_CFG) & GREG_CFG_IBURST)) {
|
|
|
|
cfg = ((2 << 1) & GREG_CFG_TXDMALIM);
|
|
|
|
cfg |= ((8 << 6) & GREG_CFG_RXDMALIM);
|
|
|
|
writel(cfg, gp->regs + GREG_CFG);
|
2006-09-14 00:24:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_check_invariants(struct gem *gp)
|
|
|
|
{
|
|
|
|
struct pci_dev *pdev = gp->pdev;
|
|
|
|
u32 mif_cfg;
|
|
|
|
|
|
|
|
/* On Apple's sungem, we can't rely on registers as the chip
|
|
|
|
* was been powered down by the firmware. The PHY is looked
|
|
|
|
* up later on.
|
|
|
|
*/
|
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_APPLE) {
|
|
|
|
gp->phy_type = phy_mii_mdio0;
|
|
|
|
gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
|
|
|
|
gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
|
|
|
|
gp->swrst_base = 0;
|
|
|
|
|
|
|
|
mif_cfg = readl(gp->regs + MIF_CFG);
|
|
|
|
mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
|
|
|
|
mif_cfg |= MIF_CFG_MDI0;
|
|
|
|
writel(mif_cfg, gp->regs + MIF_CFG);
|
|
|
|
writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
|
|
|
|
writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
|
|
|
|
|
|
|
|
/* We hard-code the PHY address so we can properly bring it out of
|
|
|
|
* reset later on, we can't really probe it at this point, though
|
|
|
|
* that isn't an issue.
|
|
|
|
*/
|
|
|
|
if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
|
|
|
|
gp->mii_phy_addr = 1;
|
|
|
|
else
|
|
|
|
gp->mii_phy_addr = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mif_cfg = readl(gp->regs + MIF_CFG);
|
|
|
|
|
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
|
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_RIO_GEM) {
|
|
|
|
/* One of the MII PHYs _must_ be present
|
|
|
|
* as this chip has no gigabit PHY.
|
|
|
|
*/
|
|
|
|
if ((mif_cfg & (MIF_CFG_MDI0 | MIF_CFG_MDI1)) == 0) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("RIO GEM lacks MII phy, mif_cfg[%08x]\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
mif_cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine initial PHY interface type guess. MDIO1 is the
|
|
|
|
* external PHY and thus takes precedence over MDIO0.
|
|
|
|
*/
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (mif_cfg & MIF_CFG_MDI1) {
|
|
|
|
gp->phy_type = phy_mii_mdio1;
|
|
|
|
mif_cfg |= MIF_CFG_PSELECT;
|
|
|
|
writel(mif_cfg, gp->regs + MIF_CFG);
|
|
|
|
} else if (mif_cfg & MIF_CFG_MDI0) {
|
|
|
|
gp->phy_type = phy_mii_mdio0;
|
|
|
|
mif_cfg &= ~MIF_CFG_PSELECT;
|
|
|
|
writel(mif_cfg, gp->regs + MIF_CFG);
|
|
|
|
} else {
|
2009-11-17 19:46:18 +07:00
|
|
|
#ifdef CONFIG_SPARC
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
p = of_get_property(gp->of_node, "shared-pins", NULL);
|
|
|
|
if (p && !strcmp(p, "serdes"))
|
|
|
|
gp->phy_type = phy_serdes;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
gp->phy_type = phy_serialink;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
if (gp->phy_type == phy_mii_mdio1 ||
|
|
|
|
gp->phy_type == phy_mii_mdio0) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
gp->mii_phy_addr = i;
|
2014-08-28 12:59:26 +07:00
|
|
|
if (sungem_phy_read(gp, MII_BMCR) != 0xffff)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == 32) {
|
|
|
|
if (pdev->device != PCI_DEVICE_ID_SUN_GEM) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("RIO MII phy will not respond\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
gp->phy_type = phy_serdes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fetch the FIFO configurations now too. */
|
|
|
|
gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
|
|
|
|
gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
|
|
|
|
|
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_SUN) {
|
|
|
|
if (pdev->device == PCI_DEVICE_ID_SUN_GEM) {
|
|
|
|
if (gp->tx_fifo_sz != (9 * 1024) ||
|
|
|
|
gp->rx_fifo_sz != (20 * 1024)) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("GEM has bogus fifo sizes tx(%d) rx(%d)\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->tx_fifo_sz, gp->rx_fifo_sz);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
gp->swrst_base = 0;
|
|
|
|
} else {
|
|
|
|
if (gp->tx_fifo_sz != (2 * 1024) ||
|
|
|
|
gp->rx_fifo_sz != (2 * 1024)) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("RIO GEM has bogus fifo sizes tx(%d) rx(%d)\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->tx_fifo_sz, gp->rx_fifo_sz);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
gp->swrst_base = (64 / 4) << GREG_SWRST_CACHE_SHIFT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_reinit_chip(struct gem *gp)
|
|
|
|
{
|
|
|
|
/* Reset the chip */
|
|
|
|
gem_reset(gp);
|
|
|
|
|
|
|
|
/* Make sure ints are disabled */
|
|
|
|
gem_disable_ints(gp);
|
|
|
|
|
|
|
|
/* Allocate & setup ring buffers */
|
|
|
|
gem_init_rings(gp);
|
|
|
|
|
|
|
|
/* Configure pause thresholds */
|
|
|
|
gem_init_pause_thresholds(gp);
|
|
|
|
|
|
|
|
/* Init DMA & MAC engines */
|
|
|
|
gem_init_dma(gp);
|
|
|
|
gem_init_mac(gp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void gem_stop_phy(struct gem *gp, int wol)
|
|
|
|
{
|
2006-02-01 08:09:20 +07:00
|
|
|
u32 mifcfg;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Let the chip settle down a bit, it seems that helps
|
|
|
|
* for sleep mode on some models
|
|
|
|
*/
|
|
|
|
msleep(10);
|
|
|
|
|
|
|
|
/* Make sure we aren't polling PHY status change. We
|
|
|
|
* don't currently use that feature though
|
|
|
|
*/
|
2006-02-01 08:09:20 +07:00
|
|
|
mifcfg = readl(gp->regs + MIF_CFG);
|
|
|
|
mifcfg &= ~MIF_CFG_POLL;
|
|
|
|
writel(mifcfg, gp->regs + MIF_CFG);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (wol && gp->has_wol) {
|
|
|
|
unsigned char *e = &gp->dev->dev_addr[0];
|
|
|
|
u32 csr;
|
|
|
|
|
|
|
|
/* Setup wake-on-lan for MAGIC packet */
|
|
|
|
writel(MAC_RXCFG_HFE | MAC_RXCFG_SFCS | MAC_RXCFG_ENAB,
|
2006-09-14 00:24:59 +07:00
|
|
|
gp->regs + MAC_RXCFG);
|
2005-04-17 05:20:36 +07:00
|
|
|
writel((e[4] << 8) | e[5], gp->regs + WOL_MATCH0);
|
|
|
|
writel((e[2] << 8) | e[3], gp->regs + WOL_MATCH1);
|
|
|
|
writel((e[0] << 8) | e[1], gp->regs + WOL_MATCH2);
|
|
|
|
|
|
|
|
writel(WOL_MCOUNT_N | WOL_MCOUNT_M, gp->regs + WOL_MCOUNT);
|
|
|
|
csr = WOL_WAKECSR_ENABLE;
|
|
|
|
if ((readl(gp->regs + MAC_XIFCFG) & MAC_XIFCFG_GMII) == 0)
|
|
|
|
csr |= WOL_WAKECSR_MII;
|
|
|
|
writel(csr, gp->regs + WOL_WAKECSR);
|
|
|
|
} else {
|
|
|
|
writel(0, gp->regs + MAC_RXCFG);
|
|
|
|
(void)readl(gp->regs + MAC_RXCFG);
|
|
|
|
/* Machine sleep will die in strange ways if we
|
|
|
|
* dont wait a bit here, looks like the chip takes
|
|
|
|
* some time to really shut down
|
|
|
|
*/
|
|
|
|
msleep(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(0, gp->regs + MAC_TXCFG);
|
|
|
|
writel(0, gp->regs + MAC_XIFCFG);
|
|
|
|
writel(0, gp->regs + TXDMA_CFG);
|
|
|
|
writel(0, gp->regs + RXDMA_CFG);
|
|
|
|
|
|
|
|
if (!wol) {
|
|
|
|
gem_reset(gp);
|
|
|
|
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
|
|
|
|
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
|
|
|
|
|
|
|
|
if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
|
|
|
|
gp->phy_mii.def->ops->suspend(&gp->phy_mii);
|
|
|
|
|
|
|
|
/* According to Apple, we must set the MDIO pins to this begnign
|
|
|
|
* state or we may 1) eat more current, 2) damage some PHYs
|
|
|
|
*/
|
2006-02-01 08:09:20 +07:00
|
|
|
writel(mifcfg | MIF_CFG_BBMODE, gp->regs + MIF_CFG);
|
2005-04-17 05:20:36 +07:00
|
|
|
writel(0, gp->regs + MIF_BBCLK);
|
|
|
|
writel(0, gp->regs + MIF_BBDATA);
|
|
|
|
writel(0, gp->regs + MIF_BBOENAB);
|
|
|
|
writel(MAC_XIFCFG_GMII | MAC_XIFCFG_LBCK, gp->regs + MAC_XIFCFG);
|
|
|
|
(void) readl(gp->regs + MAC_XIFCFG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_do_start(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2011-06-01 14:17:10 +07:00
|
|
|
int rc;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Enable the cell */
|
|
|
|
gem_get_cell(gp);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Make sure PCI access and bus master are enabled */
|
|
|
|
rc = pci_enable_device(gp->pdev);
|
|
|
|
if (rc) {
|
|
|
|
netdev_err(dev, "Failed to enable chip on PCI bus !\n");
|
2009-02-07 13:00:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Put cell and forget it for now, it will be considered as
|
|
|
|
* still asleep, a new sleep cycle may bring it back
|
|
|
|
*/
|
|
|
|
gem_put_cell(gp);
|
|
|
|
return -ENXIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2011-06-01 14:17:10 +07:00
|
|
|
pci_set_master(gp->pdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Init & setup chip hardware */
|
|
|
|
gem_reinit_chip(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* An interrupt might come in handy */
|
|
|
|
rc = request_irq(gp->pdev->irq, gem_interrupt,
|
|
|
|
IRQF_SHARED, dev->name, (void *)dev);
|
|
|
|
if (rc) {
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_err(dev, "failed to request irq !\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
gem_reset(gp);
|
|
|
|
gem_clean_rings(gp);
|
|
|
|
gem_put_cell(gp);
|
2011-06-01 14:17:10 +07:00
|
|
|
return rc;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Mark us as attached again if we come from resume(), this has
|
2015-03-07 11:49:12 +07:00
|
|
|
* no effect if we weren't detached and needs to be done now.
|
2011-06-01 14:17:10 +07:00
|
|
|
*/
|
|
|
|
netif_device_attach(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Restart NAPI & queues */
|
|
|
|
gem_netif_start(gp);
|
|
|
|
|
|
|
|
/* Detect & init PHY, start autoneg etc... this will
|
|
|
|
* eventually result in starting DMA operations when
|
|
|
|
* the link is up
|
|
|
|
*/
|
|
|
|
gem_init_phy(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_do_stop(struct net_device *dev, int wol)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Stop NAPI and stop tx queue */
|
|
|
|
gem_netif_stop(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Make sure ints are disabled. We don't care about
|
|
|
|
* synchronizing as NAPI is disabled, thus a stray
|
|
|
|
* interrupt will do nothing bad (our irq handler
|
|
|
|
* just schedules NAPI)
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
gem_disable_ints(gp);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Stop the link timer */
|
|
|
|
del_timer_sync(&gp->link_timer);
|
|
|
|
|
|
|
|
/* We cannot cancel the reset task while holding the
|
|
|
|
* rtnl lock, we'd get an A->B / B->A deadlock stituation
|
|
|
|
* if we did. This is not an issue however as the reset
|
|
|
|
* task is synchronized vs. us (rtnl_lock) and will do
|
|
|
|
* nothing if the device is down or suspended. We do
|
|
|
|
* still clear reset_task_pending to avoid a spurrious
|
|
|
|
* reset later on in case we do resume before it gets
|
|
|
|
* scheduled.
|
|
|
|
*/
|
|
|
|
gp->reset_task_pending = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* If we are going to sleep with WOL */
|
|
|
|
gem_stop_dma(gp);
|
|
|
|
msleep(10);
|
|
|
|
if (!wol)
|
|
|
|
gem_reset(gp);
|
|
|
|
msleep(10);
|
|
|
|
|
|
|
|
/* Get rid of rings */
|
|
|
|
gem_clean_rings(gp);
|
|
|
|
|
|
|
|
/* No irq needed anymore */
|
|
|
|
free_irq(gp->pdev->irq, (void *) dev);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Shut the PHY down eventually and setup WOL */
|
|
|
|
gem_stop_phy(gp, wol);
|
|
|
|
|
|
|
|
/* Make sure bus master is disabled */
|
|
|
|
pci_disable_device(gp->pdev);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Cell not needed neither if no WOL */
|
2011-06-01 14:17:10 +07:00
|
|
|
if (!wol)
|
2005-04-17 05:20:36 +07:00
|
|
|
gem_put_cell(gp);
|
|
|
|
}
|
|
|
|
|
2006-11-22 21:57:56 +07:00
|
|
|
static void gem_reset_task(struct work_struct *work)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-11-22 21:57:56 +07:00
|
|
|
struct gem *gp = container_of(work, struct gem, reset_task);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Lock out the network stack (essentially shield ourselves
|
|
|
|
* against a racing open, close, control call, or suspend
|
|
|
|
*/
|
|
|
|
rtnl_lock();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Skip the reset task if suspended or closed, or if it's
|
|
|
|
* been cancelled by gem_do_stop (see comment there)
|
|
|
|
*/
|
|
|
|
if (!netif_device_present(gp->dev) ||
|
|
|
|
!netif_running(gp->dev) ||
|
|
|
|
!gp->reset_task_pending) {
|
|
|
|
rtnl_unlock();
|
|
|
|
return;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Stop the link timer */
|
|
|
|
del_timer_sync(&gp->link_timer);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Stop NAPI and tx */
|
|
|
|
gem_netif_stop(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Reset the chip & rings */
|
|
|
|
gem_reinit_chip(gp);
|
|
|
|
if (gp->lstate == link_up)
|
|
|
|
gem_set_link_modes(gp);
|
2007-11-29 17:51:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Restart NAPI and Tx */
|
|
|
|
gem_netif_start(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* We are back ! */
|
|
|
|
gp->reset_task_pending = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* If the link is not up, restart autoneg, else restart the
|
|
|
|
* polling timer
|
|
|
|
*/
|
|
|
|
if (gp->lstate != link_up)
|
|
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
else
|
|
|
|
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_open(struct net_device *dev)
|
|
|
|
{
|
2011-06-01 14:17:10 +07:00
|
|
|
/* We allow open while suspended, we just do nothing,
|
|
|
|
* the chip will be initialized in resume()
|
|
|
|
*/
|
|
|
|
if (netif_device_present(dev))
|
|
|
|
return gem_do_start(dev);
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_close(struct net_device *dev)
|
|
|
|
{
|
2011-06-01 14:17:10 +07:00
|
|
|
if (netif_device_present(dev))
|
2005-04-17 05:20:36 +07:00
|
|
|
gem_do_stop(dev, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
|
{
|
|
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Lock the network stack first to avoid racing with open/close,
|
|
|
|
* reset task and setting calls
|
|
|
|
*/
|
|
|
|
rtnl_lock();
|
2007-11-13 09:09:25 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Not running, mark ourselves non-present, no need for
|
|
|
|
* a lock here
|
|
|
|
*/
|
|
|
|
if (!netif_running(dev)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
netif_device_detach(dev);
|
2011-06-01 14:17:10 +07:00
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
netdev_info(dev, "suspending, WakeOnLan %s\n",
|
|
|
|
(gp->wake_on_lan && netif_running(dev)) ?
|
|
|
|
"enabled" : "disabled");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Tell the network stack we're gone. gem_do_stop() below will
|
|
|
|
* synchronize with TX, stop NAPI etc...
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-06-01 14:17:10 +07:00
|
|
|
netif_device_detach(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Switch off chip, remember WOL setting */
|
2012-04-28 15:52:37 +07:00
|
|
|
gp->asleep_wol = !!gp->wake_on_lan;
|
2011-06-01 14:17:10 +07:00
|
|
|
gem_do_stop(dev, gp->asleep_wol);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Unlock the network stack */
|
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_resume(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* See locking comment in gem_suspend */
|
|
|
|
rtnl_lock();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Not running, mark ourselves present, no need for
|
|
|
|
* a lock here
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-06-01 14:17:10 +07:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
netif_device_attach(dev);
|
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Restart chip. If that fails there isn't much we can do, we
|
|
|
|
* leave things stopped.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-06-01 14:17:10 +07:00
|
|
|
gem_do_start(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* If we had WOL enabled, the cell clock was never turned off during
|
|
|
|
* sleep, so we end up beeing unbalanced. Fix that here
|
|
|
|
*/
|
|
|
|
if (gp->asleep_wol)
|
|
|
|
gem_put_cell(gp);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Unlock the network stack */
|
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
|
|
|
static struct net_device_stats *gem_get_stats(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* I have seen this being called while the PM was in progress,
|
2011-06-01 14:17:10 +07:00
|
|
|
* so we shield against this. Let's also not poke at registers
|
|
|
|
* while the reset task is going on.
|
|
|
|
*
|
|
|
|
* TODO: Move stats collection elsewhere (link timer ?) and
|
|
|
|
* make this a nop to avoid all those synchro issues
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-06-01 14:17:10 +07:00
|
|
|
if (!netif_device_present(dev) || !netif_running(dev))
|
|
|
|
goto bail;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Better safe than sorry... */
|
|
|
|
if (WARN_ON(!gp->cell_enabled))
|
|
|
|
goto bail;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
dev->stats.rx_crc_errors += readl(gp->regs + MAC_FCSERR);
|
|
|
|
writel(0, gp->regs + MAC_FCSERR);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
dev->stats.rx_frame_errors += readl(gp->regs + MAC_AERR);
|
|
|
|
writel(0, gp->regs + MAC_AERR);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
dev->stats.rx_length_errors += readl(gp->regs + MAC_LERR);
|
|
|
|
writel(0, gp->regs + MAC_LERR);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
dev->stats.tx_aborted_errors += readl(gp->regs + MAC_ECOLL);
|
|
|
|
dev->stats.collisions +=
|
|
|
|
(readl(gp->regs + MAC_ECOLL) + readl(gp->regs + MAC_LCOLL));
|
|
|
|
writel(0, gp->regs + MAC_ECOLL);
|
|
|
|
writel(0, gp->regs + MAC_LCOLL);
|
|
|
|
bail:
|
2011-01-27 16:54:12 +07:00
|
|
|
return &dev->stats;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-03-27 04:43:49 +07:00
|
|
|
static int gem_set_mac_address(struct net_device *dev, void *addr)
|
|
|
|
{
|
|
|
|
struct sockaddr *macaddr = (struct sockaddr *) addr;
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2007-03-27 04:43:49 +07:00
|
|
|
unsigned char *e = &dev->dev_addr[0];
|
|
|
|
|
|
|
|
if (!is_valid_ether_addr(macaddr->sa_data))
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
|
|
|
|
|
|
|
|
/* We'll just catch it later when the device is up'd or resumed */
|
|
|
|
if (!netif_running(dev) || !netif_device_present(dev))
|
2007-03-27 04:43:49 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Better safe than sorry... */
|
|
|
|
if (WARN_ON(!gp->cell_enabled))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
|
|
|
|
writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
|
|
|
|
writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
|
2007-03-27 04:43:49 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void gem_set_multicast(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 rxcfg, rxcfg_new;
|
|
|
|
int limit = 10000;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
if (!netif_running(dev) || !netif_device_present(dev))
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Better safe than sorry... */
|
|
|
|
if (gp->reset_task_pending || WARN_ON(!gp->cell_enabled))
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
rxcfg = readl(gp->regs + MAC_RXCFG);
|
|
|
|
rxcfg_new = gem_setup_multicast(gp);
|
|
|
|
#ifdef STRIP_FCS
|
|
|
|
rxcfg_new |= MAC_RXCFG_SFCS;
|
|
|
|
#endif
|
|
|
|
gp->mac_rx_cfg = rxcfg_new;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
|
|
|
|
while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) {
|
|
|
|
if (!limit--)
|
|
|
|
break;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
rxcfg &= ~(MAC_RXCFG_PROM | MAC_RXCFG_HFE);
|
|
|
|
rxcfg |= rxcfg_new;
|
|
|
|
|
|
|
|
writel(rxcfg, gp->regs + MAC_RXCFG);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Jumbo-grams don't seem to work :-( */
|
|
|
|
#define GEM_MIN_MTU 68
|
|
|
|
#if 1
|
|
|
|
#define GEM_MAX_MTU 1500
|
|
|
|
#else
|
|
|
|
#define GEM_MAX_MTU 9000
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int gem_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
dev->mtu = new_mtu;
|
|
|
|
|
|
|
|
/* We'll just catch it later when the device is up'd or resumed */
|
|
|
|
if (!netif_running(dev) || !netif_device_present(dev))
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Better safe than sorry... */
|
|
|
|
if (WARN_ON(!gp->cell_enabled))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
gem_netif_stop(gp);
|
|
|
|
gem_reinit_chip(gp);
|
|
|
|
if (gp->lstate == link_up)
|
|
|
|
gem_set_link_modes(gp);
|
|
|
|
gem_netif_start(gp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2011-11-09 16:58:07 +07:00
|
|
|
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
|
|
|
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
|
|
|
strlcpy(info->bus_info, pci_name(gp->pdev), sizeof(info->bus_info));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
gp->phy_type == phy_mii_mdio1) {
|
|
|
|
if (gp->phy_mii.def)
|
|
|
|
cmd->supported = gp->phy_mii.def->features;
|
|
|
|
else
|
|
|
|
cmd->supported = (SUPPORTED_10baseT_Half |
|
|
|
|
SUPPORTED_10baseT_Full);
|
|
|
|
|
|
|
|
/* XXX hardcoded stuff for now */
|
|
|
|
cmd->port = PORT_MII;
|
|
|
|
cmd->transceiver = XCVR_EXTERNAL;
|
|
|
|
cmd->phy_address = 0; /* XXX fixed PHYAD */
|
|
|
|
|
|
|
|
/* Return current PHY settings */
|
|
|
|
cmd->autoneg = gp->want_autoneg;
|
2011-04-28 01:32:40 +07:00
|
|
|
ethtool_cmd_speed_set(cmd, gp->phy_mii.speed);
|
2006-09-14 00:24:59 +07:00
|
|
|
cmd->duplex = gp->phy_mii.duplex;
|
2005-04-17 05:20:36 +07:00
|
|
|
cmd->advertising = gp->phy_mii.advertising;
|
|
|
|
|
|
|
|
/* If we started with a forced mode, we don't have a default
|
|
|
|
* advertise set, we need to return something sensible so
|
|
|
|
* userland can re-enable autoneg properly.
|
|
|
|
*/
|
|
|
|
if (cmd->advertising == 0)
|
|
|
|
cmd->advertising = cmd->supported;
|
|
|
|
} else { // XXX PCS ?
|
|
|
|
cmd->supported =
|
|
|
|
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
|
|
|
|
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
|
|
|
|
SUPPORTED_Autoneg);
|
|
|
|
cmd->advertising = cmd->supported;
|
2011-04-28 01:32:40 +07:00
|
|
|
ethtool_cmd_speed_set(cmd, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
cmd->duplex = cmd->port = cmd->phy_address =
|
|
|
|
cmd->transceiver = cmd->autoneg = 0;
|
2008-12-10 06:39:14 +07:00
|
|
|
|
|
|
|
/* serdes means usually a Fibre connector, with most fixed */
|
|
|
|
if (gp->phy_type == phy_serdes) {
|
|
|
|
cmd->port = PORT_FIBRE;
|
|
|
|
cmd->supported = (SUPPORTED_1000baseT_Half |
|
|
|
|
SUPPORTED_1000baseT_Full |
|
|
|
|
SUPPORTED_FIBRE | SUPPORTED_Autoneg |
|
|
|
|
SUPPORTED_Pause | SUPPORTED_Asym_Pause);
|
|
|
|
cmd->advertising = cmd->supported;
|
|
|
|
cmd->transceiver = XCVR_INTERNAL;
|
|
|
|
if (gp->lstate == link_up)
|
2011-04-28 01:32:40 +07:00
|
|
|
ethtool_cmd_speed_set(cmd, SPEED_1000);
|
2008-12-10 06:39:14 +07:00
|
|
|
cmd->duplex = DUPLEX_FULL;
|
|
|
|
cmd->autoneg = 1;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
cmd->maxtxpkt = cmd->maxrxpkt = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2011-04-28 01:32:39 +07:00
|
|
|
u32 speed = ethtool_cmd_speed(cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Verify the settings we care about. */
|
|
|
|
if (cmd->autoneg != AUTONEG_ENABLE &&
|
|
|
|
cmd->autoneg != AUTONEG_DISABLE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (cmd->autoneg == AUTONEG_ENABLE &&
|
|
|
|
cmd->advertising == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (cmd->autoneg == AUTONEG_DISABLE &&
|
2011-04-28 01:32:39 +07:00
|
|
|
((speed != SPEED_1000 &&
|
|
|
|
speed != SPEED_100 &&
|
|
|
|
speed != SPEED_10) ||
|
2005-04-17 05:20:36 +07:00
|
|
|
(cmd->duplex != DUPLEX_HALF &&
|
|
|
|
cmd->duplex != DUPLEX_FULL)))
|
|
|
|
return -EINVAL;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Apply settings and restart link process. */
|
2011-06-01 14:17:10 +07:00
|
|
|
if (netif_device_present(gp->dev)) {
|
|
|
|
del_timer_sync(&gp->link_timer);
|
|
|
|
gem_begin_auto_negotiation(gp, cmd);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_nway_reset(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!gp->want_autoneg)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Restart link process */
|
|
|
|
if (netif_device_present(gp->dev)) {
|
|
|
|
del_timer_sync(&gp->link_timer);
|
|
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 gem_get_msglevel(struct net_device *dev)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
return gp->msg_enable;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void gem_set_msglevel(struct net_device *dev, u32 value)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->msg_enable = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add more when I understand how to program the chip */
|
|
|
|
/* like WAKE_UCAST | WAKE_MCAST | WAKE_BCAST */
|
|
|
|
|
|
|
|
#define WOL_SUPPORTED_MASK (WAKE_MAGIC)
|
|
|
|
|
|
|
|
static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Add more when I understand how to program the chip */
|
|
|
|
if (gp->has_wol) {
|
|
|
|
wol->supported = WOL_SUPPORTED_MASK;
|
|
|
|
wol->wolopts = gp->wake_on_lan;
|
|
|
|
} else {
|
|
|
|
wol->supported = 0;
|
|
|
|
wol->wolopts = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!gp->has_wol)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
gp->wake_on_lan = wol->wolopts & WOL_SUPPORTED_MASK;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-14 01:30:00 +07:00
|
|
|
static const struct ethtool_ops gem_ethtool_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.get_drvinfo = gem_get_drvinfo,
|
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
.get_settings = gem_get_settings,
|
|
|
|
.set_settings = gem_set_settings,
|
|
|
|
.nway_reset = gem_nway_reset,
|
|
|
|
.get_msglevel = gem_get_msglevel,
|
|
|
|
.set_msglevel = gem_set_msglevel,
|
|
|
|
.get_wol = gem_get_wol,
|
|
|
|
.set_wol = gem_set_wol,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
|
{
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct mii_ioctl_data *data = if_mii(ifr);
|
|
|
|
int rc = -EOPNOTSUPP;
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* For SIOCGMIIREG and SIOCSMIIREG the core checks for us that
|
|
|
|
* netif_device_present() is true and holds rtnl_lock for us
|
|
|
|
* so we have nothing to worry about
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
|
|
|
|
data->phy_id = gp->mii_phy_addr;
|
|
|
|
/* Fallthrough... */
|
|
|
|
|
|
|
|
case SIOCGMIIREG: /* Read MII PHY register. */
|
2014-08-28 12:59:26 +07:00
|
|
|
data->val_out = __sungem_phy_read(gp, data->phy_id & 0x1f,
|
2011-06-01 14:17:10 +07:00
|
|
|
data->reg_num & 0x1f);
|
|
|
|
rc = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSMIIREG: /* Write MII PHY register. */
|
2014-08-28 12:59:26 +07:00
|
|
|
__sungem_phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
|
2011-06-01 14:17:10 +07:00
|
|
|
data->val_in);
|
|
|
|
rc = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2011-06-03 18:51:20 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2007-03-01 06:42:50 +07:00
|
|
|
#if (!defined(CONFIG_SPARC) && !defined(CONFIG_PPC_PMAC))
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Fetch MAC address from vital product data of PCI ROM. */
|
2005-09-11 23:26:20 +07:00
|
|
|
static int find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int this_offset;
|
|
|
|
|
|
|
|
for (this_offset = 0x20; this_offset < len; this_offset++) {
|
|
|
|
void __iomem *p = rom_base + this_offset;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (readb(p + 0) != 0x90 ||
|
|
|
|
readb(p + 1) != 0x00 ||
|
|
|
|
readb(p + 2) != 0x09 ||
|
|
|
|
readb(p + 3) != 0x4e ||
|
|
|
|
readb(p + 4) != 0x41 ||
|
|
|
|
readb(p + 5) != 0x06)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this_offset += 6;
|
|
|
|
p += 6;
|
|
|
|
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
dev_addr[i] = readb(p + i);
|
2005-09-11 23:26:20 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-09-11 23:26:20 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_gem_mac_nonobp(struct pci_dev *pdev, unsigned char *dev_addr)
|
|
|
|
{
|
2005-09-11 23:26:20 +07:00
|
|
|
size_t size;
|
|
|
|
void __iomem *p = pci_map_rom(pdev, &size);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-11 23:26:20 +07:00
|
|
|
if (p) {
|
|
|
|
int found;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-11 23:26:20 +07:00
|
|
|
found = readb(p) == 0x55 &&
|
|
|
|
readb(p + 1) == 0xaa &&
|
|
|
|
find_eth_addr_in_vpd(p, (64 * 1024), dev_addr);
|
|
|
|
pci_unmap_rom(pdev, p);
|
|
|
|
if (found)
|
|
|
|
return;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Sun MAC prefix then 3 random bytes. */
|
|
|
|
dev_addr[0] = 0x08;
|
|
|
|
dev_addr[1] = 0x00;
|
|
|
|
dev_addr[2] = 0x20;
|
|
|
|
get_random_bytes(dev_addr + 3, 3);
|
|
|
|
}
|
|
|
|
#endif /* not Sparc and not PPC */
|
|
|
|
|
2012-12-03 21:24:02 +07:00
|
|
|
static int gem_get_device_address(struct gem *gp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-03-01 06:42:50 +07:00
|
|
|
#if defined(CONFIG_SPARC) || defined(CONFIG_PPC_PMAC)
|
2005-04-17 05:20:36 +07:00
|
|
|
struct net_device *dev = gp->dev;
|
2006-07-12 12:41:03 +07:00
|
|
|
const unsigned char *addr;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-05-01 10:54:02 +07:00
|
|
|
addr = of_get_property(gp->of_node, "local-mac-address", NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (addr == NULL) {
|
2007-03-29 15:36:44 +07:00
|
|
|
#ifdef CONFIG_SPARC
|
|
|
|
addr = idprom->id_ethaddr;
|
|
|
|
#else
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("\n");
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("%s: can't get mac-address\n", dev->name);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -1;
|
2007-03-29 15:36:44 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2013-10-02 09:04:40 +07:00
|
|
|
memcpy(dev->dev_addr, addr, ETH_ALEN);
|
2005-04-17 05:20:36 +07:00
|
|
|
#else
|
|
|
|
get_gem_mac_nonobp(gp->pdev, gp->dev->dev_addr);
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-12-22 09:50:12 +07:00
|
|
|
static void gem_remove_one(struct pci_dev *pdev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
|
|
|
|
|
|
if (dev) {
|
2008-11-13 14:38:36 +07:00
|
|
|
struct gem *gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
unregister_netdev(dev);
|
|
|
|
|
2015-03-07 11:49:12 +07:00
|
|
|
/* Ensure reset task is truly gone */
|
2010-12-12 22:45:14 +07:00
|
|
|
cancel_work_sync(&gp->reset_task);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Free resources */
|
|
|
|
pci_free_consistent(pdev,
|
|
|
|
sizeof(struct gem_init_block),
|
|
|
|
gp->init_block,
|
|
|
|
gp->gblock_dvma);
|
|
|
|
iounmap(gp->regs);
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
free_netdev(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-08 08:28:54 +07:00
|
|
|
static const struct net_device_ops gem_netdev_ops = {
|
|
|
|
.ndo_open = gem_open,
|
|
|
|
.ndo_stop = gem_close,
|
|
|
|
.ndo_start_xmit = gem_start_xmit,
|
|
|
|
.ndo_get_stats = gem_get_stats,
|
2011-08-16 13:29:01 +07:00
|
|
|
.ndo_set_rx_mode = gem_set_multicast,
|
2009-01-08 08:28:54 +07:00
|
|
|
.ndo_do_ioctl = gem_ioctl,
|
|
|
|
.ndo_tx_timeout = gem_tx_timeout,
|
|
|
|
.ndo_change_mtu = gem_change_mtu,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2009-03-20 13:58:01 +07:00
|
|
|
.ndo_set_mac_address = gem_set_mac_address,
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = gem_poll_controller,
|
|
|
|
#endif
|
2009-01-08 08:28:54 +07:00
|
|
|
};
|
|
|
|
|
2012-12-06 21:30:56 +07:00
|
|
|
static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned long gemreg_base, gemreg_len;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct gem *gp;
|
2007-10-04 07:59:30 +07:00
|
|
|
int err, pci_using_dac;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
printk_once(KERN_INFO "%s", version);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Apple gmac note: during probe, the chip is powered up by
|
|
|
|
* the arch code to allow the code below to work (and to let
|
|
|
|
* the chip be probed on the config space. It won't stay powered
|
|
|
|
* up until the interface is brought up however, so we can't rely
|
|
|
|
* on register configuration done at this point.
|
|
|
|
*/
|
|
|
|
err = pci_enable_device(pdev);
|
|
|
|
if (err) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot enable MMIO operation, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
/* Configure DMA attributes. */
|
|
|
|
|
|
|
|
/* All of the GEM documentation states that 64-bit DMA addressing
|
|
|
|
* is fully supported and should work just fine. However the
|
|
|
|
* front end for RIO based GEMs is different and only supports
|
|
|
|
* 32-bit addressing.
|
|
|
|
*
|
|
|
|
* For now we assume the various PPC GEMs are 32-bit only as well.
|
|
|
|
*/
|
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
|
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_GEM &&
|
2009-04-07 09:01:13 +07:00
|
|
|
!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
pci_using_dac = 1;
|
|
|
|
} else {
|
2009-04-07 09:01:15 +07:00
|
|
|
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("No usable DMA configuration, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
goto err_disable_device;
|
|
|
|
}
|
|
|
|
pci_using_dac = 0;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
gemreg_base = pci_resource_start(pdev, 0);
|
|
|
|
gemreg_len = pci_resource_len(pdev, 0);
|
|
|
|
|
|
|
|
if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot find proper PCI device base address, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -ENODEV;
|
|
|
|
goto err_disable_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = alloc_etherdev(sizeof(*gp));
|
|
|
|
if (!dev) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_disable_device;
|
|
|
|
}
|
|
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
|
|
|
2008-11-13 14:38:36 +07:00
|
|
|
gp = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
err = pci_request_regions(pdev, DRV_NAME);
|
|
|
|
if (err) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot obtain PCI resources, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
goto err_out_free_netdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
gp->pdev = pdev;
|
|
|
|
gp->dev = dev;
|
|
|
|
|
|
|
|
gp->msg_enable = DEFAULT_MSG;
|
|
|
|
|
|
|
|
init_timer(&gp->link_timer);
|
|
|
|
gp->link_timer.function = gem_link_timer;
|
|
|
|
gp->link_timer.data = (unsigned long) gp;
|
|
|
|
|
2006-11-22 21:57:56 +07:00
|
|
|
INIT_WORK(&gp->reset_task, gem_reset_task);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->lstate = link_down;
|
|
|
|
gp->timer_ticks = 0;
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
|
|
|
|
gp->regs = ioremap(gemreg_base, gemreg_len);
|
2008-01-24 17:06:46 +07:00
|
|
|
if (!gp->regs) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot map device registers, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto err_out_free_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* On Apple, we want a reference to the Open Firmware device-tree
|
|
|
|
* node. We use it for clock control.
|
|
|
|
*/
|
2007-03-29 15:36:44 +07:00
|
|
|
#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC)
|
2005-04-17 05:20:36 +07:00
|
|
|
gp->of_node = pci_device_to_OF_node(pdev);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Only Apple version supports WOL afaik */
|
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_APPLE)
|
|
|
|
gp->has_wol = 1;
|
|
|
|
|
|
|
|
/* Make sure cell is enabled */
|
|
|
|
gem_get_cell(gp);
|
|
|
|
|
|
|
|
/* Make sure everything is stopped and in init state */
|
|
|
|
gem_reset(gp);
|
|
|
|
|
|
|
|
/* Fill up the mii_phy structure (even if we won't use it) */
|
|
|
|
gp->phy_mii.dev = dev;
|
2014-08-28 12:59:26 +07:00
|
|
|
gp->phy_mii.mdio_read = _sungem_phy_read;
|
|
|
|
gp->phy_mii.mdio_write = _sungem_phy_write;
|
2005-07-08 07:56:09 +07:00
|
|
|
#ifdef CONFIG_PPC_PMAC
|
|
|
|
gp->phy_mii.platform_data = gp->of_node;
|
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
/* By default, we start with autoneg */
|
|
|
|
gp->want_autoneg = 1;
|
|
|
|
|
|
|
|
/* Check fifo sizes, PHY type, etc... */
|
|
|
|
if (gem_check_invariants(gp)) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto err_out_iounmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It is guaranteed that the returned buffer will be at least
|
|
|
|
* PAGE_SIZE aligned.
|
|
|
|
*/
|
|
|
|
gp->init_block = (struct gem_init_block *)
|
|
|
|
pci_alloc_consistent(pdev, sizeof(struct gem_init_block),
|
|
|
|
&gp->gblock_dvma);
|
|
|
|
if (!gp->init_block) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot allocate init block, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_out_iounmap;
|
|
|
|
}
|
|
|
|
|
2012-10-05 19:40:54 +07:00
|
|
|
err = gem_get_device_address(gp);
|
|
|
|
if (err)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto err_out_free_consistent;
|
|
|
|
|
2009-01-08 08:28:54 +07:00
|
|
|
dev->netdev_ops = &gem_netdev_ops;
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 06:41:36 +07:00
|
|
|
netif_napi_add(dev, &gp->napi, gem_poll, 64);
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->ethtool_ops = &gem_ethtool_ops;
|
|
|
|
dev->watchdog_timeo = 5 * HZ;
|
|
|
|
dev->dma = 0;
|
|
|
|
|
|
|
|
/* Set that now, in case PM kicks in now */
|
|
|
|
pci_set_drvdata(pdev, dev);
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* We can do scatter/gather and HW checksum */
|
|
|
|
dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
|
|
|
|
dev->features |= dev->hw_features | NETIF_F_RXCSUM;
|
|
|
|
if (pci_using_dac)
|
|
|
|
dev->features |= NETIF_F_HIGHDMA;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Register with kernel */
|
|
|
|
if (register_netdev(dev)) {
|
2010-08-17 14:55:04 +07:00
|
|
|
pr_err("Cannot register net device, aborting\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_out_free_consistent;
|
|
|
|
}
|
|
|
|
|
2011-06-01 14:17:10 +07:00
|
|
|
/* Undo the get_cell with appropriate locking (we could use
|
|
|
|
* ndo_init/uninit but that would be even more clumsy imho)
|
|
|
|
*/
|
|
|
|
rtnl_lock();
|
|
|
|
gem_put_cell(gp);
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2010-08-17 14:55:04 +07:00
|
|
|
netdev_info(dev, "Sun GEM (PCI) 10/100/1000BaseT Ethernet %pM\n",
|
|
|
|
dev->dev_addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out_free_consistent:
|
|
|
|
gem_remove_one(pdev);
|
|
|
|
err_out_iounmap:
|
|
|
|
gem_put_cell(gp);
|
|
|
|
iounmap(gp->regs);
|
|
|
|
|
|
|
|
err_out_free_res:
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
|
|
|
|
err_out_free_netdev:
|
|
|
|
free_netdev(dev);
|
|
|
|
err_disable_device:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct pci_driver gem_driver = {
|
|
|
|
.name = GEM_MODULE_NAME,
|
|
|
|
.id_table = gem_pci_tbl,
|
|
|
|
.probe = gem_init_one,
|
2005-12-22 09:50:12 +07:00
|
|
|
.remove = gem_remove_one,
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.suspend = gem_suspend,
|
|
|
|
.resume = gem_resume,
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
};
|
|
|
|
|
2013-05-21 19:58:09 +07:00
|
|
|
module_pci_driver(gem_driver);
|