Skip to content
Snippets Groups Projects
au1000_eth.c 32.9 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 *
 * Alchemy Au1x00 ethernet driver
 *
 * Copyright 2001-2003, 2006 MontaVista Software Inc.
Linus Torvalds's avatar
Linus Torvalds committed
 * Copyright 2002 TimeSys Corp.
 * Added ethtool/mii-tool support,
 * Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
 * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de
 * or riemer@riemer-nt.de: fixed the link beat detection with
Linus Torvalds's avatar
Linus Torvalds committed
 * ioctls (SIOCGMIIPHY)
 * Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org>
 *  converted to use linux-2.6.x's PHY framework
 *
Linus Torvalds's avatar
Linus Torvalds committed
 * 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.
 *
 * ########################################################################
 *
Linus Torvalds's avatar
Linus Torvalds committed
 */
#include <linux/capability.h>
Ralf Baechle's avatar
Ralf Baechle committed
#include <linux/dma-mapping.h>
Linus Torvalds's avatar
Linus Torvalds committed
#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>
#include <linux/platform_device.h>
Yoichi Yuasa's avatar
Yoichi Yuasa committed

#include <asm/cpu.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <asm/mipsregs.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/processor.h>

Yoichi Yuasa's avatar
Yoichi Yuasa committed
#include <au1000.h>
#include <au1xxx_eth.h>
Yoichi Yuasa's avatar
Yoichi Yuasa committed
#include <prom.h>

Linus Torvalds's avatar
Linus Torvalds committed
#include "au1000_eth.h"

#ifdef AU1000_ETH_DEBUG
static int au1000_debug = 5;
#else
static int au1000_debug = 3;
#endif

#define DRV_NAME	"au1000_eth"
#define DRV_VERSION	"1.6"
Linus Torvalds's avatar
Linus Torvalds committed
#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");

/*
 * Theory of operation
 *
 * 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
Linus Torvalds's avatar
Linus Torvalds committed
 * hardware registers.
 *
 * Since the Au1000 has a coherent data cache, the receive and
 * transmit buffers are allocated from the KSEG0 segment. The
Linus Torvalds's avatar
Linus Torvalds committed
 * 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.
 */
static unsigned char au1000_mac_addr[6] __devinitdata = {
Linus Torvalds's avatar
Linus Torvalds committed
	0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00
};

struct au1000_private *au_macs[NUM_ETH_INTERFACES];

/*
 * board-specific configurations
 *
 * PHY detection algorithm
 *
 * If 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 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 phy1_search_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.
Linus Torvalds's avatar
Linus Torvalds committed
 */

/* autodetection defaults: phy1_search_mac0 */
Linus Torvalds's avatar
Linus Torvalds committed

/* 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
 */
Linus Torvalds's avatar
Linus Torvalds committed

static void enable_mac(struct net_device *dev, int force_reset)
{
	unsigned long flags;
	struct au1000_private *aup = netdev_priv(dev);

	spin_lock_irqsave(&aup->lock, flags);

	if(force_reset || (!aup->mac_enabled)) {
		*aup->enable = MAC_EN_CLOCK_ENABLE;
		au_sync_delay(2);
		*aup->enable = (MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
				| MAC_EN_CLOCK_ENABLE);
		au_sync_delay(2);

		aup->mac_enabled = 1;
	}

	spin_unlock_irqrestore(&aup->lock, flags);
}

static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct au1000_private *aup = netdev_priv(dev);
	volatile u32 *const mii_control_reg = &aup->mac->mii_control;
	volatile u32 *const mii_data_reg = &aup->mac->mii_data;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 timedout = 20;
	u32 mii_control;

	while (*mii_control_reg & MAC_MII_BUSY) {
		mdelay(1);
		if (--timedout == 0) {
			printk(KERN_ERR "%s: read_MII busy timeout!!\n",
Linus Torvalds's avatar
Linus Torvalds committed
					dev->name);
			return -1;
		}
	}

	mii_control = MAC_SET_MII_SELECT_REG(reg) |
		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;
Linus Torvalds's avatar
Linus Torvalds committed

	*mii_control_reg = mii_control;

	timedout = 20;
	while (*mii_control_reg & MAC_MII_BUSY) {
		mdelay(1);
		if (--timedout == 0) {
			printk(KERN_ERR "%s: mdio_read busy timeout!!\n",
Loading
Loading full blame...