This allows incrementing the correct timeout statistic without any mess. Down the road, devices can learn to reset just the specific queue. The patch was generated with the following script: use strict; use warnings; our $^I = '.bak'; my @work = ( ["arch/m68k/emu/nfeth.c", "nfeth_tx_timeout"], ["arch/um/drivers/net_kern.c", "uml_net_tx_timeout"], ["arch/um/drivers/vector_kern.c", "vector_net_tx_timeout"], ["arch/xtensa/platforms/iss/network.c", "iss_net_tx_timeout"], ["drivers/char/pcmcia/synclink_cs.c", "hdlcdev_tx_timeout"], ["drivers/infiniband/ulp/ipoib/ipoib_main.c", "ipoib_timeout"], ["drivers/infiniband/ulp/ipoib/ipoib_main.c", "ipoib_timeout"], ["drivers/message/fusion/mptlan.c", "mpt_lan_tx_timeout"], ["drivers/misc/sgi-xp/xpnet.c", "xpnet_dev_tx_timeout"], ["drivers/net/appletalk/cops.c", "cops_timeout"], ["drivers/net/arcnet/arcdevice.h", "arcnet_timeout"], ["drivers/net/arcnet/arcnet.c", "arcnet_timeout"], ["drivers/net/arcnet/com20020.c", "arcnet_timeout"], ["drivers/net/ethernet/3com/3c509.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c515.c", "corkscrew_timeout"], ["drivers/net/ethernet/3com/3c574_cs.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c589_cs.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c59x.c", "vortex_tx_timeout"], ["drivers/net/ethernet/3com/3c59x.c", "vortex_tx_timeout"], ["drivers/net/ethernet/3com/typhoon.c", "typhoon_tx_timeout"], ["drivers/net/ethernet/8390/8390.h", "ei_tx_timeout"], ["drivers/net/ethernet/8390/8390.h", "eip_tx_timeout"], ["drivers/net/ethernet/8390/8390.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/8390p.c", "eip_tx_timeout"], ["drivers/net/ethernet/8390/ax88796.c", "ax_ei_tx_timeout"], ["drivers/net/ethernet/8390/axnet_cs.c", "axnet_tx_timeout"], ["drivers/net/ethernet/8390/etherh.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/hydra.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/mac8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/mcf8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/lib8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/ne2k-pci.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/pcnet_cs.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/smc-ultra.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/wd.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/zorro8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/adaptec/starfire.c", "tx_timeout"], ["drivers/net/ethernet/agere/et131x.c", "et131x_tx_timeout"], ["drivers/net/ethernet/allwinner/sun4i-emac.c", "emac_timeout"], ["drivers/net/ethernet/alteon/acenic.c", "ace_watchdog"], ["drivers/net/ethernet/amazon/ena/ena_netdev.c", "ena_tx_timeout"], ["drivers/net/ethernet/amd/7990.h", "lance_tx_timeout"], ["drivers/net/ethernet/amd/7990.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/a2065.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/am79c961a.c", "am79c961_timeout"], ["drivers/net/ethernet/amd/amd8111e.c", "amd8111e_tx_timeout"], ["drivers/net/ethernet/amd/ariadne.c", "ariadne_tx_timeout"], ["drivers/net/ethernet/amd/atarilance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/au1000_eth.c", "au1000_tx_timeout"], ["drivers/net/ethernet/amd/declance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/lance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/mvme147.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/ni65.c", "ni65_timeout"], ["drivers/net/ethernet/amd/nmclan_cs.c", "mace_tx_timeout"], ["drivers/net/ethernet/amd/pcnet32.c", "pcnet32_tx_timeout"], ["drivers/net/ethernet/amd/sunlance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/xgbe/xgbe-drv.c", "xgbe_tx_timeout"], ["drivers/net/ethernet/apm/xgene-v2/main.c", "xge_timeout"], ["drivers/net/ethernet/apm/xgene/xgene_enet_main.c", "xgene_enet_timeout"], ["drivers/net/ethernet/apple/macmace.c", "mace_tx_timeout"], ["drivers/net/ethernet/atheros/ag71xx.c", "ag71xx_tx_timeout"], ["drivers/net/ethernet/atheros/alx/main.c", "alx_tx_timeout"], ["drivers/net/ethernet/atheros/atl1c/atl1c_main.c", "atl1c_tx_timeout"], ["drivers/net/ethernet/atheros/atl1e/atl1e_main.c", "atl1e_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl.c", "atlx_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl1.c", "atlx_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl2.c", "atl2_tx_timeout"], ["drivers/net/ethernet/broadcom/b44.c", "b44_tx_timeout"], ["drivers/net/ethernet/broadcom/bcmsysport.c", "bcm_sysport_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2.c", "bnx2_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnxt/bnxt.c", "bnxt_tx_timeout"], ["drivers/net/ethernet/broadcom/genet/bcmgenet.c", "bcmgenet_timeout"], ["drivers/net/ethernet/broadcom/sb1250-mac.c", "sbmac_tx_timeout"], ["drivers/net/ethernet/broadcom/tg3.c", "tg3_tx_timeout"], ["drivers/net/ethernet/calxeda/xgmac.c", "xgmac_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_main.c", "liquidio_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_vf_main.c", "liquidio_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c", "lio_vf_rep_tx_timeout"], ["drivers/net/ethernet/cavium/thunder/nicvf_main.c", "nicvf_tx_timeout"], ["drivers/net/ethernet/cirrus/cs89x0.c", "net_timeout"], ["drivers/net/ethernet/cisco/enic/enic_main.c", "enic_tx_timeout"], ["drivers/net/ethernet/cisco/enic/enic_main.c", "enic_tx_timeout"], ["drivers/net/ethernet/cortina/gemini.c", "gmac_tx_timeout"], ["drivers/net/ethernet/davicom/dm9000.c", "dm9000_timeout"], ["drivers/net/ethernet/dec/tulip/de2104x.c", "de_tx_timeout"], ["drivers/net/ethernet/dec/tulip/tulip_core.c", "tulip_tx_timeout"], ["drivers/net/ethernet/dec/tulip/winbond-840.c", "tx_timeout"], ["drivers/net/ethernet/dlink/dl2k.c", "rio_tx_timeout"], ["drivers/net/ethernet/dlink/sundance.c", "tx_timeout"], ["drivers/net/ethernet/emulex/benet/be_main.c", "be_tx_timeout"], ["drivers/net/ethernet/ethoc.c", "ethoc_tx_timeout"], ["drivers/net/ethernet/faraday/ftgmac100.c", "ftgmac100_tx_timeout"], ["drivers/net/ethernet/fealnx.c", "fealnx_tx_timeout"], ["drivers/net/ethernet/freescale/dpaa/dpaa_eth.c", "dpaa_tx_timeout"], ["drivers/net/ethernet/freescale/fec_main.c", "fec_timeout"], ["drivers/net/ethernet/freescale/fec_mpc52xx.c", "mpc52xx_fec_tx_timeout"], ["drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c", "fs_timeout"], ["drivers/net/ethernet/freescale/gianfar.c", "gfar_timeout"], ["drivers/net/ethernet/freescale/ucc_geth.c", "ucc_geth_timeout"], ["drivers/net/ethernet/fujitsu/fmvj18x_cs.c", "fjn_tx_timeout"], ["drivers/net/ethernet/google/gve/gve_main.c", "gve_tx_timeout"], ["drivers/net/ethernet/hisilicon/hip04_eth.c", "hip04_timeout"], ["drivers/net/ethernet/hisilicon/hix5hd2_gmac.c", "hix5hd2_net_timeout"], ["drivers/net/ethernet/hisilicon/hns/hns_enet.c", "hns_nic_net_timeout"], ["drivers/net/ethernet/hisilicon/hns3/hns3_enet.c", "hns3_nic_net_timeout"], ["drivers/net/ethernet/huawei/hinic/hinic_main.c", "hinic_tx_timeout"], ["drivers/net/ethernet/i825xx/82596.c", "i596_tx_timeout"], ["drivers/net/ethernet/i825xx/ether1.c", "ether1_timeout"], ["drivers/net/ethernet/i825xx/lib82596.c", "i596_tx_timeout"], ["drivers/net/ethernet/i825xx/sun3_82586.c", "sun3_82586_timeout"], ["drivers/net/ethernet/ibm/ehea/ehea_main.c", "ehea_tx_watchdog"], ["drivers/net/ethernet/ibm/emac/core.c", "emac_tx_timeout"], ["drivers/net/ethernet/ibm/emac/core.c", "emac_tx_timeout"], ["drivers/net/ethernet/ibm/ibmvnic.c", "ibmvnic_tx_timeout"], ["drivers/net/ethernet/intel/e100.c", "e100_tx_timeout"], ["drivers/net/ethernet/intel/e1000/e1000_main.c", "e1000_tx_timeout"], ["drivers/net/ethernet/intel/e1000e/netdev.c", "e1000_tx_timeout"], ["drivers/net/ethernet/intel/fm10k/fm10k_netdev.c", "fm10k_tx_timeout"], ["drivers/net/ethernet/intel/i40e/i40e_main.c", "i40e_tx_timeout"], ["drivers/net/ethernet/intel/iavf/iavf_main.c", "iavf_tx_timeout"], ["drivers/net/ethernet/intel/ice/ice_main.c", "ice_tx_timeout"], ["drivers/net/ethernet/intel/ice/ice_main.c", "ice_tx_timeout"], ["drivers/net/ethernet/intel/igb/igb_main.c", "igb_tx_timeout"], ["drivers/net/ethernet/intel/igbvf/netdev.c", "igbvf_tx_timeout"], ["drivers/net/ethernet/intel/ixgb/ixgb_main.c", "ixgb_tx_timeout"], ["drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c", "adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev);"], ["drivers/net/ethernet/intel/ixgbe/ixgbe_main.c", "ixgbe_tx_timeout"], ["drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c", "ixgbevf_tx_timeout"], ["drivers/net/ethernet/jme.c", "jme_tx_timeout"], ["drivers/net/ethernet/korina.c", "korina_tx_timeout"], ["drivers/net/ethernet/lantiq_etop.c", "ltq_etop_tx_timeout"], ["drivers/net/ethernet/marvell/mv643xx_eth.c", "mv643xx_eth_tx_timeout"], ["drivers/net/ethernet/marvell/pxa168_eth.c", "pxa168_eth_tx_timeout"], ["drivers/net/ethernet/marvell/skge.c", "skge_tx_timeout"], ["drivers/net/ethernet/marvell/sky2.c", "sky2_tx_timeout"], ["drivers/net/ethernet/marvell/sky2.c", "sky2_tx_timeout"], ["drivers/net/ethernet/mediatek/mtk_eth_soc.c", "mtk_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx4/en_netdev.c", "mlx4_en_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx4/en_netdev.c", "mlx4_en_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx5/core/en_main.c", "mlx5e_tx_timeout"], ["drivers/net/ethernet/micrel/ks8842.c", "ks8842_tx_timeout"], ["drivers/net/ethernet/micrel/ksz884x.c", "netdev_tx_timeout"], ["drivers/net/ethernet/microchip/enc28j60.c", "enc28j60_tx_timeout"], ["drivers/net/ethernet/microchip/encx24j600.c", "encx24j600_tx_timeout"], ["drivers/net/ethernet/natsemi/sonic.h", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/sonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/jazzsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/macsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/natsemi.c", "ns_tx_timeout"], ["drivers/net/ethernet/natsemi/ns83820.c", "ns83820_tx_timeout"], ["drivers/net/ethernet/natsemi/xtsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/neterion/s2io.h", "s2io_tx_watchdog"], ["drivers/net/ethernet/neterion/s2io.c", "s2io_tx_watchdog"], ["drivers/net/ethernet/neterion/vxge/vxge-main.c", "vxge_tx_watchdog"], ["drivers/net/ethernet/netronome/nfp/nfp_net_common.c", "nfp_net_tx_timeout"], ["drivers/net/ethernet/nvidia/forcedeth.c", "nv_tx_timeout"], ["drivers/net/ethernet/nvidia/forcedeth.c", "nv_tx_timeout"], ["drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c", "pch_gbe_tx_timeout"], ["drivers/net/ethernet/packetengines/hamachi.c", "hamachi_tx_timeout"], ["drivers/net/ethernet/packetengines/yellowfin.c", "yellowfin_tx_timeout"], ["drivers/net/ethernet/pensando/ionic/ionic_lif.c", "ionic_tx_timeout"], ["drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c", "netxen_tx_timeout"], ["drivers/net/ethernet/qlogic/qla3xxx.c", "ql3xxx_tx_timeout"], ["drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c", "qlcnic_tx_timeout"], ["drivers/net/ethernet/qualcomm/emac/emac.c", "emac_tx_timeout"], ["drivers/net/ethernet/qualcomm/qca_spi.c", "qcaspi_netdev_tx_timeout"], ["drivers/net/ethernet/qualcomm/qca_uart.c", "qcauart_netdev_tx_timeout"], ["drivers/net/ethernet/rdc/r6040.c", "r6040_tx_timeout"], ["drivers/net/ethernet/realtek/8139cp.c", "cp_tx_timeout"], ["drivers/net/ethernet/realtek/8139too.c", "rtl8139_tx_timeout"], ["drivers/net/ethernet/realtek/atp.c", "tx_timeout"], ["drivers/net/ethernet/realtek/r8169_main.c", "rtl8169_tx_timeout"], ["drivers/net/ethernet/renesas/ravb_main.c", "ravb_tx_timeout"], ["drivers/net/ethernet/renesas/sh_eth.c", "sh_eth_tx_timeout"], ["drivers/net/ethernet/renesas/sh_eth.c", "sh_eth_tx_timeout"], ["drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c", "sxgbe_tx_timeout"], ["drivers/net/ethernet/seeq/ether3.c", "ether3_timeout"], ["drivers/net/ethernet/seeq/sgiseeq.c", "timeout"], ["drivers/net/ethernet/sfc/efx.c", "efx_watchdog"], ["drivers/net/ethernet/sfc/falcon/efx.c", "ef4_watchdog"], ["drivers/net/ethernet/sgi/ioc3-eth.c", "ioc3_timeout"], ["drivers/net/ethernet/sgi/meth.c", "meth_tx_timeout"], ["drivers/net/ethernet/silan/sc92031.c", "sc92031_tx_timeout"], ["drivers/net/ethernet/sis/sis190.c", "sis190_tx_timeout"], ["drivers/net/ethernet/sis/sis900.c", "sis900_tx_timeout"], ["drivers/net/ethernet/smsc/epic100.c", "epic_tx_timeout"], ["drivers/net/ethernet/smsc/smc911x.c", "smc911x_timeout"], ["drivers/net/ethernet/smsc/smc9194.c", "smc_timeout"], ["drivers/net/ethernet/smsc/smc91c92_cs.c", "smc_tx_timeout"], ["drivers/net/ethernet/smsc/smc91x.c", "smc_timeout"], ["drivers/net/ethernet/stmicro/stmmac/stmmac_main.c", "stmmac_tx_timeout"], ["drivers/net/ethernet/sun/cassini.c", "cas_tx_timeout"], ["drivers/net/ethernet/sun/ldmvsw.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/niu.c", "niu_tx_timeout"], ["drivers/net/ethernet/sun/sunbmac.c", "bigmac_tx_timeout"], ["drivers/net/ethernet/sun/sungem.c", "gem_tx_timeout"], ["drivers/net/ethernet/sun/sunhme.c", "happy_meal_tx_timeout"], ["drivers/net/ethernet/sun/sunqe.c", "qe_tx_timeout"], ["drivers/net/ethernet/sun/sunvnet.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/sunvnet_common.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/sunvnet_common.h", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/synopsys/dwc-xlgmac-net.c", "xlgmac_tx_timeout"], ["drivers/net/ethernet/ti/cpmac.c", "cpmac_tx_timeout"], ["drivers/net/ethernet/ti/cpsw.c", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/cpsw_priv.c", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/cpsw_priv.h", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/davinci_emac.c", "emac_dev_tx_timeout"], ["drivers/net/ethernet/ti/netcp_core.c", "netcp_ndo_tx_timeout"], ["drivers/net/ethernet/ti/tlan.c", "tlan_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_net.h", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_net.c", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_wireless.c", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/spider_net.c", "spider_net_tx_timeout"], ["drivers/net/ethernet/toshiba/tc35815.c", "tc35815_tx_timeout"], ["drivers/net/ethernet/via/via-rhine.c", "rhine_tx_timeout"], ["drivers/net/ethernet/wiznet/w5100.c", "w5100_tx_timeout"], ["drivers/net/ethernet/wiznet/w5300.c", "w5300_tx_timeout"], ["drivers/net/ethernet/xilinx/xilinx_emaclite.c", "xemaclite_tx_timeout"], ["drivers/net/ethernet/xircom/xirc2ps_cs.c", "xirc_tx_timeout"], ["drivers/net/fjes/fjes_main.c", "fjes_tx_retry"], ["drivers/net/slip/slip.c", "sl_tx_timeout"], ["include/linux/usb/usbnet.h", "usbnet_tx_timeout"], ["drivers/net/usb/aqc111.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/ax88172a.c", "usbnet_tx_timeout"], ["drivers/net/usb/ax88179_178a.c", "usbnet_tx_timeout"], ["drivers/net/usb/catc.c", "catc_tx_timeout"], ["drivers/net/usb/cdc_mbim.c", "usbnet_tx_timeout"], ["drivers/net/usb/cdc_ncm.c", "usbnet_tx_timeout"], ["drivers/net/usb/dm9601.c", "usbnet_tx_timeout"], ["drivers/net/usb/hso.c", "hso_net_tx_timeout"], ["drivers/net/usb/int51x1.c", "usbnet_tx_timeout"], ["drivers/net/usb/ipheth.c", "ipheth_tx_timeout"], ["drivers/net/usb/kaweth.c", "kaweth_tx_timeout"], ["drivers/net/usb/lan78xx.c", "lan78xx_tx_timeout"], ["drivers/net/usb/mcs7830.c", "usbnet_tx_timeout"], ["drivers/net/usb/pegasus.c", "pegasus_tx_timeout"], ["drivers/net/usb/qmi_wwan.c", "usbnet_tx_timeout"], ["drivers/net/usb/r8152.c", "rtl8152_tx_timeout"], ["drivers/net/usb/rndis_host.c", "usbnet_tx_timeout"], ["drivers/net/usb/rtl8150.c", "rtl8150_tx_timeout"], ["drivers/net/usb/sierra_net.c", "usbnet_tx_timeout"], ["drivers/net/usb/smsc75xx.c", "usbnet_tx_timeout"], ["drivers/net/usb/smsc95xx.c", "usbnet_tx_timeout"], ["drivers/net/usb/sr9700.c", "usbnet_tx_timeout"], ["drivers/net/usb/sr9800.c", "usbnet_tx_timeout"], ["drivers/net/usb/usbnet.c", "usbnet_tx_timeout"], ["drivers/net/vmxnet3/vmxnet3_drv.c", "vmxnet3_tx_timeout"], ["drivers/net/wan/cosa.c", "cosa_net_timeout"], ["drivers/net/wan/farsync.c", "fst_tx_timeout"], ["drivers/net/wan/fsl_ucc_hdlc.c", "uhdlc_tx_timeout"], ["drivers/net/wan/lmc/lmc_main.c", "lmc_driver_timeout"], ["drivers/net/wan/x25_asy.c", "x25_asy_timeout"], ["drivers/net/wimax/i2400m/netdev.c", "i2400m_tx_timeout"], ["drivers/net/wireless/intel/ipw2x00/ipw2100.c", "ipw2100_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/main.c", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/orinoco_usb.c", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/orinoco.h", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_dev.c", "islpci_eth_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_eth.c", "islpci_eth_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_eth.h", "islpci_eth_tx_timeout"], ["drivers/net/wireless/marvell/mwifiex/main.c", "mwifiex_tx_timeout"], ["drivers/net/wireless/quantenna/qtnfmac/core.c", "qtnf_netdev_tx_timeout"], ["drivers/net/wireless/quantenna/qtnfmac/core.h", "qtnf_netdev_tx_timeout"], ["drivers/net/wireless/rndis_wlan.c", "usbnet_tx_timeout"], ["drivers/net/wireless/wl3501_cs.c", "wl3501_tx_timeout"], ["drivers/net/wireless/zydas/zd1201.c", "zd1201_tx_timeout"], ["drivers/s390/net/qeth_core.h", "qeth_tx_timeout"], ["drivers/s390/net/qeth_core_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l2_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l2_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l3_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l3_main.c", "qeth_tx_timeout"], ["drivers/staging/ks7010/ks_wlan_net.c", "ks_wlan_tx_timeout"], ["drivers/staging/qlge/qlge_main.c", "qlge_tx_timeout"], ["drivers/staging/rtl8192e/rtl8192e/rtl_core.c", "_rtl92e_tx_timeout"], ["drivers/staging/rtl8192u/r8192U_core.c", "tx_timeout"], ["drivers/staging/unisys/visornic/visornic_main.c", "visornic_xmit_timeout"], ["drivers/staging/wlan-ng/p80211netdev.c", "p80211knetdev_tx_timeout"], ["drivers/tty/n_gsm.c", "gsm_mux_net_tx_timeout"], ["drivers/tty/synclink.c", "hdlcdev_tx_timeout"], ["drivers/tty/synclink_gt.c", "hdlcdev_tx_timeout"], ["drivers/tty/synclinkmp.c", "hdlcdev_tx_timeout"], ["net/atm/lec.c", "lec_tx_timeout"], ["net/bluetooth/bnep/netdev.c", "bnep_net_timeout"] ); for my $p (@work) { my @pair = @$p; my $file = $pair[0]; my $func = $pair[1]; print STDERR $file , ": ", $func,"\n"; our @ARGV = ($file); while (<ARGV>) { if (m/($func\s*\(struct\s+net_device\s+\*[A-Za-z_]?[A-Za-z-0-9_]*)(\))/) { print STDERR "found $1+$2 in $file\n"; } if (s/($func\s*\(struct\s+net_device\s+\*[A-Za-z_]?[A-Za-z-0-9_]*)(\))/$1, unsigned int txqueue$2/) { print STDERR "$func found in $file\n"; } print; } } where the list of files and functions is simply from: git grep ndo_tx_timeout, with manual addition of headers in the rare cases where the function is from a header, then manually changing the few places which actually call ndo_tx_timeout. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Heiner Kallweit <hkallweit1@gmail.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Acked-by: Shannon Nelson <snelson@pensando.io> Reviewed-by: Martin Habets <mhabets@solarflare.com> changes from v9: fixup a forward declaration changes from v9: more leftovers from v3 change changes from v8: fix up a missing direct call to timeout rebased on net-next changes from v7: fixup leftovers from v3 change changes from v6: fix typo in rtl driver changes from v5: add missing files (allow any net device argument name) changes from v4: add a missing driver header changes from v3: change queue # to unsigned Changes from v2: added headers Changes from v1: Fix errors found by kbuild: generalize the pattern a bit, to pick up a couple of instances missed by the previous version. Signed-off-by: David S. Miller <davem@davemloft.net>
1511 lines
44 KiB
C
1511 lines
44 KiB
C
/* ----------------------------------------------------------------------------
|
|
Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
|
|
nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
|
|
|
|
The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
|
|
Access Controller for Ethernet (MACE). It is essentially the Am2150
|
|
PCMCIA Ethernet card contained in the Am2150 Demo Kit.
|
|
|
|
Written by Roger C. Pao <rpao@paonet.org>
|
|
Copyright 1995 Roger C. Pao
|
|
Linux 2.5 cleanups Copyright Red Hat 2003
|
|
|
|
This software may be used and distributed according to the terms of
|
|
the GNU General Public License.
|
|
|
|
Ported to Linux 1.3.* network driver environment by
|
|
Matti Aarnio <mea@utu.fi>
|
|
|
|
References
|
|
|
|
Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
|
|
Am79C940 (MACE) Data Sheet, 1994
|
|
Am79C90 (C-LANCE) Data Sheet, 1994
|
|
Linux PCMCIA Programmer's Guide v1.17
|
|
/usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
|
|
|
|
Eric Mears, New Media Corporation
|
|
Tom Pollard, New Media Corporation
|
|
Dean Siasoyco, New Media Corporation
|
|
Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
|
|
Donald Becker <becker@scyld.com>
|
|
David Hinds <dahinds@users.sourceforge.net>
|
|
|
|
The Linux client driver is based on the 3c589_cs.c client driver by
|
|
David Hinds.
|
|
|
|
The Linux network driver outline is based on the 3c589_cs.c driver,
|
|
the 8390.c driver, and the example skeleton.c kernel code, which are
|
|
by Donald Becker.
|
|
|
|
The Am2150 network driver hardware interface code is based on the
|
|
OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
|
|
|
|
Special thanks for testing and help in debugging this driver goes
|
|
to Ken Lesniak.
|
|
|
|
-------------------------------------------------------------------------------
|
|
Driver Notes and Issues
|
|
-------------------------------------------------------------------------------
|
|
|
|
1. Developed on a Dell 320SLi
|
|
PCMCIA Card Services 2.6.2
|
|
Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
|
|
|
|
2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
|
|
'insmod pcmcia_core.o io_speed=300'.
|
|
This will avoid problems with fast systems which causes rx_framecnt
|
|
to return random values.
|
|
|
|
3. If hot extraction does not work for you, use 'ifconfig eth0 down'
|
|
before extraction.
|
|
|
|
4. There is a bad slow-down problem in this driver.
|
|
|
|
5. Future: Multicast processing. In the meantime, do _not_ compile your
|
|
kernel with multicast ip enabled.
|
|
|
|
-------------------------------------------------------------------------------
|
|
History
|
|
-------------------------------------------------------------------------------
|
|
Log: nmclan_cs.c,v
|
|
* 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
|
* Fixed hang on card eject as we probe it
|
|
* Cleaned up to use new style locking.
|
|
*
|
|
* Revision 0.16 1995/07/01 06:42:17 rpao
|
|
* Bug fix: nmclan_reset() called CardServices incorrectly.
|
|
*
|
|
* Revision 0.15 1995/05/24 08:09:47 rpao
|
|
* Re-implement MULTI_TX dev->tbusy handling.
|
|
*
|
|
* Revision 0.14 1995/05/23 03:19:30 rpao
|
|
* Added, in nmclan_config(), "tuple.Attributes = 0;".
|
|
* Modified MACE ID check to ignore chip revision level.
|
|
* Avoid tx_free_frames race condition between _start_xmit and _interrupt.
|
|
*
|
|
* Revision 0.13 1995/05/18 05:56:34 rpao
|
|
* Statistics changes.
|
|
* Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
|
|
* Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup.
|
|
*
|
|
* Revision 0.12 1995/05/14 00:12:23 rpao
|
|
* Statistics overhaul.
|
|
*
|
|
|
|
95/05/13 rpao V0.10a
|
|
Bug fix: MACE statistics counters used wrong I/O ports.
|
|
Bug fix: mace_interrupt() needed to allow statistics to be
|
|
processed without RX or TX interrupts pending.
|
|
95/05/11 rpao V0.10
|
|
Multiple transmit request processing.
|
|
Modified statistics to use MACE counters where possible.
|
|
95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
|
|
*Released
|
|
95/05/10 rpao V0.08
|
|
Bug fix: Make all non-exported functions private by using
|
|
static keyword.
|
|
Bug fix: Test IntrCnt _before_ reading MACE_IR.
|
|
95/05/10 rpao V0.07 Statistics.
|
|
95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
|
|
|
|
---------------------------------------------------------------------------- */
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#define DRV_NAME "nmclan_cs"
|
|
#define DRV_VERSION "0.16"
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Conditional Compilation Options
|
|
---------------------------------------------------------------------------- */
|
|
|
|
#define MULTI_TX 0
|
|
#define RESET_ON_TIMEOUT 1
|
|
#define TX_INTERRUPTABLE 1
|
|
#define RESET_XILINX 0
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Include Files
|
|
---------------------------------------------------------------------------- */
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/in.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/bitops.h>
|
|
|
|
#include <pcmcia/cisreg.h>
|
|
#include <pcmcia/cistpl.h>
|
|
#include <pcmcia/ds.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <asm/io.h>
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Defines
|
|
---------------------------------------------------------------------------- */
|
|
|
|
#define MACE_LADRF_LEN 8
|
|
/* 8 bytes in Logical Address Filter */
|
|
|
|
/* Loop Control Defines */
|
|
#define MACE_MAX_IR_ITERATIONS 10
|
|
#define MACE_MAX_RX_ITERATIONS 12
|
|
/*
|
|
TBD: Dean brought this up, and I assumed the hardware would
|
|
handle it:
|
|
|
|
If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
|
|
non-zero when the isr exits. We may not get another interrupt
|
|
to process the remaining packets for some time.
|
|
*/
|
|
|
|
/*
|
|
The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
|
|
which manages the interface between the MACE and the PCMCIA bus. It
|
|
also includes buffer management for the 32K x 8 SRAM to control up to
|
|
four transmit and 12 receive frames at a time.
|
|
*/
|
|
#define AM2150_MAX_TX_FRAMES 4
|
|
#define AM2150_MAX_RX_FRAMES 12
|
|
|
|
/* Am2150 Ethernet Card I/O Mapping */
|
|
#define AM2150_RCV 0x00
|
|
#define AM2150_XMT 0x04
|
|
#define AM2150_XMT_SKIP 0x09
|
|
#define AM2150_RCV_NEXT 0x0A
|
|
#define AM2150_RCV_FRAME_COUNT 0x0B
|
|
#define AM2150_MACE_BANK 0x0C
|
|
#define AM2150_MACE_BASE 0x10
|
|
|
|
/* MACE Registers */
|
|
#define MACE_RCVFIFO 0
|
|
#define MACE_XMTFIFO 1
|
|
#define MACE_XMTFC 2
|
|
#define MACE_XMTFS 3
|
|
#define MACE_XMTRC 4
|
|
#define MACE_RCVFC 5
|
|
#define MACE_RCVFS 6
|
|
#define MACE_FIFOFC 7
|
|
#define MACE_IR 8
|
|
#define MACE_IMR 9
|
|
#define MACE_PR 10
|
|
#define MACE_BIUCC 11
|
|
#define MACE_FIFOCC 12
|
|
#define MACE_MACCC 13
|
|
#define MACE_PLSCC 14
|
|
#define MACE_PHYCC 15
|
|
#define MACE_CHIPIDL 16
|
|
#define MACE_CHIPIDH 17
|
|
#define MACE_IAC 18
|
|
/* Reserved */
|
|
#define MACE_LADRF 20
|
|
#define MACE_PADR 21
|
|
/* Reserved */
|
|
/* Reserved */
|
|
#define MACE_MPC 24
|
|
/* Reserved */
|
|
#define MACE_RNTPC 26
|
|
#define MACE_RCVCC 27
|
|
/* Reserved */
|
|
#define MACE_UTR 29
|
|
#define MACE_RTR1 30
|
|
#define MACE_RTR2 31
|
|
|
|
/* MACE Bit Masks */
|
|
#define MACE_XMTRC_EXDEF 0x80
|
|
#define MACE_XMTRC_XMTRC 0x0F
|
|
|
|
#define MACE_XMTFS_XMTSV 0x80
|
|
#define MACE_XMTFS_UFLO 0x40
|
|
#define MACE_XMTFS_LCOL 0x20
|
|
#define MACE_XMTFS_MORE 0x10
|
|
#define MACE_XMTFS_ONE 0x08
|
|
#define MACE_XMTFS_DEFER 0x04
|
|
#define MACE_XMTFS_LCAR 0x02
|
|
#define MACE_XMTFS_RTRY 0x01
|
|
|
|
#define MACE_RCVFS_RCVSTS 0xF000
|
|
#define MACE_RCVFS_OFLO 0x8000
|
|
#define MACE_RCVFS_CLSN 0x4000
|
|
#define MACE_RCVFS_FRAM 0x2000
|
|
#define MACE_RCVFS_FCS 0x1000
|
|
|
|
#define MACE_FIFOFC_RCVFC 0xF0
|
|
#define MACE_FIFOFC_XMTFC 0x0F
|
|
|
|
#define MACE_IR_JAB 0x80
|
|
#define MACE_IR_BABL 0x40
|
|
#define MACE_IR_CERR 0x20
|
|
#define MACE_IR_RCVCCO 0x10
|
|
#define MACE_IR_RNTPCO 0x08
|
|
#define MACE_IR_MPCO 0x04
|
|
#define MACE_IR_RCVINT 0x02
|
|
#define MACE_IR_XMTINT 0x01
|
|
|
|
#define MACE_MACCC_PROM 0x80
|
|
#define MACE_MACCC_DXMT2PD 0x40
|
|
#define MACE_MACCC_EMBA 0x20
|
|
#define MACE_MACCC_RESERVED 0x10
|
|
#define MACE_MACCC_DRCVPA 0x08
|
|
#define MACE_MACCC_DRCVBC 0x04
|
|
#define MACE_MACCC_ENXMT 0x02
|
|
#define MACE_MACCC_ENRCV 0x01
|
|
|
|
#define MACE_PHYCC_LNKFL 0x80
|
|
#define MACE_PHYCC_DLNKTST 0x40
|
|
#define MACE_PHYCC_REVPOL 0x20
|
|
#define MACE_PHYCC_DAPC 0x10
|
|
#define MACE_PHYCC_LRT 0x08
|
|
#define MACE_PHYCC_ASEL 0x04
|
|
#define MACE_PHYCC_RWAKE 0x02
|
|
#define MACE_PHYCC_AWAKE 0x01
|
|
|
|
#define MACE_IAC_ADDRCHG 0x80
|
|
#define MACE_IAC_PHYADDR 0x04
|
|
#define MACE_IAC_LOGADDR 0x02
|
|
|
|
#define MACE_UTR_RTRE 0x80
|
|
#define MACE_UTR_RTRD 0x40
|
|
#define MACE_UTR_RPA 0x20
|
|
#define MACE_UTR_FCOLL 0x10
|
|
#define MACE_UTR_RCVFCSE 0x08
|
|
#define MACE_UTR_LOOP_INCL_MENDEC 0x06
|
|
#define MACE_UTR_LOOP_NO_MENDEC 0x04
|
|
#define MACE_UTR_LOOP_EXTERNAL 0x02
|
|
#define MACE_UTR_LOOP_NONE 0x00
|
|
#define MACE_UTR_RESERVED 0x01
|
|
|
|
/* Switch MACE register bank (only 0 and 1 are valid) */
|
|
#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
|
|
|
|
#define MACE_IMR_DEFAULT \
|
|
(0xFF - \
|
|
( \
|
|
MACE_IR_CERR | \
|
|
MACE_IR_RCVCCO | \
|
|
MACE_IR_RNTPCO | \
|
|
MACE_IR_MPCO | \
|
|
MACE_IR_RCVINT | \
|
|
MACE_IR_XMTINT \
|
|
) \
|
|
)
|
|
#undef MACE_IMR_DEFAULT
|
|
#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
|
|
|
|
#define TX_TIMEOUT ((400*HZ)/1000)
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Type Definitions
|
|
---------------------------------------------------------------------------- */
|
|
|
|
typedef struct _mace_statistics {
|
|
/* MACE_XMTFS */
|
|
int xmtsv;
|
|
int uflo;
|
|
int lcol;
|
|
int more;
|
|
int one;
|
|
int defer;
|
|
int lcar;
|
|
int rtry;
|
|
|
|
/* MACE_XMTRC */
|
|
int exdef;
|
|
int xmtrc;
|
|
|
|
/* RFS1--Receive Status (RCVSTS) */
|
|
int oflo;
|
|
int clsn;
|
|
int fram;
|
|
int fcs;
|
|
|
|
/* RFS2--Runt Packet Count (RNTPC) */
|
|
int rfs_rntpc;
|
|
|
|
/* RFS3--Receive Collision Count (RCVCC) */
|
|
int rfs_rcvcc;
|
|
|
|
/* MACE_IR */
|
|
int jab;
|
|
int babl;
|
|
int cerr;
|
|
int rcvcco;
|
|
int rntpco;
|
|
int mpco;
|
|
|
|
/* MACE_MPC */
|
|
int mpc;
|
|
|
|
/* MACE_RNTPC */
|
|
int rntpc;
|
|
|
|
/* MACE_RCVCC */
|
|
int rcvcc;
|
|
} mace_statistics;
|
|
|
|
typedef struct _mace_private {
|
|
struct pcmcia_device *p_dev;
|
|
mace_statistics mace_stats; /* MACE chip statistics counters */
|
|
|
|
/* restore_multicast_list() state variables */
|
|
int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
|
|
int multicast_num_addrs;
|
|
|
|
char tx_free_frames; /* Number of free transmit frame buffers */
|
|
char tx_irq_disabled; /* MACE TX interrupt disabled */
|
|
|
|
spinlock_t bank_lock; /* Must be held if you step off bank 0 */
|
|
} mace_private;
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Private Global Variables
|
|
---------------------------------------------------------------------------- */
|
|
|
|
static const char *if_names[]={
|
|
"Auto", "10baseT", "BNC",
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Parameters
|
|
These are the parameters that can be set during loading with
|
|
'insmod'.
|
|
---------------------------------------------------------------------------- */
|
|
|
|
MODULE_DESCRIPTION("New Media PCMCIA ethernet driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
|
|
|
|
/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
|
|
INT_MODULE_PARM(if_port, 0);
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
Function Prototypes
|
|
---------------------------------------------------------------------------- */
|
|
|
|
static int nmclan_config(struct pcmcia_device *link);
|
|
static void nmclan_release(struct pcmcia_device *link);
|
|
|
|
static void nmclan_reset(struct net_device *dev);
|
|
static int mace_config(struct net_device *dev, struct ifmap *map);
|
|
static int mace_open(struct net_device *dev);
|
|
static int mace_close(struct net_device *dev);
|
|
static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev);
|
|
static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue);
|
|
static irqreturn_t mace_interrupt(int irq, void *dev_id);
|
|
static struct net_device_stats *mace_get_stats(struct net_device *dev);
|
|
static int mace_rx(struct net_device *dev, unsigned char RxCnt);
|
|
static void restore_multicast_list(struct net_device *dev);
|
|
static void set_multicast_list(struct net_device *dev);
|
|
static const struct ethtool_ops netdev_ethtool_ops;
|
|
|
|
|
|
static void nmclan_detach(struct pcmcia_device *p_dev);
|
|
|
|
static const struct net_device_ops mace_netdev_ops = {
|
|
.ndo_open = mace_open,
|
|
.ndo_stop = mace_close,
|
|
.ndo_start_xmit = mace_start_xmit,
|
|
.ndo_tx_timeout = mace_tx_timeout,
|
|
.ndo_set_config = mace_config,
|
|
.ndo_get_stats = mace_get_stats,
|
|
.ndo_set_rx_mode = set_multicast_list,
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
};
|
|
|
|
static int nmclan_probe(struct pcmcia_device *link)
|
|
{
|
|
mace_private *lp;
|
|
struct net_device *dev;
|
|
|
|
dev_dbg(&link->dev, "nmclan_attach()\n");
|
|
|
|
/* Create new ethernet device */
|
|
dev = alloc_etherdev(sizeof(mace_private));
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
lp = netdev_priv(dev);
|
|
lp->p_dev = link;
|
|
link->priv = dev;
|
|
|
|
spin_lock_init(&lp->bank_lock);
|
|
link->resource[0]->end = 32;
|
|
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
|
|
link->config_flags |= CONF_ENABLE_IRQ;
|
|
link->config_index = 1;
|
|
link->config_regs = PRESENT_OPTION;
|
|
|
|
lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
|
|
|
|
dev->netdev_ops = &mace_netdev_ops;
|
|
dev->ethtool_ops = &netdev_ethtool_ops;
|
|
dev->watchdog_timeo = TX_TIMEOUT;
|
|
|
|
return nmclan_config(link);
|
|
} /* nmclan_attach */
|
|
|
|
static void nmclan_detach(struct pcmcia_device *link)
|
|
{
|
|
struct net_device *dev = link->priv;
|
|
|
|
dev_dbg(&link->dev, "nmclan_detach\n");
|
|
|
|
unregister_netdev(dev);
|
|
|
|
nmclan_release(link);
|
|
|
|
free_netdev(dev);
|
|
} /* nmclan_detach */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_read
|
|
Reads a MACE register. This is bank independent; however, the
|
|
caller must ensure that this call is not interruptable. We are
|
|
assuming that during normal operation, the MACE is always in
|
|
bank 0.
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_read(mace_private *lp, unsigned int ioaddr, int reg)
|
|
{
|
|
int data = 0xFF;
|
|
unsigned long flags;
|
|
|
|
switch (reg >> 4) {
|
|
case 0: /* register 0-15 */
|
|
data = inb(ioaddr + AM2150_MACE_BASE + reg);
|
|
break;
|
|
case 1: /* register 16-31 */
|
|
spin_lock_irqsave(&lp->bank_lock, flags);
|
|
MACEBANK(1);
|
|
data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
|
|
MACEBANK(0);
|
|
spin_unlock_irqrestore(&lp->bank_lock, flags);
|
|
break;
|
|
}
|
|
return data & 0xFF;
|
|
} /* mace_read */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_write
|
|
Writes to a MACE register. This is bank independent; however,
|
|
the caller must ensure that this call is not interruptable. We
|
|
are assuming that during normal operation, the MACE is always in
|
|
bank 0.
|
|
---------------------------------------------------------------------------- */
|
|
static void mace_write(mace_private *lp, unsigned int ioaddr, int reg,
|
|
int data)
|
|
{
|
|
unsigned long flags;
|
|
|
|
switch (reg >> 4) {
|
|
case 0: /* register 0-15 */
|
|
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
|
|
break;
|
|
case 1: /* register 16-31 */
|
|
spin_lock_irqsave(&lp->bank_lock, flags);
|
|
MACEBANK(1);
|
|
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
|
|
MACEBANK(0);
|
|
spin_unlock_irqrestore(&lp->bank_lock, flags);
|
|
break;
|
|
}
|
|
} /* mace_write */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_init
|
|
Resets the MACE chip.
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_init(mace_private *lp, unsigned int ioaddr, char *enet_addr)
|
|
{
|
|
int i;
|
|
int ct = 0;
|
|
|
|
/* MACE Software reset */
|
|
mace_write(lp, ioaddr, MACE_BIUCC, 1);
|
|
while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
|
|
/* Wait for reset bit to be cleared automatically after <= 200ns */;
|
|
if(++ct > 500)
|
|
{
|
|
pr_err("reset failed, card removed?\n");
|
|
return -1;
|
|
}
|
|
udelay(1);
|
|
}
|
|
mace_write(lp, ioaddr, MACE_BIUCC, 0);
|
|
|
|
/* The Am2150 requires that the MACE FIFOs operate in burst mode. */
|
|
mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
|
|
|
|
mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
|
|
mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
|
|
|
|
/*
|
|
* Bit 2-1 PORTSEL[1-0] Port Select.
|
|
* 00 AUI/10Base-2
|
|
* 01 10Base-T
|
|
* 10 DAI Port (reserved in Am2150)
|
|
* 11 GPSI
|
|
* For this card, only the first two are valid.
|
|
* So, PLSCC should be set to
|
|
* 0x00 for 10Base-2
|
|
* 0x02 for 10Base-T
|
|
* Or just set ASEL in PHYCC below!
|
|
*/
|
|
switch (if_port) {
|
|
case 1:
|
|
mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
|
|
break;
|
|
case 2:
|
|
mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
|
|
break;
|
|
default:
|
|
mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
|
|
/* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
|
|
and the MACE device will automatically select the operating media
|
|
interface port. */
|
|
break;
|
|
}
|
|
|
|
mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
|
|
/* Poll ADDRCHG bit */
|
|
ct = 0;
|
|
while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
|
|
{
|
|
if(++ ct > 500)
|
|
{
|
|
pr_err("ADDRCHG timeout, card removed?\n");
|
|
return -1;
|
|
}
|
|
}
|
|
/* Set PADR register */
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
|
|
|
|
/* MAC Configuration Control Register should be written last */
|
|
/* Let set_multicast_list set this. */
|
|
/* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
|
|
mace_write(lp, ioaddr, MACE_MACCC, 0x00);
|
|
return 0;
|
|
} /* mace_init */
|
|
|
|
static int nmclan_config(struct pcmcia_device *link)
|
|
{
|
|
struct net_device *dev = link->priv;
|
|
mace_private *lp = netdev_priv(dev);
|
|
u8 *buf;
|
|
size_t len;
|
|
int i, ret;
|
|
unsigned int ioaddr;
|
|
|
|
dev_dbg(&link->dev, "nmclan_config\n");
|
|
|
|
link->io_lines = 5;
|
|
ret = pcmcia_request_io(link);
|
|
if (ret)
|
|
goto failed;
|
|
ret = pcmcia_request_irq(link, mace_interrupt);
|
|
if (ret)
|
|
goto failed;
|
|
ret = pcmcia_enable_device(link);
|
|
if (ret)
|
|
goto failed;
|
|
|
|
dev->irq = link->irq;
|
|
dev->base_addr = link->resource[0]->start;
|
|
|
|
ioaddr = dev->base_addr;
|
|
|
|
/* Read the ethernet address from the CIS. */
|
|
len = pcmcia_get_tuple(link, 0x80, &buf);
|
|
if (!buf || len < ETH_ALEN) {
|
|
kfree(buf);
|
|
goto failed;
|
|
}
|
|
memcpy(dev->dev_addr, buf, ETH_ALEN);
|
|
kfree(buf);
|
|
|
|
/* Verify configuration by reading the MACE ID. */
|
|
{
|
|
char sig[2];
|
|
|
|
sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
|
|
sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
|
|
if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
|
|
dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n",
|
|
sig[0], sig[1]);
|
|
} else {
|
|
pr_notice("mace id not found: %x %x should be 0x40 0x?9\n",
|
|
sig[0], sig[1]);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
|
|
goto failed;
|
|
|
|
/* The if_port symbol can be set when the module is loaded */
|
|
if (if_port <= 2)
|
|
dev->if_port = if_port;
|
|
else
|
|
pr_notice("invalid if_port requested\n");
|
|
|
|
SET_NETDEV_DEV(dev, &link->dev);
|
|
|
|
i = register_netdev(dev);
|
|
if (i != 0) {
|
|
pr_notice("register_netdev() failed\n");
|
|
goto failed;
|
|
}
|
|
|
|
netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n",
|
|
dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr);
|
|
return 0;
|
|
|
|
failed:
|
|
nmclan_release(link);
|
|
return -ENODEV;
|
|
} /* nmclan_config */
|
|
|
|
static void nmclan_release(struct pcmcia_device *link)
|
|
{
|
|
dev_dbg(&link->dev, "nmclan_release\n");
|
|
pcmcia_disable_device(link);
|
|
}
|
|
|
|
static int nmclan_suspend(struct pcmcia_device *link)
|
|
{
|
|
struct net_device *dev = link->priv;
|
|
|
|
if (link->open)
|
|
netif_device_detach(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nmclan_resume(struct pcmcia_device *link)
|
|
{
|
|
struct net_device *dev = link->priv;
|
|
|
|
if (link->open) {
|
|
nmclan_reset(dev);
|
|
netif_device_attach(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
nmclan_reset
|
|
Reset and restore all of the Xilinx and MACE registers.
|
|
---------------------------------------------------------------------------- */
|
|
static void nmclan_reset(struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
|
|
#if RESET_XILINX
|
|
struct pcmcia_device *link = &lp->link;
|
|
u8 OrigCorValue;
|
|
|
|
/* Save original COR value */
|
|
pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue);
|
|
|
|
/* Reset Xilinx */
|
|
dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n",
|
|
OrigCorValue);
|
|
pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET);
|
|
/* Need to wait for 20 ms for PCMCIA to finish reset. */
|
|
|
|
/* Restore original COR configuration index */
|
|
pcmcia_write_config_byte(link, CISREG_COR,
|
|
(COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK)));
|
|
/* Xilinx is now completely reset along with the MACE chip. */
|
|
lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
|
|
|
|
#endif /* #if RESET_XILINX */
|
|
|
|
/* Xilinx is now completely reset along with the MACE chip. */
|
|
lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
|
|
|
|
/* Reinitialize the MACE chip for operation. */
|
|
mace_init(lp, dev->base_addr, dev->dev_addr);
|
|
mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
|
|
|
|
/* Restore the multicast list and enable TX and RX. */
|
|
restore_multicast_list(dev);
|
|
} /* nmclan_reset */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_config
|
|
[Someone tell me what this is supposed to do? Is if_port a defined
|
|
standard? If so, there should be defines to indicate 1=10Base-T,
|
|
2=10Base-2, etc. including limited automatic detection.]
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_config(struct net_device *dev, struct ifmap *map)
|
|
{
|
|
if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
|
|
if (map->port <= 2) {
|
|
dev->if_port = map->port;
|
|
netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
|
|
} else
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
} /* mace_config */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_open
|
|
Open device driver.
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_open(struct net_device *dev)
|
|
{
|
|
unsigned int ioaddr = dev->base_addr;
|
|
mace_private *lp = netdev_priv(dev);
|
|
struct pcmcia_device *link = lp->p_dev;
|
|
|
|
if (!pcmcia_dev_present(link))
|
|
return -ENODEV;
|
|
|
|
link->open++;
|
|
|
|
MACEBANK(0);
|
|
|
|
netif_start_queue(dev);
|
|
nmclan_reset(dev);
|
|
|
|
return 0; /* Always succeed */
|
|
} /* mace_open */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_close
|
|
Closes device driver.
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_close(struct net_device *dev)
|
|
{
|
|
unsigned int ioaddr = dev->base_addr;
|
|
mace_private *lp = netdev_priv(dev);
|
|
struct pcmcia_device *link = lp->p_dev;
|
|
|
|
dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
|
|
|
|
/* Mask off all interrupts from the MACE chip. */
|
|
outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
|
|
|
|
link->open--;
|
|
netif_stop_queue(dev);
|
|
|
|
return 0;
|
|
} /* mace_close */
|
|
|
|
static void netdev_get_drvinfo(struct net_device *dev,
|
|
struct ethtool_drvinfo *info)
|
|
{
|
|
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
|
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
|
snprintf(info->bus_info, sizeof(info->bus_info),
|
|
"PCMCIA 0x%lx", dev->base_addr);
|
|
}
|
|
|
|
static const struct ethtool_ops netdev_ethtool_ops = {
|
|
.get_drvinfo = netdev_get_drvinfo,
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_start_xmit
|
|
This routine begins the packet transmit function. When completed,
|
|
it will generate a transmit interrupt.
|
|
|
|
According to /usr/src/linux/net/inet/dev.c, if _start_xmit
|
|
returns 0, the "packet is now solely the responsibility of the
|
|
driver." If _start_xmit returns non-zero, the "transmission
|
|
failed, put skb back into a list."
|
|
---------------------------------------------------------------------------- */
|
|
|
|
static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
struct pcmcia_device *link = lp->p_dev;
|
|
|
|
netdev_notice(dev, "transmit timed out -- ");
|
|
#if RESET_ON_TIMEOUT
|
|
pr_cont("resetting card\n");
|
|
pcmcia_reset_card(link->socket);
|
|
#else /* #if RESET_ON_TIMEOUT */
|
|
pr_cont("NOT resetting card\n");
|
|
#endif /* #if RESET_ON_TIMEOUT */
|
|
netif_trans_update(dev); /* prevent tx timeout */
|
|
netif_wake_queue(dev);
|
|
}
|
|
|
|
static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
unsigned int ioaddr = dev->base_addr;
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
pr_debug("%s: mace_start_xmit(length = %ld) called.\n",
|
|
dev->name, (long)skb->len);
|
|
|
|
#if (!TX_INTERRUPTABLE)
|
|
/* Disable MACE TX interrupts. */
|
|
outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
|
|
ioaddr + AM2150_MACE_BASE + MACE_IMR);
|
|
lp->tx_irq_disabled=1;
|
|
#endif /* #if (!TX_INTERRUPTABLE) */
|
|
|
|
{
|
|
/* This block must not be interrupted by another transmit request!
|
|
mace_tx_timeout will take care of timer-based retransmissions from
|
|
the upper layers. The interrupt handler is guaranteed never to
|
|
service a transmit interrupt while we are in here.
|
|
*/
|
|
|
|
dev->stats.tx_bytes += skb->len;
|
|
lp->tx_free_frames--;
|
|
|
|
/* WARNING: Write the _exact_ number of bytes written in the header! */
|
|
/* Put out the word header [must be an outw()] . . . */
|
|
outw(skb->len, ioaddr + AM2150_XMT);
|
|
/* . . . and the packet [may be any combination of outw() and outb()] */
|
|
outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
|
|
if (skb->len & 1) {
|
|
/* Odd byte transfer */
|
|
outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
|
|
}
|
|
|
|
#if MULTI_TX
|
|
if (lp->tx_free_frames > 0)
|
|
netif_start_queue(dev);
|
|
#endif /* #if MULTI_TX */
|
|
}
|
|
|
|
#if (!TX_INTERRUPTABLE)
|
|
/* Re-enable MACE TX interrupts. */
|
|
lp->tx_irq_disabled=0;
|
|
outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
|
|
#endif /* #if (!TX_INTERRUPTABLE) */
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return NETDEV_TX_OK;
|
|
} /* mace_start_xmit */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_interrupt
|
|
The interrupt handler.
|
|
---------------------------------------------------------------------------- */
|
|
static irqreturn_t mace_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct net_device *dev = (struct net_device *) dev_id;
|
|
mace_private *lp = netdev_priv(dev);
|
|
unsigned int ioaddr;
|
|
int status;
|
|
int IntrCnt = MACE_MAX_IR_ITERATIONS;
|
|
|
|
if (dev == NULL) {
|
|
pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n",
|
|
irq);
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
ioaddr = dev->base_addr;
|
|
|
|
if (lp->tx_irq_disabled) {
|
|
const char *msg;
|
|
if (lp->tx_irq_disabled)
|
|
msg = "Interrupt with tx_irq_disabled";
|
|
else
|
|
msg = "Re-entering the interrupt handler";
|
|
netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n",
|
|
msg,
|
|
inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
|
|
inb(ioaddr + AM2150_MACE_BASE + MACE_IMR));
|
|
/* WARNING: MACE_IR has been read! */
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
if (!netif_device_present(dev)) {
|
|
netdev_dbg(dev, "interrupt from dead card\n");
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
do {
|
|
/* WARNING: MACE_IR is a READ/CLEAR port! */
|
|
status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
|
|
if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS)
|
|
return IRQ_NONE;
|
|
|
|
pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
|
|
|
|
if (status & MACE_IR_RCVINT) {
|
|
mace_rx(dev, MACE_MAX_RX_ITERATIONS);
|
|
}
|
|
|
|
if (status & MACE_IR_XMTINT) {
|
|
unsigned char fifofc;
|
|
unsigned char xmtrc;
|
|
unsigned char xmtfs;
|
|
|
|
fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
|
|
if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
|
|
dev->stats.tx_errors++;
|
|
outb(0xFF, ioaddr + AM2150_XMT_SKIP);
|
|
}
|
|
|
|
/* Transmit Retry Count (XMTRC, reg 4) */
|
|
xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
|
|
if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
|
|
lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
|
|
|
|
if (
|
|
(xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
|
|
MACE_XMTFS_XMTSV /* Transmit Status Valid */
|
|
) {
|
|
lp->mace_stats.xmtsv++;
|
|
|
|
if (xmtfs & ~MACE_XMTFS_XMTSV) {
|
|
if (xmtfs & MACE_XMTFS_UFLO) {
|
|
/* Underflow. Indicates that the Transmit FIFO emptied before
|
|
the end of frame was reached. */
|
|
lp->mace_stats.uflo++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_LCOL) {
|
|
/* Late Collision */
|
|
lp->mace_stats.lcol++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_MORE) {
|
|
/* MORE than one retry was needed */
|
|
lp->mace_stats.more++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_ONE) {
|
|
/* Exactly ONE retry occurred */
|
|
lp->mace_stats.one++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_DEFER) {
|
|
/* Transmission was defered */
|
|
lp->mace_stats.defer++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_LCAR) {
|
|
/* Loss of carrier */
|
|
lp->mace_stats.lcar++;
|
|
}
|
|
if (xmtfs & MACE_XMTFS_RTRY) {
|
|
/* Retry error: transmit aborted after 16 attempts */
|
|
lp->mace_stats.rtry++;
|
|
}
|
|
} /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
|
|
|
|
} /* if (xmtfs & MACE_XMTFS_XMTSV) */
|
|
|
|
dev->stats.tx_packets++;
|
|
lp->tx_free_frames++;
|
|
netif_wake_queue(dev);
|
|
} /* if (status & MACE_IR_XMTINT) */
|
|
|
|
if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
|
|
if (status & MACE_IR_JAB) {
|
|
/* Jabber Error. Excessive transmit duration (20-150ms). */
|
|
lp->mace_stats.jab++;
|
|
}
|
|
if (status & MACE_IR_BABL) {
|
|
/* Babble Error. >1518 bytes transmitted. */
|
|
lp->mace_stats.babl++;
|
|
}
|
|
if (status & MACE_IR_CERR) {
|
|
/* Collision Error. CERR indicates the absence of the
|
|
Signal Quality Error Test message after a packet
|
|
transmission. */
|
|
lp->mace_stats.cerr++;
|
|
}
|
|
if (status & MACE_IR_RCVCCO) {
|
|
/* Receive Collision Count Overflow; */
|
|
lp->mace_stats.rcvcco++;
|
|
}
|
|
if (status & MACE_IR_RNTPCO) {
|
|
/* Runt Packet Count Overflow */
|
|
lp->mace_stats.rntpco++;
|
|
}
|
|
if (status & MACE_IR_MPCO) {
|
|
/* Missed Packet Count Overflow */
|
|
lp->mace_stats.mpco++;
|
|
}
|
|
} /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
|
|
|
|
} while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
|
|
|
|
return IRQ_HANDLED;
|
|
} /* mace_interrupt */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_rx
|
|
Receives packets.
|
|
---------------------------------------------------------------------------- */
|
|
static int mace_rx(struct net_device *dev, unsigned char RxCnt)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
unsigned int ioaddr = dev->base_addr;
|
|
unsigned char rx_framecnt;
|
|
unsigned short rx_status;
|
|
|
|
while (
|
|
((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
|
|
(rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
|
|
(RxCnt--)
|
|
) {
|
|
rx_status = inw(ioaddr + AM2150_RCV);
|
|
|
|
pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status"
|
|
" 0x%X.\n", dev->name, rx_framecnt, rx_status);
|
|
|
|
if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
|
|
dev->stats.rx_errors++;
|
|
if (rx_status & MACE_RCVFS_OFLO) {
|
|
lp->mace_stats.oflo++;
|
|
}
|
|
if (rx_status & MACE_RCVFS_CLSN) {
|
|
lp->mace_stats.clsn++;
|
|
}
|
|
if (rx_status & MACE_RCVFS_FRAM) {
|
|
lp->mace_stats.fram++;
|
|
}
|
|
if (rx_status & MACE_RCVFS_FCS) {
|
|
lp->mace_stats.fcs++;
|
|
}
|
|
} else {
|
|
short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
|
|
/* Auto Strip is off, always subtract 4 */
|
|
struct sk_buff *skb;
|
|
|
|
lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
|
|
/* runt packet count */
|
|
lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
|
|
/* rcv collision count */
|
|
|
|
pr_debug(" receiving packet size 0x%X rx_status"
|
|
" 0x%X.\n", pkt_len, rx_status);
|
|
|
|
skb = netdev_alloc_skb(dev, pkt_len + 2);
|
|
|
|
if (skb != NULL) {
|
|
skb_reserve(skb, 2);
|
|
insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
|
|
if (pkt_len & 1)
|
|
*(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
|
|
netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
|
|
|
|
dev->stats.rx_packets++;
|
|
dev->stats.rx_bytes += pkt_len;
|
|
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
|
|
continue;
|
|
} else {
|
|
pr_debug("%s: couldn't allocate a sk_buff of size"
|
|
" %d.\n", dev->name, pkt_len);
|
|
dev->stats.rx_dropped++;
|
|
}
|
|
}
|
|
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
|
|
} /* while */
|
|
|
|
return 0;
|
|
} /* mace_rx */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
pr_linux_stats
|
|
---------------------------------------------------------------------------- */
|
|
static void pr_linux_stats(struct net_device_stats *pstats)
|
|
{
|
|
pr_debug("pr_linux_stats\n");
|
|
pr_debug(" rx_packets=%-7ld tx_packets=%ld\n",
|
|
(long)pstats->rx_packets, (long)pstats->tx_packets);
|
|
pr_debug(" rx_errors=%-7ld tx_errors=%ld\n",
|
|
(long)pstats->rx_errors, (long)pstats->tx_errors);
|
|
pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n",
|
|
(long)pstats->rx_dropped, (long)pstats->tx_dropped);
|
|
pr_debug(" multicast=%-7ld collisions=%ld\n",
|
|
(long)pstats->multicast, (long)pstats->collisions);
|
|
|
|
pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n",
|
|
(long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
|
|
pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
|
|
(long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
|
|
pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
|
|
(long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
|
|
|
|
pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
|
|
(long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
|
|
pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
|
|
(long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
|
|
pr_debug(" tx_window_errors=%ld\n",
|
|
(long)pstats->tx_window_errors);
|
|
} /* pr_linux_stats */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
pr_mace_stats
|
|
---------------------------------------------------------------------------- */
|
|
static void pr_mace_stats(mace_statistics *pstats)
|
|
{
|
|
pr_debug("pr_mace_stats\n");
|
|
|
|
pr_debug(" xmtsv=%-7d uflo=%d\n",
|
|
pstats->xmtsv, pstats->uflo);
|
|
pr_debug(" lcol=%-7d more=%d\n",
|
|
pstats->lcol, pstats->more);
|
|
pr_debug(" one=%-7d defer=%d\n",
|
|
pstats->one, pstats->defer);
|
|
pr_debug(" lcar=%-7d rtry=%d\n",
|
|
pstats->lcar, pstats->rtry);
|
|
|
|
/* MACE_XMTRC */
|
|
pr_debug(" exdef=%-7d xmtrc=%d\n",
|
|
pstats->exdef, pstats->xmtrc);
|
|
|
|
/* RFS1--Receive Status (RCVSTS) */
|
|
pr_debug(" oflo=%-7d clsn=%d\n",
|
|
pstats->oflo, pstats->clsn);
|
|
pr_debug(" fram=%-7d fcs=%d\n",
|
|
pstats->fram, pstats->fcs);
|
|
|
|
/* RFS2--Runt Packet Count (RNTPC) */
|
|
/* RFS3--Receive Collision Count (RCVCC) */
|
|
pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n",
|
|
pstats->rfs_rntpc, pstats->rfs_rcvcc);
|
|
|
|
/* MACE_IR */
|
|
pr_debug(" jab=%-7d babl=%d\n",
|
|
pstats->jab, pstats->babl);
|
|
pr_debug(" cerr=%-7d rcvcco=%d\n",
|
|
pstats->cerr, pstats->rcvcco);
|
|
pr_debug(" rntpco=%-7d mpco=%d\n",
|
|
pstats->rntpco, pstats->mpco);
|
|
|
|
/* MACE_MPC */
|
|
pr_debug(" mpc=%d\n", pstats->mpc);
|
|
|
|
/* MACE_RNTPC */
|
|
pr_debug(" rntpc=%d\n", pstats->rntpc);
|
|
|
|
/* MACE_RCVCC */
|
|
pr_debug(" rcvcc=%d\n", pstats->rcvcc);
|
|
|
|
} /* pr_mace_stats */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
update_stats
|
|
Update statistics. We change to register window 1, so this
|
|
should be run single-threaded if the device is active. This is
|
|
expected to be a rare operation, and it's simpler for the rest
|
|
of the driver to assume that window 0 is always valid rather
|
|
than use a special window-state variable.
|
|
|
|
oflo & uflo should _never_ occur since it would mean the Xilinx
|
|
was not able to transfer data between the MACE FIFO and the
|
|
card's SRAM fast enough. If this happens, something is
|
|
seriously wrong with the hardware.
|
|
---------------------------------------------------------------------------- */
|
|
static void update_stats(unsigned int ioaddr, struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
|
|
lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
|
|
lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
|
|
lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
|
|
/* At this point, mace_stats is fully updated for this call.
|
|
We may now update the netdev stats. */
|
|
|
|
/* The MACE has no equivalent for netdev stats field which are commented
|
|
out. */
|
|
|
|
/* dev->stats.multicast; */
|
|
dev->stats.collisions =
|
|
lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
|
|
/* Collision: The MACE may retry sending a packet 15 times
|
|
before giving up. The retry count is in XMTRC.
|
|
Does each retry constitute a collision?
|
|
If so, why doesn't the RCVCC record these collisions? */
|
|
|
|
/* detailed rx_errors: */
|
|
dev->stats.rx_length_errors =
|
|
lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
|
|
/* dev->stats.rx_over_errors */
|
|
dev->stats.rx_crc_errors = lp->mace_stats.fcs;
|
|
dev->stats.rx_frame_errors = lp->mace_stats.fram;
|
|
dev->stats.rx_fifo_errors = lp->mace_stats.oflo;
|
|
dev->stats.rx_missed_errors =
|
|
lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
|
|
|
|
/* detailed tx_errors */
|
|
dev->stats.tx_aborted_errors = lp->mace_stats.rtry;
|
|
dev->stats.tx_carrier_errors = lp->mace_stats.lcar;
|
|
/* LCAR usually results from bad cabling. */
|
|
dev->stats.tx_fifo_errors = lp->mace_stats.uflo;
|
|
dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr;
|
|
/* dev->stats.tx_window_errors; */
|
|
} /* update_stats */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
mace_get_stats
|
|
Gathers ethernet statistics from the MACE chip.
|
|
---------------------------------------------------------------------------- */
|
|
static struct net_device_stats *mace_get_stats(struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
|
|
update_stats(dev->base_addr, dev);
|
|
|
|
pr_debug("%s: updating the statistics.\n", dev->name);
|
|
pr_linux_stats(&dev->stats);
|
|
pr_mace_stats(&lp->mace_stats);
|
|
|
|
return &dev->stats;
|
|
} /* net_device_stats */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
updateCRC
|
|
Modified from Am79C90 data sheet.
|
|
---------------------------------------------------------------------------- */
|
|
|
|
#ifdef BROKEN_MULTICAST
|
|
|
|
static void updateCRC(int *CRC, int bit)
|
|
{
|
|
static const int poly[]={
|
|
1,1,1,0, 1,1,0,1,
|
|
1,0,1,1, 1,0,0,0,
|
|
1,0,0,0, 0,0,1,1,
|
|
0,0,1,0, 0,0,0,0
|
|
}; /* CRC polynomial. poly[n] = coefficient of the x**n term of the
|
|
CRC generator polynomial. */
|
|
|
|
int j;
|
|
|
|
/* shift CRC and control bit (CRC[32]) */
|
|
for (j = 32; j > 0; j--)
|
|
CRC[j] = CRC[j-1];
|
|
CRC[0] = 0;
|
|
|
|
/* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
|
|
if (bit ^ CRC[32])
|
|
for (j = 0; j < 32; j++)
|
|
CRC[j] ^= poly[j];
|
|
} /* updateCRC */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
BuildLAF
|
|
Build logical address filter.
|
|
Modified from Am79C90 data sheet.
|
|
|
|
Input
|
|
ladrf: logical address filter (contents initialized to 0)
|
|
adr: ethernet address
|
|
---------------------------------------------------------------------------- */
|
|
static void BuildLAF(int *ladrf, int *adr)
|
|
{
|
|
int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
|
|
|
|
int i, byte; /* temporary array indices */
|
|
int hashcode; /* the output object */
|
|
|
|
CRC[32]=0;
|
|
|
|
for (byte = 0; byte < 6; byte++)
|
|
for (i = 0; i < 8; i++)
|
|
updateCRC(CRC, (adr[byte] >> i) & 1);
|
|
|
|
hashcode = 0;
|
|
for (i = 0; i < 6; i++)
|
|
hashcode = (hashcode << 1) + CRC[i];
|
|
|
|
byte = hashcode >> 3;
|
|
ladrf[byte] |= (1 << (hashcode & 7));
|
|
|
|
#ifdef PCMCIA_DEBUG
|
|
if (0)
|
|
printk(KERN_DEBUG " adr =%pM\n", adr);
|
|
printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode);
|
|
for (i = 0; i < 8; i++)
|
|
pr_cont(" %02X", ladrf[i]);
|
|
pr_cont("\n");
|
|
#endif
|
|
} /* BuildLAF */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
restore_multicast_list
|
|
Restores the multicast filter for MACE chip to the last
|
|
set_multicast_list() call.
|
|
|
|
Input
|
|
multicast_num_addrs
|
|
multicast_ladrf[]
|
|
---------------------------------------------------------------------------- */
|
|
static void restore_multicast_list(struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
int num_addrs = lp->multicast_num_addrs;
|
|
int *ladrf = lp->multicast_ladrf;
|
|
unsigned int ioaddr = dev->base_addr;
|
|
int i;
|
|
|
|
pr_debug("%s: restoring Rx mode to %d addresses.\n",
|
|
dev->name, num_addrs);
|
|
|
|
if (num_addrs > 0) {
|
|
|
|
pr_debug("Attempt to restore multicast list detected.\n");
|
|
|
|
mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
|
|
/* Poll ADDRCHG bit */
|
|
while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
|
|
;
|
|
/* Set LADRF register */
|
|
for (i = 0; i < MACE_LADRF_LEN; i++)
|
|
mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
|
|
|
|
mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
|
|
mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
|
|
|
|
} else if (num_addrs < 0) {
|
|
|
|
/* Promiscuous mode: receive all packets */
|
|
mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
|
|
mace_write(lp, ioaddr, MACE_MACCC,
|
|
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
|
|
);
|
|
|
|
} else {
|
|
|
|
/* Normal mode */
|
|
mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
|
|
mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
|
|
|
|
}
|
|
} /* restore_multicast_list */
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
set_multicast_list
|
|
Set or clear the multicast filter for this adaptor.
|
|
|
|
Input
|
|
num_addrs == -1 Promiscuous mode, receive all packets
|
|
num_addrs == 0 Normal mode, clear multicast list
|
|
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
|
|
best-effort filtering.
|
|
Output
|
|
multicast_num_addrs
|
|
multicast_ladrf[]
|
|
---------------------------------------------------------------------------- */
|
|
|
|
static void set_multicast_list(struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
int adr[ETH_ALEN] = {0}; /* Ethernet address */
|
|
struct netdev_hw_addr *ha;
|
|
|
|
#ifdef PCMCIA_DEBUG
|
|
{
|
|
static int old;
|
|
if (netdev_mc_count(dev) != old) {
|
|
old = netdev_mc_count(dev);
|
|
pr_debug("%s: setting Rx mode to %d addresses.\n",
|
|
dev->name, old);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Set multicast_num_addrs. */
|
|
lp->multicast_num_addrs = netdev_mc_count(dev);
|
|
|
|
/* Set multicast_ladrf. */
|
|
if (num_addrs > 0) {
|
|
/* Calculate multicast logical address filter */
|
|
memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
|
|
netdev_for_each_mc_addr(ha, dev) {
|
|
memcpy(adr, ha->addr, ETH_ALEN);
|
|
BuildLAF(lp->multicast_ladrf, adr);
|
|
}
|
|
}
|
|
|
|
restore_multicast_list(dev);
|
|
|
|
} /* set_multicast_list */
|
|
|
|
#endif /* BROKEN_MULTICAST */
|
|
|
|
static void restore_multicast_list(struct net_device *dev)
|
|
{
|
|
unsigned int ioaddr = dev->base_addr;
|
|
mace_private *lp = netdev_priv(dev);
|
|
|
|
pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name,
|
|
lp->multicast_num_addrs);
|
|
|
|
if (dev->flags & IFF_PROMISC) {
|
|
/* Promiscuous mode: receive all packets */
|
|
mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
|
|
mace_write(lp, ioaddr, MACE_MACCC,
|
|
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
|
|
);
|
|
} else {
|
|
/* Normal mode */
|
|
mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
|
|
mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
|
|
}
|
|
} /* restore_multicast_list */
|
|
|
|
static void set_multicast_list(struct net_device *dev)
|
|
{
|
|
mace_private *lp = netdev_priv(dev);
|
|
|
|
#ifdef PCMCIA_DEBUG
|
|
{
|
|
static int old;
|
|
if (netdev_mc_count(dev) != old) {
|
|
old = netdev_mc_count(dev);
|
|
pr_debug("%s: setting Rx mode to %d addresses.\n",
|
|
dev->name, old);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
lp->multicast_num_addrs = netdev_mc_count(dev);
|
|
restore_multicast_list(dev);
|
|
|
|
} /* set_multicast_list */
|
|
|
|
static const struct pcmcia_device_id nmclan_ids[] = {
|
|
PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941),
|
|
PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf),
|
|
PCMCIA_DEVICE_NULL,
|
|
};
|
|
MODULE_DEVICE_TABLE(pcmcia, nmclan_ids);
|
|
|
|
static struct pcmcia_driver nmclan_cs_driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "nmclan_cs",
|
|
.probe = nmclan_probe,
|
|
.remove = nmclan_detach,
|
|
.id_table = nmclan_ids,
|
|
.suspend = nmclan_suspend,
|
|
.resume = nmclan_resume,
|
|
};
|
|
module_pcmcia_driver(nmclan_cs_driver);
|