/* sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver.
* Once again I am out to prove that every ethernet
* controller out there can be most efficiently programmed
* if you make it look like a LANCE.
*
* Copyright (C) 1996, 1999, 2003, 2006 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/idprom.h>
#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include "sunqe.h"
#define DRV_NAME "sunqe"
#define DRV_VERSION "4.0"
#define DRV_RELDATE "June 23, 2006"
#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"
static char version[] =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION("Sun QuadEthernet 10baseT SBUS card driver");
MODULE_LICENSE("GPL");
static struct sunqec *root_qec_dev;
static void qe_set_multicast(struct net_device *dev);
#define QEC_RESET_TRIES 200
static inline int qec_global_reset(void __iomem *gregs)
{
int tries = QEC_RESET_TRIES;
sbus_writel(GLOB_CTRL_RESET, gregs + GLOB_CTRL);
while (--tries) {
u32 tmp = sbus_readl(gregs + GLOB_CTRL);
if (tmp & GLOB_CTRL_RESET) {
udelay(20);
continue;
}
break;
}
if (tries)
return 0;
printk(KERN_ERR "QuadEther: AIEEE cannot reset the QEC!\n");
return -1;
}
#define MACE_RESET_RETRIES 200
#define QE_RESET_RETRIES 200
static inline int qe_stop(struct sunqe *qep)
{
void __iomem *cregs = qep->qcregs;
void __iomem *mregs = qep->mregs;
int tries;
/* Reset the MACE, then the QEC channel. */
sbus_writeb(MREGS_BCONFIG_RESET, mregs + MREGS_BCONFIG);
tries = MACE_RESET_RETRIES;
while (--tries) {
u8 tmp = sbus_readb(mregs + MREGS_BCONFIG);
if (tmp & MREGS_BCONFIG_RESET) {
udelay(20);
continue;
}
break;
}
if (!tries) {
printk(KERN_ERR "QuadEther: AIEEE cannot reset the MACE!\n");
return -1;
}
sbus_writel(CREG_CTRL_RESET, cregs + CREG_CTRL);
tries = QE_RESET_RETRIES;
while (--tries) {
u32 tmp = sbus_readl(cregs + CREG_CTRL);
if (tmp & CREG_CTRL_RESET) {
udelay(20);
continue;
}
break;
}
if (!tries) {
printk(KERN_ERR "QuadEther: Cannot reset QE channel!\n");
return -1;
}
return 0;
}
static void qe_init_rings(struct sunqe *qep)
{
struct qe_init_block *qb = qep->qe_block;
struct sunqe_buffers *qbufs = qep->buffers;
__u32 qbufs_dvma = qep->buffers_dvma;
int i;
qep->rx_new = qep->rx_old = qep->tx_new = qep->tx_old = 0;
memset(qb, 0, sizeof(struct qe_init_block));
memset(qbufs, 0, sizeof(struct sunqe_buffers));
for (i = 0; i < RX_RING_SIZE; i++) {
qb->qe_rxd[i].rx_addr = qbufs_dvma + qebuf_offset(rx_buf, i);
qb->qe_rxd[i].rx_flags =
(RXD_OWN | ((RXD_PKT_SZ) & RXD_LENGTH));
}
}
static int qe_init(struct sunqe *qep, int from_irq)
{
struct sunqec *qecp = qep->parent;
void __iomem *cregs = qep->qcregs;
void __iomem *mregs = qep->mregs;
void __iomem *gregs = qecp->gregs;
unsigned char *e = &qep->dev->dev_addr[</