openwrtv3/target/linux/rdc-2.6/files/drivers/net/r6040.c
2007-03-30 21:56:07 +00:00

980 lines
25 KiB
C

/* rdc.c: A RDC FastEthernet driver for linux. */
/*
Re-written 2004 by Sten Wang.
Copyright 1994-2000 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU General Public License,
incorporated herein by reference.
This driver is for RDC FastEthernet MAC series.
For kernel version after 2.4.22
Modification List
---------- ------------------------------------------------
12-22-2004 Sten Init MAC MBCR register=0x012A
PHY_CAP = 0x01E1
*/
#define FORICPLUS /* Supports ICPlus IP175C switch chip */
#define BOOSTRDC /* Accelerate Ethernet performance */
#define DRV_NAME "rdc"
#define DRV_VERSION "0.6"
#define DRV_RELDATE "9July2004"
/* PHY CHIP Address */
#define PHY1_ADDR 1 /* For MAC1 */
#define PHY2_ADDR 2 /* For MAC2 */
#define PHY_MODE 0x3100 /* PHY CHIP Register 0 */
#define PHY_CAP 0x01E1 /* PHY CHIP Register 4 */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (400 * HZ / 1000)
#define TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */
/* RDC MAC ID */
#define RDC_MAC_ID 0x6040
/* RDC MAC I/O Size */
#define R6040_IO_SIZE 256
/* RDC Chip PCI Command */
#define R6040_PCI_CMD 0x0005 /* IO, Master */
/* MAX RDC MAC */
#define MAX_MAC 2
/* MAC setting */
#ifdef BOOSTRDC
#define TX_DCNT 32 /* TX descriptor count */
#define RX_DCNT 32 /* RX descriptor count */
#else
#define TX_DCNT 0x8 /* TX descriptor count */
#define RX_DCNT 0x8 /* RX descriptor count */
#endif
#define MAX_BUF_SIZE 0x600
#define ALLOC_DESC_SIZE ((TX_DCNT+RX_DCNT)*sizeof(struct rdc_descriptor)+0x10)
#define MBCR_DEFAULT 0x012A /* MAC Control Register */
/* Debug enable or not */
#define RDC_DEBUG 0
#if RDC_DEBUG > 1
#define RDC_DBUG(msg, value) printk("%s %x\n", msg, value);
#else
#define RDC_DBUG(msg, value)
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/delay.h> /* for udelay() */
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/crc32.h>
#include <linux/spinlock.h>
#include <asm/processor.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Sten Wang <sten.wang@rdc.com.tw>");
MODULE_DESCRIPTION("RDC R6040 PCI FastEthernet Driver");
MODULE_LICENSE("GPL");
//MODULE_PARM(adr_table, "2-4i");
MODULE_PARM_DESC(adr_table, "MAC Address (assigned)");
struct rdc_descriptor {
u16 status, len; /* 0-3 */
u32 buf; /* 4-7 */
u32 ndesc; /* 8-B */
u32 rev1; /* C-F */
char *vbufp; /* 10-13 */
struct rdc_descriptor *vndescp; /* 14-17 */
struct sk_buff *skb_ptr; /* 18-1B */
u32 rev2; /* 1C-1F */
} __attribute__(( aligned(32) ));
struct rdc_private {
struct net_device_stats stats;
spinlock_t lock;
struct timer_list timer;
struct pci_dev *pdev;
struct rdc_descriptor *rx_insert_ptr;
struct rdc_descriptor *rx_remove_ptr;
struct rdc_descriptor *tx_insert_ptr;
struct rdc_descriptor *tx_remove_ptr;
u16 tx_free_desc, rx_free_desc, phy_addr, phy_mode;
u16 mcr0, mcr1;
dma_addr_t desc_dma;
char *desc_pool;
};
struct rdc_chip_info {
const char *name;
u16 pci_flags;
int io_size;
int drv_flags;
};
static int __devinitdata printed_version;
static char version[] __devinitdata =
KERN_INFO DRV_NAME ": RDC R6040 net driver, version "
DRV_VERSION " (" DRV_RELDATE ")\n";
static struct rdc_chip_info rdc_chip_info[] __devinitdata =
{
{ "RDC R6040 Knight", R6040_PCI_CMD, R6040_IO_SIZE, 0}
};
static int phy_table[] = { 0x1, 0x2};
static u8 adr_table[2][8] = {{0x00, 0x00, 0x60, 0x00, 0x00, 0x01}, {0x00, 0x00, 0x60, 0x00, 0x00, 0x02}};
static int rdc_open(struct net_device *dev);
static int rdc_start_xmit(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t rdc_interrupt(int irq, void *dev_id);
static struct net_device_stats *rdc_get_stats(struct net_device *dev);
static int rdc_close(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static struct ethtool_ops netdev_ethtool_ops;
static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
static void rdc_down(struct net_device *dev);
static void rdc_up(struct net_device *dev);
static void rdc_tx_timeout (struct net_device *dev);
static void rdc_timer(unsigned long);
static int phy_mode_chk(struct net_device *dev);
static int phy_read(int ioaddr, int phy_adr, int reg_idx);
static void phy_write(int ioaddr, int phy_adr, int reg_idx, int dat);
#ifdef BOOSTRDC
#define rx_buf_alloc(lp) \
do { \
struct rdc_descriptor *descptr; \
descptr = lp->rx_insert_ptr; \
while(lp->rx_free_desc < RX_DCNT){ \
descptr->skb_ptr = dev_alloc_skb(MAX_BUF_SIZE); \
if (!descptr->skb_ptr) break; \
descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, descptr->skb_ptr->tail, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE)); \
descptr->status = 0x8000; \
descptr = descptr->vndescp; \
lp->rx_free_desc++; \
} \
lp->rx_insert_ptr = descptr; \
} while(0)
#else
static void rx_buf_alloc(struct rdc_private *lp);
#endif
#ifdef FORICPLUS
static void process_ioctl(struct net_device*, unsigned long* );
#endif
static int __devinit rdc_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *dev;
struct rdc_private *lp;
int ioaddr, io_size, err;
static int card_idx = -1;
int chip_id = (int)ent->driver_data;
RDC_DBUG("rdc_init_one()", 0);
if (printed_version++)
printk(version);
if ((err = pci_enable_device (pdev)))
return err;
/* this should always be supported */
if (pci_set_dma_mask(pdev, 0xffffffff)) {
printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses not supported by the card!?\n");
return -ENODEV;
}
/* IO Size check */
io_size = rdc_chip_info[chip_id].io_size;
if (pci_resource_len (pdev, 0) < io_size) {
return -ENODEV;
}
ioaddr = pci_resource_start (pdev, 0); /* IO map base address */
pci_set_master(pdev);
dev = alloc_etherdev(sizeof(struct rdc_private));
if (dev == NULL)
return -ENOMEM;
SET_MODULE_OWNER(dev);
if (pci_request_regions(pdev, DRV_NAME)) {
printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n");
err = -ENODEV;
goto err_out_disable;
}
/* Init system & device */
lp = dev->priv;
dev->base_addr = ioaddr;
dev->irq = pdev->irq;
spin_lock_init(&lp->lock);
pci_set_drvdata(pdev, dev);
/* Set MAC address */
card_idx++;
memcpy(dev->dev_addr, (u8 *)&adr_table[card_idx][0], 6);
/* Link new device into rdc_root_dev */
lp->pdev = pdev;
/* Init RDC private data */
lp->mcr0 = 0x1002;
lp->phy_addr = phy_table[card_idx];
/* The RDC-specific entries in the device structure. */
dev->open = &rdc_open;
dev->hard_start_xmit = &rdc_start_xmit;
dev->stop = &rdc_close;
dev->get_stats = &rdc_get_stats;
dev->set_multicast_list = &set_multicast_list;
dev->do_ioctl = &netdev_ioctl;
dev->ethtool_ops = &netdev_ethtool_ops;
dev->tx_timeout = &rdc_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
/* Register net device. After this dev->name assign */
if ((err = register_netdev(dev))) {
printk(KERN_ERR DRV_NAME ": Failed to register net device\n");
goto err_out_res;
}
netif_carrier_on(dev);
return 0;
err_out_res:
pci_release_regions(pdev);
err_out_disable:
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
kfree(dev);
return err;
}
static void __devexit rdc_remove_one (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
unregister_netdev(dev);
pci_release_regions(pdev);
kfree(dev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static int
rdc_open(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
int i;
RDC_DBUG("rdc_open()", 0);
/* Request IRQ and Register interrupt handler */
i = request_irq(dev->irq, &rdc_interrupt, SA_SHIRQ, dev->name, dev);
if (i) return i;
/* Allocate Descriptor memory */
lp->desc_pool = pci_alloc_consistent(lp->pdev, ALLOC_DESC_SIZE, &lp->desc_dma);
if (!lp->desc_pool) return -ENOMEM;
rdc_up(dev);
netif_start_queue(dev);
#ifndef FORICPLUS
/* set and active a timer process */
init_timer(&lp->timer);
lp->timer.expires = TIMER_WUT;
lp->timer.data = (unsigned long)dev;
lp->timer.function = &rdc_timer;
add_timer(&lp->timer);
#endif
return 0;
}
static void
rdc_tx_timeout (struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
//int ioaddr = dev->base_addr;
//struct rdc_descriptor *descptr = lp->tx_remove_ptr;
RDC_DBUG("rdc_tx_timeout()", 0);
/* Transmitter timeout, serious problems. */
/* Sten: Nothing need to do so far. */
printk(KERN_ERR DRV_NAME ": Big Trobule, transmit timeout/n");
lp->stats.tx_errors++;
netif_stop_queue(dev);
//printk("<RDC> XMT timedout: CR0 %x, CR40 %x, CR3C %x, CR2C %x, CR30 %x, CR34 %x, CR38 %x\n", inw(ioaddr), inw(ioaddr+0x40), inw(ioaddr+0x3c), inw(ioaddr+0x2c), inw(ioaddr+0x30), inw(ioaddr+0x34), inw(ioaddr+0x38));
//printk("<RDC> XMT_TO: %08lx:%04x %04x %08lx %08lx %08lx %08lx\n", descptr, descptr->status, descptr->len, descptr->buf, descptr->skb_ptr, descptr->ndesc, descptr->vndescp);
}
static int
rdc_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
struct rdc_descriptor *descptr;
int ioaddr = dev->base_addr;
unsigned long flags;
RDC_DBUG("rdc_start_xmit()", 0);
if (skb == NULL) /* NULL skb directly return */
return 0;
if (skb->len >= MAX_BUF_SIZE) { /* Packet too long, drop it */
dev_kfree_skb(skb);
return 0;
}
/* Critical Section */
spin_lock_irqsave(&lp->lock, flags);
/* TX resource check */
if (!lp->tx_free_desc) {
spin_unlock_irqrestore(&lp->lock, flags);
printk(KERN_ERR DRV_NAME ": NO TX DESC ");
return 1;
}
/* Statistic Counter */
lp->stats.tx_packets++;
lp->stats.tx_bytes += skb->len;
/* Set TX descriptor & Transmit it */
lp->tx_free_desc--;
descptr = lp->tx_insert_ptr;
if (skb->len < 0x3c) descptr->len = 0x3c;
else descptr->len = skb->len;
descptr->skb_ptr = skb;
descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE));
descptr->status = 0x8000;
outw(0x01, ioaddr + 0x14);
lp->tx_insert_ptr = descptr->vndescp;
#if RDC_DEBUG
printk("Xmit(): %08lx:%04x %04x %08lx %08lx %08lx %08lx\n", descptr, descptr->status, descptr->len, descptr->buf, descptr->skb_ptr, descptr->ndesc, descptr->vndescp);
#endif
/* If no tx resource, stop */
if (!lp->tx_free_desc)
netif_stop_queue(dev);
dev->trans_start = jiffies;
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
/* The RDC interrupt handler. */
static irqreturn_t
rdc_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct rdc_private *lp;
struct rdc_descriptor *descptr;
struct sk_buff *skb_ptr;
int ioaddr, status;
unsigned long flags;
int handled = 0;
RDC_DBUG("rdc_interrupt()", 0);
if (dev == NULL) {
printk (KERN_ERR DRV_NAME ": INT() unknown device.\n");
return IRQ_RETVAL(handled);
}
lp = (struct rdc_private *)dev->priv;
spin_lock_irqsave(&lp->lock, flags);
/* Check MAC Interrupt status */
ioaddr = dev->base_addr;
outw(0x0, ioaddr + 0x40); /* Mask Off RDC MAC interrupt */
status = inw(ioaddr + 0x3c); /* Read INTR status and clear */
/* TX interrupt request */
if (status & 0x10) {
handled = 1;
descptr = lp->tx_remove_ptr;
while(lp->tx_free_desc < TX_DCNT) {
if (descptr->status & 0x8000) break; /* Not complte */
skb_ptr = descptr->skb_ptr;
pci_unmap_single(lp->pdev, descptr->buf, skb_ptr->len, PCI_DMA_TODEVICE);
dev_kfree_skb_irq(skb_ptr); /* Free buffer */
descptr->skb_ptr = 0;
descptr = descptr->vndescp; /* To next descriptor */
lp->tx_free_desc++;
}
lp->tx_remove_ptr = descptr;
if (lp->tx_free_desc) netif_wake_queue(dev);
}
/* RX interrupt request */
if (status & 0x01) {
handled = 1;
descptr = lp->rx_remove_ptr;
while(lp->rx_free_desc) {
if (descptr->status & 0x8000) break; /* No Rx packet */
skb_ptr = descptr->skb_ptr;
descptr->skb_ptr = 0;
skb_ptr->dev = dev;
skb_put(skb_ptr, descptr->len - 4);
pci_unmap_single(lp->pdev, descptr->buf, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE);
skb_ptr->protocol = eth_type_trans(skb_ptr, dev);
netif_rx(skb_ptr); /* Send to upper layer */
lp->stats.rx_packets++;
lp->stats.rx_bytes += descptr->len;
descptr = descptr->vndescp; /* To next descriptor */
lp->rx_free_desc--;
}
lp->rx_remove_ptr = descptr;
}
/* Allocate new RX buffer */
if (lp->rx_free_desc < RX_DCNT) rx_buf_alloc(lp);
outw(0x0011, ioaddr + 0x40); /* TX/RX interrupt enable */
spin_unlock_irqrestore(&lp->lock, flags);
return IRQ_RETVAL(handled);
}
static struct net_device_stats *
rdc_get_stats(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
RDC_DBUG("rdc_get_stats()", 0);
return &lp->stats;
}
/*
* Set or clear the multicast filter for this adaptor.
*/
static void
set_multicast_list(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
struct dev_mc_list *mcptr;
int ioaddr = dev->base_addr;
u16 *adrp, i;
unsigned long flags;
RDC_DBUG("set_multicast_list()", 0);
/* MAC Address */
ioaddr += 0x68;
adrp = (u16 *) dev->dev_addr;
outw(adrp[0], ioaddr); ioaddr += 2;
outw(adrp[1], ioaddr); ioaddr += 2;
outw(adrp[2], ioaddr); ioaddr += 2;
#if RDC_DEBUG
printk("MAC ADDR: %04x %04x %04x\n", adrp[0], adrp[1], adrp[2]);
#endif
/* Promiscous Mode */
spin_lock_irqsave(lp->lock, flags);
i = inw(ioaddr) & ~0x0120; /* Clear AMCP & PROM */
if (dev->flags & IFF_PROMISC) i |= 0x0020;
if (dev->mc_count > 4) i |= 0x0100; /* Too many multicast address */
outw(i, ioaddr);
spin_unlock_irqrestore(lp->lock, flags);
/* Multicast Address */
if (dev->mc_count > 4) /* Wait to do: Hash Table for multicast */
return;
/* Multicast Address 1~4 case */
for (i = 0, mcptr = dev->mc_list; (i<dev->mc_count) && (i<4); i++) {
adrp = (u16 *)mcptr->dmi_addr;
outw(adrp[0], ioaddr); ioaddr += 2;
outw(adrp[1], ioaddr); ioaddr += 2;
outw(adrp[2], ioaddr); ioaddr += 2;
mcptr = mcptr->next;
#if RDC_DEBUG
printk("M_ADDR: %04x %04x %04x\n", adrp[0], adrp[1], adrp[2]);
#endif
}
for (i = dev->mc_count; i < 4; i++) {
outw(0xffff, ioaddr); ioaddr += 2;
outw(0xffff, ioaddr); ioaddr += 2;
outw(0xffff, ioaddr); ioaddr += 2;
}
}
static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
{
struct rdc_private *rp = dev->priv;
strcpy (info->driver, DRV_NAME);
strcpy (info->version, DRV_VERSION);
strcpy (info->bus_info, pci_name(rp->pdev));
}
static struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
};
static int
rdc_close(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
RDC_DBUG("rdc_close()", 0);
/* deleted timer */
del_timer_sync(&lp->timer);
spin_lock_irq(&lp->lock);
netif_stop_queue(dev);
rdc_down(dev);
spin_unlock_irq(&lp->lock);
return 0;
}
/**
*/
static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
RDC_DBUG("netdev_ioctl()", 0);
#ifdef FORICPLUS
switch(cmd)
{
case SIOCDEVPRIVATE:
//printk(KERN_INFO"Ethernet IOCTL: cmd SIOCDEVPRIVATE\n");
{
unsigned long *data;
unsigned long args[4];
data = (unsigned long *)rq->ifr_data;
if (copy_from_user(args, data, 4*sizeof(unsigned long)))
return -EFAULT;
process_ioctl(dev, args);
}
break;
default:
break;
}
#endif
return 0;
}
/**
Stop RDC MAC and Free the allocated resource
*/
static void rdc_down(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
int i;
int ioaddr = dev->base_addr;
RDC_DBUG("rdc_down()", 0);
/* Stop MAC */
outw(0x0000, ioaddr + 0x40); /* Mask Off Interrupt */
outw(0x0001, ioaddr + 0x04); /* Reset RDC MAC */
i = 0;
do{}while((i++ < 2048) && (inw(ioaddr + 0x04) & 0x1));
free_irq(dev->irq, dev);
/* Free RX buffer */
for (i = 0; i < RX_DCNT; i++) {
if (lp->rx_insert_ptr->skb_ptr) {
pci_unmap_single(lp->pdev, lp->rx_insert_ptr->buf, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE);
dev_kfree_skb(lp->rx_insert_ptr->skb_ptr);
lp->rx_insert_ptr->skb_ptr = 0;
}
lp->rx_insert_ptr = lp->rx_insert_ptr->vndescp;
}
/* Free TX buffer */
for (i = 0; i < TX_DCNT; i++) {
if (lp->tx_insert_ptr->skb_ptr) {
pci_unmap_single(lp->pdev, lp->tx_insert_ptr->buf, MAX_BUF_SIZE, PCI_DMA_TODEVICE);
dev_kfree_skb(lp->tx_insert_ptr->skb_ptr);
lp->rx_insert_ptr->skb_ptr = 0;
}
lp->tx_insert_ptr = lp->tx_insert_ptr->vndescp;
}
/* Free Descriptor memory */
pci_free_consistent(lp->pdev, ALLOC_DESC_SIZE, lp->desc_pool, lp->desc_dma);
}
/* Init RDC MAC */
static void rdc_up(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
struct rdc_descriptor *descptr;
int i;
int ioaddr = dev->base_addr;
u32 tmp_addr;
dma_addr_t desc_dma, start_dma;
RDC_DBUG("rdc_up()", 0);
/* Initilize */
lp->tx_free_desc = TX_DCNT;
lp->rx_free_desc = 0;
/* Init descriptor */
memset(lp->desc_pool, 0, ALLOC_DESC_SIZE); /* Let all descriptor = 0 */
lp->tx_insert_ptr = (struct rdc_descriptor *)lp->desc_pool;
lp->tx_remove_ptr = lp->tx_insert_ptr;
lp->rx_insert_ptr = (struct rdc_descriptor *)lp->tx_insert_ptr+TX_DCNT;
lp->rx_remove_ptr = lp->rx_insert_ptr;
/* Init TX descriptor */
descptr = lp->tx_insert_ptr;
desc_dma = lp->desc_dma;
start_dma = desc_dma;
for (i = 0; i < TX_DCNT; i++) {
descptr->ndesc = cpu_to_le32(desc_dma + sizeof(struct rdc_descriptor));
descptr->vndescp = (descptr + 1);
descptr = (descptr + 1);
desc_dma += sizeof(struct rdc_descriptor);
}
(descptr - 1)->ndesc = cpu_to_le32(start_dma);
(descptr - 1)->vndescp = lp->tx_insert_ptr;
/* Init RX descriptor */
start_dma = desc_dma;
descptr = lp->rx_insert_ptr;
for (i = 0; i < RX_DCNT; i++) {
descptr->ndesc = cpu_to_le32(desc_dma + sizeof(struct rdc_descriptor));
descptr->vndescp = (descptr + 1);
descptr = (descptr + 1);
desc_dma += sizeof(struct rdc_descriptor);
}
(descptr - 1)->ndesc = cpu_to_le32(start_dma);
(descptr - 1)->vndescp = lp->rx_insert_ptr;
/* Allocate buffer for RX descriptor */
rx_buf_alloc(lp);
#if RDC_DEBUG
descptr = lp->tx_insert_ptr;
for (i = 0; i < TX_DCNT; i++) {
printk("%08lx:%04x %04x %08lx %08lx %08lx %08lx\n", descptr, descptr->status, descptr->len, descptr->buf, descptr->skb_ptr, descptr->ndesc, descptr->vndescp);
descptr = descptr->vndescp;
}
descptr = lp->rx_insert_ptr;
for (i = 0; i < RX_DCNT; i++) {
printk("%08lx:%04x %04x %08lx %08lx %08lx %08lx\n", descptr, descptr->status, descptr->len, descptr->buf, descptr->skb_ptr, descptr->ndesc, descptr->vndescp);
descptr = descptr->vndescp;
}
#endif
/* MAC operation register */
outw(0x01, ioaddr); /* Reset MAC */
outw(2 , ioaddr+0xAC);
outw(0 , ioaddr+0xAC);
udelay(5000);
/* TX and RX descriptor start Register */
tmp_addr = cpu_to_le32(lp->tx_insert_ptr);
//timc
tmp_addr = virt_to_bus((volatile void *)tmp_addr);
outw((u16) tmp_addr, ioaddr+0x2c);
outw(tmp_addr >> 16, ioaddr+0x30);
tmp_addr = cpu_to_le32(lp->rx_insert_ptr);
//timc
tmp_addr = virt_to_bus((volatile void *)tmp_addr);
outw((u16) tmp_addr, ioaddr+0x34);
outw(tmp_addr >> 16, ioaddr+0x38);
/* Buffer Size Register */
outw(MAX_BUF_SIZE, ioaddr+0x18);
#ifdef FORICPLUS
if(phy_read(ioaddr, 0, 2) == 0x0243) // ICPlus IP175C Signature
{
phy_write(ioaddr, 29,31, 0x175C); //Enable registers
}
lp->phy_mode = 0x8000;
#else
/* PHY Mode Check */
phy_write(ioaddr, lp->phy_addr, 4, PHY_CAP);
phy_write(ioaddr, lp->phy_addr, 0, PHY_MODE);
if (PHY_MODE == 0x3100)
lp->phy_mode = phy_mode_chk(dev);
else lp->phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
#endif
/* MAC Bus Control Register */
outw(MBCR_DEFAULT, ioaddr+0x8);
/* MAC TX/RX Enable */
lp->mcr0 |= lp->phy_mode;
// Dante
// BIT15 | BIT12 | BIT5 | BIT1
lp->mcr0 |= 0x0020;
//Xavier, only set promiscuous mode with eth1 (LAN i/f)
//This is a very bad hard code...
//if(ioaddr == 0xe900)lp->mcr0 |= 0x0020;
outw(lp->mcr0, ioaddr);
#ifdef BOOSTRDC
/* set interrupt waiting time and packet numbers */
outw(0x0802, ioaddr + 0x0C);
outw(0x0802, ioaddr + 0x10);
#ifdef FORICPLUS
/* upgrade performance (by RDC guys) */
phy_write(ioaddr,30,17,(phy_read(ioaddr,30,17)|0x4000)); //bit 14=1
phy_write(ioaddr,30,17,~((~phy_read(ioaddr,30,17))|0x2000)); //bit 13=0
phy_write(ioaddr,0,19,0x0000);
phy_write(ioaddr,0,30,0x01F0);
#endif
#endif
/* Interrupt Mask Register */
outw(0x0011, ioaddr + 0x40);
}
/*
A periodic timer routine
Polling PHY Chip Link Status
*/
static void rdc_timer(unsigned long data)
{
struct net_device *dev=(struct net_device *)data;
struct rdc_private *lp = dev->priv;
u16 ioaddr = dev->base_addr, phy_mode;
RDC_DBUG("rdc_timer()", 0);
/* Polling PHY Chip Status */
if (PHY_MODE == 0x3100)
phy_mode = phy_mode_chk(dev);
else phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
if (phy_mode != lp->phy_mode) {
lp->phy_mode = phy_mode;
lp->mcr0 = (lp->mcr0 & 0x7fff) | phy_mode;
outw(lp->mcr0, ioaddr);
printk("<RDC> Link Change %x \n", inw(ioaddr));
}
/* Debug */
// printk("<RDC> Timer: CR0 %x CR40 %x CR3C %x\n", inw(ioaddr), inw(ioaddr+0x40), inw(ioaddr+0x3c));
/* Timer active again */
lp->timer.expires = TIMER_WUT;
add_timer(&lp->timer);
}
#ifndef BOOSTRDC
/* Allocate skb buffer for rx descriptor */
static void rx_buf_alloc(struct rdc_private *lp)
{
struct rdc_descriptor *descptr;
RDC_DBUG("rx_buf_alloc()", 0);
descptr = lp->rx_insert_ptr;
while(lp->rx_free_desc < RX_DCNT){
descptr->skb_ptr = dev_alloc_skb(MAX_BUF_SIZE);
if (!descptr->skb_ptr) break;
descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, descptr->skb_ptr->tail, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE));
descptr->status = 0x8000;
descptr = descptr->vndescp;
lp->rx_free_desc++;
}
lp->rx_insert_ptr = descptr;
}
#endif
/* Status of PHY CHIP */
static int phy_mode_chk(struct net_device *dev)
{
struct rdc_private *lp = dev->priv;
int ioaddr = dev->base_addr, phy_dat;
RDC_DBUG("phy_mode_chk()", 0);
/* PHY Link Status Check */
phy_dat = phy_read(ioaddr, lp->phy_addr, 1);
if (!(phy_dat & 0x4)) return 0x8000; /* Link Failed, full duplex */
/* PHY Chip Auto-Negotiation Status */
phy_dat = phy_read(ioaddr, lp->phy_addr, 1);
if (phy_dat & 0x0020) {
/* Auto Negotiation Mode */
phy_dat = phy_read(ioaddr, lp->phy_addr, 5);
phy_dat &= phy_read(ioaddr, lp->phy_addr, 4);
if (phy_dat & 0x140) phy_dat = 0x8000;
else phy_dat = 0;
} else {
/* Force Mode */
phy_dat = phy_read(ioaddr, lp->phy_addr, 0);
if (phy_dat & 0x100) phy_dat = 0x8000;
else phy_dat = 0x0000;
}
return phy_dat;
};
/* Read a word data from PHY Chip */
static int phy_read(int ioaddr, int phy_addr, int reg_idx)
{
int i = 0;
RDC_DBUG("phy_read()", 0);
outw(0x2000 + reg_idx + (phy_addr << 8), ioaddr + 0x20);
do{}while( (i++ < 2048) && (inw(ioaddr + 0x20) & 0x2000) );
return inw(ioaddr + 0x24);
}
/* Write a word data from PHY Chip */
static void phy_write(int ioaddr, int phy_addr, int reg_idx, int dat)
{
int i = 0;
RDC_DBUG("phy_write()", 0);
outw(dat, ioaddr + 0x28);
outw(0x4000 + reg_idx + (phy_addr << 8), ioaddr + 0x20);
do{}while( (i++ < 2048) && (inw(ioaddr + 0x20) & 0x4000) );
}
enum {
RDC_6040 = 0
};
static struct pci_device_id rdc_pci_tbl[] = {
{0x17F3, 0x6040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RDC_6040},
//{0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RDC_6040},
{0,} /* terminate list */
};
MODULE_DEVICE_TABLE(pci, rdc_pci_tbl);
static struct pci_driver rdc_driver = {
.name = "r6040",
.id_table = rdc_pci_tbl,
.probe = rdc_init_one,
.remove = __devexit_p(rdc_remove_one),
};
static int __init rdc_init (void)
{
RDC_DBUG("rdc_init()", 0);
printk(version);
printed_version = 1;
return pci_module_init (&rdc_driver);
}
static void __exit rdc_cleanup (void)
{
RDC_DBUG("rdc_cleanup()", 0);
pci_unregister_driver (&rdc_driver);
}
module_init(rdc_init);
module_exit(rdc_cleanup);
/*
* Local variables:
* compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c rdc.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/
#ifdef FORICPLUS
#define DMZ_GPIO 1
#define RDC3210_CFGREG_ADDR 0x0CF8
#define RDC3210_CFGREG_DATA 0x0CFC
static void process_ioctl(struct net_device *dev, unsigned long* args)
{
int ioaddr = dev->base_addr;
/* port priority */
if(args[0]&(1<<31))phy_write(ioaddr,29,19,(phy_read(ioaddr,29,19)|0x2000)); /* port 0 */
if(args[0]&(1<<29))phy_write(ioaddr,29,19,(phy_read(ioaddr,29,19)|0x0020)); /* port 1 */
if(args[0]&(1<<27))phy_write(ioaddr,29,20,(phy_read(ioaddr,29,20)|0x2000)); /* port 2 */
if(args[0]&(1<<25))phy_write(ioaddr,29,20,(phy_read(ioaddr,29,20)|0x0020)); /* port 3 */
/* DMZ LED */
{
unsigned int val;
val = 0x80000000 | (7 << 11) | ((0x48));
outl(val, RDC3210_CFGREG_ADDR);
udelay(10);
val = inl(RDC3210_CFGREG_DATA);
val |= (0x1 << DMZ_GPIO);
outl(val, RDC3210_CFGREG_DATA);
udelay(10);
val = 0x80000000 | (7 << 11) | ((0x4C));
outl(val, RDC3210_CFGREG_ADDR);
udelay(10);
val = inl(RDC3210_CFGREG_DATA);
if(args[0]&(1<<23)) /* DMZ enabled */
val &= ~(0x1 << DMZ_GPIO); /* low activated */
else val |= (0x1 << DMZ_GPIO);
outl(val, RDC3210_CFGREG_DATA);
udelay(10);
}
}
#endif /* FORICPLUS */