Skip to content
Snippets Groups Projects
bcm63xx_enet.c 48.3 KiB
Newer Older
/*
 * Driver for BCM963xx builtin Ethernet mac
 *
 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/crc32.h>
#include <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/if_vlan.h>

#include <bcm63xx_dev_enet.h>
#include "bcm63xx_enet.h"

static char bcm_enet_driver_name[] = "bcm63xx_enet";
static char bcm_enet_driver_version[] = "1.0";

static int copybreak __read_mostly = 128;
module_param(copybreak, int, 0);
MODULE_PARM_DESC(copybreak, "Receive copy threshold");

/* io memory shared between all devices */
static void __iomem *bcm_enet_shared_base;

/*
 * io helpers to access mac registers
 */
static inline u32 enet_readl(struct bcm_enet_priv *priv, u32 off)
{
	return bcm_readl(priv->base + off);
}

static inline void enet_writel(struct bcm_enet_priv *priv,
			       u32 val, u32 off)
{
	bcm_writel(val, priv->base + off);
}

/*
 * io helpers to access shared registers
 */
static inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off)
{
	return bcm_readl(bcm_enet_shared_base + off);
}

static inline void enet_dma_writel(struct bcm_enet_priv *priv,
				       u32 val, u32 off)
{
	bcm_writel(val, bcm_enet_shared_base + off);
}

/*
 * write given data into mii register and wait for transfer to end
 * with timeout (average measured transfer time is 25us)
 */
static int do_mdio_op(struct bcm_enet_priv *priv, unsigned int data)
{
	int limit;

	/* make sure mii interrupt status is cleared */
	enet_writel(priv, ENET_IR_MII, ENET_IR_REG);

	enet_writel(priv, data, ENET_MIIDATA_REG);
	wmb();

	/* busy wait on mii interrupt bit, with timeout */
	limit = 1000;
	do {
		if (enet_readl(priv, ENET_IR_REG) & ENET_IR_MII)
			break;
		udelay(1);
	} while (limit-- > 0);

	return (limit < 0) ? 1 : 0;
}

/*
 * MII internal read callback
 */
static int bcm_enet_mdio_read(struct bcm_enet_priv *priv, int mii_id,
			      int regnum)
{
	u32 tmp, val;

	tmp = regnum << ENET_MIIDATA_REG_SHIFT;
	tmp |= 0x2 << ENET_MIIDATA_TA_SHIFT;
	tmp |= mii_id << ENET_MIIDATA_PHYID_SHIFT;
	tmp |= ENET_MIIDATA_OP_READ_MASK;

	if (do_mdio_op(priv, tmp))
		return -1;

	val = enet_readl(priv, ENET_MIIDATA_REG);
	val &= 0xffff;
	return val;
}

/*
 * MII internal write callback
 */
static int bcm_enet_mdio_write(struct bcm_enet_priv *priv, int mii_id,
			       int regnum, u16 value)
{
	u32 tmp;

	tmp = (value & 0xffff) << ENET_MIIDATA_DATA_SHIFT;
	tmp |= 0x2 << ENET_MIIDATA_TA_SHIFT;
	tmp |= regnum << ENET_MIIDATA_REG_SHIFT;
	tmp |= mii_id << ENET_MIIDATA_PHYID_SHIFT;
	tmp |= ENET_MIIDATA_OP_WRITE_MASK;

	(void)do_mdio_op(priv, tmp);
	return 0;
}

/*
 * MII read callback from phylib
 */
static int bcm_enet_mdio_read_phylib(struct mii_bus *bus, int mii_id,
				     int regnum)
{
	return bcm_enet_mdio_read(bus->priv, mii_id, regnum);
}

/*
 * MII write callback from phylib
 */
static int bcm_enet_mdio_write_phylib(struct mii_bus *bus, int mii_id,
				      int regnum, u16 value)
{
	return bcm_enet_mdio_write(bus->priv, mii_id, regnum, value);
}

/*
 * MII read callback from mii core
 */
static int bcm_enet_mdio_read_mii(struct net_device *dev, int mii_id,
				  int regnum)
{
	return bcm_enet_mdio_read(netdev_priv(dev), mii_id, regnum);
}

/*
 * MII write callback from mii core
 */
static void bcm_enet_mdio_write_mii(struct net_device *dev, int mii_id,
				    int regnum, int value)
{
	bcm_enet_mdio_write(netdev_priv(dev), mii_id, regnum, value);
}

/*
 * refill rx queue
 */
static int bcm_enet_refill_rx(struct net_device *dev)
{
	struct bcm_enet_priv *priv;

	priv = netdev_priv(dev);

	while (priv->rx_desc_count < priv->rx_ring_size) {
		struct bcm_enet_desc *desc;
		struct sk_buff *skb;
		dma_addr_t p;
		int desc_idx;
		u32 len_stat;

		desc_idx = priv->rx_dirty_desc;
		desc = &priv->rx_desc_cpu[desc_idx];

		if (!priv->rx_skb[desc_idx]) {
			skb = netdev_alloc_skb(dev, priv->rx_skb_size);
			if (!skb)
				break;
			priv->rx_skb[desc_idx] = skb;

			p = dma_map_single(&priv->pdev->dev, skb->data,
					   priv->rx_skb_size,
					   DMA_FROM_DEVICE);
Loading
Loading full blame...