/*
* Driver for Xilinx TEMAC Ethernet device
*
* Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi
* Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. <dhlii@dlasys.net>
* Copyright (c) 2008-2009 Secret Lab Technologies Ltd.
*
* This is a driver for the Xilinx ll_temac ipcore which is often used
* in the Virtex and Spartan series of chips.
*
* Notes:
* - The ll_temac hardware uses indirect access for many of the TEMAC
* registers, include the MDIO bus. However, indirect access to MDIO
* registers take considerably more clock cycles than to TEMAC registers.
* MDIO accesses are long, so threads doing them should probably sleep
* rather than busywait. However, since only one indirect access can be
* in progress at any given time, that means that *all* indirect accesses
* could end up sleeping (to wait for an MDIO access to complete).
* Fortunately none of the indirect accesses are on the 'hot' path for tx
* or rx, so this should be okay.
*
* TODO:
* - Factor out locallink DMA code into separate driver
* - Fix multicast assignment.
* - Fix support for hardware checksumming.
* - Testing. Lots and lots of testing.
*
*/
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/tcp.h> /* needed for sizeof(tcphdr) */
#include <linux/udp.h> /* needed for sizeof(udphdr) */
#include <linux/phy.h>
#include <linux/in.h>
#include <linux/io.h>
#include <linux/ip.h>
#include <linux/slab.h>
#include "ll_temac.h"
#define TX_BD_NUM 64
#define RX_BD_NUM 128
/* ---------------------------------------------------------------------
* Low level register access functions
*/
u32 temac_ior(struct temac_local *lp, int offset)
{
return in_be32((u32 *)(lp->regs + offset));
}
void temac_iow(struct temac_local *lp, int offset, u32 value)
{
out_be32((u32 *) (lp->regs + offset), value);
}
int temac_indirect_busywait(struct temac_local *lp)
{
long end = jiffies + 2;
while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) {
if (end - jiffies <= 0) {
WARN_ON(1);
return -ETIMEDOUT;
}
msleep(1);
}
return 0;
}
/**
* temac_indirect_in32
*
* lp->indirect_mutex must be held when calling this function
*/
u32 temac_indirect_in32(struct temac_local *lp, int reg)
{
u32 val;
if (temac_indirect_busywait(lp))
return -ETIMEDOUT;
temac_iow(lp, XTE_CTL0_OFFSET, reg);
if (temac_indirect_busywait(lp))
return -ETIMEDOUT;
val = temac_ior(lp, XTE_LSW0_OFFSET);
return val;
}
/**
* temac_indirect_out32
*
* lp->indirect_mutex must be held when calling this function
*/
void temac_indirect_out32(struct temac_local *lp, int reg, u32 value)
{
if (temac_indirect_busywait(lp))
return;
temac_iow(lp, XTE_LSW0_OFFSET, value);
temac_iow(lp, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK | reg);
}
/**
* temac_dma_in32 - Memory mapped DMA read, this function expects a
* register input that is based on DCR word addresses which
* are then converted to memory mapped byte addresses
*/
static u32 temac_dma_in32(struct temac_local *lp, int reg)
{
return in_be32((u32 *)(lp->sdma_regs + (reg << 2)));
}
/**
* temac_dma_out32 - Memory mapped DMA read, this function expects a
* register input that is based on DCR word addresses which
* are then converted to memory mapped byte addresses
*/
static void temac_dma_out32(struct temac_local *lp, int reg, u32 value)
{
out_be32((u32 *)(lp->sdma_regs + (reg << 2)), value);
}
/* DMA register access functions can be DCR based or memory mapped.
* The PowerPC 440 is DCR based, the PowerPC 405 and MicroBlaze are both
* memory mapped.
*/
#ifdef CONFIG_PPC_DCR
/**
* temac_dma_dcr_in32 - DCR based DMA read
*/
static u32 temac_dma_dcr_in(struct temac_local *lp, int reg)
{
return dcr_read(lp->sdma_dcrs, reg);
}
/**
* temac_dma_dcr_out32 - DCR based DMA write
*/
static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value)
{
dcr_write(lp->sdma_dcrs, reg, value);
}
/**
* temac_dcr_setup - If the DMA is DCR based, then setup the address and
* I/O functions
*/
static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
struct device_node *np)
{
unsigned int dcrs;
/* setup the dcr address mapping if it's in the device tree */
dcrs = dcr_resource_start(np, 0);
if (dcrs !=