2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Alchemy Au1x00 ethernet driver
|
|
|
|
*
|
2006-04-20 01:46:21 +07:00
|
|
|
* Copyright 2001-2003, 2006 MontaVista Software Inc.
|
2005-04-17 05:20:36 +07:00
|
|
|
* Copyright 2002 TimeSys Corp.
|
|
|
|
* Added ethtool/mii-tool support,
|
|
|
|
* Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
|
2006-09-14 00:24:59 +07:00
|
|
|
* Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de
|
|
|
|
* or riemer@riemer-nt.de: fixed the link beat detection with
|
2005-04-17 05:20:36 +07:00
|
|
|
* ioctls (SIOCGMIIPHY)
|
2006-06-01 14:41:04 +07:00
|
|
|
* Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org>
|
|
|
|
* converted to use linux-2.6.x's PHY framework
|
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Author: MontaVista Software, Inc.
|
|
|
|
* ppopov@mvista.com or source@mvista.com
|
|
|
|
*
|
|
|
|
* ########################################################################
|
|
|
|
*
|
|
|
|
* This program is free software; you can distribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License (Version 2) as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* ########################################################################
|
|
|
|
*
|
2006-09-14 00:24:59 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2007-06-24 20:59:54 +07:00
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/mii.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/delay.h>
|
2006-05-01 20:37:09 +07:00
|
|
|
#include <linux/crc32.h>
|
2006-06-01 14:41:04 +07:00
|
|
|
#include <linux/phy.h>
|
2007-10-15 17:11:24 +07:00
|
|
|
|
|
|
|
#include <asm/cpu.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <asm/mipsregs.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
|
2007-10-15 17:11:24 +07:00
|
|
|
#include <au1000.h>
|
|
|
|
#include <prom.h>
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "au1000_eth.h"
|
|
|
|
|
|
|
|
#ifdef AU1000_ETH_DEBUG
|
|
|
|
static int au1000_debug = 5;
|
|
|
|
#else
|
|
|
|
static int au1000_debug = 3;
|
|
|
|
#endif
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
#define DRV_NAME "au1000_eth"
|
2006-09-12 04:39:18 +07:00
|
|
|
#define DRV_VERSION "1.6"
|
2005-04-17 05:20:36 +07:00
|
|
|
#define DRV_AUTHOR "Pete Popov <ppopov@embeddedalley.com>"
|
|
|
|
#define DRV_DESC "Au1xxx on-chip Ethernet driver"
|
|
|
|
|
|
|
|
MODULE_AUTHOR(DRV_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION(DRV_DESC);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
// prototypes
|
|
|
|
static void hard_stop(struct net_device *);
|
|
|
|
static void enable_rx_tx(struct net_device *dev);
|
2006-04-20 01:46:21 +07:00
|
|
|
static struct net_device * au1000_probe(int port_num);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int au1000_init(struct net_device *);
|
|
|
|
static int au1000_open(struct net_device *);
|
|
|
|
static int au1000_close(struct net_device *);
|
|
|
|
static int au1000_tx(struct sk_buff *, struct net_device *);
|
|
|
|
static int au1000_rx(struct net_device *);
|
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 au1000_interrupt(int, void *);
|
2005-04-17 05:20:36 +07:00
|
|
|
static void au1000_tx_timeout(struct net_device *);
|
|
|
|
static void set_rx_mode(struct net_device *);
|
|
|
|
static int au1000_ioctl(struct net_device *, struct ifreq *, int);
|
2008-10-13 11:02:19 +07:00
|
|
|
static int au1000_mdio_read(struct net_device *, int, int);
|
|
|
|
static void au1000_mdio_write(struct net_device *, int, int, u16);
|
2006-06-01 14:41:04 +07:00
|
|
|
static void au1000_adjust_link(struct net_device *);
|
|
|
|
static void enable_mac(struct net_device *, int);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Theory of operation
|
|
|
|
*
|
2006-09-14 00:24:59 +07:00
|
|
|
* The Au1000 MACs use a simple rx and tx descriptor ring scheme.
|
|
|
|
* There are four receive and four transmit descriptors. These
|
|
|
|
* descriptors are not in memory; rather, they are just a set of
|
2005-04-17 05:20:36 +07:00
|
|
|
* hardware registers.
|
|
|
|
*
|
|
|
|
* Since the Au1000 has a coherent data cache, the receive and
|
2006-09-14 00:24:59 +07:00
|
|
|
* transmit buffers are allocated from the KSEG0 segment. The
|
2005-04-17 05:20:36 +07:00
|
|
|
* hardware registers, however, are still mapped at KSEG1 to
|
|
|
|
* make sure there's no out-of-order writes, and that all writes
|
|
|
|
* complete immediately.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* These addresses are only used if yamon doesn't tell us what
|
|
|
|
* the mac address is, and the mac address is not passed on the
|
|
|
|
* command line.
|
|
|
|
*/
|
2006-09-14 00:24:59 +07:00
|
|
|
static unsigned char au1000_mac_addr[6] __devinitdata = {
|
2005-04-17 05:20:36 +07:00
|
|
|
0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00
|
|
|
|
};
|
|
|
|
|
|
|
|
struct au1000_private *au_macs[NUM_ETH_INTERFACES];
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/*
|
|
|
|
* board-specific configurations
|
|
|
|
*
|
|
|
|
* PHY detection algorithm
|
|
|
|
*
|
|
|
|
* If AU1XXX_PHY_STATIC_CONFIG is undefined, the PHY setup is
|
|
|
|
* autodetected:
|
|
|
|
*
|
|
|
|
* mii_probe() first searches the current MAC's MII bus for a PHY,
|
|
|
|
* selecting the first (or last, if AU1XXX_PHY_SEARCH_HIGHEST_ADDR is
|
|
|
|
* defined) PHY address not already claimed by another netdev.
|
|
|
|
*
|
|
|
|
* If nothing was found that way when searching for the 2nd ethernet
|
|
|
|
* controller's PHY and AU1XXX_PHY1_SEARCH_ON_MAC0 is defined, then
|
|
|
|
* the first MII bus is searched as well for an unclaimed PHY; this is
|
|
|
|
* needed in case of a dual-PHY accessible only through the MAC0's MII
|
|
|
|
* bus.
|
|
|
|
*
|
|
|
|
* Finally, if no PHY is found, then the corresponding ethernet
|
|
|
|
* controller is not registered to the network subsystem.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/* autodetection defaults */
|
|
|
|
#undef AU1XXX_PHY_SEARCH_HIGHEST_ADDR
|
|
|
|
#define AU1XXX_PHY1_SEARCH_ON_MAC0
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/* static PHY setup
|
|
|
|
*
|
|
|
|
* most boards PHY setup should be detectable properly with the
|
|
|
|
* autodetection algorithm in mii_probe(), but in some cases (e.g. if
|
|
|
|
* you have a switch attached, or want to use the PHY's interrupt
|
|
|
|
* notification capabilities) you can provide a static PHY
|
|
|
|
* configuration here
|
|
|
|
*
|
|
|
|
* IRQs may only be set, if a PHY address was configured
|
|
|
|
* If a PHY address is given, also a bus id is required to be set
|
|
|
|
*
|
|
|
|
* ps: make sure the used irqs are configured properly in the board
|
|
|
|
* specific irq-map
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
#if defined(CONFIG_MIPS_BOSPORUS)
|
|
|
|
/*
|
|
|
|
* Micrel/Kendin 5 port switch attached to MAC0,
|
|
|
|
* MAC0 is associated with PHY address 5 (== WAN port)
|
|
|
|
* MAC1 is not associated with any PHY, since it's connected directly
|
|
|
|
* to the switch.
|
|
|
|
* no interrupts are used
|
|
|
|
*/
|
|
|
|
# define AU1XXX_PHY_STATIC_CONFIG
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
# define AU1XXX_PHY0_ADDR 5
|
|
|
|
# define AU1XXX_PHY0_BUSID 0
|
|
|
|
# undef AU1XXX_PHY0_IRQ
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
# undef AU1XXX_PHY1_ADDR
|
|
|
|
# undef AU1XXX_PHY1_BUSID
|
|
|
|
# undef AU1XXX_PHY1_IRQ
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
#if defined(AU1XXX_PHY0_BUSID) && (AU1XXX_PHY0_BUSID > 0)
|
|
|
|
# error MAC0-associated PHY attached 2nd MACs MII bus not supported yet
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/*
|
|
|
|
* MII operations
|
|
|
|
*/
|
2008-10-13 11:02:19 +07:00
|
|
|
static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2006-06-01 14:41:04 +07:00
|
|
|
volatile u32 *const mii_control_reg = &aup->mac->mii_control;
|
|
|
|
volatile u32 *const mii_data_reg = &aup->mac->mii_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 timedout = 20;
|
|
|
|
u32 mii_control;
|
|
|
|
|
|
|
|
while (*mii_control_reg & MAC_MII_BUSY) {
|
|
|
|
mdelay(1);
|
|
|
|
if (--timedout == 0) {
|
2006-09-14 00:24:59 +07:00
|
|
|
printk(KERN_ERR "%s: read_MII busy timeout!!\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
mii_control = MAC_SET_MII_SELECT_REG(reg) |
|
2006-06-01 14:41:04 +07:00
|
|
|
MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
*mii_control_reg = mii_control;
|
|
|
|
|
|
|
|
timedout = 20;
|
|
|
|
while (*mii_control_reg & MAC_MII_BUSY) {
|
|
|
|
mdelay(1);
|
|
|
|
if (--timedout == 0) {
|
2006-09-14 00:24:59 +07:00
|
|
|
printk(KERN_ERR "%s: mdio_read busy timeout!!\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (int)*mii_data_reg;
|
|
|
|
}
|
|
|
|
|
2008-10-13 11:02:19 +07:00
|
|
|
static void au1000_mdio_write(struct net_device *dev, int phy_addr,
|
|
|
|
int reg, u16 value)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2006-06-01 14:41:04 +07:00
|
|
|
volatile u32 *const mii_control_reg = &aup->mac->mii_control;
|
|
|
|
volatile u32 *const mii_data_reg = &aup->mac->mii_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 timedout = 20;
|
|
|
|
u32 mii_control;
|
|
|
|
|
|
|
|
while (*mii_control_reg & MAC_MII_BUSY) {
|
|
|
|
mdelay(1);
|
|
|
|
if (--timedout == 0) {
|
2006-09-14 00:24:59 +07:00
|
|
|
printk(KERN_ERR "%s: mdio_write busy timeout!!\n",
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
mii_control = MAC_SET_MII_SELECT_REG(reg) |
|
2006-06-01 14:41:04 +07:00
|
|
|
MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_WRITE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
*mii_data_reg = value;
|
|
|
|
*mii_control_reg = mii_control;
|
|
|
|
}
|
|
|
|
|
2008-10-13 11:02:19 +07:00
|
|
|
static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
|
2006-06-01 14:41:04 +07:00
|
|
|
{
|
|
|
|
/* WARNING: bus->phy_map[phy_addr].attached_dev == dev does
|
|
|
|
* _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus) */
|
|
|
|
struct net_device *const dev = bus->priv;
|
|
|
|
|
|
|
|
enable_mac(dev, 0); /* make sure the MAC associated with this
|
|
|
|
* mii_bus is enabled */
|
2008-10-13 11:02:19 +07:00
|
|
|
return au1000_mdio_read(dev, phy_addr, regnum);
|
2006-06-01 14:41:04 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-10-13 11:02:19 +07:00
|
|
|
static int au1000_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
|
|
|
|
u16 value)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-06-01 14:41:04 +07:00
|
|
|
struct net_device *const dev = bus->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
enable_mac(dev, 0); /* make sure the MAC associated with this
|
|
|
|
* mii_bus is enabled */
|
2008-10-13 11:02:19 +07:00
|
|
|
au1000_mdio_write(dev, phy_addr, regnum, value);
|
2006-06-01 14:41:04 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-10-13 11:02:19 +07:00
|
|
|
static int au1000_mdiobus_reset(struct mii_bus *bus)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-06-01 14:41:04 +07:00
|
|
|
struct net_device *const dev = bus->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
enable_mac(dev, 0); /* make sure the MAC associated with this
|
|
|
|
* mii_bus is enabled */
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static int mii_probe (struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *const aup = (struct au1000_private *) dev->priv;
|
|
|
|
struct phy_device *phydev = NULL;
|
|
|
|
|
|
|
|
#if defined(AU1XXX_PHY_STATIC_CONFIG)
|
|
|
|
BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
|
|
|
|
|
|
|
|
if(aup->mac_id == 0) { /* get PHY0 */
|
|
|
|
# if defined(AU1XXX_PHY0_ADDR)
|
2008-10-09 06:29:57 +07:00
|
|
|
phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus->phy_map[AU1XXX_PHY0_ADDR];
|
2006-06-01 14:41:04 +07:00
|
|
|
# else
|
|
|
|
printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
|
|
|
|
dev->name);
|
|
|
|
return 0;
|
|
|
|
# endif /* defined(AU1XXX_PHY0_ADDR) */
|
|
|
|
} else if (aup->mac_id == 1) { /* get PHY1 */
|
|
|
|
# if defined(AU1XXX_PHY1_ADDR)
|
2008-10-09 06:29:57 +07:00
|
|
|
phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus->phy_map[AU1XXX_PHY1_ADDR];
|
2006-06-01 14:41:04 +07:00
|
|
|
# else
|
|
|
|
printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
|
|
|
|
dev->name);
|
|
|
|
return 0;
|
|
|
|
# endif /* defined(AU1XXX_PHY1_ADDR) */
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* defined(AU1XXX_PHY_STATIC_CONFIG) */
|
|
|
|
int phy_addr;
|
|
|
|
|
|
|
|
/* find the first (lowest address) PHY on the current MAC's MII bus */
|
|
|
|
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
|
2008-10-09 06:29:57 +07:00
|
|
|
if (aup->mii_bus->phy_map[phy_addr]) {
|
|
|
|
phydev = aup->mii_bus->phy_map[phy_addr];
|
2006-06-01 14:41:04 +07:00
|
|
|
# if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR)
|
|
|
|
break; /* break out with first one found */
|
|
|
|
# endif
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
# if defined(AU1XXX_PHY1_SEARCH_ON_MAC0)
|
|
|
|
/* try harder to find a PHY */
|
|
|
|
if (!phydev && (aup->mac_id == 1)) {
|
|
|
|
/* no PHY found, maybe we have a dual PHY? */
|
|
|
|
printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "
|
|
|
|
"let's see if it's attached to MAC0...\n");
|
|
|
|
|
|
|
|
BUG_ON(!au_macs[0]);
|
|
|
|
|
|
|
|
/* find the first (lowest address) non-attached PHY on
|
|
|
|
* the MAC0 MII bus */
|
|
|
|
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
|
|
|
|
struct phy_device *const tmp_phydev =
|
2008-10-09 06:29:57 +07:00
|
|
|
au_macs[0]->mii_bus->phy_map[phy_addr];
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
if (!tmp_phydev)
|
|
|
|
continue; /* no PHY here... */
|
|
|
|
|
|
|
|
if (tmp_phydev->attached_dev)
|
|
|
|
continue; /* already claimed by MAC0 */
|
|
|
|
|
|
|
|
phydev = tmp_phydev;
|
|
|
|
break; /* found it */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2006-06-01 14:41:04 +07:00
|
|
|
# endif /* defined(AU1XXX_PHY1_SEARCH_OTHER_BUS) */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
#endif /* defined(AU1XXX_PHY_STATIC_CONFIG) */
|
|
|
|
if (!phydev) {
|
|
|
|
printk (KERN_ERR DRV_NAME ":%s: no PHY found\n", dev->name);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/* now we are supposed to have a proper phydev, to attach to... */
|
|
|
|
BUG_ON(!phydev);
|
|
|
|
BUG_ON(phydev->attached_dev);
|
|
|
|
|
2006-12-02 01:01:06 +07:00
|
|
|
phydev = phy_connect(dev, phydev->dev.bus_id, &au1000_adjust_link, 0,
|
|
|
|
PHY_INTERFACE_MODE_MII);
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
if (IS_ERR(phydev)) {
|
|
|
|
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
|
|
|
|
return PTR_ERR(phydev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mask with MAC supported features */
|
|
|
|
phydev->supported &= (SUPPORTED_10baseT_Half
|
|
|
|
| SUPPORTED_10baseT_Full
|
|
|
|
| SUPPORTED_100baseT_Half
|
|
|
|
| SUPPORTED_100baseT_Full
|
|
|
|
| SUPPORTED_Autoneg
|
|
|
|
/* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */
|
|
|
|
| SUPPORTED_MII
|
|
|
|
| SUPPORTED_TP);
|
|
|
|
|
|
|
|
phydev->advertising = phydev->supported;
|
|
|
|
|
|
|
|
aup->old_link = 0;
|
|
|
|
aup->old_speed = 0;
|
|
|
|
aup->old_duplex = -1;
|
|
|
|
aup->phy_dev = phydev;
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: attached PHY driver [%s] "
|
|
|
|
"(mii_bus:phy_addr=%s, irq=%d)\n",
|
|
|
|
dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Buffer allocation/deallocation routines. The buffer descriptor returned
|
2006-09-14 00:24:59 +07:00
|
|
|
* has the virtual and dma address of a buffer suitable for
|
2005-04-17 05:20:36 +07:00
|
|
|
* both, receive and transmit operations.
|
|
|
|
*/
|
|
|
|
static db_dest_t *GetFreeDB(struct au1000_private *aup)
|
|
|
|
{
|
|
|
|
db_dest_t *pDB;
|
|
|
|
pDB = aup->pDBfree;
|
|
|
|
|
|
|
|
if (pDB) {
|
|
|
|
aup->pDBfree = pDB->pnext;
|
|
|
|
}
|
|
|
|
return pDB;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB)
|
|
|
|
{
|
|
|
|
db_dest_t *pDBfree = aup->pDBfree;
|
|
|
|
if (pDBfree)
|
|
|
|
pDBfree->pnext = pDB;
|
|
|
|
aup->pDBfree = pDB;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void enable_rx_tx(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk(KERN_INFO "%s: enable_rx_tx\n", dev->name);
|
|
|
|
|
|
|
|
aup->mac->control |= (MAC_RX_ENABLE | MAC_TX_ENABLE);
|
|
|
|
au_sync_delay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hard_stop(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk(KERN_INFO "%s: hard stop\n", dev->name);
|
|
|
|
|
|
|
|
aup->mac->control &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);
|
|
|
|
au_sync_delay(10);
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static void enable_mac(struct net_device *dev, int force_reset)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-06-01 14:41:04 +07:00
|
|
|
unsigned long flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&aup->lock, flags);
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if(force_reset || (!aup->mac_enabled)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
*aup->enable = MAC_EN_CLOCK_ENABLE;
|
|
|
|
au_sync_delay(2);
|
2006-06-01 14:41:04 +07:00
|
|
|
*aup->enable = (MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
|
|
|
|
| MAC_EN_CLOCK_ENABLE);
|
2005-04-17 05:20:36 +07:00
|
|
|
au_sync_delay(2);
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
aup->mac_enabled = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&aup->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reset_mac_unlocked(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *const aup = (struct au1000_private *) dev->priv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
hard_stop(dev);
|
|
|
|
|
|
|
|
*aup->enable = MAC_EN_CLOCK_ENABLE;
|
|
|
|
au_sync_delay(2);
|
|
|
|
*aup->enable = 0;
|
|
|
|
au_sync_delay(2);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
aup->tx_full = 0;
|
|
|
|
for (i = 0; i < NUM_RX_DMA; i++) {
|
|
|
|
/* reset control bits */
|
|
|
|
aup->rx_dma_ring[i]->buff_stat &= ~0xf;
|
|
|
|
}
|
|
|
|
for (i = 0; i < NUM_TX_DMA; i++) {
|
|
|
|
/* reset control bits */
|
|
|
|
aup->tx_dma_ring[i]->buff_stat &= ~0xf;
|
|
|
|
}
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
aup->mac_enabled = 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static void reset_mac(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *const aup = (struct au1000_private *) dev->priv;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk(KERN_INFO "%s: reset mac, aup %x\n",
|
|
|
|
dev->name, (unsigned)aup);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&aup->lock, flags);
|
|
|
|
|
|
|
|
reset_mac_unlocked (dev);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&aup->lock, flags);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Setup the receive and transmit "rings". These pointers are the addresses
|
|
|
|
* of the rx and tx MAC DMA registers so they are fixed by the hardware --
|
|
|
|
* these are not descriptors sitting in memory.
|
|
|
|
*/
|
2006-09-14 00:24:59 +07:00
|
|
|
static void
|
2005-04-17 05:20:36 +07:00
|
|
|
setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_RX_DMA; i++) {
|
2006-09-14 00:24:59 +07:00
|
|
|
aup->rx_dma_ring[i] =
|
2005-04-17 05:20:36 +07:00
|
|
|
(volatile rx_dma_t *) (rx_base + sizeof(rx_dma_t)*i);
|
|
|
|
}
|
|
|
|
for (i = 0; i < NUM_TX_DMA; i++) {
|
2006-09-14 00:24:59 +07:00
|
|
|
aup->tx_dma_ring[i] =
|
2005-04-17 05:20:36 +07:00
|
|
|
(volatile tx_dma_t *) (tx_base + sizeof(tx_dma_t)*i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
u32 base_addr;
|
|
|
|
u32 macen_addr;
|
|
|
|
int irq;
|
|
|
|
struct net_device *dev;
|
2006-04-20 01:46:21 +07:00
|
|
|
} iflist[2] = {
|
|
|
|
#ifdef CONFIG_SOC_AU1000
|
|
|
|
{AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT},
|
|
|
|
{AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SOC_AU1100
|
|
|
|
{AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SOC_AU1500
|
|
|
|
{AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT},
|
|
|
|
{AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SOC_AU1550
|
|
|
|
{AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT},
|
|
|
|
{AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT}
|
|
|
|
#endif
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int num_ifs;
|
|
|
|
|
|
|
|
/*
|
2007-10-20 04:10:43 +07:00
|
|
|
* Setup the base address and interrupt of the Au1xxx ethernet macs
|
2005-04-17 05:20:36 +07:00
|
|
|
* based on cpu type and whether the interface is enabled in sys_pinfunc
|
|
|
|
* register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0.
|
|
|
|
*/
|
|
|
|
static int __init au1000_init_module(void)
|
|
|
|
{
|
|
|
|
int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
|
|
|
|
struct net_device *dev;
|
|
|
|
int i, found_one = 0;
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
num_ifs = NUM_ETH_INTERFACES - ni;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
for(i = 0; i < num_ifs; i++) {
|
2006-04-20 01:46:21 +07:00
|
|
|
dev = au1000_probe(i);
|
2005-04-17 05:20:36 +07:00
|
|
|
iflist[i].dev = dev;
|
|
|
|
if (dev)
|
|
|
|
found_one++;
|
|
|
|
}
|
|
|
|
if (!found_one)
|
|
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
/*
|
|
|
|
* ethtool operations
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static int au1000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *)dev->priv;
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (aup->phy_dev)
|
|
|
|
return phy_ethtool_gset(aup->phy_dev, cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static int au1000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *)dev->priv;
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (aup->phy_dev)
|
|
|
|
return phy_ethtool_sset(aup->phy_dev, cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *)dev->priv;
|
|
|
|
|
|
|
|
strcpy(info->driver, DRV_NAME);
|
|
|
|
strcpy(info->version, DRV_VERSION);
|
|
|
|
info->fw_version[0] = '\0';
|
|
|
|
sprintf(info->bus_info, "%s %d", DRV_NAME, aup->mac_id);
|
|
|
|
info->regdump_len = 0;
|
|
|
|
}
|
|
|
|
|
2006-09-14 01:30:00 +07:00
|
|
|
static const struct ethtool_ops au1000_ethtool_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.get_settings = au1000_get_settings,
|
|
|
|
.set_settings = au1000_set_settings,
|
|
|
|
.get_drvinfo = au1000_get_drvinfo,
|
2006-06-01 14:41:04 +07:00
|
|
|
.get_link = ethtool_op_get_link,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
static struct net_device * au1000_probe(int port_num)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
static unsigned version_printed = 0;
|
|
|
|
struct au1000_private *aup = NULL;
|
|
|
|
struct net_device *dev = NULL;
|
|
|
|
db_dest_t *pDB, *pDBfree;
|
|
|
|
char ethaddr[6];
|
2006-04-20 01:46:21 +07:00
|
|
|
int irq, i, err;
|
|
|
|
u32 base, macen;
|
|
|
|
|
|
|
|
if (port_num >= NUM_ETH_INTERFACES)
|
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
base = CPHYSADDR(iflist[port_num].base_addr );
|
|
|
|
macen = CPHYSADDR(iflist[port_num].macen_addr);
|
|
|
|
irq = iflist[port_num].irq;
|
|
|
|
|
|
|
|
if (!request_mem_region( base, MAC_IOSIZE, "Au1x00 ENET") ||
|
|
|
|
!request_mem_region(macen, 4, "Au1x00 ENET"))
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
if (version_printed++ == 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
|
|
|
|
|
|
|
|
dev = alloc_etherdev(sizeof(struct au1000_private));
|
|
|
|
if (!dev) {
|
2006-04-20 01:46:21 +07:00
|
|
|
printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME);
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
if ((err = register_netdev(dev)) != 0) {
|
|
|
|
printk(KERN_ERR "%s: Cannot register net device, error %d\n",
|
|
|
|
DRV_NAME, err);
|
2005-04-17 05:20:36 +07:00
|
|
|
free_netdev(dev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
|
|
|
|
dev->name, base, irq);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
aup = dev->priv;
|
|
|
|
|
2008-07-23 14:40:09 +07:00
|
|
|
spin_lock_init(&aup->lock);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Allocate the data buffers */
|
|
|
|
/* Snooping works fine with eth on all au1xxx */
|
2006-04-20 01:46:21 +07:00
|
|
|
aup->vaddr = (u32)dma_alloc_noncoherent(NULL, MAX_BUF_SIZE *
|
|
|
|
(NUM_TX_BUFFS + NUM_RX_BUFFS),
|
|
|
|
&aup->dma_addr, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!aup->vaddr) {
|
|
|
|
free_netdev(dev);
|
2006-04-20 01:46:21 +07:00
|
|
|
release_mem_region( base, MAC_IOSIZE);
|
|
|
|
release_mem_region(macen, 4);
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* aup->mac is the base address of the MAC's registers */
|
2006-04-20 01:46:21 +07:00
|
|
|
aup->mac = (volatile mac_reg_t *)iflist[port_num].base_addr;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Setup some variables for quick register address access */
|
2006-04-20 01:46:21 +07:00
|
|
|
aup->enable = (volatile u32 *)iflist[port_num].macen_addr;
|
|
|
|
aup->mac_id = port_num;
|
|
|
|
au_macs[port_num] = aup;
|
|
|
|
|
|
|
|
if (port_num == 0) {
|
2007-10-15 17:06:20 +07:00
|
|
|
if (prom_get_ethernet_addr(ethaddr) == 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr));
|
2006-04-20 01:46:21 +07:00
|
|
|
else {
|
2007-10-15 17:06:20 +07:00
|
|
|
printk(KERN_INFO "%s: No MAC address found\n",
|
|
|
|
dev->name);
|
2006-04-20 01:46:21 +07:00
|
|
|
/* Use the hard coded MAC addresses */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-04-20 01:46:21 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
|
2006-04-20 01:46:21 +07:00
|
|
|
} else if (port_num == 1)
|
2005-04-17 05:20:36 +07:00
|
|
|
setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
/*
|
|
|
|
* Assign to the Ethernet ports two consecutive MAC addresses
|
|
|
|
* to match those that are printed on their stickers
|
|
|
|
*/
|
|
|
|
memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
|
|
|
|
dev->dev_addr[5] += port_num;
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
*aup->enable = 0;
|
|
|
|
aup->mac_enabled = 0;
|
|
|
|
|
2008-10-09 06:29:57 +07:00
|
|
|
aup->mii_bus = mdiobus_alloc();
|
|
|
|
if (aup->mii_bus == NULL)
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
aup->mii_bus->priv = dev;
|
2008-10-13 11:02:19 +07:00
|
|
|
aup->mii_bus->read = au1000_mdiobus_read;
|
|
|
|
aup->mii_bus->write = au1000_mdiobus_write;
|
|
|
|
aup->mii_bus->reset = au1000_mdiobus_reset;
|
2008-10-09 06:29:57 +07:00
|
|
|
aup->mii_bus->name = "au1000_eth_mii";
|
|
|
|
snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%x", aup->mac_id);
|
|
|
|
aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
|
2006-06-01 14:41:04 +07:00
|
|
|
for(i = 0; i < PHY_MAX_ADDR; ++i)
|
2008-10-09 06:29:57 +07:00
|
|
|
aup->mii_bus->irq[i] = PHY_POLL;
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
/* if known, set corresponding PHY IRQs */
|
|
|
|
#if defined(AU1XXX_PHY_STATIC_CONFIG)
|
|
|
|
# if defined(AU1XXX_PHY0_IRQ)
|
2008-04-10 07:38:13 +07:00
|
|
|
if (AU1XXX_PHY0_BUSID == aup->mac_id)
|
2008-10-09 06:29:57 +07:00
|
|
|
aup->mii_bus->irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ;
|
2006-06-01 14:41:04 +07:00
|
|
|
# endif
|
|
|
|
# if defined(AU1XXX_PHY1_IRQ)
|
2008-04-10 07:38:13 +07:00
|
|
|
if (AU1XXX_PHY1_BUSID == aup->mac_id)
|
2008-10-09 06:29:57 +07:00
|
|
|
aup->mii_bus->irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ;
|
2006-06-01 14:41:04 +07:00
|
|
|
# endif
|
|
|
|
#endif
|
2008-10-09 06:29:57 +07:00
|
|
|
mdiobus_register(aup->mii_bus);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (mii_probe(dev) != 0) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDBfree = NULL;
|
|
|
|
/* setup the data buffer descriptors and attach a buffer to each one */
|
|
|
|
pDB = aup->db;
|
|
|
|
for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
|
|
|
|
pDB->pnext = pDBfree;
|
|
|
|
pDBfree = pDB;
|
|
|
|
pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
|
|
|
|
pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
|
|
|
|
pDB++;
|
|
|
|
}
|
|
|
|
aup->pDBfree = pDBfree;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_RX_DMA; i++) {
|
|
|
|
pDB = GetFreeDB(aup);
|
|
|
|
if (!pDB) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
|
|
|
|
aup->rx_db_inuse[i] = pDB;
|
|
|
|
}
|
|
|
|
for (i = 0; i < NUM_TX_DMA; i++) {
|
|
|
|
pDB = GetFreeDB(aup);
|
|
|
|
if (!pDB) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
|
|
|
|
aup->tx_dma_ring[i]->len = 0;
|
|
|
|
aup->tx_db_inuse[i] = pDB;
|
|
|
|
}
|
|
|
|
|
2006-04-20 01:46:21 +07:00
|
|
|
dev->base_addr = base;
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->irq = irq;
|
|
|
|
dev->open = au1000_open;
|
|
|
|
dev->hard_start_xmit = au1000_tx;
|
|
|
|
dev->stop = au1000_close;
|
|
|
|
dev->set_multicast_list = &set_rx_mode;
|
|
|
|
dev->do_ioctl = &au1000_ioctl;
|
|
|
|
SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
|
|
|
|
dev->tx_timeout = au1000_tx_timeout;
|
|
|
|
dev->watchdog_timeo = ETH_TX_TIMEOUT;
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
/*
|
|
|
|
* The boot code uses the ethernet controller, so reset it to start
|
2005-04-17 05:20:36 +07:00
|
|
|
* fresh. au1000_init() expects that the device is in reset state.
|
|
|
|
*/
|
|
|
|
reset_mac(dev);
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
|
|
|
|
err_out:
|
2008-10-09 06:29:57 +07:00
|
|
|
if (aup->mii_bus != NULL) {
|
|
|
|
mdiobus_unregister(aup->mii_bus);
|
|
|
|
mdiobus_free(aup->mii_bus);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* here we should have a valid dev plus aup-> register addresses
|
|
|
|
* so we can reset the mac properly.*/
|
|
|
|
reset_mac(dev);
|
2006-06-01 14:41:04 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < NUM_RX_DMA; i++) {
|
|
|
|
if (aup->rx_db_inuse[i])
|
|
|
|
ReleaseDB(aup, aup->rx_db_inuse[i]);
|
|
|
|
}
|
|
|
|
for (i = 0; i < NUM_TX_DMA; i++) {
|
|
|
|
if (aup->tx_db_inuse[i])
|
|
|
|
ReleaseDB(aup, aup->tx_db_inuse[i]);
|
|
|
|
}
|
2006-04-20 01:46:21 +07:00
|
|
|
dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
|
|
|
|
(void *)aup->vaddr, aup->dma_addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
unregister_netdev(dev);
|
|
|
|
free_netdev(dev);
|
2006-04-20 01:46:21 +07:00
|
|
|
release_mem_region( base, MAC_IOSIZE);
|
|
|
|
release_mem_region(macen, 4);
|
2005-04-17 05:20:36 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Initialize the interface.
|
|
|
|
*
|
|
|
|
* When the device powers up, the clocks are disabled and the
|
|
|
|
* mac is in reset state. When the interface is closed, we
|
|
|
|
* do the same -- reset the device and disable the clocks to
|
|
|
|
* conserve power. Thus, whenever au1000_init() is called,
|
|
|
|
* the device should already be in reset state.
|
|
|
|
*/
|
|
|
|
static int au1000_init(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2008-08-06 19:12:11 +07:00
|
|
|
unsigned long flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
int i;
|
|
|
|
u32 control;
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
if (au1000_debug > 4)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("%s: au1000_init\n", dev->name);
|
|
|
|
|
|
|
|
/* bring the device out of reset */
|
2006-06-01 14:41:04 +07:00
|
|
|
enable_mac(dev, 1);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&aup->lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
aup->mac->control = 0;
|
|
|
|
aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2;
|
|
|
|
aup->tx_tail = aup->tx_head;
|
|
|
|
aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2;
|
|
|
|
|
|
|
|
aup->mac->mac_addr_high = dev->dev_addr[5]<<8 | dev->dev_addr[4];
|
|
|
|
aup->mac->mac_addr_low = dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
|
|
|
|
dev->dev_addr[1]<<8 | dev->dev_addr[0];
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_RX_DMA; i++) {
|
|
|
|
aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
|
|
|
|
}
|
|
|
|
au_sync();
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
control = MAC_RX_ENABLE | MAC_TX_ENABLE;
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifndef CONFIG_CPU_LITTLE_ENDIAN
|
|
|
|
control |= MAC_BIG_ENDIAN;
|
|
|
|
#endif
|
2006-06-01 14:41:04 +07:00
|
|
|
if (aup->phy_dev) {
|
|
|
|
if (aup->phy_dev->link && (DUPLEX_FULL == aup->phy_dev->duplex))
|
|
|
|
control |= MAC_FULL_DUPLEX;
|
|
|
|
else
|
|
|
|
control |= MAC_DISABLE_RX_OWN;
|
|
|
|
} else { /* PHY-less op, assume full-duplex */
|
2005-04-17 05:20:36 +07:00
|
|
|
control |= MAC_FULL_DUPLEX;
|
|
|
|
}
|
|
|
|
|
|
|
|
aup->mac->control = control;
|
|
|
|
aup->mac->vlan1_tag = 0x8100; /* activate vlan support */
|
|
|
|
au_sync();
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&aup->lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
static void
|
|
|
|
au1000_adjust_link(struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2006-06-01 14:41:04 +07:00
|
|
|
struct phy_device *phydev = aup->phy_dev;
|
|
|
|
unsigned long flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
int status_change = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
BUG_ON(!aup->phy_dev);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&aup->lock, flags);
|
|
|
|
|
|
|
|
if (phydev->link && (aup->old_speed != phydev->speed)) {
|
|
|
|
// speed changed
|
|
|
|
|
|
|
|
switch(phydev->speed) {
|
|
|
|
case SPEED_10:
|
|
|
|
case SPEED_100:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: Speed (%d) is not 10/100 ???\n",
|
|
|
|
dev->name, phydev->speed);
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
aup->old_speed = phydev->speed;
|
|
|
|
|
|
|
|
status_change = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (phydev->link && (aup->old_duplex != phydev->duplex)) {
|
|
|
|
// duplex mode changed
|
|
|
|
|
|
|
|
/* switching duplex mode requires to disable rx and tx! */
|
2005-04-17 05:20:36 +07:00
|
|
|
hard_stop(dev);
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
if (DUPLEX_FULL == phydev->duplex)
|
|
|
|
aup->mac->control = ((aup->mac->control
|
|
|
|
| MAC_FULL_DUPLEX)
|
|
|
|
& ~MAC_DISABLE_RX_OWN);
|
|
|
|
else
|
|
|
|
aup->mac->control = ((aup->mac->control
|
|
|
|
& ~MAC_FULL_DUPLEX)
|
|
|
|
| MAC_DISABLE_RX_OWN);
|
|
|
|
au_sync_delay(1);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
enable_rx_tx(dev);
|
2006-06-01 14:41:04 +07:00
|
|
|
aup->old_duplex = phydev->duplex;
|
|
|
|
|
|
|
|
status_change = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(phydev->link != aup->old_link) {
|
|
|
|
// link state changed
|
|
|
|
|
2008-07-23 05:41:24 +07:00
|
|
|
if (!phydev->link) {
|
|
|
|
/* link went down */
|
2006-06-01 14:41:04 +07:00
|
|
|
aup->old_speed = 0;
|
|
|
|
aup->old_duplex = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
aup->old_link = phydev->link;
|
|
|
|
status_change = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
spin_unlock_irqrestore(&aup->lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (status_change) {
|
|
|
|
if (phydev->link)
|
|
|
|
printk(KERN_INFO "%s: link up (%d/%s)\n",
|
|
|
|
dev->name, phydev->speed,
|
|
|
|
DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
|
|
|
|
else
|
|
|
|
printk(KERN_INFO "%s: link down\n", dev->name);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int au1000_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk("%s: open: dev=%p\n", dev->name, dev);
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if ((retval = request_irq(dev->irq, &au1000_interrupt, 0,
|
|
|
|
dev->name, dev))) {
|
|
|
|
printk(KERN_ERR "%s: unable to get IRQ %d\n",
|
|
|
|
dev->name, dev->irq);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if ((retval = au1000_init(dev))) {
|
|
|
|
printk(KERN_ERR "%s: error in au1000_init\n", dev->name);
|
|
|
|
free_irq(dev->irq, dev);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (aup->phy_dev) {
|
|
|
|
/* cause the PHY state machine to schedule a link state check */
|
|
|
|
aup->phy_dev->state = PHY_CHANGELINK;
|
|
|
|
phy_start(aup->phy_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
netif_start_queue(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk("%s: open: Initialization done.\n", dev->name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int au1000_close(struct net_device *dev)
|
|
|
|
{
|
2006-06-01 14:41:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
struct au1000_private *const aup = (struct au1000_private *) dev->priv;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (au1000_debug > 4)
|
|
|
|
printk("%s: close: dev=%p\n", dev->name, dev);
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (aup->phy_dev)
|
|
|
|
phy_stop(aup->phy_dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&aup->lock, flags);
|
2006-06-01 14:41:04 +07:00
|
|
|
|
|
|
|
reset_mac_unlocked (dev);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* stop the device */
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
/* disable the interrupt */
|
|
|
|
free_irq(dev->irq, dev);
|
|
|
|
spin_unlock_irqrestore(&aup->lock, flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit au1000_cleanup_module(void)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct au1000_private *aup;
|
|
|
|
|
|
|
|
for (i = 0; i < num_ifs; i++) {
|
|
|
|
dev = iflist[i].dev;
|
|
|
|
if (dev) {
|
|
|
|
aup = (struct au1000_private *) dev->priv;
|
|
|
|
unregister_netdev(dev);
|
2008-10-09 06:29:57 +07:00
|
|
|
mdiobus_unregister(aup->mii_bus);
|
|
|
|
mdiobus_free(aup->mii_bus);
|
2006-04-20 01:46:21 +07:00
|
|
|
for (j = 0; j < NUM_RX_DMA; j++)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (aup->rx_db_inuse[j])
|
|
|
|
ReleaseDB(aup, aup->rx_db_inuse[j]);
|
2006-04-20 01:46:21 +07:00
|
|
|
for (j = 0; j < NUM_TX_DMA; j++)
|
2005-04-17 05:20:36 +07:00
|
|
|
if (aup->tx_db_inuse[j])
|
|
|
|
ReleaseDB(aup, aup->tx_db_inuse[j]);
|
2006-04-20 01:46:21 +07:00
|
|
|
dma_free_noncoherent(NULL, MAX_BUF_SIZE *
|
|
|
|
(NUM_TX_BUFFS + NUM_RX_BUFFS),
|
|
|
|
(void *)aup->vaddr, aup->dma_addr);
|
|
|
|
release_mem_region(dev->base_addr, MAC_IOSIZE);
|
|
|
|
release_mem_region(CPHYSADDR(iflist[i].macen_addr), 4);
|
2005-04-17 05:20:36 +07:00
|
|
|
free_netdev(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-22 13:53:52 +07:00
|
|
|
static void update_tx_stats(struct net_device *dev, u32 status)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2007-10-04 07:41:50 +07:00
|
|
|
struct net_device_stats *ps = &dev->stats;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (status & TX_FRAME_ABORTED) {
|
2006-06-01 14:41:04 +07:00
|
|
|
if (!aup->phy_dev || (DUPLEX_FULL == aup->phy_dev->duplex)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) {
|
|
|
|
/* any other tx errors are only valid
|
|
|
|
* in half duplex mode */
|
|
|
|
ps->tx_errors++;
|
|
|
|
ps->tx_aborted_errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ps->tx_errors++;
|
|
|
|
ps->tx_aborted_errors++;
|
|
|
|
if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER))
|
|
|
|
ps->tx_carrier_errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called from the interrupt service routine to acknowledge
|
|
|
|
* the TX DONE bits. This is a must if the irq is setup as
|
|
|
|
* edge triggered.
|
|
|
|
*/
|
|
|
|
static void au1000_tx_ack(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
volatile tx_dma_t *ptxd;
|
|
|
|
|
|
|
|
ptxd = aup->tx_dma_ring[aup->tx_tail];
|
|
|
|
|
|
|
|
while (ptxd->buff_stat & TX_T_DONE) {
|
2006-03-22 13:53:52 +07:00
|
|
|
update_tx_stats(dev, ptxd->status);
|
2005-04-17 05:20:36 +07:00
|
|
|
ptxd->buff_stat &= ~TX_T_DONE;
|
|
|
|
ptxd->len = 0;
|
|
|
|
au_sync();
|
|
|
|
|
|
|
|
aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1);
|
|
|
|
ptxd = aup->tx_dma_ring[aup->tx_tail];
|
|
|
|
|
|
|
|
if (aup->tx_full) {
|
|
|
|
aup->tx_full = 0;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Au1000 transmit routine.
|
|
|
|
*/
|
|
|
|
static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2007-10-04 07:41:50 +07:00
|
|
|
struct net_device_stats *ps = &dev->stats;
|
2005-04-17 05:20:36 +07:00
|
|
|
volatile tx_dma_t *ptxd;
|
|
|
|
u32 buff_stat;
|
|
|
|
db_dest_t *pDB;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (au1000_debug > 5)
|
2006-09-14 00:24:59 +07:00
|
|
|
printk("%s: tx: aup %x len=%d, data=%p, head %d\n",
|
|
|
|
dev->name, (unsigned)aup, skb->len,
|
2005-04-17 05:20:36 +07:00
|
|
|
skb->data, aup->tx_head);
|
|
|
|
|
|
|
|
ptxd = aup->tx_dma_ring[aup->tx_head];
|
|
|
|
buff_stat = ptxd->buff_stat;
|
|
|
|
if (buff_stat & TX_DMA_ENABLE) {
|
|
|
|
/* We've wrapped around and the transmitter is still busy */
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
aup->tx_full = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (buff_stat & TX_T_DONE) {
|
2006-03-22 13:53:52 +07:00
|
|
|
update_tx_stats(dev, ptxd->status);
|
2005-04-17 05:20:36 +07:00
|
|
|
ptxd->len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aup->tx_full) {
|
|
|
|
aup->tx_full = 0;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
pDB = aup->tx_db_inuse[aup->tx_head];
|
2007-03-28 04:55:52 +07:00
|
|
|
skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (skb->len < ETH_ZLEN) {
|
2006-09-14 00:24:59 +07:00
|
|
|
for (i=skb->len; i<ETH_ZLEN; i++) {
|
2005-04-17 05:20:36 +07:00
|
|
|
((char *)pDB->vaddr)[i] = 0;
|
|
|
|
}
|
|
|
|
ptxd->len = ETH_ZLEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ptxd->len = skb->len;
|
|
|
|
|
2006-03-22 13:53:52 +07:00
|
|
|
ps->tx_packets++;
|
|
|
|
ps->tx_bytes += ptxd->len;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE;
|
|
|
|
au_sync();
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1);
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void update_rx_stats(struct net_device *dev, u32 status)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
2007-10-04 07:41:50 +07:00
|
|
|
struct net_device_stats *ps = &dev->stats;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ps->rx_packets++;
|
|
|
|
if (status & RX_MCAST_FRAME)
|
|
|
|
ps->multicast++;
|
|
|
|
|
|
|
|
if (status & RX_ERROR) {
|
|
|
|
ps->rx_errors++;
|
|
|
|
if (status & RX_MISSED_FRAME)
|
|
|
|
ps->rx_missed_errors++;
|
|
|
|
if (status & (RX_OVERLEN | RX_OVERLEN | RX_LEN_ERROR))
|
|
|
|
ps->rx_length_errors++;
|
|
|
|
if (status & RX_CRC_ERROR)
|
|
|
|
ps->rx_crc_errors++;
|
|
|
|
if (status & RX_COLL)
|
|
|
|
ps->collisions++;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
else
|
2005-04-17 05:20:36 +07:00
|
|
|
ps->rx_bytes += status & RX_FRAME_LEN_MASK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Au1000 receive routine.
|
|
|
|
*/
|
|
|
|
static int au1000_rx(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
volatile rx_dma_t *prxd;
|
|
|
|
u32 buff_stat, status;
|
|
|
|
db_dest_t *pDB;
|
|
|
|
u32 frmlen;
|
|
|
|
|
|
|
|
if (au1000_debug > 5)
|
|
|
|
printk("%s: au1000_rx head %d\n", dev->name, aup->rx_head);
|
|
|
|
|
|
|
|
prxd = aup->rx_dma_ring[aup->rx_head];
|
|
|
|
buff_stat = prxd->buff_stat;
|
|
|
|
while (buff_stat & RX_T_DONE) {
|
|
|
|
status = prxd->status;
|
|
|
|
pDB = aup->rx_db_inuse[aup->rx_head];
|
|
|
|
update_rx_stats(dev, status);
|
|
|
|
if (!(status & RX_ERROR)) {
|
|
|
|
|
|
|
|
/* good frame */
|
|
|
|
frmlen = (status & RX_FRAME_LEN_MASK);
|
|
|
|
frmlen -= 4; /* Remove FCS */
|
|
|
|
skb = dev_alloc_skb(frmlen + 2);
|
|
|
|
if (skb == NULL) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"%s: Memory squeeze, dropping packet.\n",
|
|
|
|
dev->name);
|
2007-10-04 07:41:50 +07:00
|
|
|
dev->stats.rx_dropped++;
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
skb_reserve(skb, 2); /* 16 byte IP header align */
|
2007-07-11 12:08:12 +07:00
|
|
|
skb_copy_to_linear_data(skb,
|
|
|
|
(unsigned char *)pDB->vaddr, frmlen);
|
2005-04-17 05:20:36 +07:00
|
|
|
skb_put(skb, frmlen);
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
|
|
netif_rx(skb); /* pass the packet to upper layers */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (au1000_debug > 4) {
|
2006-09-14 00:24:59 +07:00
|
|
|
if (status & RX_MISSED_FRAME)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("rx miss\n");
|
2006-09-14 00:24:59 +07:00
|
|
|
if (status & RX_WDOG_TIMER)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("rx wdog\n");
|
2006-09-14 00:24:59 +07:00
|
|
|
if (status & RX_RUNT)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("rx runt\n");
|
2006-09-14 00:24:59 +07:00
|
|
|
if (status & RX_OVERLEN)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("rx overlen\n");
|
|
|
|
if (status & RX_COLL)
|
|
|
|
printk("rx coll\n");
|
|
|
|
if (status & RX_MII_ERROR)
|
|
|
|
printk("rx mii error\n");
|
|
|
|
if (status & RX_CRC_ERROR)
|
|
|
|
printk("rx crc error\n");
|
|
|
|
if (status & RX_LEN_ERROR)
|
|
|
|
printk("rx len error\n");
|
|
|
|
if (status & RX_U_CNTRL_FRAME)
|
|
|
|
printk("rx u control frame\n");
|
|
|
|
if (status & RX_MISSED_FRAME)
|
|
|
|
printk("rx miss\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE);
|
|
|
|
aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1);
|
|
|
|
au_sync();
|
|
|
|
|
|
|
|
/* next descriptor */
|
|
|
|
prxd = aup->rx_dma_ring[aup->rx_head];
|
|
|
|
buff_stat = prxd->buff_stat;
|
|
|
|
dev->last_rx = jiffies;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Au1000 interrupt service routine.
|
|
|
|
*/
|
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 au1000_interrupt(int irq, void *dev_id)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-05-12 23:44:21 +07:00
|
|
|
struct net_device *dev = dev_id;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Handle RX interrupts first to minimize chance of overrun */
|
|
|
|
|
|
|
|
au1000_rx(dev);
|
|
|
|
au1000_tx_ack(dev);
|
|
|
|
return IRQ_RETVAL(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Tx ring has been full longer than the watchdog timeout
|
|
|
|
* value. The transmitter must be hung?
|
|
|
|
*/
|
|
|
|
static void au1000_tx_timeout(struct net_device *dev)
|
|
|
|
{
|
|
|
|
printk(KERN_ERR "%s: au1000_tx_timeout: dev=%p\n", dev->name, dev);
|
|
|
|
reset_mac(dev);
|
|
|
|
au1000_init(dev);
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_rx_mode(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *) dev->priv;
|
|
|
|
|
2006-09-14 00:24:59 +07:00
|
|
|
if (au1000_debug > 4)
|
2005-04-17 05:20:36 +07:00
|
|
|
printk("%s: set_rx_mode: flags=%x\n", dev->name, dev->flags);
|
|
|
|
|
|
|
|
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
|
|
|
|
aup->mac->control |= MAC_PROMISCUOUS;
|
|
|
|
} else if ((dev->flags & IFF_ALLMULTI) ||
|
|
|
|
dev->mc_count > MULTICAST_FILTER_LIMIT) {
|
|
|
|
aup->mac->control |= MAC_PASS_ALL_MULTI;
|
|
|
|
aup->mac->control &= ~MAC_PROMISCUOUS;
|
|
|
|
printk(KERN_INFO "%s: Pass all multicast\n", dev->name);
|
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
struct dev_mc_list *mclist;
|
|
|
|
u32 mc_filter[2]; /* Multicast hash filter */
|
|
|
|
|
|
|
|
mc_filter[1] = mc_filter[0] = 0;
|
|
|
|
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
|
|
|
|
i++, mclist = mclist->next) {
|
2006-09-14 00:24:59 +07:00
|
|
|
set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26,
|
2005-04-17 05:20:36 +07:00
|
|
|
(long *)mc_filter);
|
|
|
|
}
|
|
|
|
aup->mac->multi_hash_high = mc_filter[1];
|
|
|
|
aup->mac->multi_hash_low = mc_filter[0];
|
|
|
|
aup->mac->control &= ~MAC_PROMISCUOUS;
|
|
|
|
aup->mac->control |= MAC_HASH_MODE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
|
|
{
|
|
|
|
struct au1000_private *aup = (struct au1000_private *)dev->priv;
|
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (!netif_running(dev)) return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
if (!aup->phy_dev) return -EINVAL; // PHY not controllable
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-01 14:41:04 +07:00
|
|
|
return phy_mii_ioctl(aup->phy_dev, if_mii(rq), cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(au1000_init_module);
|
|
|
|
module_exit(au1000_cleanup_module);
|