2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1997-2000 LAN Media Corporation (LMC)
|
|
|
|
* All rights reserved. www.lanmedia.com
|
2008-07-03 02:46:21 +08:00
|
|
|
* Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* This code is written by:
|
|
|
|
* Andrew Stanley-Jones (asj@cban.com)
|
|
|
|
* Rob Braun (bbraun@vix.com),
|
|
|
|
* Michael Graff (explorer@vix.com) and
|
|
|
|
* Matt Thomas (matt@3am-software.com).
|
|
|
|
*
|
|
|
|
* With Help By:
|
|
|
|
* David Boggs
|
|
|
|
* Ron Crane
|
|
|
|
* Alan Cox
|
|
|
|
*
|
|
|
|
* This software may be used and distributed according to the terms
|
|
|
|
* of the GNU General Public License version 2, incorporated herein by reference.
|
|
|
|
*
|
|
|
|
* Driver for the LanMedia LMC5200, LMC5245, LMC1000, LMC1200 cards.
|
|
|
|
*
|
|
|
|
* To control link specific options lmcctl is required.
|
|
|
|
* It can be obtained from ftp.lanmedia.com.
|
|
|
|
*
|
|
|
|
* Linux driver notes:
|
|
|
|
* Linux uses the device struct lmc_private to pass private information
|
|
|
|
* arround.
|
|
|
|
*
|
|
|
|
* The initialization portion of this driver (the lmc_reset() and the
|
|
|
|
* lmc_dec_reset() functions, as well as the led controls and the
|
|
|
|
* lmc_initcsrs() functions.
|
|
|
|
*
|
|
|
|
* The watchdog function runs every second and checks to see if
|
|
|
|
* we still have link, and that the timing source is what we expected
|
|
|
|
* it to be. If link is lost, the interface is marked down, and
|
|
|
|
* we no longer can transmit.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/ptrace.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/delay.h>
|
2008-07-03 02:46:21 +08:00
|
|
|
#include <linux/hdlc.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/inet.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <asm/processor.h> /* Processor type for cache alignment. */
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
//#include <asm/spinlock.h>
|
|
|
|
|
|
|
|
#define DRIVER_MAJOR_VERSION 1
|
|
|
|
#define DRIVER_MINOR_VERSION 34
|
|
|
|
#define DRIVER_SUB_VERSION 0
|
|
|
|
|
|
|
|
#define DRIVER_VERSION ((DRIVER_MAJOR_VERSION << 8) + DRIVER_MINOR_VERSION)
|
|
|
|
|
|
|
|
#include "lmc.h"
|
|
|
|
#include "lmc_var.h"
|
|
|
|
#include "lmc_ioctl.h"
|
|
|
|
#include "lmc_debug.h"
|
|
|
|
#include "lmc_proto.h"
|
|
|
|
|
|
|
|
static int LMC_PKT_BUF_SZ = 1542;
|
|
|
|
|
|
|
|
static struct pci_device_id lmc_pci_tbl[] = {
|
|
|
|
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
|
|
|
|
PCI_VENDOR_ID_LMC, PCI_ANY_ID },
|
|
|
|
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
|
|
|
|
PCI_ANY_ID, PCI_VENDOR_ID_LMC },
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, lmc_pci_tbl);
|
2008-07-03 02:46:21 +08:00
|
|
|
MODULE_LICENSE("GPL v2");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
static int lmc_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
|
|
|
static int lmc_rx (struct net_device *dev);
|
|
|
|
static int lmc_open(struct net_device *dev);
|
|
|
|
static int lmc_close(struct net_device *dev);
|
|
|
|
static struct net_device_stats *lmc_get_stats(struct net_device *dev);
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t lmc_interrupt(int irq, void *dev_instance);
|
2005-04-17 06:20:36 +08:00
|
|
|
static void lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base, size_t csr_size);
|
|
|
|
static void lmc_softreset(lmc_softc_t * const);
|
|
|
|
static void lmc_running_reset(struct net_device *dev);
|
|
|
|
static int lmc_ifdown(struct net_device * const);
|
|
|
|
static void lmc_watchdog(unsigned long data);
|
|
|
|
static void lmc_reset(lmc_softc_t * const sc);
|
|
|
|
static void lmc_dec_reset(lmc_softc_t * const sc);
|
|
|
|
static void lmc_driver_timeout(struct net_device *dev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* linux reserves 16 device specific IOCTLs. We call them
|
|
|
|
* LMCIOC* to control various bits of our world.
|
|
|
|
*/
|
2008-07-03 02:46:21 +08:00
|
|
|
int lmc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
lmc_ctl_t ctl;
|
2008-07-03 02:46:21 +08:00
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
u16 regVal;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_ioctl in");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most functions mess with the structure
|
|
|
|
* Disable interrupts while we do the polling
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
/*
|
|
|
|
* Return current driver state. Since we keep this up
|
|
|
|
* To date internally, just copy this out to the user.
|
|
|
|
*/
|
|
|
|
case LMCIOCGINFO: /*fold01*/
|
2007-10-30 16:11:46 +08:00
|
|
|
if (copy_to_user(ifr->ifr_data, &sc->ictl, sizeof(lmc_ctl_t)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LMCIOCSINFO: /*fold01*/
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dev->flags & IFF_UP){
|
|
|
|
ret = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-10-30 16:11:46 +08:00
|
|
|
if (copy_from_user(&ctl, ifr->ifr_data, sizeof(lmc_ctl_t))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_media->set_status (sc, &ctl);
|
|
|
|
|
|
|
|
if(ctl.crc_length != sc->ictl.crc_length) {
|
|
|
|
sc->lmc_media->set_crc_length(sc, ctl.crc_length);
|
|
|
|
if (sc->ictl.crc_length == LMC_CTL_CRC_LENGTH_16)
|
|
|
|
sc->TxDescriptControlInit |= LMC_TDES_ADD_CRC_DISABLE;
|
|
|
|
else
|
|
|
|
sc->TxDescriptControlInit &= ~LMC_TDES_ADD_CRC_DISABLE;
|
|
|
|
}
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LMCIOCIFTYPE: /*fold01*/
|
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
u16 old_type = sc->if_type;
|
|
|
|
u16 new_type;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if (copy_from_user(&new_type, ifr->ifr_data, sizeof(u16))) {
|
2007-10-30 16:11:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
if (new_type == old_type)
|
|
|
|
{
|
|
|
|
ret = 0 ;
|
|
|
|
break; /* no change */
|
|
|
|
}
|
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
lmc_proto_close(sc);
|
|
|
|
|
|
|
|
sc->if_type = new_type;
|
|
|
|
lmc_proto_attach(sc);
|
2008-07-03 02:46:21 +08:00
|
|
|
ret = lmc_proto_open(sc);
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2008-07-03 02:46:21 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case LMCIOCGETXINFO: /*fold01*/
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_xinfo.Magic0 = 0xBEEFCAFE;
|
|
|
|
|
|
|
|
sc->lmc_xinfo.PciCardType = sc->lmc_cardtype;
|
|
|
|
sc->lmc_xinfo.PciSlotNumber = 0;
|
|
|
|
sc->lmc_xinfo.DriverMajorVersion = DRIVER_MAJOR_VERSION;
|
|
|
|
sc->lmc_xinfo.DriverMinorVersion = DRIVER_MINOR_VERSION;
|
|
|
|
sc->lmc_xinfo.DriverSubVersion = DRIVER_SUB_VERSION;
|
|
|
|
sc->lmc_xinfo.XilinxRevisionNumber =
|
|
|
|
lmc_mii_readreg (sc, 0, 3) & 0xf;
|
|
|
|
sc->lmc_xinfo.MaxFrameSize = LMC_PKT_BUF_SZ;
|
|
|
|
sc->lmc_xinfo.link_status = sc->lmc_media->get_link_status (sc);
|
|
|
|
sc->lmc_xinfo.mii_reg16 = lmc_mii_readreg (sc, 0, 16);
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sc->lmc_xinfo.Magic1 = 0xDEADBEEF;
|
|
|
|
|
|
|
|
if (copy_to_user(ifr->ifr_data, &sc->lmc_xinfo,
|
2007-10-31 06:34:34 +08:00
|
|
|
sizeof(struct lmc_xinfo)))
|
2007-10-30 16:11:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
case LMCIOCGETLMCSTATS:
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2008-07-03 02:46:21 +08:00
|
|
|
if (sc->lmc_cardtype == LMC_CARDTYPE_T1) {
|
|
|
|
lmc_mii_writereg(sc, 0, 17, T1FRAMER_FERR_LSB);
|
|
|
|
sc->extra_stats.framingBitErrorCount +=
|
|
|
|
lmc_mii_readreg(sc, 0, 18) & 0xff;
|
|
|
|
lmc_mii_writereg(sc, 0, 17, T1FRAMER_FERR_MSB);
|
|
|
|
sc->extra_stats.framingBitErrorCount +=
|
|
|
|
(lmc_mii_readreg(sc, 0, 18) & 0xff) << 8;
|
|
|
|
lmc_mii_writereg(sc, 0, 17, T1FRAMER_LCV_LSB);
|
|
|
|
sc->extra_stats.lineCodeViolationCount +=
|
|
|
|
lmc_mii_readreg(sc, 0, 18) & 0xff;
|
|
|
|
lmc_mii_writereg(sc, 0, 17, T1FRAMER_LCV_MSB);
|
|
|
|
sc->extra_stats.lineCodeViolationCount +=
|
|
|
|
(lmc_mii_readreg(sc, 0, 18) & 0xff) << 8;
|
|
|
|
lmc_mii_writereg(sc, 0, 17, T1FRAMER_AERR);
|
|
|
|
regVal = lmc_mii_readreg(sc, 0, 18) & 0xff;
|
|
|
|
|
|
|
|
sc->extra_stats.lossOfFrameCount +=
|
|
|
|
(regVal & T1FRAMER_LOF_MASK) >> 4;
|
|
|
|
sc->extra_stats.changeOfFrameAlignmentCount +=
|
|
|
|
(regVal & T1FRAMER_COFA_MASK) >> 2;
|
|
|
|
sc->extra_stats.severelyErroredFrameCount +=
|
|
|
|
regVal & T1FRAMER_SEF_MASK;
|
|
|
|
}
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2008-07-03 02:46:21 +08:00
|
|
|
if (copy_to_user(ifr->ifr_data, &sc->lmc_device->stats,
|
|
|
|
sizeof(sc->lmc_device->stats)) ||
|
|
|
|
copy_to_user(ifr->ifr_data + sizeof(sc->lmc_device->stats),
|
|
|
|
&sc->extra_stats, sizeof(sc->extra_stats)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
case LMCIOCCLEARLMCSTATS:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2008-07-03 02:46:21 +08:00
|
|
|
memset(&sc->lmc_device->stats, 0, sizeof(sc->lmc_device->stats));
|
|
|
|
memset(&sc->extra_stats, 0, sizeof(sc->extra_stats));
|
|
|
|
sc->extra_stats.check = STATCHECK;
|
|
|
|
sc->extra_stats.version_size = (DRIVER_VERSION << 16) +
|
|
|
|
sizeof(sc->lmc_device->stats) + sizeof(sc->extra_stats);
|
|
|
|
sc->extra_stats.lmc_cardtype = sc->lmc_cardtype;
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2008-07-03 02:46:21 +08:00
|
|
|
ret = 0;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case LMCIOCSETCIRCUIT: /*fold01*/
|
|
|
|
if (!capable(CAP_NET_ADMIN)){
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dev->flags & IFF_UP){
|
|
|
|
ret = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-10-30 16:11:46 +08:00
|
|
|
if (copy_from_user(&ctl, ifr->ifr_data, sizeof(lmc_ctl_t))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_media->set_circuit_type(sc, ctl.circuit_type);
|
|
|
|
sc->ictl.circuit_type = ctl.circuit_type;
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LMCIOCRESET: /*fold01*/
|
|
|
|
if (!capable(CAP_NET_ADMIN)){
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Reset driver and bring back to current state */
|
|
|
|
printk (" REG16 before reset +%04x\n", lmc_mii_readreg (sc, 0, 16));
|
|
|
|
lmc_running_reset (dev);
|
|
|
|
printk (" REG16 after reset +%04x\n", lmc_mii_readreg (sc, 0, 16));
|
|
|
|
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_FORCEDRESET, LMC_CSR_READ (sc, csr_status), lmc_mii_readreg (sc, 0, 16));
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
case LMCIOCDUMPEVENTLOG:
|
2007-10-30 16:11:46 +08:00
|
|
|
if (copy_to_user(ifr->ifr_data, &lmcEventLogIndex, sizeof(u32))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2008-07-03 06:39:46 +08:00
|
|
|
if (copy_to_user(ifr->ifr_data + sizeof(u32), lmcEventLogBuf,
|
|
|
|
sizeof(lmcEventLogBuf)))
|
2007-10-30 16:11:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
#endif /* end ifdef _DBG_EVENTLOG */
|
|
|
|
case LMCIOCT1CONTROL: /*fold01*/
|
|
|
|
if (sc->lmc_cardtype != LMC_CARDTYPE_T1){
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LMCIOCXILINX: /*fold01*/
|
|
|
|
{
|
|
|
|
struct lmc_xilinx_control xc; /*fold02*/
|
|
|
|
|
|
|
|
if (!capable(CAP_NET_ADMIN)){
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop the xwitter whlie we restart the hardware
|
|
|
|
*/
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
if (copy_from_user(&xc, ifr->ifr_data, sizeof(struct lmc_xilinx_control))) {
|
2007-10-30 16:11:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
2008-10-15 18:50:42 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
switch(xc.command){
|
|
|
|
case lmc_xilinx_reset: /*fold02*/
|
|
|
|
{
|
|
|
|
u16 mii;
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
mii = lmc_mii_readreg (sc, 0, 16);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make all of them 0 and make input
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make the reset output
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkoutput(sc, LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RESET low to force configuration. This also forces
|
|
|
|
* the transmitter clock to be internal, but we expect to reset
|
|
|
|
* that later anyway.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->lmc_gpio &= ~LMC_GEP_RESET;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hold for more than 10 microseconds
|
|
|
|
*/
|
|
|
|
udelay(50);
|
|
|
|
|
|
|
|
sc->lmc_gpio |= LMC_GEP_RESET;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* stop driving Xilinx-related signals
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
|
|
|
|
|
|
|
/* Reset the frammer hardware */
|
|
|
|
sc->lmc_media->set_link_status (sc, 1);
|
|
|
|
sc->lmc_media->set_status (sc, NULL);
|
|
|
|
// lmc_softreset(sc);
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < 5; i++){
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED0);
|
|
|
|
mdelay(100);
|
|
|
|
lmc_led_off(sc, LMC_DS3_LED0);
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED1);
|
|
|
|
mdelay(100);
|
|
|
|
lmc_led_off(sc, LMC_DS3_LED1);
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED3);
|
|
|
|
mdelay(100);
|
|
|
|
lmc_led_off(sc, LMC_DS3_LED3);
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED2);
|
|
|
|
mdelay(100);
|
|
|
|
lmc_led_off(sc, LMC_DS3_LED2);
|
|
|
|
}
|
|
|
|
}
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ret = 0x0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case lmc_xilinx_load_prom: /*fold02*/
|
|
|
|
{
|
|
|
|
u16 mii;
|
|
|
|
int timeout = 500000;
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
mii = lmc_mii_readreg (sc, 0, 16);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make all of them 0 and make input
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make the reset output
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkoutput(sc, LMC_GEP_DP | LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RESET low to force configuration. This also forces
|
|
|
|
* the transmitter clock to be internal, but we expect to reset
|
|
|
|
* that later anyway.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->lmc_gpio &= ~(LMC_GEP_RESET | LMC_GEP_DP);
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hold for more than 10 microseconds
|
|
|
|
*/
|
|
|
|
udelay(50);
|
|
|
|
|
|
|
|
sc->lmc_gpio |= LMC_GEP_DP | LMC_GEP_RESET;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* busy wait for the chip to reset
|
|
|
|
*/
|
|
|
|
while( (LMC_CSR_READ(sc, csr_gp) & LMC_GEP_INIT) == 0 &&
|
|
|
|
(timeout-- > 0))
|
2008-10-15 18:50:42 +08:00
|
|
|
cpu_relax();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* stop driving Xilinx-related signals
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ret = 0x0;
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
case lmc_xilinx_load: /*fold02*/
|
|
|
|
{
|
|
|
|
char *data;
|
|
|
|
int pos;
|
|
|
|
int timeout = 500000;
|
|
|
|
|
2008-01-24 18:06:46 +08:00
|
|
|
if (!xc.data) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = kmalloc(xc.len, GFP_KERNEL);
|
2008-01-24 18:06:46 +08:00
|
|
|
if (!data) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_WARNING "%s: Failed to allocate memory for copy\n", dev->name);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(copy_from_user(data, xc.data, xc.len))
|
|
|
|
{
|
|
|
|
kfree(data);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk("%s: Starting load of data Len: %d at 0x%p == 0x%p\n", dev->name, xc.len, xc.data, data);
|
|
|
|
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the Xilinx and start prgramming from the DEC
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set ouput as:
|
|
|
|
* Reset: 0 (active)
|
|
|
|
* DP: 0 (active)
|
|
|
|
* Mode: 1
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sc->lmc_gpio = 0x00;
|
|
|
|
sc->lmc_gpio &= ~LMC_GEP_DP;
|
|
|
|
sc->lmc_gpio &= ~LMC_GEP_RESET;
|
|
|
|
sc->lmc_gpio |= LMC_GEP_MODE;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
lmc_gpio_mkoutput(sc, LMC_GEP_MODE | LMC_GEP_DP | LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait at least 10 us 20 to be safe
|
|
|
|
*/
|
|
|
|
udelay(50);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear reset and activate programming lines
|
|
|
|
* Reset: Input
|
|
|
|
* DP: Input
|
|
|
|
* Clock: Output
|
|
|
|
* Data: Output
|
|
|
|
* Mode: Output
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, LMC_GEP_DP | LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set LOAD, DATA, Clock to 1
|
|
|
|
*/
|
|
|
|
sc->lmc_gpio = 0x00;
|
|
|
|
sc->lmc_gpio |= LMC_GEP_MODE;
|
|
|
|
sc->lmc_gpio |= LMC_GEP_DATA;
|
|
|
|
sc->lmc_gpio |= LMC_GEP_CLK;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
lmc_gpio_mkoutput(sc, LMC_GEP_DATA | LMC_GEP_CLK | LMC_GEP_MODE );
|
|
|
|
|
|
|
|
/*
|
|
|
|
* busy wait for the chip to reset
|
|
|
|
*/
|
|
|
|
while( (LMC_CSR_READ(sc, csr_gp) & LMC_GEP_INIT) == 0 &&
|
|
|
|
(timeout-- > 0))
|
2008-10-15 18:50:42 +08:00
|
|
|
cpu_relax();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
printk(KERN_DEBUG "%s: Waited %d for the Xilinx to clear it's memory\n", dev->name, 500000-timeout);
|
|
|
|
|
|
|
|
for(pos = 0; pos < xc.len; pos++){
|
|
|
|
switch(data[pos]){
|
|
|
|
case 0:
|
|
|
|
sc->lmc_gpio &= ~LMC_GEP_DATA; /* Data is 0 */
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
sc->lmc_gpio |= LMC_GEP_DATA; /* Data is 1 */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(KERN_WARNING "%s Bad data in xilinx programming data at %d, got %d wanted 0 or 1\n", dev->name, pos, data[pos]);
|
|
|
|
sc->lmc_gpio |= LMC_GEP_DATA; /* Assume it's 1 */
|
|
|
|
}
|
|
|
|
sc->lmc_gpio &= ~LMC_GEP_CLK; /* Clock to zero */
|
|
|
|
sc->lmc_gpio |= LMC_GEP_MODE;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
sc->lmc_gpio |= LMC_GEP_CLK; /* Put the clack back to one */
|
|
|
|
sc->lmc_gpio |= LMC_GEP_MODE;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
if((LMC_CSR_READ(sc, csr_gp) & LMC_GEP_INIT) == 0){
|
|
|
|
printk(KERN_WARNING "%s: Reprogramming FAILED. Needs to be reprogrammed. (corrupted data)\n", dev->name);
|
|
|
|
}
|
|
|
|
else if((LMC_CSR_READ(sc, csr_gp) & LMC_GEP_DP) == 0){
|
|
|
|
printk(KERN_WARNING "%s: Reprogramming FAILED. Needs to be reprogrammed. (done)\n", dev->name);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printk(KERN_DEBUG "%s: Done reprogramming Xilinx, %d bits, good luck!\n", dev->name, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_gpio_mkinput(sc, 0xff);
|
|
|
|
|
|
|
|
sc->lmc_miireg16 |= LMC_MII16_FIFO_RESET;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
|
|
|
|
|
|
|
sc->lmc_miireg16 &= ~LMC_MII16_FIFO_RESET;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
2008-10-15 18:50:42 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
kfree(data);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: /*fold02*/
|
|
|
|
ret = -EBADE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
sc->lmc_txfull = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: /*fold01*/
|
|
|
|
/* If we don't know what to do, give the protocol a shot. */
|
|
|
|
ret = lmc_proto_ioctl (sc, ifr, cmd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_ioctl out");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* the watchdog process that cruises around */
|
|
|
|
static void lmc_watchdog (unsigned long data) /*fold00*/
|
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
struct net_device *dev = (struct net_device *)data;
|
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
int link_status;
|
2008-07-03 06:39:46 +08:00
|
|
|
u32 ticks;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_watchdog in");
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
if(sc->check != 0xBEAFCAFE){
|
2006-01-15 09:12:54 +08:00
|
|
|
printk("LMC: Corrupt net_device struct, breaking out\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Make sure the tx jabber and rx watchdog are off,
|
|
|
|
* and the transmit and receive processes are running.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LMC_CSR_WRITE (sc, csr_15, 0x00000011);
|
|
|
|
sc->lmc_cmdmode |= TULIP_CMD_TXRUN | TULIP_CMD_RXRUN;
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, sc->lmc_cmdmode);
|
|
|
|
|
|
|
|
if (sc->lmc_ok == 0)
|
|
|
|
goto kick_timer;
|
|
|
|
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_WATCHDOG, LMC_CSR_READ (sc, csr_status), lmc_mii_readreg (sc, 0, 16));
|
|
|
|
|
|
|
|
/* --- begin time out check -----------------------------------
|
|
|
|
* check for a transmit interrupt timeout
|
|
|
|
* Has the packet xmt vs xmt serviced threshold been exceeded */
|
|
|
|
if (sc->lmc_taint_tx == sc->lastlmc_taint_tx &&
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.tx_packets > sc->lasttx_packets &&
|
|
|
|
sc->tx_TimeoutInd == 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
/* wait for the watchdog to come around again */
|
|
|
|
sc->tx_TimeoutInd = 1;
|
|
|
|
}
|
|
|
|
else if (sc->lmc_taint_tx == sc->lastlmc_taint_tx &&
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.tx_packets > sc->lasttx_packets &&
|
|
|
|
sc->tx_TimeoutInd)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_XMTINTTMO, LMC_CSR_READ (sc, csr_status), 0);
|
|
|
|
|
|
|
|
sc->tx_TimeoutDisplay = 1;
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_TimeoutCnt++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* DEC chip is stuck, hit it with a RESET!!!! */
|
|
|
|
lmc_running_reset (dev);
|
|
|
|
|
|
|
|
|
|
|
|
/* look at receive & transmit process state to make sure they are running */
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET1, LMC_CSR_READ (sc, csr_status), 0);
|
|
|
|
|
|
|
|
/* look at: DSR - 02 for Reg 16
|
|
|
|
* CTS - 08
|
|
|
|
* DCD - 10
|
|
|
|
* RI - 20
|
|
|
|
* for Reg 17
|
|
|
|
*/
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET2, lmc_mii_readreg (sc, 0, 16), lmc_mii_readreg (sc, 0, 17));
|
|
|
|
|
|
|
|
/* reset the transmit timeout detection flag */
|
|
|
|
sc->tx_TimeoutInd = 0;
|
|
|
|
sc->lastlmc_taint_tx = sc->lmc_taint_tx;
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lasttx_packets = sc->lmc_device->stats.tx_packets;
|
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->tx_TimeoutInd = 0;
|
|
|
|
sc->lastlmc_taint_tx = sc->lmc_taint_tx;
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lasttx_packets = sc->lmc_device->stats.tx_packets;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* --- end time out check ----------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
link_status = sc->lmc_media->get_link_status (sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hardware level link lost, but the interface is marked as up.
|
|
|
|
* Mark it as down.
|
|
|
|
*/
|
|
|
|
if ((link_status == 0) && (sc->last_link_status != 0)) {
|
|
|
|
printk(KERN_WARNING "%s: hardware/physical link down\n", dev->name);
|
|
|
|
sc->last_link_status = 0;
|
|
|
|
/* lmc_reset (sc); Why reset??? The link can go down ok */
|
|
|
|
|
|
|
|
/* Inform the world that link has been lost */
|
2005-05-13 07:45:25 +08:00
|
|
|
netif_carrier_off(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hardware link is up, but the interface is marked as down.
|
|
|
|
* Bring it back up again.
|
|
|
|
*/
|
|
|
|
if (link_status != 0 && sc->last_link_status == 0) {
|
|
|
|
printk(KERN_WARNING "%s: hardware/physical link up\n", dev->name);
|
|
|
|
sc->last_link_status = 1;
|
|
|
|
/* lmc_reset (sc); Again why reset??? */
|
|
|
|
|
2005-05-13 07:45:25 +08:00
|
|
|
netif_carrier_on(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call media specific watchdog functions */
|
|
|
|
sc->lmc_media->watchdog(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Poke the transmitter to make sure it
|
|
|
|
* never stops, even if we run out of mem
|
|
|
|
*/
|
|
|
|
LMC_CSR_WRITE(sc, csr_rxpoll, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for code that failed
|
|
|
|
* and try and fix it as appropriate
|
|
|
|
*/
|
|
|
|
if(sc->failed_ring == 1){
|
|
|
|
/*
|
|
|
|
* Failed to setup the recv/xmit rin
|
|
|
|
* Try again
|
|
|
|
*/
|
|
|
|
sc->failed_ring = 0;
|
|
|
|
lmc_softreset(sc);
|
|
|
|
}
|
|
|
|
if(sc->failed_recv_alloc == 1){
|
|
|
|
/*
|
|
|
|
* We failed to alloc mem in the
|
|
|
|
* interrupt handler, go through the rings
|
|
|
|
* and rebuild them
|
|
|
|
*/
|
|
|
|
sc->failed_recv_alloc = 0;
|
|
|
|
lmc_softreset(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remember the timer value
|
|
|
|
*/
|
|
|
|
kick_timer:
|
|
|
|
|
|
|
|
ticks = LMC_CSR_READ (sc, csr_gp_timer);
|
|
|
|
LMC_CSR_WRITE (sc, csr_gp_timer, 0xffffffffUL);
|
|
|
|
sc->ictl.ticks = 0x0000ffff - (ticks & 0x0000ffff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* restart this timer.
|
|
|
|
*/
|
|
|
|
sc->timer.expires = jiffies + (HZ);
|
|
|
|
add_timer (&sc->timer);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_watchdog out");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
static int lmc_attach(struct net_device *dev, unsigned short encoding,
|
|
|
|
unsigned short parity)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT)
|
|
|
|
return 0;
|
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-01-09 05:52:11 +08:00
|
|
|
static const struct net_device_ops lmc_ops = {
|
|
|
|
.ndo_open = lmc_open,
|
|
|
|
.ndo_stop = lmc_close,
|
|
|
|
.ndo_change_mtu = hdlc_change_mtu,
|
|
|
|
.ndo_start_xmit = hdlc_start_xmit,
|
|
|
|
.ndo_do_ioctl = lmc_ioctl,
|
|
|
|
.ndo_tx_timeout = lmc_driver_timeout,
|
|
|
|
.ndo_get_stats = lmc_get_stats,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int __devinit lmc_init_one(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *ent)
|
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc;
|
|
|
|
struct net_device *dev;
|
|
|
|
u16 subdevice;
|
|
|
|
u16 AdapModelNum;
|
|
|
|
int err;
|
|
|
|
static int cards_found;
|
|
|
|
|
|
|
|
/* lmc_trace(dev, "lmc_init_one in"); */
|
|
|
|
|
|
|
|
err = pci_enable_device(pdev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "lmc: pci enable failed: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
err = pci_request_regions(pdev, "lmc");
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "lmc: pci_request_region failed\n");
|
|
|
|
goto err_req_io;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
/*
|
|
|
|
* Allocate our own device structure
|
|
|
|
*/
|
|
|
|
sc = kzalloc(sizeof(lmc_softc_t), GFP_KERNEL);
|
|
|
|
if (!sc) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_kzalloc;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
dev = alloc_hdlcdev(sc);
|
|
|
|
if (!dev) {
|
|
|
|
printk(KERN_ERR "lmc:alloc_netdev for device failed\n");
|
|
|
|
goto err_hdlcdev;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
dev->type = ARPHRD_HDLC;
|
|
|
|
dev_to_hdlc(dev)->xmit = lmc_start_xmit;
|
|
|
|
dev_to_hdlc(dev)->attach = lmc_attach;
|
2009-01-09 05:52:11 +08:00
|
|
|
dev->netdev_ops = &lmc_ops;
|
2008-07-03 02:46:21 +08:00
|
|
|
dev->watchdog_timeo = HZ; /* 1 second */
|
|
|
|
dev->tx_queue_len = 100;
|
|
|
|
sc->lmc_device = dev;
|
|
|
|
sc->name = dev->name;
|
|
|
|
sc->if_type = LMC_PPP;
|
|
|
|
sc->check = 0xBEAFCAFE;
|
|
|
|
dev->base_addr = pci_resource_start(pdev, 0);
|
|
|
|
dev->irq = pdev->irq;
|
|
|
|
pci_set_drvdata(pdev, dev);
|
|
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This will get the protocol layer ready and do any 1 time init's
|
|
|
|
* Must have a valid sc and dev structure
|
|
|
|
*/
|
|
|
|
lmc_proto_attach(sc);
|
|
|
|
|
|
|
|
/* Init the spin lock so can call it latter */
|
|
|
|
|
|
|
|
spin_lock_init(&sc->lmc_lock);
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: detected at %lx, irq %d\n", dev->name,
|
|
|
|
dev->base_addr, dev->irq);
|
|
|
|
|
|
|
|
err = register_hdlc_device(dev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: register_netdev failed.\n", dev->name);
|
|
|
|
free_netdev(dev);
|
|
|
|
goto err_hdlcdev;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sc->lmc_cardtype = LMC_CARDTYPE_UNKNOWN;
|
|
|
|
sc->lmc_timing = LMC_CTL_CLOCK_SOURCE_EXT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Check either the subvendor or the subdevice, some systems reverse
|
|
|
|
* the setting in the bois, seems to be version and arch dependent?
|
|
|
|
* Fix the error, exchange the two values
|
|
|
|
*/
|
|
|
|
if ((subdevice = pdev->subsystem_device) == PCI_VENDOR_ID_LMC)
|
|
|
|
subdevice = pdev->subsystem_vendor;
|
|
|
|
|
|
|
|
switch (subdevice) {
|
|
|
|
case PCI_DEVICE_ID_LMC_HSSI:
|
2008-07-03 02:46:21 +08:00
|
|
|
printk(KERN_INFO "%s: LMC HSSI\n", dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_cardtype = LMC_CARDTYPE_HSSI;
|
|
|
|
sc->lmc_media = &lmc_hssi_media;
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_LMC_DS3:
|
2008-07-03 02:46:21 +08:00
|
|
|
printk(KERN_INFO "%s: LMC DS3\n", dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_cardtype = LMC_CARDTYPE_DS3;
|
|
|
|
sc->lmc_media = &lmc_ds3_media;
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_LMC_SSI:
|
2008-07-03 02:46:21 +08:00
|
|
|
printk(KERN_INFO "%s: LMC SSI\n", dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_cardtype = LMC_CARDTYPE_SSI;
|
|
|
|
sc->lmc_media = &lmc_ssi_media;
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_LMC_T1:
|
2008-07-03 02:46:21 +08:00
|
|
|
printk(KERN_INFO "%s: LMC T1\n", dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_cardtype = LMC_CARDTYPE_T1;
|
|
|
|
sc->lmc_media = &lmc_t1_media;
|
|
|
|
break;
|
|
|
|
default:
|
2008-07-03 02:46:21 +08:00
|
|
|
printk(KERN_WARNING "%s: LMC UNKOWN CARD!\n", dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_initcsrs (sc, dev->base_addr, 8);
|
|
|
|
|
|
|
|
lmc_gpio_mkinput (sc, 0xff);
|
|
|
|
sc->lmc_gpio = 0; /* drive no signals yet */
|
|
|
|
|
|
|
|
sc->lmc_media->defaults (sc);
|
|
|
|
|
|
|
|
sc->lmc_media->set_link_status (sc, LMC_LINK_UP);
|
|
|
|
|
|
|
|
/* verify that the PCI Sub System ID matches the Adapter Model number
|
|
|
|
* from the MII register
|
|
|
|
*/
|
|
|
|
AdapModelNum = (lmc_mii_readreg (sc, 0, 3) & 0x3f0) >> 4;
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if ((AdapModelNum != LMC_ADAP_T1 || /* detect LMC1200 */
|
|
|
|
subdevice != PCI_DEVICE_ID_LMC_T1) &&
|
|
|
|
(AdapModelNum != LMC_ADAP_SSI || /* detect LMC1000 */
|
|
|
|
subdevice != PCI_DEVICE_ID_LMC_SSI) &&
|
|
|
|
(AdapModelNum != LMC_ADAP_DS3 || /* detect LMC5245 */
|
|
|
|
subdevice != PCI_DEVICE_ID_LMC_DS3) &&
|
|
|
|
(AdapModelNum != LMC_ADAP_HSSI || /* detect LMC5200 */
|
|
|
|
subdevice != PCI_DEVICE_ID_LMC_HSSI))
|
|
|
|
printk(KERN_WARNING "%s: Model number (%d) miscompare for PCI"
|
|
|
|
" Subsystem ID = 0x%04x\n",
|
|
|
|
dev->name, AdapModelNum, subdevice);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* reset clock
|
|
|
|
*/
|
|
|
|
LMC_CSR_WRITE (sc, csr_gp_timer, 0xFFFFFFFFUL);
|
|
|
|
|
|
|
|
sc->board_idx = cards_found++;
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.check = STATCHECK;
|
|
|
|
sc->extra_stats.version_size = (DRIVER_VERSION << 16) +
|
|
|
|
sizeof(sc->lmc_device->stats) + sizeof(sc->extra_stats);
|
|
|
|
sc->extra_stats.lmc_cardtype = sc->lmc_cardtype;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sc->lmc_ok = 0;
|
|
|
|
sc->last_link_status = 0;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_init_one out");
|
|
|
|
return 0;
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
err_hdlcdev:
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
kfree(sc);
|
|
|
|
err_kzalloc:
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
err_req_io:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called from pci when removing module.
|
|
|
|
*/
|
2008-07-03 02:46:21 +08:00
|
|
|
static void __devexit lmc_remove_one(struct pci_dev *pdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
printk(KERN_DEBUG "%s: removing...\n", dev->name);
|
|
|
|
unregister_hdlc_device(dev);
|
|
|
|
free_netdev(dev);
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* After this is called, packets can be sent.
|
|
|
|
* Does not initialize the addresses
|
|
|
|
*/
|
2008-07-03 02:46:21 +08:00
|
|
|
static int lmc_open(struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_open in");
|
|
|
|
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED0);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_dec_reset(sc);
|
|
|
|
lmc_reset(sc);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET1, LMC_CSR_READ(sc, csr_status), 0);
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET2, lmc_mii_readreg(sc, 0, 16),
|
|
|
|
lmc_mii_readreg(sc, 0, 17));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sc->lmc_ok){
|
|
|
|
lmc_trace(dev, "lmc_open lmc_ok out");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_softreset (sc);
|
|
|
|
|
|
|
|
/* Since we have to use PCI bus, this should work on x86,alpha,ppc */
|
2006-07-02 10:29:39 +08:00
|
|
|
if (request_irq (dev->irq, &lmc_interrupt, IRQF_SHARED, dev->name, dev)){
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq);
|
|
|
|
lmc_trace(dev, "lmc_open irq failed out");
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
sc->got_irq = 1;
|
|
|
|
|
|
|
|
/* Assert Terminal Active */
|
|
|
|
sc->lmc_miireg16 |= LMC_MII16_LED_ALL;
|
|
|
|
sc->lmc_media->set_link_status (sc, LMC_LINK_UP);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reset to last state.
|
|
|
|
*/
|
|
|
|
sc->lmc_media->set_status (sc, NULL);
|
|
|
|
|
|
|
|
/* setup default bits to be used in tulip_desc_t transmit descriptor
|
|
|
|
* -baz */
|
|
|
|
sc->TxDescriptControlInit = (
|
|
|
|
LMC_TDES_INTERRUPT_ON_COMPLETION
|
|
|
|
| LMC_TDES_FIRST_SEGMENT
|
|
|
|
| LMC_TDES_LAST_SEGMENT
|
|
|
|
| LMC_TDES_SECOND_ADDR_CHAINED
|
|
|
|
| LMC_TDES_DISABLE_PADDING
|
|
|
|
);
|
|
|
|
|
|
|
|
if (sc->ictl.crc_length == LMC_CTL_CRC_LENGTH_16) {
|
|
|
|
/* disable 32 bit CRC generated by ASIC */
|
|
|
|
sc->TxDescriptControlInit |= LMC_TDES_ADD_CRC_DISABLE;
|
|
|
|
}
|
|
|
|
sc->lmc_media->set_crc_length(sc, sc->ictl.crc_length);
|
|
|
|
/* Acknoledge the Terminal Active and light LEDs */
|
|
|
|
|
|
|
|
/* dev->flags |= IFF_UP; */
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if ((err = lmc_proto_open(sc)) != 0)
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
netif_start_queue(dev);
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy0++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* select what interrupts we want to get
|
|
|
|
*/
|
|
|
|
sc->lmc_intrmask = 0;
|
|
|
|
/* Should be using the default interrupt mask defined in the .h file. */
|
|
|
|
sc->lmc_intrmask |= (TULIP_STS_NORMALINTR
|
|
|
|
| TULIP_STS_RXINTR
|
|
|
|
| TULIP_STS_TXINTR
|
|
|
|
| TULIP_STS_ABNRMLINTR
|
|
|
|
| TULIP_STS_SYSERROR
|
|
|
|
| TULIP_STS_TXSTOPPED
|
|
|
|
| TULIP_STS_TXUNDERFLOW
|
|
|
|
| TULIP_STS_RXSTOPPED
|
|
|
|
| TULIP_STS_RXNOBUF
|
|
|
|
);
|
|
|
|
LMC_CSR_WRITE (sc, csr_intr, sc->lmc_intrmask);
|
|
|
|
|
|
|
|
sc->lmc_cmdmode |= TULIP_CMD_TXRUN;
|
|
|
|
sc->lmc_cmdmode |= TULIP_CMD_RXRUN;
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, sc->lmc_cmdmode);
|
|
|
|
|
|
|
|
sc->lmc_ok = 1; /* Run watchdog */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the if up now - pfb
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->last_link_status = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup a timer for the watchdog on probe, and start it running.
|
|
|
|
* Since lmc_ok == 0, it will be a NOP for now.
|
|
|
|
*/
|
|
|
|
init_timer (&sc->timer);
|
|
|
|
sc->timer.expires = jiffies + HZ;
|
|
|
|
sc->timer.data = (unsigned long) dev;
|
|
|
|
sc->timer.function = &lmc_watchdog;
|
|
|
|
add_timer (&sc->timer);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_open out");
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Total reset to compensate for the AdTran DSU doing bad things
|
|
|
|
* under heavy load
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void lmc_running_reset (struct net_device *dev) /*fold00*/
|
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_runnig_reset in");
|
|
|
|
|
|
|
|
/* stop interrupts */
|
|
|
|
/* Clear the interrupt mask */
|
|
|
|
LMC_CSR_WRITE (sc, csr_intr, 0x00000000);
|
|
|
|
|
|
|
|
lmc_dec_reset (sc);
|
|
|
|
lmc_reset (sc);
|
|
|
|
lmc_softreset (sc);
|
|
|
|
/* sc->lmc_miireg16 |= LMC_MII16_LED_ALL; */
|
|
|
|
sc->lmc_media->set_link_status (sc, 1);
|
|
|
|
sc->lmc_media->set_status (sc, NULL);
|
|
|
|
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
|
|
|
|
sc->lmc_txfull = 0;
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy0++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sc->lmc_intrmask = TULIP_DEFAULT_INTR_MASK;
|
|
|
|
LMC_CSR_WRITE (sc, csr_intr, sc->lmc_intrmask);
|
|
|
|
|
|
|
|
sc->lmc_cmdmode |= (TULIP_CMD_TXRUN | TULIP_CMD_RXRUN);
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, sc->lmc_cmdmode);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_runnin_reset_out");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is what is called when you ifconfig down a device.
|
|
|
|
* This disables the timer for the watchdog and keepalives,
|
|
|
|
* and disables the irq for dev.
|
|
|
|
*/
|
2008-07-03 02:46:21 +08:00
|
|
|
static int lmc_close(struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
/* not calling release_region() as we should */
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_close in");
|
2008-07-03 02:46:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_ok = 0;
|
|
|
|
sc->lmc_media->set_link_status (sc, 0);
|
|
|
|
del_timer (&sc->timer);
|
|
|
|
lmc_proto_close(sc);
|
|
|
|
lmc_ifdown (dev);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_close out");
|
2008-07-03 02:46:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ends the transfer of packets */
|
|
|
|
/* When the interface goes down, this is called */
|
|
|
|
static int lmc_ifdown (struct net_device *dev) /*fold00*/
|
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 csr6;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_ifdown in");
|
2008-07-03 02:46:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Don't let anything else go on right now */
|
|
|
|
// dev->start = 0;
|
|
|
|
netif_stop_queue(dev);
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy1++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* stop interrupts */
|
|
|
|
/* Clear the interrupt mask */
|
|
|
|
LMC_CSR_WRITE (sc, csr_intr, 0x00000000);
|
|
|
|
|
|
|
|
/* Stop Tx and Rx on the chip */
|
|
|
|
csr6 = LMC_CSR_READ (sc, csr_command);
|
|
|
|
csr6 &= ~LMC_DEC_ST; /* Turn off the Transmission bit */
|
|
|
|
csr6 &= ~LMC_DEC_SR; /* Turn off the Receive bit */
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, csr6);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.rx_missed_errors +=
|
|
|
|
LMC_CSR_READ(sc, csr_missed_frames) & 0xffff;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* release the interrupt */
|
|
|
|
if(sc->got_irq == 1){
|
|
|
|
free_irq (dev->irq, dev);
|
|
|
|
sc->got_irq = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free skbuffs in the Rx queue */
|
|
|
|
for (i = 0; i < LMC_RXDESCS; i++)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb = sc->lmc_rxq[i];
|
|
|
|
sc->lmc_rxq[i] = NULL;
|
|
|
|
sc->lmc_rxring[i].status = 0;
|
|
|
|
sc->lmc_rxring[i].length = 0;
|
|
|
|
sc->lmc_rxring[i].buffer1 = 0xDEADBEEF;
|
|
|
|
if (skb != NULL)
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
sc->lmc_rxq[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < LMC_TXDESCS; i++)
|
|
|
|
{
|
|
|
|
if (sc->lmc_txq[i] != NULL)
|
|
|
|
dev_kfree_skb(sc->lmc_txq[i]);
|
|
|
|
sc->lmc_txq[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_led_off (sc, LMC_MII16_LED_ALL);
|
|
|
|
|
|
|
|
netif_wake_queue(dev);
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy0++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_ifdown out");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Interrupt handling routine. This will take an incoming packet, or clean
|
|
|
|
* up after a trasmit.
|
|
|
|
*/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t lmc_interrupt (int irq, void *dev_instance) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct net_device *dev = (struct net_device *) dev_instance;
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 csr;
|
|
|
|
int i;
|
|
|
|
s32 stat;
|
|
|
|
unsigned int badtx;
|
|
|
|
u32 firstcsr;
|
|
|
|
int max_work = LMC_RXDESCS;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_interrupt in");
|
|
|
|
|
|
|
|
spin_lock(&sc->lmc_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the csr to find what interrupts we have (if any)
|
|
|
|
*/
|
|
|
|
csr = LMC_CSR_READ (sc, csr_status);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure this is our interrupt
|
|
|
|
*/
|
|
|
|
if ( ! (csr & sc->lmc_intrmask)) {
|
|
|
|
goto lmc_int_fail_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
firstcsr = csr;
|
|
|
|
|
|
|
|
/* always go through this loop at least once */
|
|
|
|
while (csr & sc->lmc_intrmask) {
|
|
|
|
handled = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear interrupt bits, we handle all case below
|
|
|
|
*/
|
|
|
|
LMC_CSR_WRITE (sc, csr_status, csr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* One of
|
|
|
|
* - Transmit process timed out CSR5<1>
|
|
|
|
* - Transmit jabber timeout CSR5<3>
|
|
|
|
* - Transmit underflow CSR5<5>
|
|
|
|
* - Transmit Receiver buffer unavailable CSR5<7>
|
|
|
|
* - Receive process stopped CSR5<8>
|
|
|
|
* - Receive watchdog timeout CSR5<9>
|
|
|
|
* - Early transmit interrupt CSR5<10>
|
|
|
|
*
|
|
|
|
* Is this really right? Should we do a running reset for jabber?
|
|
|
|
* (being a WAN card and all)
|
|
|
|
*/
|
|
|
|
if (csr & TULIP_STS_ABNRMLINTR){
|
|
|
|
lmc_running_reset (dev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (csr & TULIP_STS_RXINTR){
|
|
|
|
lmc_trace(dev, "rx interrupt");
|
|
|
|
lmc_rx (dev);
|
|
|
|
|
|
|
|
}
|
|
|
|
if (csr & (TULIP_STS_TXINTR | TULIP_STS_TXNOBUF | TULIP_STS_TXSTOPPED)) {
|
|
|
|
|
|
|
|
int n_compl = 0 ;
|
|
|
|
/* reset the transmit timeout detection flag -baz */
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_NoCompleteCnt = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
badtx = sc->lmc_taint_tx;
|
|
|
|
i = badtx % LMC_TXDESCS;
|
|
|
|
|
|
|
|
while ((badtx < sc->lmc_next_tx)) {
|
|
|
|
stat = sc->lmc_txring[i].status;
|
|
|
|
|
|
|
|
LMC_EVENT_LOG (LMC_EVENT_XMTINT, stat,
|
|
|
|
sc->lmc_txring[i].length);
|
|
|
|
/*
|
|
|
|
* If bit 31 is 1 the tulip owns it break out of the loop
|
|
|
|
*/
|
|
|
|
if (stat & 0x80000000)
|
|
|
|
break;
|
|
|
|
|
|
|
|
n_compl++ ; /* i.e., have an empty slot in ring */
|
|
|
|
/*
|
|
|
|
* If we have no skbuff or have cleared it
|
|
|
|
* Already continue to the next buffer
|
|
|
|
*/
|
|
|
|
if (sc->lmc_txq[i] == NULL)
|
|
|
|
continue;
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
/*
|
|
|
|
* Check the total error summary to look for any errors
|
|
|
|
*/
|
|
|
|
if (stat & 0x8000) {
|
|
|
|
sc->lmc_device->stats.tx_errors++;
|
|
|
|
if (stat & 0x4104)
|
|
|
|
sc->lmc_device->stats.tx_aborted_errors++;
|
|
|
|
if (stat & 0x0C00)
|
|
|
|
sc->lmc_device->stats.tx_carrier_errors++;
|
|
|
|
if (stat & 0x0200)
|
|
|
|
sc->lmc_device->stats.tx_window_errors++;
|
|
|
|
if (stat & 0x0002)
|
|
|
|
sc->lmc_device->stats.tx_fifo_errors++;
|
|
|
|
} else {
|
|
|
|
sc->lmc_device->stats.tx_bytes += sc->lmc_txring[i].length & 0x7ff;
|
|
|
|
|
|
|
|
sc->lmc_device->stats.tx_packets++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-07-03 02:46:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
// dev_kfree_skb(sc->lmc_txq[i]);
|
|
|
|
dev_kfree_skb_irq(sc->lmc_txq[i]);
|
|
|
|
sc->lmc_txq[i] = NULL;
|
|
|
|
|
|
|
|
badtx++;
|
|
|
|
i = badtx % LMC_TXDESCS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->lmc_next_tx - badtx > LMC_TXDESCS)
|
|
|
|
{
|
|
|
|
printk ("%s: out of sync pointer\n", dev->name);
|
|
|
|
badtx += LMC_TXDESCS;
|
|
|
|
}
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_TBUSY0, n_compl, 0);
|
|
|
|
sc->lmc_txfull = 0;
|
|
|
|
netif_wake_queue(dev);
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy0++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.dirtyTx = badtx;
|
|
|
|
sc->extra_stats.lmc_next_tx = sc->lmc_next_tx;
|
|
|
|
sc->extra_stats.lmc_txfull = sc->lmc_txfull;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
sc->lmc_taint_tx = badtx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Why was there a break here???
|
|
|
|
*/
|
|
|
|
} /* end handle transmit interrupt */
|
|
|
|
|
|
|
|
if (csr & TULIP_STS_SYSERROR) {
|
|
|
|
u32 error;
|
|
|
|
printk (KERN_WARNING "%s: system bus error csr: %#8.8x\n", dev->name, csr);
|
|
|
|
error = csr>>23 & 0x7;
|
|
|
|
switch(error){
|
|
|
|
case 0x000:
|
|
|
|
printk(KERN_WARNING "%s: Parity Fault (bad)\n", dev->name);
|
|
|
|
break;
|
|
|
|
case 0x001:
|
|
|
|
printk(KERN_WARNING "%s: Master Abort (naughty)\n", dev->name);
|
|
|
|
break;
|
|
|
|
case 0x010:
|
|
|
|
printk(KERN_WARNING "%s: Target Abort (not so naughty)\n", dev->name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(KERN_WARNING "%s: This bus error code was supposed to be reserved!\n", dev->name);
|
|
|
|
}
|
|
|
|
lmc_dec_reset (sc);
|
|
|
|
lmc_reset (sc);
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET1, LMC_CSR_READ (sc, csr_status), 0);
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET2,
|
|
|
|
lmc_mii_readreg (sc, 0, 16),
|
|
|
|
lmc_mii_readreg (sc, 0, 17));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(max_work-- <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get current csr status to make sure
|
|
|
|
* we've cleared all interrupts
|
|
|
|
*/
|
|
|
|
csr = LMC_CSR_READ (sc, csr_status);
|
|
|
|
} /* end interrupt loop */
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_INT, firstcsr, csr);
|
|
|
|
|
|
|
|
lmc_int_fail_out:
|
|
|
|
|
|
|
|
spin_unlock(&sc->lmc_lock);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_interrupt out");
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
static int lmc_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 flag;
|
|
|
|
int entry;
|
2009-07-06 10:23:38 +08:00
|
|
|
int ret = NETDEV_TX_OK;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_start_xmit in");
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
/* normal path, tbusy known to be zero */
|
|
|
|
|
|
|
|
entry = sc->lmc_next_tx % LMC_TXDESCS;
|
|
|
|
|
|
|
|
sc->lmc_txq[entry] = skb;
|
|
|
|
sc->lmc_txring[entry].buffer1 = virt_to_bus (skb->data);
|
|
|
|
|
|
|
|
LMC_CONSOLE_LOG("xmit", skb->data, skb->len);
|
|
|
|
|
|
|
|
#ifndef GCOM
|
|
|
|
/* If the queue is less than half full, don't interrupt */
|
|
|
|
if (sc->lmc_next_tx - sc->lmc_taint_tx < LMC_TXDESCS / 2)
|
|
|
|
{
|
|
|
|
/* Do not interrupt on completion of this packet */
|
|
|
|
flag = 0x60000000;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
else if (sc->lmc_next_tx - sc->lmc_taint_tx == LMC_TXDESCS / 2)
|
|
|
|
{
|
|
|
|
/* This generates an interrupt on completion of this packet */
|
|
|
|
flag = 0xe0000000;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
else if (sc->lmc_next_tx - sc->lmc_taint_tx < LMC_TXDESCS - 1)
|
|
|
|
{
|
|
|
|
/* Do not interrupt on completion of this packet */
|
|
|
|
flag = 0x60000000;
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This generates an interrupt on completion of this packet */
|
|
|
|
flag = 0xe0000000;
|
|
|
|
sc->lmc_txfull = 1;
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
flag = LMC_TDES_INTERRUPT_ON_COMPLETION;
|
|
|
|
|
|
|
|
if (sc->lmc_next_tx - sc->lmc_taint_tx >= LMC_TXDESCS - 1)
|
|
|
|
{ /* ring full, go busy */
|
|
|
|
sc->lmc_txfull = 1;
|
2008-07-03 02:46:21 +08:00
|
|
|
netif_stop_queue(dev);
|
|
|
|
sc->extra_stats.tx_tbusy1++;
|
2005-04-17 06:20:36 +08:00
|
|
|
LMC_EVENT_LOG(LMC_EVENT_TBUSY1, entry, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
if (entry == LMC_TXDESCS - 1) /* last descriptor in ring */
|
|
|
|
flag |= LMC_TDES_END_OF_RING; /* flag as such for Tulip */
|
|
|
|
|
|
|
|
/* don't pad small packets either */
|
|
|
|
flag = sc->lmc_txring[entry].length = (skb->len) | flag |
|
|
|
|
sc->TxDescriptControlInit;
|
|
|
|
|
|
|
|
/* set the transmit timeout flag to be checked in
|
|
|
|
* the watchdog timer handler. -baz
|
|
|
|
*/
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_NoCompleteCnt++;
|
2005-04-17 06:20:36 +08:00
|
|
|
sc->lmc_next_tx++;
|
|
|
|
|
|
|
|
/* give ownership to the chip */
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_XMT, flag, entry);
|
|
|
|
sc->lmc_txring[entry].status = 0x80000000;
|
|
|
|
|
|
|
|
/* send now! */
|
|
|
|
LMC_CSR_WRITE (sc, csr_txpoll, 0);
|
|
|
|
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_start_xmit_out");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
static int lmc_rx(struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
int rx_work_limit = LMC_RXDESCS;
|
|
|
|
unsigned int next_rx;
|
|
|
|
int rxIntLoopCnt; /* debug -baz */
|
|
|
|
int localLengthErrCnt = 0;
|
|
|
|
long stat;
|
|
|
|
struct sk_buff *skb, *nsb;
|
|
|
|
u16 len;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_rx in");
|
|
|
|
|
|
|
|
lmc_led_on(sc, LMC_DS3_LED3);
|
|
|
|
|
|
|
|
rxIntLoopCnt = 0; /* debug -baz */
|
|
|
|
|
|
|
|
i = sc->lmc_next_rx % LMC_RXDESCS;
|
|
|
|
next_rx = sc->lmc_next_rx;
|
|
|
|
|
|
|
|
while (((stat = sc->lmc_rxring[i].status) & LMC_RDES_OWN_BIT) != DESC_OWNED_BY_DC21X4)
|
|
|
|
{
|
|
|
|
rxIntLoopCnt++; /* debug -baz */
|
|
|
|
len = ((stat & LMC_RDES_FRAME_LENGTH) >> RDES_FRAME_LENGTH_BIT_NUMBER);
|
|
|
|
if ((stat & 0x0300) != 0x0300) { /* Check first segment and last segment */
|
2008-07-03 02:46:21 +08:00
|
|
|
if ((stat & 0x0000ffff) != 0x7fff) {
|
|
|
|
/* Oversized frame */
|
|
|
|
sc->lmc_device->stats.rx_length_errors++;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if (stat & 0x00000008) { /* Catch a dribbling bit error */
|
|
|
|
sc->lmc_device->stats.rx_errors++;
|
|
|
|
sc->lmc_device->stats.rx_frame_errors++;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if (stat & 0x00000004) { /* Catch a CRC error by the Xilinx */
|
|
|
|
sc->lmc_device->stats.rx_errors++;
|
|
|
|
sc->lmc_device->stats.rx_crc_errors++;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if (len > LMC_PKT_BUF_SZ) {
|
|
|
|
sc->lmc_device->stats.rx_length_errors++;
|
|
|
|
localLengthErrCnt++;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
if (len < sc->lmc_crcSize + 2) {
|
|
|
|
sc->lmc_device->stats.rx_length_errors++;
|
|
|
|
sc->extra_stats.rx_SmallPktCnt++;
|
|
|
|
localLengthErrCnt++;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if(stat & 0x00004000){
|
|
|
|
printk(KERN_WARNING "%s: Receiver descriptor error, receiver out of sync?\n", dev->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
len -= sc->lmc_crcSize;
|
|
|
|
|
|
|
|
skb = sc->lmc_rxq[i];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We ran out of memory at some point
|
|
|
|
* just allocate an skb buff and continue.
|
|
|
|
*/
|
|
|
|
|
2008-01-24 18:06:46 +08:00
|
|
|
if (!skb) {
|
2005-04-17 06:20:36 +08:00
|
|
|
nsb = dev_alloc_skb (LMC_PKT_BUF_SZ + 2);
|
|
|
|
if (nsb) {
|
|
|
|
sc->lmc_rxq[i] = nsb;
|
|
|
|
nsb->dev = dev;
|
2007-04-20 11:29:13 +08:00
|
|
|
sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
sc->failed_recv_alloc = 1;
|
|
|
|
goto skip_packet;
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.rx_packets++;
|
|
|
|
sc->lmc_device->stats.rx_bytes += len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
LMC_CONSOLE_LOG("recv", skb->data, len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I'm not sure of the sanity of this
|
|
|
|
* Packets could be arriving at a constant
|
|
|
|
* 44.210mbits/sec and we're going to copy
|
|
|
|
* them into a new buffer??
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(len > (LMC_MTU - (LMC_MTU>>2))){ /* len > LMC_MTU * 0.75 */
|
|
|
|
/*
|
|
|
|
* If it's a large packet don't copy it just hand it up
|
|
|
|
*/
|
|
|
|
give_it_anyways:
|
|
|
|
|
|
|
|
sc->lmc_rxq[i] = NULL;
|
|
|
|
sc->lmc_rxring[i].buffer1 = 0x0;
|
|
|
|
|
|
|
|
skb_put (skb, len);
|
|
|
|
skb->protocol = lmc_proto_type(sc, skb);
|
2007-03-20 06:30:44 +08:00
|
|
|
skb_reset_mac_header(skb);
|
2007-04-11 11:45:18 +08:00
|
|
|
/* skb_reset_network_header(skb); */
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->dev = dev;
|
|
|
|
lmc_proto_netif(sc, skb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This skb will be destroyed by the upper layers, make a new one
|
|
|
|
*/
|
|
|
|
nsb = dev_alloc_skb (LMC_PKT_BUF_SZ + 2);
|
|
|
|
if (nsb) {
|
|
|
|
sc->lmc_rxq[i] = nsb;
|
|
|
|
nsb->dev = dev;
|
2007-04-20 11:29:13 +08:00
|
|
|
sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Transferred to 21140 below */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* We've run out of memory, stop trying to allocate
|
|
|
|
* memory and exit the interrupt handler
|
|
|
|
*
|
|
|
|
* The chip may run out of receivers and stop
|
|
|
|
* in which care we'll try to allocate the buffer
|
|
|
|
* again. (once a second)
|
|
|
|
*/
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.rx_BuffAllocErr++;
|
2005-04-17 06:20:36 +08:00
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RCVINT, stat, len);
|
|
|
|
sc->failed_recv_alloc = 1;
|
|
|
|
goto skip_out_of_mem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsb = dev_alloc_skb(len);
|
|
|
|
if(!nsb) {
|
|
|
|
goto give_it_anyways;
|
|
|
|
}
|
2007-03-28 05:55:52 +08:00
|
|
|
skb_copy_from_linear_data(skb, skb_put(nsb, len), len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
nsb->protocol = lmc_proto_type(sc, skb);
|
2007-03-20 06:30:44 +08:00
|
|
|
skb_reset_mac_header(nsb);
|
2007-04-11 11:45:18 +08:00
|
|
|
/* skb_reset_network_header(nsb); */
|
2005-04-17 06:20:36 +08:00
|
|
|
nsb->dev = dev;
|
|
|
|
lmc_proto_netif(sc, nsb);
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_packet:
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RCVINT, stat, len);
|
|
|
|
sc->lmc_rxring[i].status = DESC_OWNED_BY_DC21X4;
|
|
|
|
|
|
|
|
sc->lmc_next_rx++;
|
|
|
|
i = sc->lmc_next_rx % LMC_RXDESCS;
|
|
|
|
rx_work_limit--;
|
|
|
|
if (rx_work_limit < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* detect condition for LMC1000 where DSU cable attaches and fills
|
|
|
|
* descriptors with bogus packets
|
|
|
|
*
|
|
|
|
if (localLengthErrCnt > LMC_RXDESCS - 3) {
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.rx_BadPktSurgeCnt++;
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_BADPKTSURGE, localLengthErrCnt,
|
|
|
|
sc->extra_stats.rx_BadPktSurgeCnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
} */
|
|
|
|
|
|
|
|
/* save max count of receive descriptors serviced */
|
2008-07-03 02:46:21 +08:00
|
|
|
if (rxIntLoopCnt > sc->extra_stats.rxIntLoopCnt)
|
|
|
|
sc->extra_stats.rxIntLoopCnt = rxIntLoopCnt; /* debug -baz */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (rxIntLoopCnt == 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < LMC_RXDESCS; i++)
|
|
|
|
{
|
|
|
|
if ((sc->lmc_rxring[i].status & LMC_RDES_OWN_BIT)
|
|
|
|
!= DESC_OWNED_BY_DC21X4)
|
|
|
|
{
|
|
|
|
rxIntLoopCnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RCVEND, rxIntLoopCnt, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
lmc_led_off(sc, LMC_DS3_LED3);
|
|
|
|
|
|
|
|
skip_out_of_mem:
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_rx out");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
static struct net_device_stats *lmc_get_stats(struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-03 02:46:21 +08:00
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_get_stats in");
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.rx_missed_errors += LMC_CSR_READ(sc, csr_missed_frames) & 0xffff;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_get_stats out");
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
return &sc->lmc_device->stats;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_driver lmc_driver = {
|
|
|
|
.name = "lmc",
|
|
|
|
.id_table = lmc_pci_tbl,
|
|
|
|
.probe = lmc_init_one,
|
|
|
|
.remove = __devexit_p(lmc_remove_one),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init init_lmc(void)
|
|
|
|
{
|
2006-08-20 05:48:59 +08:00
|
|
|
return pci_register_driver(&lmc_driver);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit exit_lmc(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&lmc_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(init_lmc);
|
|
|
|
module_exit(exit_lmc);
|
|
|
|
|
|
|
|
unsigned lmc_mii_readreg (lmc_softc_t * const sc, unsigned devaddr, unsigned regno) /*fold00*/
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int command = (0xf6 << 10) | (devaddr << 5) | regno;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_readreg in");
|
|
|
|
|
|
|
|
LMC_MII_SYNC (sc);
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_readreg: done sync");
|
|
|
|
|
|
|
|
for (i = 15; i >= 0; i--)
|
|
|
|
{
|
|
|
|
int dataval = (command & (1 << i)) ? 0x20000 : 0;
|
|
|
|
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, dataval);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, dataval | 0x10000);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_readreg: done1");
|
|
|
|
|
|
|
|
for (i = 19; i > 0; i--)
|
|
|
|
{
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, 0x40000);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
retval = (retval << 1) | ((LMC_CSR_READ (sc, csr_9) & 0x80000) ? 1 : 0);
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, 0x40000 | 0x10000);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_readreg out");
|
|
|
|
|
|
|
|
return (retval >> 1) & 0xffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lmc_mii_writereg (lmc_softc_t * const sc, unsigned devaddr, unsigned regno, unsigned data) /*fold00*/
|
|
|
|
{
|
|
|
|
int i = 32;
|
|
|
|
int command = (0x5002 << 16) | (devaddr << 23) | (regno << 18) | data;
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_writereg in");
|
|
|
|
|
|
|
|
LMC_MII_SYNC (sc);
|
|
|
|
|
|
|
|
i = 31;
|
|
|
|
while (i >= 0)
|
|
|
|
{
|
|
|
|
int datav;
|
|
|
|
|
|
|
|
if (command & (1 << i))
|
|
|
|
datav = 0x20000;
|
|
|
|
else
|
|
|
|
datav = 0x00000;
|
|
|
|
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, datav);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, (datav | 0x10000));
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 2;
|
|
|
|
while (i > 0)
|
|
|
|
{
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, 0x40000);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
LMC_CSR_WRITE (sc, csr_9, 0x50000);
|
|
|
|
lmc_delay ();
|
|
|
|
/* __SLOW_DOWN_IO; */
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_mii_writereg out");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lmc_softreset (lmc_softc_t * const sc) /*fold00*/
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_softreset in");
|
|
|
|
|
|
|
|
/* Initialize the receive rings and buffers. */
|
|
|
|
sc->lmc_txfull = 0;
|
|
|
|
sc->lmc_next_rx = 0;
|
|
|
|
sc->lmc_next_tx = 0;
|
|
|
|
sc->lmc_taint_rx = 0;
|
|
|
|
sc->lmc_taint_tx = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup each one of the receiver buffers
|
|
|
|
* allocate an skbuff for each one, setup the descriptor table
|
|
|
|
* and point each buffer at the next one
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (i = 0; i < LMC_RXDESCS; i++)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
if (sc->lmc_rxq[i] == NULL)
|
|
|
|
{
|
|
|
|
skb = dev_alloc_skb (LMC_PKT_BUF_SZ + 2);
|
|
|
|
if(skb == NULL){
|
|
|
|
printk(KERN_WARNING "%s: Failed to allocate receiver ring, will try again\n", sc->name);
|
|
|
|
sc->failed_ring = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
sc->lmc_rxq[i] = skb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
skb = sc->lmc_rxq[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
skb->dev = sc->lmc_device;
|
|
|
|
|
|
|
|
/* owned by 21140 */
|
|
|
|
sc->lmc_rxring[i].status = 0x80000000;
|
|
|
|
|
|
|
|
/* used to be PKT_BUF_SZ now uses skb since we lose some to head room */
|
2007-03-20 23:00:44 +08:00
|
|
|
sc->lmc_rxring[i].length = skb_tailroom(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* use to be tail which is dumb since you're thinking why write
|
|
|
|
* to the end of the packj,et but since there's nothing there tail == data
|
|
|
|
*/
|
|
|
|
sc->lmc_rxring[i].buffer1 = virt_to_bus (skb->data);
|
|
|
|
|
|
|
|
/* This is fair since the structure is static and we have the next address */
|
|
|
|
sc->lmc_rxring[i].buffer2 = virt_to_bus (&sc->lmc_rxring[i + 1]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets end of ring
|
|
|
|
*/
|
2009-08-10 09:29:52 +08:00
|
|
|
if (i != 0) {
|
|
|
|
sc->lmc_rxring[i - 1].length |= 0x02000000; /* Set end of buffers flag */
|
|
|
|
sc->lmc_rxring[i - 1].buffer2 = virt_to_bus(&sc->lmc_rxring[0]); /* Point back to the start */
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
LMC_CSR_WRITE (sc, csr_rxlist, virt_to_bus (sc->lmc_rxring)); /* write base address */
|
|
|
|
|
|
|
|
/* Initialize the transmit rings and buffers */
|
|
|
|
for (i = 0; i < LMC_TXDESCS; i++)
|
|
|
|
{
|
|
|
|
if (sc->lmc_txq[i] != NULL){ /* have buffer */
|
|
|
|
dev_kfree_skb(sc->lmc_txq[i]); /* free it */
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.tx_dropped++; /* We just dropped a packet */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
sc->lmc_txq[i] = NULL;
|
|
|
|
sc->lmc_txring[i].status = 0x00000000;
|
|
|
|
sc->lmc_txring[i].buffer2 = virt_to_bus (&sc->lmc_txring[i + 1]);
|
|
|
|
}
|
|
|
|
sc->lmc_txring[i - 1].buffer2 = virt_to_bus (&sc->lmc_txring[0]);
|
|
|
|
LMC_CSR_WRITE (sc, csr_txlist, virt_to_bus (sc->lmc_txring));
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_softreset out");
|
|
|
|
}
|
|
|
|
|
2008-07-03 06:39:46 +08:00
|
|
|
void lmc_gpio_mkinput(lmc_softc_t * const sc, u32 bits) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_gpio_mkinput in");
|
|
|
|
sc->lmc_gpio_io &= ~bits;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io));
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_gpio_mkinput out");
|
|
|
|
}
|
|
|
|
|
2008-07-03 06:39:46 +08:00
|
|
|
void lmc_gpio_mkoutput(lmc_softc_t * const sc, u32 bits) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_gpio_mkoutput in");
|
|
|
|
sc->lmc_gpio_io |= bits;
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io));
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_gpio_mkoutput out");
|
|
|
|
}
|
|
|
|
|
2008-07-03 06:39:46 +08:00
|
|
|
void lmc_led_on(lmc_softc_t * const sc, u32 led) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_on in");
|
|
|
|
if((~sc->lmc_miireg16) & led){ /* Already on! */
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_on aon out");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->lmc_miireg16 &= ~led;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_on out");
|
|
|
|
}
|
|
|
|
|
2008-07-03 06:39:46 +08:00
|
|
|
void lmc_led_off(lmc_softc_t * const sc, u32 led) /*fold00*/
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_off in");
|
|
|
|
if(sc->lmc_miireg16 & led){ /* Already set don't do anything */
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_off aoff out");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->lmc_miireg16 |= led;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_led_off out");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lmc_reset(lmc_softc_t * const sc) /*fold00*/
|
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_reset in");
|
|
|
|
sc->lmc_miireg16 |= LMC_MII16_FIFO_RESET;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
|
|
|
|
|
|
|
sc->lmc_miireg16 &= ~LMC_MII16_FIFO_RESET;
|
|
|
|
lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make some of the GPIO pins be outputs
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkoutput(sc, LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RESET low to force state reset. This also forces
|
|
|
|
* the transmitter clock to be internal, but we expect to reset
|
|
|
|
* that later anyway.
|
|
|
|
*/
|
|
|
|
sc->lmc_gpio &= ~(LMC_GEP_RESET);
|
|
|
|
LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hold for more than 10 microseconds
|
|
|
|
*/
|
|
|
|
udelay(50);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* stop driving Xilinx-related signals
|
|
|
|
*/
|
|
|
|
lmc_gpio_mkinput(sc, LMC_GEP_RESET);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call media specific init routine
|
|
|
|
*/
|
|
|
|
sc->lmc_media->init(sc);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.resetCount++;
|
2005-04-17 06:20:36 +08:00
|
|
|
lmc_trace(sc->lmc_device, "lmc_reset out");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lmc_dec_reset(lmc_softc_t * const sc) /*fold00*/
|
|
|
|
{
|
2008-07-03 06:39:46 +08:00
|
|
|
u32 val;
|
2005-04-17 06:20:36 +08:00
|
|
|
lmc_trace(sc->lmc_device, "lmc_dec_reset in");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* disable all interrupts
|
|
|
|
*/
|
|
|
|
sc->lmc_intrmask = 0;
|
|
|
|
LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the chip with a software reset command.
|
|
|
|
* Wait 10 microseconds (actually 50 PCI cycles but at
|
|
|
|
* 33MHz that comes to two microseconds but wait a
|
|
|
|
* bit longer anyways)
|
|
|
|
*/
|
|
|
|
LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET);
|
|
|
|
udelay(25);
|
|
|
|
#ifdef __sparc__
|
|
|
|
sc->lmc_busmode = LMC_CSR_READ(sc, csr_busmode);
|
|
|
|
sc->lmc_busmode = 0x00100000;
|
|
|
|
sc->lmc_busmode &= ~TULIP_BUSMODE_SWRESET;
|
|
|
|
LMC_CSR_WRITE(sc, csr_busmode, sc->lmc_busmode);
|
|
|
|
#endif
|
|
|
|
sc->lmc_cmdmode = LMC_CSR_READ(sc, csr_command);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We want:
|
|
|
|
* no ethernet address in frames we write
|
|
|
|
* disable padding (txdesc, padding disable)
|
|
|
|
* ignore runt frames (rdes0 bit 15)
|
|
|
|
* no receiver watchdog or transmitter jabber timer
|
|
|
|
* (csr15 bit 0,14 == 1)
|
|
|
|
* if using 16-bit CRC, turn off CRC (trans desc, crc disable)
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->lmc_cmdmode |= ( TULIP_CMD_PROMISCUOUS
|
|
|
|
| TULIP_CMD_FULLDUPLEX
|
|
|
|
| TULIP_CMD_PASSBADPKT
|
|
|
|
| TULIP_CMD_NOHEARTBEAT
|
|
|
|
| TULIP_CMD_PORTSELECT
|
|
|
|
| TULIP_CMD_RECEIVEALL
|
|
|
|
| TULIP_CMD_MUSTBEONE
|
|
|
|
);
|
|
|
|
sc->lmc_cmdmode &= ~( TULIP_CMD_OPERMODE
|
|
|
|
| TULIP_CMD_THRESHOLDCTL
|
|
|
|
| TULIP_CMD_STOREFWD
|
|
|
|
| TULIP_CMD_TXTHRSHLDCTL
|
|
|
|
);
|
|
|
|
|
|
|
|
LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* disable receiver watchdog and transmit jabber
|
|
|
|
*/
|
|
|
|
val = LMC_CSR_READ(sc, csr_sia_general);
|
|
|
|
val |= (TULIP_WATCHDOG_TXDISABLE | TULIP_WATCHDOG_RXDISABLE);
|
|
|
|
LMC_CSR_WRITE(sc, csr_sia_general, val);
|
|
|
|
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_dec_reset out");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base, /*fold00*/
|
|
|
|
size_t csr_size)
|
|
|
|
{
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_initcsrs in");
|
|
|
|
sc->lmc_csrs.csr_busmode = csr_base + 0 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_txpoll = csr_base + 1 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_rxpoll = csr_base + 2 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_rxlist = csr_base + 3 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_txlist = csr_base + 4 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_status = csr_base + 5 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_command = csr_base + 6 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_intr = csr_base + 7 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_missed_frames = csr_base + 8 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_9 = csr_base + 9 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_10 = csr_base + 10 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_11 = csr_base + 11 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_12 = csr_base + 12 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_13 = csr_base + 13 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_14 = csr_base + 14 * csr_size;
|
|
|
|
sc->lmc_csrs.csr_15 = csr_base + 15 * csr_size;
|
|
|
|
lmc_trace(sc->lmc_device, "lmc_initcsrs out");
|
|
|
|
}
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
static void lmc_driver_timeout(struct net_device *dev)
|
|
|
|
{
|
|
|
|
lmc_softc_t *sc = dev_to_sc(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 csr6;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_driver_timeout in");
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
printk("%s: Xmitter busy|\n", dev->name);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_tbusy_calls++;
|
|
|
|
if (jiffies - dev->trans_start < TX_TIMEOUT)
|
|
|
|
goto bug_out;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Chip seems to have locked up
|
|
|
|
* Reset it
|
|
|
|
* This whips out all our decriptor
|
|
|
|
* table and starts from scartch
|
|
|
|
*/
|
|
|
|
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_XMTPRCTMO,
|
|
|
|
LMC_CSR_READ (sc, csr_status),
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->extra_stats.tx_ProcTimeout);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
lmc_running_reset (dev);
|
|
|
|
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET1, LMC_CSR_READ (sc, csr_status), 0);
|
|
|
|
LMC_EVENT_LOG(LMC_EVENT_RESET2,
|
|
|
|
lmc_mii_readreg (sc, 0, 16),
|
|
|
|
lmc_mii_readreg (sc, 0, 17));
|
|
|
|
|
|
|
|
/* restart the tx processes */
|
|
|
|
csr6 = LMC_CSR_READ (sc, csr_command);
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, csr6 | 0x0002);
|
|
|
|
LMC_CSR_WRITE (sc, csr_command, csr6 | 0x2002);
|
|
|
|
|
|
|
|
/* immediate transmit */
|
|
|
|
LMC_CSR_WRITE (sc, csr_txpoll, 0);
|
|
|
|
|
2008-07-03 02:46:21 +08:00
|
|
|
sc->lmc_device->stats.tx_errors++;
|
|
|
|
sc->extra_stats.tx_ProcTimeout++; /* -baz */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
|
|
|
|
bug_out:
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&sc->lmc_lock, flags);
|
|
|
|
|
|
|
|
lmc_trace(dev, "lmc_driver_timout out");
|
|
|
|
|
|
|
|
|
|
|
|
}
|