#define TX_TIMEOUT (2*HZ)
#define NATSEMI_HW_TIMEOUT 400
-#define NATSEMI_TIMER_FREQ 3*HZ
+#define NATSEMI_TIMER_FREQ 5*HZ
#define NATSEMI_PG0_NREGS 64
#define NATSEMI_RFDR_NREGS 8
#define NATSEMI_PG1_NREGS 4
#define NATSEMI_RX_LIMIT 2046 /* maximum supported by hardware */
/* These identify the driver base version and may not be removed. */
-static const char version[] __devinitdata =
+static const char version[] __devinitconst =
KERN_INFO DRV_NAME " dp8381x driver, version "
DRV_VERSION ", " DRV_RELDATE "\n"
- KERN_INFO " originally by Donald Becker <becker@scyld.com>\n"
- KERN_INFO " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
+ " originally by Donald Becker <becker@scyld.com>\n"
+ " 2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
IIId. Synchronization
Most operations are synchronized on the np->lock irq spinlock, except the
-performance critical codepaths:
-
-The rx process only runs in the interrupt handler. Access from outside
-the interrupt handler is only permitted after disable_irq().
-
-The rx process usually runs under the netif_tx_lock. If np->intr_tx_reap
-is set, then access is permitted under spin_lock_irq(&np->lock).
-
-Thus configuration functions that want to access everything must call
- disable_irq(dev->irq);
- netif_tx_lock_bh(dev);
- spin_lock_irq(&np->lock);
-
-IV. Notes
-
-NatSemi PCI network controllers are very uncommon.
+recieve and transmit paths which are synchronised using a combination of
+hardware descriptor ownership, disabling interrupts and NAPI poll scheduling.
IVb. References
};
/* array of board data directly indexed by pci_tbl[x].driver_data */
-static const struct {
+static struct {
const char *name;
unsigned long flags;
unsigned int eeprom_size;
{ "NatSemi DP8381[56]", 0, 24 },
};
-static const struct pci_device_id natsemi_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(natsemi_pci_tbl) = {
{ PCI_VENDOR_ID_NS, 0x0020, 0x12d9, 0x000c, 0, 0, 0 },
{ PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
{ } /* terminate list */
/* Note that using only 32 bit fields simplifies conversion to big-endian
architectures. */
struct netdev_desc {
- u32 next_desc;
- s32 cmd_status;
- u32 addr;
- u32 software_use;
+ __le32 next_desc;
+ __le32 cmd_status;
+ __le32 addr;
+ __le32 software_use;
};
/* Bits in network_desc.status */
/* address of a sent-in-place packet/buffer, for later free() */
struct sk_buff *tx_skbuff[TX_RING_SIZE];
dma_addr_t tx_dma[TX_RING_SIZE];
+ struct net_device *dev;
+ struct napi_struct napi;
struct net_device_stats stats;
/* Media monitoring timer */
struct timer_list timer;
static void check_link(struct net_device *dev);
static void netdev_timer(unsigned long data);
static void dump_ring(struct net_device *dev);
-static void tx_timeout(struct net_device *dev);
+static void ns_tx_timeout(struct net_device *dev);
static int alloc_ring(struct net_device *dev);
static void refill_rx(struct net_device *dev);
static void init_ring(struct net_device *dev);
static void free_ring(struct net_device *dev);
static void reinit_ring(struct net_device *dev);
static void init_registers(struct net_device *dev);
-static int start_tx(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t intr_handler(int irq, void *dev_instance);
static void netdev_error(struct net_device *dev, int intr_status);
-static int natsemi_poll(struct net_device *dev, int *budget);
+static int natsemi_poll(struct napi_struct *napi, int budget);
static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);
static void netdev_tx_done(struct net_device *dev);
static int natsemi_change_mtu(struct net_device *dev, int new_mtu);
#define NATSEMI_CREATE_FILE(_dev, _name) \
device_create_file(&_dev->dev, &dev_attr_##_name)
#define NATSEMI_REMOVE_FILE(_dev, _name) \
- device_create_file(&_dev->dev, &dev_attr_##_name)
+ device_remove_file(&_dev->dev, &dev_attr_##_name)
NATSEMI_ATTR(dspcfg_workaround);
static ssize_t natsemi_show_dspcfg_workaround(struct device *dev,
- struct device_attribute *attr,
+ struct device_attribute *attr,
char *buf)
{
struct netdev_private *np = netdev_priv(to_net_dev(dev));
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
new_setting = 1;
- else if (!strncmp("off", buf, count - 1)
- || !strncmp("0", buf, count - 1))
+ else if (!strncmp("off", buf, count - 1) ||
+ !strncmp("0", buf, count - 1))
new_setting = 0;
else
- return count;
+ return count;
spin_lock_irqsave(&np->lock, flags);
np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
np->advertising= mdio_read(dev, MII_ADVERTISE);
- if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
- && netif_msg_probe(np)) {
+ if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL &&
+ netif_msg_probe(np)) {
printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
"10%s %s duplex.\n",
pci_name(np->pci_dev),
}
+static const struct net_device_ops natsemi_netdev_ops = {
+ .ndo_open = netdev_open,
+ .ndo_stop = netdev_close,
+ .ndo_start_xmit = start_tx,
+ .ndo_get_stats = get_stats,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_change_mtu = natsemi_change_mtu,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_tx_timeout = ns_tx_timeout,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = natsemi_poll_controller,
+#endif
+};
+
static int __devinit natsemi_probe1 (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct netdev_private *np;
int i, option, irq, chip_idx = ent->driver_data;
static int find_cnt = -1;
- unsigned long iostart, iosize;
+ resource_size_t iostart;
+ unsigned long iosize;
void __iomem *ioaddr;
const int pcibar = 1; /* PCI base address register */
int prev_eedata;
dev = alloc_etherdev(sizeof (struct netdev_private));
if (!dev)
return -ENOMEM;
- SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
i = pci_request_regions(pdev, DRV_NAME);
dev->irq = irq;
np = netdev_priv(dev);
+ netif_napi_add(dev, &np->napi, natsemi_poll, 64);
+ np->dev = dev;
np->pci_dev = pdev;
pci_set_drvdata(pdev, dev);
if (find_cnt < MAX_UNITS && full_duplex[find_cnt])
np->full_duplex = 1;
- /* The chip-specific entries in the device structure. */
- dev->open = &netdev_open;
- dev->hard_start_xmit = &start_tx;
- dev->stop = &netdev_close;
- dev->get_stats = &get_stats;
- dev->set_multicast_list = &set_rx_mode;
- dev->change_mtu = &natsemi_change_mtu;
- dev->do_ioctl = &netdev_ioctl;
- dev->tx_timeout = &tx_timeout;
+ dev->netdev_ops = &natsemi_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
- dev->poll = natsemi_poll;
- dev->weight = 64;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = &natsemi_poll_controller;
-#endif
SET_ETHTOOL_OPS(dev, ðtool_ops);
if (mtu)
goto err_create_file;
if (netif_msg_drv(np)) {
- printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ",
- dev->name, natsemi_pci_info[chip_idx].name, iostart,
- pci_name(np->pci_dev));
- for (i = 0; i < ETH_ALEN-1; i++)
- printk("%02x:", dev->dev_addr[i]);
- printk("%02x, IRQ %d", dev->dev_addr[i], irq);
+ printk(KERN_INFO "natsemi %s: %s at %#08llx "
+ "(%s), %pM, IRQ %d",
+ dev->name, natsemi_pci_info[chip_idx].name,
+ (unsigned long long)iostart, pci_name(np->pci_dev),
+ dev->dev_addr, irq);
if (dev->if_port == PORT_TP)
printk(", port TP.\n");
else if (np->ignore_phy)
a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
made udelay() unreliable.
The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
- depricated.
+ deprecated.
*/
#define eeprom_delay(ee_addr) readl(ee_addr)
tmp = mdio_read(dev, MII_BMCR);
if (np->autoneg == AUTONEG_ENABLE) {
/* renegotiate if something changed */
- if ((tmp & BMCR_ANENABLE) == 0
- || np->advertising != mdio_read(dev, MII_ADVERTISE))
+ if ((tmp & BMCR_ANENABLE) == 0 ||
+ np->advertising != mdio_read(dev, MII_ADVERTISE))
{
/* turn on autonegotiation and force negotiation */
tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
/* Reset the chip, just in case. */
natsemi_reset(dev);
- i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev);
if (i) return i;
if (netif_msg_ifup(np))
free_irq(dev->irq, dev);
return i;
}
+ napi_enable(&np->napi);
+
init_ring(dev);
spin_lock_irq(&np->lock);
init_registers(dev);
/* Set the timer to check for link beat. */
init_timer(&np->timer);
- np->timer.expires = jiffies + NATSEMI_TIMER_FREQ;
+ np->timer.expires = round_jiffies(jiffies + NATSEMI_TIMER_FREQ);
np->timer.data = (unsigned long)dev;
np->timer.function = &netdev_timer; /* timer handler */
add_timer(&np->timer);
* (these values all come from National)
*/
if (!(data & 0x80) || ((data >= 0xd8) && (data <= 0xff))) {
- struct netdev_private *np = netdev_priv(dev);
+ np = netdev_priv(dev);
/* the bug has been triggered - fix the coefficient */
writew(TSTDAT_FIXED, ioaddr + TSTDAT);
struct net_device *dev = (struct net_device *)data;
struct netdev_private *np = netdev_priv(dev);
void __iomem * ioaddr = ns_ioaddr(dev);
- int next_tick = 5*HZ;
+ int next_tick = NATSEMI_TIMER_FREQ;
if (netif_msg_timer(np)) {
/* DO NOT read the IntrStatus register,
next_tick = 1;
}
}
- mod_timer(&np->timer, jiffies + next_tick);
+
+ if (next_tick > 1)
+ mod_timer(&np->timer, round_jiffies(jiffies + next_tick));
+ else
+ mod_timer(&np->timer, jiffies + next_tick);
}
static void dump_ring(struct net_device *dev)
}
}
-static void tx_timeout(struct net_device *dev)
+static void ns_tx_timeout(struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
void __iomem * ioaddr = ns_ioaddr(dev);
spin_unlock_irq(&np->lock);
enable_irq(dev->irq);
- dev->trans_start = jiffies;
+ dev->trans_start = jiffies; /* prevent tx timeout */
np->stats.tx_errors++;
netif_wake_queue(dev);
}
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
np->rx_ring[i].cmd_status = 0;
- np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+ np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
if (np->rx_skbuff[i]) {
pci_unmap_single(np->pci_dev,
np->rx_dma[i], buflen,
reinit_rx(dev);
}
-static int start_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
void __iomem * ioaddr = ns_ioaddr(dev);
}
spin_unlock_irqrestore(&np->lock, flags);
- dev->trans_start = jiffies;
-
if (netif_msg_tx_queued(np)) {
printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
dev->name, np->cur_tx, entry);
}
- return 0;
+ return NETDEV_TX_OK;
}
static void netdev_tx_done(struct net_device *dev)
dev_kfree_skb_irq(np->tx_skbuff[entry]);
np->tx_skbuff[entry] = NULL;
}
- if (netif_queue_stopped(dev)
- && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+ if (netif_queue_stopped(dev) &&
+ np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
/* The ring is no longer full, wake queue. */
netif_wake_queue(dev);
}
prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
- if (netif_rx_schedule_prep(dev)) {
+ if (napi_schedule_prep(&np->napi)) {
/* Disable interrupts and register for poll */
natsemi_irq_disable(dev);
- __netif_rx_schedule(dev);
+ __napi_schedule(&np->napi);
} else
printk(KERN_WARNING
"%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
/* This is the NAPI poll routine. As well as the standard RX handling
* it also handles all other interrupts that the chip might raise.
*/
-static int natsemi_poll(struct net_device *dev, int *budget)
+static int natsemi_poll(struct napi_struct *napi, int budget)
{
- struct netdev_private *np = netdev_priv(dev);
+ struct netdev_private *np = container_of(napi, struct netdev_private, napi);
+ struct net_device *dev = np->dev;
void __iomem * ioaddr = ns_ioaddr(dev);
-
- int work_to_do = min(*budget, dev->quota);
int work_done = 0;
do {
if (np->intr_status &
(IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
IntrRxErr | IntrRxOverrun)) {
- netdev_rx(dev, &work_done, work_to_do);
+ netdev_rx(dev, &work_done, budget);
}
if (np->intr_status &
if (np->intr_status & IntrAbnormalSummary)
netdev_error(dev, np->intr_status);
- *budget -= work_done;
- dev->quota -= work_done;
-
- if (work_done >= work_to_do)
- return 1;
+ if (work_done >= budget)
+ return work_done;
np->intr_status = readl(ioaddr + IntrStatus);
} while (np->intr_status);
- netif_rx_complete(dev);
+ napi_complete(napi);
/* Reenable interrupts providing nothing is trying to shut
* the chip down. */
spin_lock(&np->lock);
- if (!np->hands_off && netif_running(dev))
+ if (!np->hands_off)
natsemi_irq_enable(dev);
spin_unlock(&np->lock);
- return 0;
+ return work_done;
}
/* This routine is logically part of the interrupt handler, but separated
/* Omit CRC size. */
/* Check if the packet is long enough to accept
* without copying to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
/* 16 byte align the IP header */
skb_reserve(skb, RX_OFFSET);
pci_dma_sync_single_for_cpu(np->pci_dev,
}
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += pkt_len;
}
spin_lock(&np->lock);
if (intr_status & LinkChange) {
u16 lpa = mdio_read(dev, MII_LPA);
- if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE
- && netif_msg_link(np)) {
+ if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE &&
+ netif_msg_link(np)) {
printk(KERN_INFO
"%s: Autonegotiation advertising"
" %#04x partner %#04x.\n", dev->name,
dev->name);
}
np->stats.rx_fifo_errors++;
+ np->stats.rx_errors++;
}
/* Hmmmmm, it's not clear how to recover from PCI faults. */
if (intr_status & IntrPCIErr) {
printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
intr_status & IntrPCIErr);
np->stats.tx_fifo_errors++;
+ np->stats.tx_errors++;
np->stats.rx_fifo_errors++;
+ np->stats.rx_errors++;
}
spin_unlock(&np->lock);
}
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
rx_mode = RxFilterEnable | AcceptBroadcast
| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
rx_mode = RxFilterEnable | AcceptBroadcast
| AcceptAllMulticast | AcceptMyPhys;
} else {
- struct dev_mc_list *mclist;
+ struct netdev_hw_addr *ha;
int i;
+
memset(mc_filter, 0, sizeof(mc_filter));
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next) {
- int i = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 23) & 0x1ff;
- mc_filter[i/8] |= (1 << (i & 0x07));
+ netdev_for_each_mc_addr(ha, dev) {
+ int b = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x1ff;
+ mc_filter[b/8] |= (1 << (b & 0x07));
}
rx_mode = RxFilterEnable | AcceptBroadcast
| AcceptMulticast | AcceptMyPhys;
switch(cmd) {
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
- case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */
data->phy_id = np->phy_addr_external;
/* Fall Through */
case SIOCGMIIREG: /* Read MII PHY register. */
- case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */
/* The phy_id is not enough to uniquely identify
* the intended target. Therefore the command is sent to
* the given mii on the current port.
return 0;
case SIOCSMIIREG: /* Write MII PHY register. */
- case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
if (dev->if_port == PORT_TP) {
if ((data->phy_id & 0x1f) == np->phy_addr_external) {
if ((data->reg_num & 0x1f) == MII_ADVERTISE)
dev->name, np->cur_tx, np->dirty_tx,
np->cur_rx, np->dirty_rx);
+ napi_disable(&np->napi);
+
/*
* FIXME: what if someone tries to close a device
* that is suspended?
* suspend/resume synchronization:
* entry points:
* netdev_open, netdev_close, netdev_ioctl, set_rx_mode, intr_handler,
- * start_tx, tx_timeout
+ * start_tx, ns_tx_timeout
*
* No function accesses the hardware without checking np->hands_off.
* the check occurs under spin_lock_irq(&np->lock);
* disable_irq() to enforce synchronization.
* * natsemi_poll: checks before reenabling interrupts. suspend
* sets hands_off, disables interrupts and then waits with
- * netif_poll_disable().
+ * napi_disable().
*
* Interrupts must be disabled, otherwise hands_off can cause irq storms.
*/
spin_unlock_irq(&np->lock);
enable_irq(dev->irq);
- netif_poll_disable(dev);
+ napi_disable(&np->napi);
/* Update the error counts. */
__get_stats(dev);
{
struct net_device *dev = pci_get_drvdata (pdev);
struct netdev_private *np = netdev_priv(dev);
+ int ret = 0;
rtnl_lock();
if (netif_device_present(dev))
goto out;
if (netif_running(dev)) {
BUG_ON(!np->hands_off);
- pci_enable_device(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "pci_enable_device() failed: %d\n", ret);
+ goto out;
+ }
/* pci_power_on(pdev); */
+ napi_enable(&np->napi);
+
natsemi_reset(dev);
init_ring(dev);
disable_irq(dev->irq);
spin_unlock_irq(&np->lock);
enable_irq(dev->irq);
- mod_timer(&np->timer, jiffies + 1*HZ);
+ mod_timer(&np->timer, round_jiffies(jiffies + 1*HZ));
}
netif_device_attach(dev);
- netif_poll_enable(dev);
out:
rtnl_unlock();
- return 0;
+ return ret;
}
#endif /* CONFIG_PM */