410 Severn Ave., Suite 210
Annapolis MD 21403
- -----------------------------------------------------------
-
- Linux kernel-specific changes:
-
- LK1.0 (Ion Badulescu)
- - Major cleanup
- - Use 2.4 PCI API
- - Support ethtool
- - Rewrite perfect filter/hash code
- - Use interrupts for media changes
-
- LK1.1 (Ion Badulescu)
- - Disallow negotiation of unsupported full-duplex modes
*/
#define DRV_NAME "xircom_tulip_cb"
-#define DRV_VERSION "0.91+LK1.1"
-#define DRV_RELDATE "October 11, 2001"
-
-#define CARDBUS 1
+#define DRV_VERSION "0.92"
+#define DRV_RELDATE "June 27, 2006"
/* A few user-configurable values. */
+#define xircom_debug debug
+#ifdef XIRCOM_DEBUG
+static int xircom_debug = XIRCOM_DEBUG;
+#else
+static int xircom_debug = 1;
+#endif
+
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 25;
/* PCI registers */
#define PCI_POWERMGMT 0x40
-#include <linux/config.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver");
MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(csr0, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+module_param(debug, int, 0);
+module_param(max_interrupt_work, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param(csr0, int, 0);
-#define RUN_AT(x) (jiffies + (x))
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
-#define xircom_debug debug
-#ifdef XIRCOM_DEBUG
-static int xircom_debug = XIRCOM_DEBUG;
-#else
-static int xircom_debug = 1;
-#endif
+#define RUN_AT(x) (jiffies + (x))
/*
Theory of Operation
struct xircom_tx_desc tx_ring[TX_RING_SIZE];
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
+
/* The X3201-3 requires 4-byte aligned tx bufs */
struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
+
/* The addresses of receive-in-place skbuffs. */
struct sk_buff* rx_skbuff[RX_RING_SIZE];
u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */
int saved_if_port;
struct pci_dev *pdev;
spinlock_t lock;
-#ifdef CONFIG_PM
- u32 pci_state[16];
-#endif
};
static int mdio_read(struct net_device *dev, int phy_id, int location);
static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev);
static int xircom_rx(struct net_device *dev);
static void xircom_media_change(struct net_device *dev);
-static irqreturn_t xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static irqreturn_t xircom_interrupt(int irq, void *dev_instance);
static int xircom_close(struct net_device *dev);
static struct net_device_stats *xircom_get_stats(struct net_device *dev);
static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void set_rx_mode(struct net_device *dev);
static void check_duplex(struct net_device *dev);
+static const struct ethtool_ops ops;
/* The Xircom cards are picky about when certain bits in CSR6 can be
*/
static void find_mii_transceivers(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
int phy, phy_idx;
if (media_cap[tp->default_port] & MediaIsMII) {
*/
static void transceiver_voodoo(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
/* Clear the missed-packet counter. */
(volatile int)inl(ioaddr + CSR8);
- tp = dev->priv;
+ tp = netdev_priv(dev);
- tp->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&tp->lock);
tp->pdev = pdev;
tp->chip_id = chip_idx;
/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */
#endif
dev->tx_timeout = xircom_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
+ SET_ETHTOOL_OPS(dev, &ops);
transceiver_voodoo(dev);
static void
xircom_up(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
int i;
xircom_init_ring(dev);
/* Clear the tx ring */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
tp->tx_ring[i].status = 0;
}
static int
xircom_open(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
- if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev))
+ if (request_irq(dev->irq, &xircom_interrupt, IRQF_SHARED, dev->name, dev))
return -EAGAIN;
xircom_up(dev);
static void xircom_tx_timeout(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
if (media_cap[dev->if_port] & MediaIsMII) {
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void xircom_init_ring(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
int i;
tp->tx_full = 0;
break;
skb->dev = dev; /* Mark as being used by this device. */
tp->rx_ring[i].status = Rx0DescOwned; /* Owned by Xircom chip */
- tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+ tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
}
tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
/* The Tx buffer descriptor is filled in as needed, but we
do need to clear the ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
tp->tx_ring[i].status = 0;
tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
if (tp->chip_id == X3201_3)
tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif /* CARDBUS */
}
tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
}
static int
xircom_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
int entry;
u32 flag;
entry = tp->cur_tx % TX_RING_SIZE;
tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
if (tp->chip_id == X3201_3) {
memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
} else
-#endif
tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
static void xircom_media_change(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
u16 reg0, reg1, reg4, reg5;
u32 csr6 = inl(ioaddr + CSR6), newcsr6;
static void check_duplex(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
u16 reg0;
mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET);
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-static irqreturn_t xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+static irqreturn_t xircom_interrupt(int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
int csr5, work_budget = max_interrupt_work;
int handled = 0;
/* Free the original skb. */
dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
}
#ifndef final_version
static int
xircom_rx(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
int entry = tp->cur_rx % RX_RING_SIZE;
int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
int work_done = 0;
if (skb == NULL)
break;
skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
+ tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data);
work_done++;
}
tp->rx_ring[entry].status = Rx0DescOwned;
xircom_down(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
/* Disable interrupts by clearing the interrupt mask. */
outl(0, ioaddr + CSR7);
xircom_close(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
int i;
if (xircom_debug > 1)
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
+ tp->rx_skbuff[i] = NULL;
tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */
tp->rx_ring[i].length = 0;
tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
for (i = 0; i < TX_RING_SIZE; i++) {
if (tp->tx_skbuff[i])
dev_kfree_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
}
tp->open = 0;
static struct net_device_stats *xircom_get_stats(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
long ioaddr = dev->base_addr;
if (netif_device_present(dev))
return &tp->stats;
}
-
-static int xircom_ethtool_ioctl(struct net_device *dev, void __user *useraddr)
+static int xircom_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{
- struct ethtool_cmd ecmd;
- struct xircom_private *tp = dev->priv;
-
- if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
- return -EFAULT;
-
- switch (ecmd.cmd) {
- case ETHTOOL_GSET:
- ecmd.supported =
+ struct xircom_private *tp = netdev_priv(dev);
+ ecmd->supported =
SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_Autoneg |
SUPPORTED_MII;
- ecmd.advertising = ADVERTISED_MII;
- if (tp->advertising[0] & ADVERTISE_10HALF)
- ecmd.advertising |= ADVERTISED_10baseT_Half;
- if (tp->advertising[0] & ADVERTISE_10FULL)
- ecmd.advertising |= ADVERTISED_10baseT_Full;
- if (tp->advertising[0] & ADVERTISE_100HALF)
- ecmd.advertising |= ADVERTISED_100baseT_Half;
- if (tp->advertising[0] & ADVERTISE_100FULL)
- ecmd.advertising |= ADVERTISED_100baseT_Full;
- if (tp->autoneg) {
- ecmd.advertising |= ADVERTISED_Autoneg;
- ecmd.autoneg = AUTONEG_ENABLE;
- } else
- ecmd.autoneg = AUTONEG_DISABLE;
-
- ecmd.port = PORT_MII;
- ecmd.transceiver = XCVR_INTERNAL;
- ecmd.phy_address = tp->phys[0];
- ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10;
- ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
- ecmd.maxtxpkt = TX_RING_SIZE / 2;
- ecmd.maxrxpkt = 0;
-
- if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
- return -EFAULT;
- return 0;
+ ecmd->advertising = ADVERTISED_MII;
+ if (tp->advertising[0] & ADVERTISE_10HALF)
+ ecmd->advertising |= ADVERTISED_10baseT_Half;
+ if (tp->advertising[0] & ADVERTISE_10FULL)
+ ecmd->advertising |= ADVERTISED_10baseT_Full;
+ if (tp->advertising[0] & ADVERTISE_100HALF)
+ ecmd->advertising |= ADVERTISED_100baseT_Half;
+ if (tp->advertising[0] & ADVERTISE_100FULL)
+ ecmd->advertising |= ADVERTISED_100baseT_Full;
+ if (tp->autoneg) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ ecmd->autoneg = AUTONEG_ENABLE;
+ } else
+ ecmd->autoneg = AUTONEG_DISABLE;
+
+ ecmd->port = PORT_MII;
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->phy_address = tp->phys[0];
+ ecmd->speed = tp->speed100 ? SPEED_100 : SPEED_10;
+ ecmd->duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+ ecmd->maxtxpkt = TX_RING_SIZE / 2;
+ ecmd->maxrxpkt = 0;
+ return 0;
+}
- case ETHTOOL_SSET: {
- u16 autoneg, speed100, full_duplex;
-
- autoneg = (ecmd.autoneg == AUTONEG_ENABLE);
- speed100 = (ecmd.speed == SPEED_100);
- full_duplex = (ecmd.duplex == DUPLEX_FULL);
-
- tp->autoneg = autoneg;
- if (speed100 != tp->speed100 ||
- full_duplex != tp->full_duplex) {
- tp->speed100 = speed100;
- tp->full_duplex = full_duplex;
- /* change advertising bits */
- tp->advertising[0] &= ~(ADVERTISE_10HALF |
- ADVERTISE_10FULL |
- ADVERTISE_100HALF |
- ADVERTISE_100FULL |
- ADVERTISE_100BASE4);
- if (speed100) {
- if (full_duplex)
- tp->advertising[0] |= ADVERTISE_100FULL;
- else
- tp->advertising[0] |= ADVERTISE_100HALF;
- } else {
- if (full_duplex)
- tp->advertising[0] |= ADVERTISE_10FULL;
- else
- tp->advertising[0] |= ADVERTISE_10HALF;
- }
+static int xircom_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct xircom_private *tp = netdev_priv(dev);
+ u16 autoneg, speed100, full_duplex;
+
+ autoneg = (ecmd->autoneg == AUTONEG_ENABLE);
+ speed100 = (ecmd->speed == SPEED_100);
+ full_duplex = (ecmd->duplex == DUPLEX_FULL);
+
+ tp->autoneg = autoneg;
+ if (speed100 != tp->speed100 ||
+ full_duplex != tp->full_duplex) {
+ tp->speed100 = speed100;
+ tp->full_duplex = full_duplex;
+ /* change advertising bits */
+ tp->advertising[0] &= ~(ADVERTISE_10HALF |
+ ADVERTISE_10FULL |
+ ADVERTISE_100HALF |
+ ADVERTISE_100FULL |
+ ADVERTISE_100BASE4);
+ if (speed100) {
+ if (full_duplex)
+ tp->advertising[0] |= ADVERTISE_100FULL;
+ else
+ tp->advertising[0] |= ADVERTISE_100HALF;
+ } else {
+ if (full_duplex)
+ tp->advertising[0] |= ADVERTISE_10FULL;
+ else
+ tp->advertising[0] |= ADVERTISE_10HALF;
}
- check_duplex(dev);
- return 0;
- }
-
- case ETHTOOL_GDRVINFO: {
- struct ethtool_drvinfo info;
- memset(&info, 0, sizeof(info));
- info.cmd = ecmd.cmd;
- strcpy(info.driver, DRV_NAME);
- strcpy(info.version, DRV_VERSION);
- *info.fw_version = 0;
- strcpy(info.bus_info, pci_name(tp->pdev));
- if (copy_to_user(useraddr, &info, sizeof(info)))
- return -EFAULT;
- return 0;
}
+ check_duplex(dev);
+ return 0;
+}
- default:
- return -EOPNOTSUPP;
- }
+static void xircom_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct xircom_private *tp = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(tp->pdev));
}
+static const struct ethtool_ops ops = {
+ .get_settings = xircom_get_settings,
+ .set_settings = xircom_set_settings,
+ .get_drvinfo = xircom_get_drvinfo,
+};
/* Provide ioctl() calls to examine the MII xcvr state. */
static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
u16 *data = (u16 *)&rq->ifr_ifru;
int phy = tp->phys[0] & 0x1f;
unsigned long flags;
switch(cmd) {
- case SIOCETHTOOL:
- return xircom_ethtool_ioctl(dev, rq->ifr_data);
-
/* Legacy mii-diag interface */
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
if (tp->mii_cnt)
when re-entered but still correct. */
static void set_rx_mode(struct net_device *dev)
{
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
struct dev_mc_list *mclist;
long ioaddr = dev->base_addr;
int csr6 = inl(ioaddr + CSR6);
if (entry != 0) {
/* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
tp->tx_ring[entry].length =
(entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0;
tp->tx_ring[entry].buffer1 = 0;
entry = tp->cur_tx++ % TX_RING_SIZE;
}
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
/* Put the setup frame on the Tx list. */
if (entry == TX_RING_SIZE - 1)
tx_flags |= Tx1RingWrap; /* Wrap ring. */
#ifdef CONFIG_PM
-static int xircom_suspend(struct pci_dev *pdev, u32 state)
+static int xircom_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
printk(KERN_INFO "xircom_suspend(%s)\n", dev->name);
if (tp->open)
xircom_down(dev);
- pci_save_state(pdev, tp->pci_state);
+ pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, 3);
static int xircom_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct xircom_private *tp = dev->priv;
+ struct xircom_private *tp = netdev_priv(dev);
printk(KERN_INFO "xircom_resume(%s)\n", dev->name);
pci_set_power_state(pdev,0);
pci_enable_device(pdev);
- pci_restore_state(pdev, tp->pci_state);
+ pci_restore_state(pdev);
/* Bring the chip out of sleep mode.
Caution: Snooze mode does not work with some boards! */
#ifdef MODULE
printk(version);
#endif
- return pci_module_init(&xircom_driver);
+ return pci_register_driver(&xircom_driver);
}