Skip to content
Snippets Groups Projects
au1000_eth.c 32.7 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
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * ########################################################################
 *
 *  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
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#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>
#include <linux/cpu.h>
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <asm/mipsregs.h>
#include <asm/irq.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 AU1000_DEF_MSG_ENABLE	(NETIF_MSG_DRV	| \
				NETIF_MSG_PROBE	| \
				NETIF_MSG_LINK)

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

/*
 * 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.
 */

/*
 * 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 au1000_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)) {
		writel(MAC_EN_CLOCK_ENABLE, aup->enable);
		au_sync_delay(2);
		writel((MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
				| MAC_EN_CLOCK_ENABLE), aup->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);
	u32 *const mii_control_reg = &aup->mac->mii_control;
	u32 *const mii_data_reg = &aup->mac->mii_data;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 timedout = 20;
	u32 mii_control;

	while (readl(mii_control_reg) & MAC_MII_BUSY) {
Linus Torvalds's avatar
Linus Torvalds committed
		mdelay(1);
		if (--timedout == 0) {
			netdev_err(dev, "read_MII busy timeout!!\n");
Linus Torvalds's avatar
Linus Torvalds committed
			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

	writel(mii_control, mii_control_reg);
Linus Torvalds's avatar
Linus Torvalds committed

	timedout = 20;
	while (readl(mii_control_reg) & MAC_MII_BUSY) {
Linus Torvalds's avatar
Linus Torvalds committed
		mdelay(1);
		if (--timedout == 0) {
			netdev_err(dev, "mdio_read busy timeout!!\n");
Linus Torvalds's avatar
Linus Torvalds committed
			return -1;
		}
	}
	return readl(mii_data_reg);
Loading
Loading full blame...