forked from Minki/linux
0290bd291c
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>
1651 lines
45 KiB
C
1651 lines
45 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Microchip ENC28J60 ethernet driver (MAC + PHY)
|
|
*
|
|
* Copyright (C) 2007 Eurek srl
|
|
* Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
|
|
* based on enc28j60.c written by David Anders for 2.4 kernel version
|
|
*
|
|
* $Id: enc28j60.c,v 1.22 2007/12/20 10:47:01 claudio Exp $
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/property.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/spi/spi.h>
|
|
|
|
#include "enc28j60_hw.h"
|
|
|
|
#define DRV_NAME "enc28j60"
|
|
#define DRV_VERSION "1.02"
|
|
|
|
#define SPI_OPLEN 1
|
|
|
|
#define ENC28J60_MSG_DEFAULT \
|
|
(NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
|
|
|
|
/* Buffer size required for the largest SPI transfer (i.e., reading a
|
|
* frame).
|
|
*/
|
|
#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN)
|
|
|
|
#define TX_TIMEOUT (4 * HZ)
|
|
|
|
/* Max TX retries in case of collision as suggested by errata datasheet */
|
|
#define MAX_TX_RETRYCOUNT 16
|
|
|
|
enum {
|
|
RXFILTER_NORMAL,
|
|
RXFILTER_MULTI,
|
|
RXFILTER_PROMISC
|
|
};
|
|
|
|
/* Driver local data */
|
|
struct enc28j60_net {
|
|
struct net_device *netdev;
|
|
struct spi_device *spi;
|
|
struct mutex lock;
|
|
struct sk_buff *tx_skb;
|
|
struct work_struct tx_work;
|
|
struct work_struct irq_work;
|
|
struct work_struct setrx_work;
|
|
struct work_struct restart_work;
|
|
u8 bank; /* current register bank selected */
|
|
u16 next_pk_ptr; /* next packet pointer within FIFO */
|
|
u16 max_pk_counter; /* statistics: max packet counter */
|
|
u16 tx_retry_count;
|
|
bool hw_enable;
|
|
bool full_duplex;
|
|
int rxfilter;
|
|
u32 msg_enable;
|
|
u8 spi_transfer_buf[SPI_TRANSFER_BUF_LEN];
|
|
};
|
|
|
|
/* use ethtool to change the level for any given device */
|
|
static struct {
|
|
u32 msg_enable;
|
|
} debug = { -1 };
|
|
|
|
/*
|
|
* SPI read buffer
|
|
* Wait for the SPI transfer and copy received data to destination.
|
|
*/
|
|
static int
|
|
spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
u8 *rx_buf = priv->spi_transfer_buf + 4;
|
|
u8 *tx_buf = priv->spi_transfer_buf;
|
|
struct spi_transfer tx = {
|
|
.tx_buf = tx_buf,
|
|
.len = SPI_OPLEN,
|
|
};
|
|
struct spi_transfer rx = {
|
|
.rx_buf = rx_buf,
|
|
.len = len,
|
|
};
|
|
struct spi_message msg;
|
|
int ret;
|
|
|
|
tx_buf[0] = ENC28J60_READ_BUF_MEM;
|
|
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&tx, &msg);
|
|
spi_message_add_tail(&rx, &msg);
|
|
|
|
ret = spi_sync(priv->spi, &msg);
|
|
if (ret == 0) {
|
|
memcpy(data, rx_buf, len);
|
|
ret = msg.status;
|
|
}
|
|
if (ret && netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() failed: ret = %d\n",
|
|
__func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* SPI write buffer
|
|
*/
|
|
static int spi_write_buf(struct enc28j60_net *priv, int len, const u8 *data)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
int ret;
|
|
|
|
if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
|
|
ret = -EINVAL;
|
|
else {
|
|
priv->spi_transfer_buf[0] = ENC28J60_WRITE_BUF_MEM;
|
|
memcpy(&priv->spi_transfer_buf[1], data, len);
|
|
ret = spi_write(priv->spi, priv->spi_transfer_buf, len + 1);
|
|
if (ret && netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() failed: ret = %d\n",
|
|
__func__, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* basic SPI read operation
|
|
*/
|
|
static u8 spi_read_op(struct enc28j60_net *priv, u8 op, u8 addr)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
u8 tx_buf[2];
|
|
u8 rx_buf[4];
|
|
u8 val = 0;
|
|
int ret;
|
|
int slen = SPI_OPLEN;
|
|
|
|
/* do dummy read if needed */
|
|
if (addr & SPRD_MASK)
|
|
slen++;
|
|
|
|
tx_buf[0] = op | (addr & ADDR_MASK);
|
|
ret = spi_write_then_read(priv->spi, tx_buf, 1, rx_buf, slen);
|
|
if (ret)
|
|
dev_printk(KERN_DEBUG, dev, "%s() failed: ret = %d\n",
|
|
__func__, ret);
|
|
else
|
|
val = rx_buf[slen - 1];
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* basic SPI write operation
|
|
*/
|
|
static int spi_write_op(struct enc28j60_net *priv, u8 op, u8 addr, u8 val)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
int ret;
|
|
|
|
priv->spi_transfer_buf[0] = op | (addr & ADDR_MASK);
|
|
priv->spi_transfer_buf[1] = val;
|
|
ret = spi_write(priv->spi, priv->spi_transfer_buf, 2);
|
|
if (ret && netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() failed: ret = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void enc28j60_soft_reset(struct enc28j60_net *priv)
|
|
{
|
|
spi_write_op(priv, ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
|
|
/* Errata workaround #1, CLKRDY check is unreliable,
|
|
* delay at least 1 ms instead */
|
|
udelay(2000);
|
|
}
|
|
|
|
/*
|
|
* select the current register bank if necessary
|
|
*/
|
|
static void enc28j60_set_bank(struct enc28j60_net *priv, u8 addr)
|
|
{
|
|
u8 b = (addr & BANK_MASK) >> 5;
|
|
|
|
/* These registers (EIE, EIR, ESTAT, ECON2, ECON1)
|
|
* are present in all banks, no need to switch bank.
|
|
*/
|
|
if (addr >= EIE && addr <= ECON1)
|
|
return;
|
|
|
|
/* Clear or set each bank selection bit as needed */
|
|
if ((b & ECON1_BSEL0) != (priv->bank & ECON1_BSEL0)) {
|
|
if (b & ECON1_BSEL0)
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
|
|
ECON1_BSEL0);
|
|
else
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
|
|
ECON1_BSEL0);
|
|
}
|
|
if ((b & ECON1_BSEL1) != (priv->bank & ECON1_BSEL1)) {
|
|
if (b & ECON1_BSEL1)
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
|
|
ECON1_BSEL1);
|
|
else
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
|
|
ECON1_BSEL1);
|
|
}
|
|
priv->bank = b;
|
|
}
|
|
|
|
/*
|
|
* Register access routines through the SPI bus.
|
|
* Every register access comes in two flavours:
|
|
* - nolock_xxx: caller needs to invoke mutex_lock, usually to access
|
|
* atomically more than one register
|
|
* - locked_xxx: caller doesn't need to invoke mutex_lock, single access
|
|
*
|
|
* Some registers can be accessed through the bit field clear and
|
|
* bit field set to avoid a read modify write cycle.
|
|
*/
|
|
|
|
/*
|
|
* Register bit field Set
|
|
*/
|
|
static void nolock_reg_bfset(struct enc28j60_net *priv, u8 addr, u8 mask)
|
|
{
|
|
enc28j60_set_bank(priv, addr);
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_SET, addr, mask);
|
|
}
|
|
|
|
static void locked_reg_bfset(struct enc28j60_net *priv, u8 addr, u8 mask)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
nolock_reg_bfset(priv, addr, mask);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Register bit field Clear
|
|
*/
|
|
static void nolock_reg_bfclr(struct enc28j60_net *priv, u8 addr, u8 mask)
|
|
{
|
|
enc28j60_set_bank(priv, addr);
|
|
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, addr, mask);
|
|
}
|
|
|
|
static void locked_reg_bfclr(struct enc28j60_net *priv, u8 addr, u8 mask)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
nolock_reg_bfclr(priv, addr, mask);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Register byte read
|
|
*/
|
|
static int nolock_regb_read(struct enc28j60_net *priv, u8 address)
|
|
{
|
|
enc28j60_set_bank(priv, address);
|
|
return spi_read_op(priv, ENC28J60_READ_CTRL_REG, address);
|
|
}
|
|
|
|
static int locked_regb_read(struct enc28j60_net *priv, u8 address)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&priv->lock);
|
|
ret = nolock_regb_read(priv, address);
|
|
mutex_unlock(&priv->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Register word read
|
|
*/
|
|
static int nolock_regw_read(struct enc28j60_net *priv, u8 address)
|
|
{
|
|
int rl, rh;
|
|
|
|
enc28j60_set_bank(priv, address);
|
|
rl = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address);
|
|
rh = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address + 1);
|
|
|
|
return (rh << 8) | rl;
|
|
}
|
|
|
|
static int locked_regw_read(struct enc28j60_net *priv, u8 address)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&priv->lock);
|
|
ret = nolock_regw_read(priv, address);
|
|
mutex_unlock(&priv->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Register byte write
|
|
*/
|
|
static void nolock_regb_write(struct enc28j60_net *priv, u8 address, u8 data)
|
|
{
|
|
enc28j60_set_bank(priv, address);
|
|
spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, data);
|
|
}
|
|
|
|
static void locked_regb_write(struct enc28j60_net *priv, u8 address, u8 data)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
nolock_regb_write(priv, address, data);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Register word write
|
|
*/
|
|
static void nolock_regw_write(struct enc28j60_net *priv, u8 address, u16 data)
|
|
{
|
|
enc28j60_set_bank(priv, address);
|
|
spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, (u8) data);
|
|
spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address + 1,
|
|
(u8) (data >> 8));
|
|
}
|
|
|
|
static void locked_regw_write(struct enc28j60_net *priv, u8 address, u16 data)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
nolock_regw_write(priv, address, data);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Buffer memory read
|
|
* Select the starting address and execute a SPI buffer read.
|
|
*/
|
|
static void enc28j60_mem_read(struct enc28j60_net *priv, u16 addr, int len,
|
|
u8 *data)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
nolock_regw_write(priv, ERDPTL, addr);
|
|
#ifdef CONFIG_ENC28J60_WRITEVERIFY
|
|
if (netif_msg_drv(priv)) {
|
|
struct device *dev = &priv->spi->dev;
|
|
u16 reg;
|
|
|
|
reg = nolock_regw_read(priv, ERDPTL);
|
|
if (reg != addr)
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() error writing ERDPT (0x%04x - 0x%04x)\n",
|
|
__func__, reg, addr);
|
|
}
|
|
#endif
|
|
spi_read_buf(priv, len, data);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Write packet to enc28j60 TX buffer memory
|
|
*/
|
|
static void
|
|
enc28j60_packet_write(struct enc28j60_net *priv, int len, const u8 *data)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
mutex_lock(&priv->lock);
|
|
/* Set the write pointer to start of transmit buffer area */
|
|
nolock_regw_write(priv, EWRPTL, TXSTART_INIT);
|
|
#ifdef CONFIG_ENC28J60_WRITEVERIFY
|
|
if (netif_msg_drv(priv)) {
|
|
u16 reg;
|
|
reg = nolock_regw_read(priv, EWRPTL);
|
|
if (reg != TXSTART_INIT)
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() ERWPT:0x%04x != 0x%04x\n",
|
|
__func__, reg, TXSTART_INIT);
|
|
}
|
|
#endif
|
|
/* Set the TXND pointer to correspond to the packet size given */
|
|
nolock_regw_write(priv, ETXNDL, TXSTART_INIT + len);
|
|
/* write per-packet control byte */
|
|
spi_write_op(priv, ENC28J60_WRITE_BUF_MEM, 0, 0x00);
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() after control byte ERWPT:0x%04x\n",
|
|
__func__, nolock_regw_read(priv, EWRPTL));
|
|
/* copy the packet into the transmit buffer */
|
|
spi_write_buf(priv, len, data);
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() after write packet ERWPT:0x%04x, len=%d\n",
|
|
__func__, nolock_regw_read(priv, EWRPTL), len);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
unsigned long timeout = jiffies + msecs_to_jiffies(20);
|
|
|
|
/* 20 msec timeout read */
|
|
while ((nolock_regb_read(priv, reg) & mask) != val) {
|
|
if (time_after(jiffies, timeout)) {
|
|
if (netif_msg_drv(priv))
|
|
dev_dbg(dev, "reg %02x ready timeout!\n", reg);
|
|
return -ETIMEDOUT;
|
|
}
|
|
cpu_relax();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Wait until the PHY operation is complete.
|
|
*/
|
|
static int wait_phy_ready(struct enc28j60_net *priv)
|
|
{
|
|
return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1;
|
|
}
|
|
|
|
/*
|
|
* PHY register read
|
|
* PHY registers are not accessed directly, but through the MII.
|
|
*/
|
|
static u16 enc28j60_phy_read(struct enc28j60_net *priv, u8 address)
|
|
{
|
|
u16 ret;
|
|
|
|
mutex_lock(&priv->lock);
|
|
/* set the PHY register address */
|
|
nolock_regb_write(priv, MIREGADR, address);
|
|
/* start the register read operation */
|
|
nolock_regb_write(priv, MICMD, MICMD_MIIRD);
|
|
/* wait until the PHY read completes */
|
|
wait_phy_ready(priv);
|
|
/* quit reading */
|
|
nolock_regb_write(priv, MICMD, 0x00);
|
|
/* return the data */
|
|
ret = nolock_regw_read(priv, MIRDL);
|
|
mutex_unlock(&priv->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int enc28j60_phy_write(struct enc28j60_net *priv, u8 address, u16 data)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&priv->lock);
|
|
/* set the PHY register address */
|
|
nolock_regb_write(priv, MIREGADR, address);
|
|
/* write the PHY data */
|
|
nolock_regw_write(priv, MIWRL, data);
|
|
/* wait until the PHY write completes and return */
|
|
ret = wait_phy_ready(priv);
|
|
mutex_unlock(&priv->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Program the hardware MAC address from dev->dev_addr.
|
|
*/
|
|
static int enc28j60_set_hw_macaddr(struct net_device *ndev)
|
|
{
|
|
int ret;
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
mutex_lock(&priv->lock);
|
|
if (!priv->hw_enable) {
|
|
if (netif_msg_drv(priv))
|
|
dev_info(dev, "%s: Setting MAC address to %pM\n",
|
|
ndev->name, ndev->dev_addr);
|
|
/* NOTE: MAC address in ENC28J60 is byte-backward */
|
|
nolock_regb_write(priv, MAADR5, ndev->dev_addr[0]);
|
|
nolock_regb_write(priv, MAADR4, ndev->dev_addr[1]);
|
|
nolock_regb_write(priv, MAADR3, ndev->dev_addr[2]);
|
|
nolock_regb_write(priv, MAADR2, ndev->dev_addr[3]);
|
|
nolock_regb_write(priv, MAADR1, ndev->dev_addr[4]);
|
|
nolock_regb_write(priv, MAADR0, ndev->dev_addr[5]);
|
|
ret = 0;
|
|
} else {
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() Hardware must be disabled to set Mac address\n",
|
|
__func__);
|
|
ret = -EBUSY;
|
|
}
|
|
mutex_unlock(&priv->lock);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Store the new hardware address in dev->dev_addr, and update the MAC.
|
|
*/
|
|
static int enc28j60_set_mac_address(struct net_device *dev, void *addr)
|
|
{
|
|
struct sockaddr *address = addr;
|
|
|
|
if (netif_running(dev))
|
|
return -EBUSY;
|
|
if (!is_valid_ether_addr(address->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
|
|
ether_addr_copy(dev->dev_addr, address->sa_data);
|
|
return enc28j60_set_hw_macaddr(dev);
|
|
}
|
|
|
|
/*
|
|
* Debug routine to dump useful register contents
|
|
*/
|
|
static void enc28j60_dump_regs(struct enc28j60_net *priv, const char *msg)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
mutex_lock(&priv->lock);
|
|
dev_printk(KERN_DEBUG, dev,
|
|
" %s\n"
|
|
"HwRevID: 0x%02x\n"
|
|
"Cntrl: ECON1 ECON2 ESTAT EIR EIE\n"
|
|
" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n"
|
|
"MAC : MACON1 MACON3 MACON4\n"
|
|
" 0x%02x 0x%02x 0x%02x\n"
|
|
"Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\n"
|
|
" 0x%04x 0x%04x 0x%04x 0x%04x "
|
|
"0x%02x 0x%02x 0x%04x\n"
|
|
"Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\n"
|
|
" 0x%04x 0x%04x 0x%02x 0x%02x 0x%02x\n",
|
|
msg, nolock_regb_read(priv, EREVID),
|
|
nolock_regb_read(priv, ECON1), nolock_regb_read(priv, ECON2),
|
|
nolock_regb_read(priv, ESTAT), nolock_regb_read(priv, EIR),
|
|
nolock_regb_read(priv, EIE), nolock_regb_read(priv, MACON1),
|
|
nolock_regb_read(priv, MACON3), nolock_regb_read(priv, MACON4),
|
|
nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXNDL),
|
|
nolock_regw_read(priv, ERXWRPTL),
|
|
nolock_regw_read(priv, ERXRDPTL),
|
|
nolock_regb_read(priv, ERXFCON),
|
|
nolock_regb_read(priv, EPKTCNT),
|
|
nolock_regw_read(priv, MAMXFLL), nolock_regw_read(priv, ETXSTL),
|
|
nolock_regw_read(priv, ETXNDL),
|
|
nolock_regb_read(priv, MACLCON1),
|
|
nolock_regb_read(priv, MACLCON2),
|
|
nolock_regb_read(priv, MAPHSUP));
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* ERXRDPT need to be set always at odd addresses, refer to errata datasheet
|
|
*/
|
|
static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end)
|
|
{
|
|
u16 erxrdpt;
|
|
|
|
if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end))
|
|
erxrdpt = end;
|
|
else
|
|
erxrdpt = next_packet_ptr - 1;
|
|
|
|
return erxrdpt;
|
|
}
|
|
|
|
/*
|
|
* Calculate wrap around when reading beyond the end of the RX buffer
|
|
*/
|
|
static u16 rx_packet_start(u16 ptr)
|
|
{
|
|
if (ptr + RSV_SIZE > RXEND_INIT)
|
|
return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART_INIT + 1);
|
|
else
|
|
return ptr + RSV_SIZE;
|
|
}
|
|
|
|
static void nolock_rxfifo_init(struct enc28j60_net *priv, u16 start, u16 end)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
u16 erxrdpt;
|
|
|
|
if (start > 0x1FFF || end > 0x1FFF || start > end) {
|
|
if (netif_msg_drv(priv))
|
|
dev_err(dev, "%s(%d, %d) RXFIFO bad parameters!\n",
|
|
__func__, start, end);
|
|
return;
|
|
}
|
|
/* set receive buffer start + end */
|
|
priv->next_pk_ptr = start;
|
|
nolock_regw_write(priv, ERXSTL, start);
|
|
erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end);
|
|
nolock_regw_write(priv, ERXRDPTL, erxrdpt);
|
|
nolock_regw_write(priv, ERXNDL, end);
|
|
}
|
|
|
|
static void nolock_txfifo_init(struct enc28j60_net *priv, u16 start, u16 end)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
if (start > 0x1FFF || end > 0x1FFF || start > end) {
|
|
if (netif_msg_drv(priv))
|
|
dev_err(dev, "%s(%d, %d) TXFIFO bad parameters!\n",
|
|
__func__, start, end);
|
|
return;
|
|
}
|
|
/* set transmit buffer start + end */
|
|
nolock_regw_write(priv, ETXSTL, start);
|
|
nolock_regw_write(priv, ETXNDL, end);
|
|
}
|
|
|
|
/*
|
|
* Low power mode shrinks power consumption about 100x, so we'd like
|
|
* the chip to be in that mode whenever it's inactive. (However, we
|
|
* can't stay in low power mode during suspend with WOL active.)
|
|
*/
|
|
static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
if (netif_msg_drv(priv))
|
|
dev_dbg(dev, "%s power...\n", is_low ? "low" : "high");
|
|
|
|
mutex_lock(&priv->lock);
|
|
if (is_low) {
|
|
nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
|
|
poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0);
|
|
poll_ready(priv, ECON1, ECON1_TXRTS, 0);
|
|
/* ECON2_VRPS was set during initialization */
|
|
nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
|
|
} else {
|
|
nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
|
|
poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
|
|
/* caller sets ECON1_RXEN */
|
|
}
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
static int enc28j60_hw_init(struct enc28j60_net *priv)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
u8 reg;
|
|
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() - %s\n", __func__,
|
|
priv->full_duplex ? "FullDuplex" : "HalfDuplex");
|
|
|
|
mutex_lock(&priv->lock);
|
|
/* first reset the chip */
|
|
enc28j60_soft_reset(priv);
|
|
/* Clear ECON1 */
|
|
spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, ECON1, 0x00);
|
|
priv->bank = 0;
|
|
priv->hw_enable = false;
|
|
priv->tx_retry_count = 0;
|
|
priv->max_pk_counter = 0;
|
|
priv->rxfilter = RXFILTER_NORMAL;
|
|
/* enable address auto increment and voltage regulator powersave */
|
|
nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
|
|
|
|
nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
|
|
nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
|
|
mutex_unlock(&priv->lock);
|
|
|
|
/*
|
|
* Check the RevID.
|
|
* If it's 0x00 or 0xFF probably the enc28j60 is not mounted or
|
|
* damaged.
|
|
*/
|
|
reg = locked_regb_read(priv, EREVID);
|
|
if (netif_msg_drv(priv))
|
|
dev_info(dev, "chip RevID: 0x%02x\n", reg);
|
|
if (reg == 0x00 || reg == 0xff) {
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() Invalid RevId %d\n",
|
|
__func__, reg);
|
|
return 0;
|
|
}
|
|
|
|
/* default filter mode: (unicast OR broadcast) AND crc valid */
|
|
locked_regb_write(priv, ERXFCON,
|
|
ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
|
|
|
|
/* enable MAC receive */
|
|
locked_regb_write(priv, MACON1,
|
|
MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
|
|
/* enable automatic padding and CRC operations */
|
|
if (priv->full_duplex) {
|
|
locked_regb_write(priv, MACON3,
|
|
MACON3_PADCFG0 | MACON3_TXCRCEN |
|
|
MACON3_FRMLNEN | MACON3_FULDPX);
|
|
/* set inter-frame gap (non-back-to-back) */
|
|
locked_regb_write(priv, MAIPGL, 0x12);
|
|
/* set inter-frame gap (back-to-back) */
|
|
locked_regb_write(priv, MABBIPG, 0x15);
|
|
} else {
|
|
locked_regb_write(priv, MACON3,
|
|
MACON3_PADCFG0 | MACON3_TXCRCEN |
|
|
MACON3_FRMLNEN);
|
|
locked_regb_write(priv, MACON4, 1 << 6); /* DEFER bit */
|
|
/* set inter-frame gap (non-back-to-back) */
|
|
locked_regw_write(priv, MAIPGL, 0x0C12);
|
|
/* set inter-frame gap (back-to-back) */
|
|
locked_regb_write(priv, MABBIPG, 0x12);
|
|
}
|
|
/*
|
|
* MACLCON1 (default)
|
|
* MACLCON2 (default)
|
|
* Set the maximum packet size which the controller will accept.
|
|
*/
|
|
locked_regw_write(priv, MAMXFLL, MAX_FRAMELEN);
|
|
|
|
/* Configure LEDs */
|
|
if (!enc28j60_phy_write(priv, PHLCON, ENC28J60_LAMPS_MODE))
|
|
return 0;
|
|
|
|
if (priv->full_duplex) {
|
|
if (!enc28j60_phy_write(priv, PHCON1, PHCON1_PDPXMD))
|
|
return 0;
|
|
if (!enc28j60_phy_write(priv, PHCON2, 0x00))
|
|
return 0;
|
|
} else {
|
|
if (!enc28j60_phy_write(priv, PHCON1, 0x00))
|
|
return 0;
|
|
if (!enc28j60_phy_write(priv, PHCON2, PHCON2_HDLDIS))
|
|
return 0;
|
|
}
|
|
if (netif_msg_hw(priv))
|
|
enc28j60_dump_regs(priv, "Hw initialized.");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void enc28j60_hw_enable(struct enc28j60_net *priv)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
/* enable interrupts */
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() enabling interrupts.\n",
|
|
__func__);
|
|
|
|
enc28j60_phy_write(priv, PHIE, PHIE_PGEIE | PHIE_PLNKIE);
|
|
|
|
mutex_lock(&priv->lock);
|
|
nolock_reg_bfclr(priv, EIR, EIR_DMAIF | EIR_LINKIF |
|
|
EIR_TXIF | EIR_TXERIF | EIR_RXERIF | EIR_PKTIF);
|
|
nolock_regb_write(priv, EIE, EIE_INTIE | EIE_PKTIE | EIE_LINKIE |
|
|
EIE_TXIE | EIE_TXERIE | EIE_RXERIE);
|
|
|
|
/* enable receive logic */
|
|
nolock_reg_bfset(priv, ECON1, ECON1_RXEN);
|
|
priv->hw_enable = true;
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
static void enc28j60_hw_disable(struct enc28j60_net *priv)
|
|
{
|
|
mutex_lock(&priv->lock);
|
|
/* disable interrupts and packet reception */
|
|
nolock_regb_write(priv, EIE, 0x00);
|
|
nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
|
|
priv->hw_enable = false;
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
static int
|
|
enc28j60_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
int ret = 0;
|
|
|
|
if (!priv->hw_enable) {
|
|
/* link is in low power mode now; duplex setting
|
|
* will take effect on next enc28j60_hw_init().
|
|
*/
|
|
if (autoneg == AUTONEG_DISABLE && speed == SPEED_10)
|
|
priv->full_duplex = (duplex == DUPLEX_FULL);
|
|
else {
|
|
if (netif_msg_link(priv))
|
|
netdev_warn(ndev, "unsupported link setting\n");
|
|
ret = -EOPNOTSUPP;
|
|
}
|
|
} else {
|
|
if (netif_msg_link(priv))
|
|
netdev_warn(ndev, "Warning: hw must be disabled to set link mode\n");
|
|
ret = -EBUSY;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Read the Transmit Status Vector
|
|
*/
|
|
static void enc28j60_read_tsv(struct enc28j60_net *priv, u8 tsv[TSV_SIZE])
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
int endptr;
|
|
|
|
endptr = locked_regw_read(priv, ETXNDL);
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev, "reading TSV at addr:0x%04x\n",
|
|
endptr + 1);
|
|
enc28j60_mem_read(priv, endptr + 1, TSV_SIZE, tsv);
|
|
}
|
|
|
|
static void enc28j60_dump_tsv(struct enc28j60_net *priv, const char *msg,
|
|
u8 tsv[TSV_SIZE])
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
u16 tmp1, tmp2;
|
|
|
|
dev_printk(KERN_DEBUG, dev, "%s - TSV:\n", msg);
|
|
tmp1 = tsv[1];
|
|
tmp1 <<= 8;
|
|
tmp1 |= tsv[0];
|
|
|
|
tmp2 = tsv[5];
|
|
tmp2 <<= 8;
|
|
tmp2 |= tsv[4];
|
|
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"ByteCount: %d, CollisionCount: %d, TotByteOnWire: %d\n",
|
|
tmp1, tsv[2] & 0x0f, tmp2);
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"TxDone: %d, CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
|
|
TSV_GETBIT(tsv, TSV_TXDONE),
|
|
TSV_GETBIT(tsv, TSV_TXCRCERROR),
|
|
TSV_GETBIT(tsv, TSV_TXLENCHKERROR),
|
|
TSV_GETBIT(tsv, TSV_TXLENOUTOFRANGE));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"Multicast: %d, Broadcast: %d, PacketDefer: %d, ExDefer: %d\n",
|
|
TSV_GETBIT(tsv, TSV_TXMULTICAST),
|
|
TSV_GETBIT(tsv, TSV_TXBROADCAST),
|
|
TSV_GETBIT(tsv, TSV_TXPACKETDEFER),
|
|
TSV_GETBIT(tsv, TSV_TXEXDEFER));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"ExCollision: %d, LateCollision: %d, Giant: %d, Underrun: %d\n",
|
|
TSV_GETBIT(tsv, TSV_TXEXCOLLISION),
|
|
TSV_GETBIT(tsv, TSV_TXLATECOLLISION),
|
|
TSV_GETBIT(tsv, TSV_TXGIANT), TSV_GETBIT(tsv, TSV_TXUNDERRUN));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"ControlFrame: %d, PauseFrame: %d, BackPressApp: %d, VLanTagFrame: %d\n",
|
|
TSV_GETBIT(tsv, TSV_TXCONTROLFRAME),
|
|
TSV_GETBIT(tsv, TSV_TXPAUSEFRAME),
|
|
TSV_GETBIT(tsv, TSV_BACKPRESSUREAPP),
|
|
TSV_GETBIT(tsv, TSV_TXVLANTAGFRAME));
|
|
}
|
|
|
|
/*
|
|
* Receive Status vector
|
|
*/
|
|
static void enc28j60_dump_rsv(struct enc28j60_net *priv, const char *msg,
|
|
u16 pk_ptr, int len, u16 sts)
|
|
{
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
dev_printk(KERN_DEBUG, dev, "%s - NextPk: 0x%04x - RSV:\n", msg, pk_ptr);
|
|
dev_printk(KERN_DEBUG, dev, "ByteCount: %d, DribbleNibble: %d\n",
|
|
len, RSV_GETBIT(sts, RSV_DRIBBLENIBBLE));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"RxOK: %d, CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
|
|
RSV_GETBIT(sts, RSV_RXOK),
|
|
RSV_GETBIT(sts, RSV_CRCERROR),
|
|
RSV_GETBIT(sts, RSV_LENCHECKERR),
|
|
RSV_GETBIT(sts, RSV_LENOUTOFRANGE));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n",
|
|
RSV_GETBIT(sts, RSV_RXMULTICAST),
|
|
RSV_GETBIT(sts, RSV_RXBROADCAST),
|
|
RSV_GETBIT(sts, RSV_RXLONGEVDROPEV),
|
|
RSV_GETBIT(sts, RSV_CARRIEREV));
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n",
|
|
RSV_GETBIT(sts, RSV_RXCONTROLFRAME),
|
|
RSV_GETBIT(sts, RSV_RXPAUSEFRAME),
|
|
RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE),
|
|
RSV_GETBIT(sts, RSV_RXTYPEVLAN));
|
|
}
|
|
|
|
static void dump_packet(const char *msg, int len, const char *data)
|
|
{
|
|
printk(KERN_DEBUG DRV_NAME ": %s - packet len:%d\n", msg, len);
|
|
print_hex_dump(KERN_DEBUG, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
data, len, true);
|
|
}
|
|
|
|
/*
|
|
* Hardware receive function.
|
|
* Read the buffer memory, update the FIFO pointer to free the buffer,
|
|
* check the status vector and decrement the packet counter.
|
|
*/
|
|
static void enc28j60_hw_rx(struct net_device *ndev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
struct device *dev = &priv->spi->dev;
|
|
struct sk_buff *skb = NULL;
|
|
u16 erxrdpt, next_packet, rxstat;
|
|
u8 rsv[RSV_SIZE];
|
|
int len;
|
|
|
|
if (netif_msg_rx_status(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "RX pk_addr:0x%04x\n",
|
|
priv->next_pk_ptr);
|
|
|
|
if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
|
|
if (netif_msg_rx_err(priv))
|
|
netdev_err(ndev, "%s() Invalid packet address!! 0x%04x\n",
|
|
__func__, priv->next_pk_ptr);
|
|
/* packet address corrupted: reset RX logic */
|
|
mutex_lock(&priv->lock);
|
|
nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
|
|
nolock_reg_bfset(priv, ECON1, ECON1_RXRST);
|
|
nolock_reg_bfclr(priv, ECON1, ECON1_RXRST);
|
|
nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
|
|
nolock_reg_bfclr(priv, EIR, EIR_RXERIF);
|
|
nolock_reg_bfset(priv, ECON1, ECON1_RXEN);
|
|
mutex_unlock(&priv->lock);
|
|
ndev->stats.rx_errors++;
|
|
return;
|
|
}
|
|
/* Read next packet pointer and rx status vector */
|
|
enc28j60_mem_read(priv, priv->next_pk_ptr, sizeof(rsv), rsv);
|
|
|
|
next_packet = rsv[1];
|
|
next_packet <<= 8;
|
|
next_packet |= rsv[0];
|
|
|
|
len = rsv[3];
|
|
len <<= 8;
|
|
len |= rsv[2];
|
|
|
|
rxstat = rsv[5];
|
|
rxstat <<= 8;
|
|
rxstat |= rsv[4];
|
|
|
|
if (netif_msg_rx_status(priv))
|
|
enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat);
|
|
|
|
if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
|
|
if (netif_msg_rx_err(priv))
|
|
netdev_err(ndev, "Rx Error (%04x)\n", rxstat);
|
|
ndev->stats.rx_errors++;
|
|
if (RSV_GETBIT(rxstat, RSV_CRCERROR))
|
|
ndev->stats.rx_crc_errors++;
|
|
if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
|
|
ndev->stats.rx_frame_errors++;
|
|
if (len > MAX_FRAMELEN)
|
|
ndev->stats.rx_over_errors++;
|
|
} else {
|
|
skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
|
|
if (!skb) {
|
|
if (netif_msg_rx_err(priv))
|
|
netdev_err(ndev, "out of memory for Rx'd frame\n");
|
|
ndev->stats.rx_dropped++;
|
|
} else {
|
|
skb_reserve(skb, NET_IP_ALIGN);
|
|
/* copy the packet from the receive buffer */
|
|
enc28j60_mem_read(priv,
|
|
rx_packet_start(priv->next_pk_ptr),
|
|
len, skb_put(skb, len));
|
|
if (netif_msg_pktdata(priv))
|
|
dump_packet(__func__, skb->len, skb->data);
|
|
skb->protocol = eth_type_trans(skb, ndev);
|
|
/* update statistics */
|
|
ndev->stats.rx_packets++;
|
|
ndev->stats.rx_bytes += len;
|
|
netif_rx_ni(skb);
|
|
}
|
|
}
|
|
/*
|
|
* Move the RX read pointer to the start of the next
|
|
* received packet.
|
|
* This frees the memory we just read out.
|
|
*/
|
|
erxrdpt = erxrdpt_workaround(next_packet, RXSTART_INIT, RXEND_INIT);
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev, "%s() ERXRDPT:0x%04x\n",
|
|
__func__, erxrdpt);
|
|
|
|
mutex_lock(&priv->lock);
|
|
nolock_regw_write(priv, ERXRDPTL, erxrdpt);
|
|
#ifdef CONFIG_ENC28J60_WRITEVERIFY
|
|
if (netif_msg_drv(priv)) {
|
|
u16 reg;
|
|
reg = nolock_regw_read(priv, ERXRDPTL);
|
|
if (reg != erxrdpt)
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() ERXRDPT verify error (0x%04x - 0x%04x)\n",
|
|
__func__, reg, erxrdpt);
|
|
}
|
|
#endif
|
|
priv->next_pk_ptr = next_packet;
|
|
/* we are done with this packet, decrement the packet counter */
|
|
nolock_reg_bfset(priv, ECON2, ECON2_PKTDEC);
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/*
|
|
* Calculate free space in RxFIFO
|
|
*/
|
|
static int enc28j60_get_free_rxfifo(struct enc28j60_net *priv)
|
|
{
|
|
struct net_device *ndev = priv->netdev;
|
|
int epkcnt, erxst, erxnd, erxwr, erxrd;
|
|
int free_space;
|
|
|
|
mutex_lock(&priv->lock);
|
|
epkcnt = nolock_regb_read(priv, EPKTCNT);
|
|
if (epkcnt >= 255)
|
|
free_space = -1;
|
|
else {
|
|
erxst = nolock_regw_read(priv, ERXSTL);
|
|
erxnd = nolock_regw_read(priv, ERXNDL);
|
|
erxwr = nolock_regw_read(priv, ERXWRPTL);
|
|
erxrd = nolock_regw_read(priv, ERXRDPTL);
|
|
|
|
if (erxwr > erxrd)
|
|
free_space = (erxnd - erxst) - (erxwr - erxrd);
|
|
else if (erxwr == erxrd)
|
|
free_space = (erxnd - erxst);
|
|
else
|
|
free_space = erxrd - erxwr - 1;
|
|
}
|
|
mutex_unlock(&priv->lock);
|
|
if (netif_msg_rx_status(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "%s() free_space = %d\n",
|
|
__func__, free_space);
|
|
return free_space;
|
|
}
|
|
|
|
/*
|
|
* Access the PHY to determine link status
|
|
*/
|
|
static void enc28j60_check_link_status(struct net_device *ndev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
struct device *dev = &priv->spi->dev;
|
|
u16 reg;
|
|
int duplex;
|
|
|
|
reg = enc28j60_phy_read(priv, PHSTAT2);
|
|
if (netif_msg_hw(priv))
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"%s() PHSTAT1: %04x, PHSTAT2: %04x\n", __func__,
|
|
enc28j60_phy_read(priv, PHSTAT1), reg);
|
|
duplex = reg & PHSTAT2_DPXSTAT;
|
|
|
|
if (reg & PHSTAT2_LSTAT) {
|
|
netif_carrier_on(ndev);
|
|
if (netif_msg_ifup(priv))
|
|
netdev_info(ndev, "link up - %s\n",
|
|
duplex ? "Full duplex" : "Half duplex");
|
|
} else {
|
|
if (netif_msg_ifdown(priv))
|
|
netdev_info(ndev, "link down\n");
|
|
netif_carrier_off(ndev);
|
|
}
|
|
}
|
|
|
|
static void enc28j60_tx_clear(struct net_device *ndev, bool err)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
|
|
if (err)
|
|
ndev->stats.tx_errors++;
|
|
else
|
|
ndev->stats.tx_packets++;
|
|
|
|
if (priv->tx_skb) {
|
|
if (!err)
|
|
ndev->stats.tx_bytes += priv->tx_skb->len;
|
|
dev_kfree_skb(priv->tx_skb);
|
|
priv->tx_skb = NULL;
|
|
}
|
|
locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
|
|
netif_wake_queue(ndev);
|
|
}
|
|
|
|
/*
|
|
* RX handler
|
|
* Ignore PKTIF because is unreliable! (Look at the errata datasheet)
|
|
* Check EPKTCNT is the suggested workaround.
|
|
* We don't need to clear interrupt flag, automatically done when
|
|
* enc28j60_hw_rx() decrements the packet counter.
|
|
* Returns how many packet processed.
|
|
*/
|
|
static int enc28j60_rx_interrupt(struct net_device *ndev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
int pk_counter, ret;
|
|
|
|
pk_counter = locked_regb_read(priv, EPKTCNT);
|
|
if (pk_counter && netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intRX, pk_cnt: %d\n",
|
|
pk_counter);
|
|
if (pk_counter > priv->max_pk_counter) {
|
|
/* update statistics */
|
|
priv->max_pk_counter = pk_counter;
|
|
if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
|
|
netdev_printk(KERN_DEBUG, ndev, "RX max_pk_cnt: %d\n",
|
|
priv->max_pk_counter);
|
|
}
|
|
ret = pk_counter;
|
|
while (pk_counter-- > 0)
|
|
enc28j60_hw_rx(ndev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void enc28j60_irq_work_handler(struct work_struct *work)
|
|
{
|
|
struct enc28j60_net *priv =
|
|
container_of(work, struct enc28j60_net, irq_work);
|
|
struct net_device *ndev = priv->netdev;
|
|
int intflags, loop;
|
|
|
|
/* disable further interrupts */
|
|
locked_reg_bfclr(priv, EIE, EIE_INTIE);
|
|
|
|
do {
|
|
loop = 0;
|
|
intflags = locked_regb_read(priv, EIR);
|
|
/* DMA interrupt handler (not currently used) */
|
|
if ((intflags & EIR_DMAIF) != 0) {
|
|
loop++;
|
|
if (netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intDMA(%d)\n",
|
|
loop);
|
|
locked_reg_bfclr(priv, EIR, EIR_DMAIF);
|
|
}
|
|
/* LINK changed handler */
|
|
if ((intflags & EIR_LINKIF) != 0) {
|
|
loop++;
|
|
if (netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intLINK(%d)\n",
|
|
loop);
|
|
enc28j60_check_link_status(ndev);
|
|
/* read PHIR to clear the flag */
|
|
enc28j60_phy_read(priv, PHIR);
|
|
}
|
|
/* TX complete handler */
|
|
if (((intflags & EIR_TXIF) != 0) &&
|
|
((intflags & EIR_TXERIF) == 0)) {
|
|
bool err = false;
|
|
loop++;
|
|
if (netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intTX(%d)\n",
|
|
loop);
|
|
priv->tx_retry_count = 0;
|
|
if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
|
|
if (netif_msg_tx_err(priv))
|
|
netdev_err(ndev, "Tx Error (aborted)\n");
|
|
err = true;
|
|
}
|
|
if (netif_msg_tx_done(priv)) {
|
|
u8 tsv[TSV_SIZE];
|
|
enc28j60_read_tsv(priv, tsv);
|
|
enc28j60_dump_tsv(priv, "Tx Done", tsv);
|
|
}
|
|
enc28j60_tx_clear(ndev, err);
|
|
locked_reg_bfclr(priv, EIR, EIR_TXIF);
|
|
}
|
|
/* TX Error handler */
|
|
if ((intflags & EIR_TXERIF) != 0) {
|
|
u8 tsv[TSV_SIZE];
|
|
|
|
loop++;
|
|
if (netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intTXErr(%d)\n",
|
|
loop);
|
|
locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
|
|
enc28j60_read_tsv(priv, tsv);
|
|
if (netif_msg_tx_err(priv))
|
|
enc28j60_dump_tsv(priv, "Tx Error", tsv);
|
|
/* Reset TX logic */
|
|
mutex_lock(&priv->lock);
|
|
nolock_reg_bfset(priv, ECON1, ECON1_TXRST);
|
|
nolock_reg_bfclr(priv, ECON1, ECON1_TXRST);
|
|
nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
|
|
mutex_unlock(&priv->lock);
|
|
/* Transmit Late collision check for retransmit */
|
|
if (TSV_GETBIT(tsv, TSV_TXLATECOLLISION)) {
|
|
if (netif_msg_tx_err(priv))
|
|
netdev_printk(KERN_DEBUG, ndev,
|
|
"LateCollision TXErr (%d)\n",
|
|
priv->tx_retry_count);
|
|
if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
|
|
locked_reg_bfset(priv, ECON1,
|
|
ECON1_TXRTS);
|
|
else
|
|
enc28j60_tx_clear(ndev, true);
|
|
} else
|
|
enc28j60_tx_clear(ndev, true);
|
|
locked_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF);
|
|
}
|
|
/* RX Error handler */
|
|
if ((intflags & EIR_RXERIF) != 0) {
|
|
loop++;
|
|
if (netif_msg_intr(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "intRXErr(%d)\n",
|
|
loop);
|
|
/* Check free FIFO space to flag RX overrun */
|
|
if (enc28j60_get_free_rxfifo(priv) <= 0) {
|
|
if (netif_msg_rx_err(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "RX Overrun\n");
|
|
ndev->stats.rx_dropped++;
|
|
}
|
|
locked_reg_bfclr(priv, EIR, EIR_RXERIF);
|
|
}
|
|
/* RX handler */
|
|
if (enc28j60_rx_interrupt(ndev))
|
|
loop++;
|
|
} while (loop);
|
|
|
|
/* re-enable interrupts */
|
|
locked_reg_bfset(priv, EIE, EIE_INTIE);
|
|
}
|
|
|
|
/*
|
|
* Hardware transmit function.
|
|
* Fill the buffer memory and send the contents of the transmit buffer
|
|
* onto the network
|
|
*/
|
|
static void enc28j60_hw_tx(struct enc28j60_net *priv)
|
|
{
|
|
struct net_device *ndev = priv->netdev;
|
|
|
|
BUG_ON(!priv->tx_skb);
|
|
|
|
if (netif_msg_tx_queued(priv))
|
|
netdev_printk(KERN_DEBUG, ndev, "Tx Packet Len:%d\n",
|
|
priv->tx_skb->len);
|
|
|
|
if (netif_msg_pktdata(priv))
|
|
dump_packet(__func__,
|
|
priv->tx_skb->len, priv->tx_skb->data);
|
|
enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data);
|
|
|
|
#ifdef CONFIG_ENC28J60_WRITEVERIFY
|
|
/* readback and verify written data */
|
|
if (netif_msg_drv(priv)) {
|
|
struct device *dev = &priv->spi->dev;
|
|
int test_len, k;
|
|
u8 test_buf[64]; /* limit the test to the first 64 bytes */
|
|
int okflag;
|
|
|
|
test_len = priv->tx_skb->len;
|
|
if (test_len > sizeof(test_buf))
|
|
test_len = sizeof(test_buf);
|
|
|
|
/* + 1 to skip control byte */
|
|
enc28j60_mem_read(priv, TXSTART_INIT + 1, test_len, test_buf);
|
|
okflag = 1;
|
|
for (k = 0; k < test_len; k++) {
|
|
if (priv->tx_skb->data[k] != test_buf[k]) {
|
|
dev_printk(KERN_DEBUG, dev,
|
|
"Error, %d location differ: 0x%02x-0x%02x\n",
|
|
k, priv->tx_skb->data[k], test_buf[k]);
|
|
okflag = 0;
|
|
}
|
|
}
|
|
if (!okflag)
|
|
dev_printk(KERN_DEBUG, dev, "Tx write buffer, verify ERROR!\n");
|
|
}
|
|
#endif
|
|
/* set TX request flag */
|
|
locked_reg_bfset(priv, ECON1, ECON1_TXRTS);
|
|
}
|
|
|
|
static netdev_tx_t enc28j60_send_packet(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
|
|
/* If some error occurs while trying to transmit this
|
|
* packet, you should return '1' from this function.
|
|
* In such a case you _may not_ do anything to the
|
|
* SKB, it is still owned by the network queueing
|
|
* layer when an error is returned. This means you
|
|
* may not modify any SKB fields, you may not free
|
|
* the SKB, etc.
|
|
*/
|
|
netif_stop_queue(dev);
|
|
|
|
/* Remember the skb for deferred processing */
|
|
priv->tx_skb = skb;
|
|
schedule_work(&priv->tx_work);
|
|
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
static void enc28j60_tx_work_handler(struct work_struct *work)
|
|
{
|
|
struct enc28j60_net *priv =
|
|
container_of(work, struct enc28j60_net, tx_work);
|
|
|
|
/* actual delivery of data */
|
|
enc28j60_hw_tx(priv);
|
|
}
|
|
|
|
static irqreturn_t enc28j60_irq(int irq, void *dev_id)
|
|
{
|
|
struct enc28j60_net *priv = dev_id;
|
|
|
|
/*
|
|
* Can't do anything in interrupt context because we need to
|
|
* block (spi_sync() is blocking) so fire of the interrupt
|
|
* handling workqueue.
|
|
* Remember that we access enc28j60 registers through SPI bus
|
|
* via spi_sync() call.
|
|
*/
|
|
schedule_work(&priv->irq_work);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void enc28j60_tx_timeout(struct net_device *ndev, unsigned int txqueue)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(ndev);
|
|
|
|
if (netif_msg_timer(priv))
|
|
netdev_err(ndev, "tx timeout\n");
|
|
|
|
ndev->stats.tx_errors++;
|
|
/* can't restart safely under softirq */
|
|
schedule_work(&priv->restart_work);
|
|
}
|
|
|
|
/*
|
|
* Open/initialize the board. This is called (in the current kernel)
|
|
* sometime after booting when the 'ifconfig' program is run.
|
|
*
|
|
* This routine should set everything up anew at each open, even
|
|
* registers that "should" only need to be set once at boot, so that
|
|
* there is non-reboot way to recover if something goes wrong.
|
|
*/
|
|
static int enc28j60_net_open(struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
|
|
if (!is_valid_ether_addr(dev->dev_addr)) {
|
|
if (netif_msg_ifup(priv))
|
|
netdev_err(dev, "invalid MAC address %pM\n", dev->dev_addr);
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
/* Reset the hardware here (and take it out of low power mode) */
|
|
enc28j60_lowpower(priv, false);
|
|
enc28j60_hw_disable(priv);
|
|
if (!enc28j60_hw_init(priv)) {
|
|
if (netif_msg_ifup(priv))
|
|
netdev_err(dev, "hw_reset() failed\n");
|
|
return -EINVAL;
|
|
}
|
|
/* Update the MAC address (in case user has changed it) */
|
|
enc28j60_set_hw_macaddr(dev);
|
|
/* Enable interrupts */
|
|
enc28j60_hw_enable(priv);
|
|
/* check link status */
|
|
enc28j60_check_link_status(dev);
|
|
/* We are now ready to accept transmit requests from
|
|
* the queueing layer of the networking.
|
|
*/
|
|
netif_start_queue(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The inverse routine to net_open(). */
|
|
static int enc28j60_net_close(struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
|
|
enc28j60_hw_disable(priv);
|
|
enc28j60_lowpower(priv, true);
|
|
netif_stop_queue(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set or clear the multicast filter for this adapter
|
|
* num_addrs == -1 Promiscuous mode, receive all packets
|
|
* num_addrs == 0 Normal mode, filter out multicast packets
|
|
* num_addrs > 0 Multicast mode, receive normal and MC packets
|
|
*/
|
|
static void enc28j60_set_multicast_list(struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
int oldfilter = priv->rxfilter;
|
|
|
|
if (dev->flags & IFF_PROMISC) {
|
|
if (netif_msg_link(priv))
|
|
netdev_info(dev, "promiscuous mode\n");
|
|
priv->rxfilter = RXFILTER_PROMISC;
|
|
} else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
|
|
if (netif_msg_link(priv))
|
|
netdev_info(dev, "%smulticast mode\n",
|
|
(dev->flags & IFF_ALLMULTI) ? "all-" : "");
|
|
priv->rxfilter = RXFILTER_MULTI;
|
|
} else {
|
|
if (netif_msg_link(priv))
|
|
netdev_info(dev, "normal mode\n");
|
|
priv->rxfilter = RXFILTER_NORMAL;
|
|
}
|
|
|
|
if (oldfilter != priv->rxfilter)
|
|
schedule_work(&priv->setrx_work);
|
|
}
|
|
|
|
static void enc28j60_setrx_work_handler(struct work_struct *work)
|
|
{
|
|
struct enc28j60_net *priv =
|
|
container_of(work, struct enc28j60_net, setrx_work);
|
|
struct device *dev = &priv->spi->dev;
|
|
|
|
if (priv->rxfilter == RXFILTER_PROMISC) {
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "promiscuous mode\n");
|
|
locked_regb_write(priv, ERXFCON, 0x00);
|
|
} else if (priv->rxfilter == RXFILTER_MULTI) {
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "multicast mode\n");
|
|
locked_regb_write(priv, ERXFCON,
|
|
ERXFCON_UCEN | ERXFCON_CRCEN |
|
|
ERXFCON_BCEN | ERXFCON_MCEN);
|
|
} else {
|
|
if (netif_msg_drv(priv))
|
|
dev_printk(KERN_DEBUG, dev, "normal mode\n");
|
|
locked_regb_write(priv, ERXFCON,
|
|
ERXFCON_UCEN | ERXFCON_CRCEN |
|
|
ERXFCON_BCEN);
|
|
}
|
|
}
|
|
|
|
static void enc28j60_restart_work_handler(struct work_struct *work)
|
|
{
|
|
struct enc28j60_net *priv =
|
|
container_of(work, struct enc28j60_net, restart_work);
|
|
struct net_device *ndev = priv->netdev;
|
|
int ret;
|
|
|
|
rtnl_lock();
|
|
if (netif_running(ndev)) {
|
|
enc28j60_net_close(ndev);
|
|
ret = enc28j60_net_open(ndev);
|
|
if (unlikely(ret)) {
|
|
netdev_info(ndev, "could not restart %d\n", ret);
|
|
dev_close(ndev);
|
|
}
|
|
}
|
|
rtnl_unlock();
|
|
}
|
|
|
|
/* ......................... ETHTOOL SUPPORT ........................... */
|
|
|
|
static void
|
|
enc28j60_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));
|
|
strlcpy(info->bus_info,
|
|
dev_name(dev->dev.parent), sizeof(info->bus_info));
|
|
}
|
|
|
|
static int
|
|
enc28j60_get_link_ksettings(struct net_device *dev,
|
|
struct ethtool_link_ksettings *cmd)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
|
|
ethtool_link_ksettings_zero_link_mode(cmd, supported);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
|
|
|
|
cmd->base.speed = SPEED_10;
|
|
cmd->base.duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
|
|
cmd->base.port = PORT_TP;
|
|
cmd->base.autoneg = AUTONEG_DISABLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
enc28j60_set_link_ksettings(struct net_device *dev,
|
|
const struct ethtool_link_ksettings *cmd)
|
|
{
|
|
return enc28j60_setlink(dev, cmd->base.autoneg,
|
|
cmd->base.speed, cmd->base.duplex);
|
|
}
|
|
|
|
static u32 enc28j60_get_msglevel(struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
return priv->msg_enable;
|
|
}
|
|
|
|
static void enc28j60_set_msglevel(struct net_device *dev, u32 val)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
priv->msg_enable = val;
|
|
}
|
|
|
|
static const struct ethtool_ops enc28j60_ethtool_ops = {
|
|
.get_drvinfo = enc28j60_get_drvinfo,
|
|
.get_msglevel = enc28j60_get_msglevel,
|
|
.set_msglevel = enc28j60_set_msglevel,
|
|
.get_link_ksettings = enc28j60_get_link_ksettings,
|
|
.set_link_ksettings = enc28j60_set_link_ksettings,
|
|
};
|
|
|
|
static int enc28j60_chipset_init(struct net_device *dev)
|
|
{
|
|
struct enc28j60_net *priv = netdev_priv(dev);
|
|
|
|
return enc28j60_hw_init(priv);
|
|
}
|
|
|
|
static const struct net_device_ops enc28j60_netdev_ops = {
|
|
.ndo_open = enc28j60_net_open,
|
|
.ndo_stop = enc28j60_net_close,
|
|
.ndo_start_xmit = enc28j60_send_packet,
|
|
.ndo_set_rx_mode = enc28j60_set_multicast_list,
|
|
.ndo_set_mac_address = enc28j60_set_mac_address,
|
|
.ndo_tx_timeout = enc28j60_tx_timeout,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
};
|
|
|
|
static int enc28j60_probe(struct spi_device *spi)
|
|
{
|
|
unsigned char macaddr[ETH_ALEN];
|
|
struct net_device *dev;
|
|
struct enc28j60_net *priv;
|
|
int ret = 0;
|
|
|
|
if (netif_msg_drv(&debug))
|
|
dev_info(&spi->dev, "Ethernet driver %s loaded\n", DRV_VERSION);
|
|
|
|
dev = alloc_etherdev(sizeof(struct enc28j60_net));
|
|
if (!dev) {
|
|
ret = -ENOMEM;
|
|
goto error_alloc;
|
|
}
|
|
priv = netdev_priv(dev);
|
|
|
|
priv->netdev = dev; /* priv to netdev reference */
|
|
priv->spi = spi; /* priv to spi reference */
|
|
priv->msg_enable = netif_msg_init(debug.msg_enable, ENC28J60_MSG_DEFAULT);
|
|
mutex_init(&priv->lock);
|
|
INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
|
|
INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler);
|
|
INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
|
|
INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler);
|
|
spi_set_drvdata(spi, priv); /* spi to priv reference */
|
|
SET_NETDEV_DEV(dev, &spi->dev);
|
|
|
|
if (!enc28j60_chipset_init(dev)) {
|
|
if (netif_msg_probe(priv))
|
|
dev_info(&spi->dev, "chip not found\n");
|
|
ret = -EIO;
|
|
goto error_irq;
|
|
}
|
|
|
|
if (device_get_mac_address(&spi->dev, macaddr, sizeof(macaddr)))
|
|
ether_addr_copy(dev->dev_addr, macaddr);
|
|
else
|
|
eth_hw_addr_random(dev);
|
|
enc28j60_set_hw_macaddr(dev);
|
|
|
|
/* Board setup must set the relevant edge trigger type;
|
|
* level triggers won't currently work.
|
|
*/
|
|
ret = request_irq(spi->irq, enc28j60_irq, 0, DRV_NAME, priv);
|
|
if (ret < 0) {
|
|
if (netif_msg_probe(priv))
|
|
dev_err(&spi->dev, "request irq %d failed (ret = %d)\n",
|
|
spi->irq, ret);
|
|
goto error_irq;
|
|
}
|
|
|
|
dev->if_port = IF_PORT_10BASET;
|
|
dev->irq = spi->irq;
|
|
dev->netdev_ops = &enc28j60_netdev_ops;
|
|
dev->watchdog_timeo = TX_TIMEOUT;
|
|
dev->ethtool_ops = &enc28j60_ethtool_ops;
|
|
|
|
enc28j60_lowpower(priv, true);
|
|
|
|
ret = register_netdev(dev);
|
|
if (ret) {
|
|
if (netif_msg_probe(priv))
|
|
dev_err(&spi->dev, "register netdev failed (ret = %d)\n",
|
|
ret);
|
|
goto error_register;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error_register:
|
|
free_irq(spi->irq, priv);
|
|
error_irq:
|
|
free_netdev(dev);
|
|
error_alloc:
|
|
return ret;
|
|
}
|
|
|
|
static int enc28j60_remove(struct spi_device *spi)
|
|
{
|
|
struct enc28j60_net *priv = spi_get_drvdata(spi);
|
|
|
|
unregister_netdev(priv->netdev);
|
|
free_irq(spi->irq, priv);
|
|
free_netdev(priv->netdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id enc28j60_dt_ids[] = {
|
|
{ .compatible = "microchip,enc28j60" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, enc28j60_dt_ids);
|
|
|
|
static struct spi_driver enc28j60_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.of_match_table = enc28j60_dt_ids,
|
|
},
|
|
.probe = enc28j60_probe,
|
|
.remove = enc28j60_remove,
|
|
};
|
|
module_spi_driver(enc28j60_driver);
|
|
|
|
MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
|
|
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
|
|
MODULE_LICENSE("GPL");
|
|
module_param_named(debug, debug.msg_enable, int, 0);
|
|
MODULE_PARM_DESC(debug, "Debug verbosity level in amount of bits set (0=none, ..., 31=all)");
|
|
MODULE_ALIAS("spi:" DRV_NAME);
|