aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/touchright.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/touchright.c')
-rw-r--r--drivers/input/touchscreen/touchright.c1
1 files changed, 0 insertions, 1 deletions
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
index 8a2887daf19..4000e520540 100644
--- a/drivers/input/touchscreen/touchright.c
+++ b/drivers/input/touchscreen/touchright.c
@@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
-#include <linux/init.h>
#define DRIVER_DESC "Touchright serial touchscreen driver"
ary='file diffstat' width='100%'> -rw-r--r--drivers/net/ethernet/3com/Kconfig107
-rw-r--r--drivers/net/ethernet/3com/Makefile10
-rw-r--r--drivers/net/ethernet/3com/typhoon.c2556
-rw-r--r--drivers/net/ethernet/3com/typhoon.h624
-rw-r--r--drivers/net/ethernet/8390/8390.c103
-rw-r--r--drivers/net/ethernet/8390/8390.h225
-rw-r--r--drivers/net/ethernet/8390/8390p.c105
-rw-r--r--drivers/net/ethernet/8390/Kconfig205
-rw-r--r--drivers/net/ethernet/8390/Makefile18
-rw-r--r--drivers/net/ethernet/8390/apne.c626
-rw-r--r--drivers/net/ethernet/8390/ax88796.c1016
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c1706
-rw-r--r--drivers/net/ethernet/8390/etherh.c874
-rw-r--r--drivers/net/ethernet/8390/hydra.c278
-rw-r--r--drivers/net/ethernet/8390/lib8390.c1084
-rw-r--r--drivers/net/ethernet/8390/mac8390.c882
-rw-r--r--drivers/net/ethernet/8390/mcf8390.c481
-rw-r--r--drivers/net/ethernet/8390/ne.c1017
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c741
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c1702
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c631
-rw-r--r--drivers/net/ethernet/8390/stnic.c303
-rw-r--r--drivers/net/ethernet/8390/wd.c574
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c458
-rw-r--r--drivers/net/ethernet/Kconfig186
-rw-r--r--drivers/net/ethernet/Makefile83
-rw-r--r--drivers/net/ethernet/adaptec/Kconfig35
-rw-r--r--drivers/net/ethernet/adaptec/Makefile5
-rw-r--r--drivers/net/ethernet/adaptec/starfire.c2060
-rw-r--r--drivers/net/ethernet/adi/Kconfig69
-rw-r--r--drivers/net/ethernet/adi/Makefile5
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c1921
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.h109
-rw-r--r--drivers/net/ethernet/aeroflex/Kconfig11
-rw-r--r--drivers/net/ethernet/aeroflex/Makefile5
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c1607
-rw-r--r--drivers/net/ethernet/aeroflex/greth.h142
-rw-r--r--drivers/net/ethernet/allwinner/Kconfig37
-rw-r--r--drivers/net/ethernet/allwinner/Makefile5
-rw-r--r--drivers/net/ethernet/allwinner/sun4i-emac.c970
-rw-r--r--drivers/net/ethernet/allwinner/sun4i-emac.h108
-rw-r--r--drivers/net/ethernet/alteon/Kconfig48
-rw-r--r--drivers/net/ethernet/alteon/Makefile5
-rw-r--r--drivers/net/ethernet/alteon/acenic.c3189
-rw-r--r--drivers/net/ethernet/alteon/acenic.h790
-rw-r--r--drivers/net/ethernet/altera/Kconfig9
-rw-r--r--drivers/net/ethernet/altera/Makefile8
-rw-r--r--drivers/net/ethernet/altera/altera_msgdma.c206
-rw-r--r--drivers/net/ethernet/altera/altera_msgdma.h35
-rw-r--r--drivers/net/ethernet/altera/altera_msgdmahw.h162
-rw-r--r--drivers/net/ethernet/altera/altera_sgdma.c540
-rw-r--r--drivers/net/ethernet/altera/altera_sgdma.h36
-rw-r--r--drivers/net/ethernet/altera/altera_sgdmahw.h126
-rw-r--r--drivers/net/ethernet/altera/altera_tse.h537
-rw-r--r--drivers/net/ethernet/altera/altera_tse_ethtool.c275
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c1577
-rw-r--r--drivers/net/ethernet/altera/altera_utils.c44
-rw-r--r--drivers/net/ethernet/altera/altera_utils.h27
-rw-r--r--drivers/net/ethernet/amd/7990.c666
-rw-r--r--drivers/net/ethernet/amd/7990.h250
-rw-r--r--drivers/net/ethernet/amd/Kconfig192
-rw-r--r--drivers/net/ethernet/amd/Makefile20
-rw-r--r--drivers/net/ethernet/amd/a2065.c783
-rw-r--r--drivers/net/ethernet/amd/a2065.h173
-rw-r--r--drivers/net/ethernet/amd/am79c961a.c768
-rw-r--r--drivers/net/ethernet/amd/am79c961a.h145
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c1982
-rw-r--r--drivers/net/ethernet/amd/amd8111e.h813
-rw-r--r--drivers/net/ethernet/amd/ariadne.c792
-rw-r--r--drivers/net/ethernet/amd/ariadne.h415
-rw-r--r--drivers/net/ethernet/amd/atarilance.c1169
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c1347
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.h133
-rw-r--r--drivers/net/ethernet/amd/declance.c1376
-rw-r--r--drivers/net/ethernet/amd/hplance.c232
-rw-r--r--drivers/net/ethernet/amd/hplance.h26
-rw-r--r--drivers/net/ethernet/amd/lance.c1312
-rw-r--r--drivers/net/ethernet/amd/mvme147.c200
-rw-r--r--drivers/net/ethernet/amd/ni65.c1252
-rw-r--r--drivers/net/ethernet/amd/ni65.h121
-rw-r--r--drivers/net/ethernet/amd/nmclan_cs.c1510
-rw-r--r--drivers/net/ethernet/amd/pcnet32.c2971
-rw-r--r--drivers/net/ethernet/amd/sun3lance.c956
-rw-r--r--drivers/net/ethernet/amd/sunlance.c1534
-rw-r--r--drivers/net/ethernet/amd/xgbe/Makefile6
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-common.h1007
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c375
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-desc.c556
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c2182
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c1351
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c510
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-main.c513
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c433
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h676
-rw-r--r--drivers/net/ethernet/apple/Kconfig65
-rw-r--r--drivers/net/ethernet/apple/Makefile7
-rw-r--r--drivers/net/ethernet/apple/bmac.c1679
-rw-r--r--drivers/net/ethernet/apple/bmac.h164
-rw-r--r--drivers/net/ethernet/apple/mace.c1028
-rw-r--r--drivers/net/ethernet/apple/mace.h173
-rw-r--r--drivers/net/ethernet/apple/macmace.c789
-rw-r--r--drivers/net/ethernet/arc/Kconfig31
-rw-r--r--drivers/net/ethernet/arc/Makefile6
-rw-r--r--drivers/net/ethernet/arc/emac.h214
-rw-r--r--drivers/net/ethernet/arc/emac_main.c887
-rw-r--r--drivers/net/ethernet/arc/emac_mdio.c152
-rw-r--r--drivers/net/ethernet/atheros/Kconfig83
-rw-r--r--drivers/net/ethernet/atheros/Makefile9
-rw-r--r--drivers/net/ethernet/atheros/alx/Makefile3
-rw-r--r--drivers/net/ethernet/atheros/alx/alx.h117
-rw-r--r--drivers/net/ethernet/atheros/alx/ethtool.c313
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.c1110
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.h581
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c1557
-rw-r--r--drivers/net/ethernet/atheros/alx/reg.h854
-rw-r--r--drivers/net/ethernet/atheros/atl1c/Makefile2
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c.h605
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c309
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_hw.c865
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_hw.h1024
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c2803
-rw-r--r--drivers/net/ethernet/atheros/atl1e/Makefile2
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e.h507
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c392
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_hw.c651
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_hw.h690
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c2582
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_param.c269
-rw-r--r--drivers/net/ethernet/atheros/atlx/Makefile3
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.c3691
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.h803
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.c3090
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.h524
-rw-r--r--drivers/net/ethernet/atheros/atlx/atlx.c279
-rw-r--r--drivers/net/ethernet/atheros/atlx/atlx.h502
-rw-r--r--drivers/net/ethernet/broadcom/Kconfig164
-rw-r--r--drivers/net/ethernet/broadcom/Makefile14
-rw-r--r--drivers/net/ethernet/broadcom/b44.c2611
-rw-r--r--drivers/net/ethernet/broadcom/b44.h409
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c2919
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.h360
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c1633
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.h678
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c1588
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.h488
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c8818
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.h7464
-rw-r--r--drivers/net/ethernet/broadcom/bnx2_fw.h88
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/Makefile8
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h2557
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c4971
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h1326
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c2555
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h205
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h2220
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c3513
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h392
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h38
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h5794
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h786
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h934
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c13716
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h543
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c14166
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h168
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h7491
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c5943
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h1425
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c3001
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h584
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c2006
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h555
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c2034
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h434
-rw-r--r--drivers/net/ethernet/broadcom/cnic.c5760
-rw-r--r--drivers/net/ethernet/broadcom/cnic.h426
-rw-r--r--drivers/net/ethernet/broadcom/cnic_defs.h5461
-rw-r--r--drivers/net/ethernet/broadcom/cnic_if.h370
-rw-r--r--drivers/net/ethernet/broadcom/genet/Makefile2
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c2584
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h628
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c469
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c2671
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c18214
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h3426
-rw-r--r--drivers/net/ethernet/brocade/Kconfig23
-rw-r--r--drivers/net/ethernet/brocade/Makefile5
-rw-r--r--drivers/net/ethernet/brocade/bna/Kconfig17
-rw-r--r--drivers/net/ethernet/brocade/bna/Makefile12
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cee.c287
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cee.h65
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cs.h124
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs.h295
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_cna.h220
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_mfg_comm.h154
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_defs_status.h215
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.c3398
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.h368
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c944
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.c667
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.h130
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi.h570
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_cna.h163
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_enet.h858
-rw-r--r--drivers/net/ethernet/brocade/bna/bfi_reg.h456
-rw-r--r--drivers/net/ethernet/brocade/bna/bna.h564
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_enet.c2155
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_hw_defs.h410
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_tx_rx.c4017
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_types.h956
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c3899
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.h431
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_debugfs.c588
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_ethtool.c1141
-rw-r--r--drivers/net/ethernet/brocade/bna/cna.h106
-rw-r--r--drivers/net/ethernet/brocade/bna/cna_fwimg.c93
-rw-r--r--drivers/net/ethernet/cadence/Kconfig44
-rw-r--r--drivers/net/ethernet/cadence/Makefile6
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.c483
-rw-r--r--drivers/net/ethernet/cadence/macb.c2067
-rw-r--r--drivers/net/ethernet/cadence/macb.h621
-rw-r--r--drivers/net/ethernet/calxeda/Kconfig7
-rw-r--r--drivers/net/ethernet/calxeda/Makefile1
-rw-r--r--drivers/net/ethernet/calxeda/xgmac.c1950
-rw-r--r--drivers/net/ethernet/chelsio/Kconfig108
-rw-r--r--drivers/net/ethernet/chelsio/Makefile8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/Makefile9
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/common.h351
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cphy.h174
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h638
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cxgb2.c1357
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/elmer0.h157
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/espi.c372
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/espi.h67
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/fpga_defs.h232
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/gmac.h141
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c397
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.h127
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88x201x.c259
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/my3126.c209
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/pm3393.c795
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/regs.h2167
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/sge.c2123
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/sge.h93
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/subr.c1128
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h1642
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/tp.c171
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/tp.h72
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326.c730
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326_reg.h297
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/Makefile8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/adapter.h334
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/ael1002.c941
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/aq100x.c354
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/common.h773
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_ctl_defs.h189
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_defs.h114
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_ioctl.h177
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c3438
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c1427
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.h209
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/firmware_exports.h177
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.c470
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.h149
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/mc5.c438
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/regs.h2563
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge.c3305
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge_defs.h255
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_cpl.h1495
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c3777
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3cdev.h70
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/version.h44
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/vsc8211.c416
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/xgmac.c657
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/Makefile7
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h1052
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c6445
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h295
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c665
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h111
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c2831
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c3986
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.h220
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h835
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h1298
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h2208
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/Makefile7
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/adapter.h533
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c3009
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c2461
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h305
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h121
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c1392
-rw-r--r--drivers/net/ethernet/cirrus/Kconfig64
-rw-r--r--drivers/net/ethernet/cirrus/Makefile7
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c1925
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.h465
-rw-r--r--drivers/net/ethernet/cirrus/ep93xx_eth.c891
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c637
-rw-r--r--drivers/net/ethernet/cisco/Kconfig23
-rw-r--r--drivers/net/ethernet/cisco/Makefile5
-rw-r--r--drivers/net/ethernet/cisco/enic/Kconfig9
-rw-r--r--drivers/net/ethernet/cisco/enic/Makefile6
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_desc.h80
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_enet_desc.h185
-rw-r--r--drivers/net/ethernet/cisco/enic/enic.h213
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_api.c48
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_api.h30
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_dev.c292
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_dev.h65
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_ethtool.c306
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c2476
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_pp.c368
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_pp.h36
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.c384
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.h153
-rw-r--r--drivers/net/ethernet/cisco/enic/rq_enet_desc.h60
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_cq.c91
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_cq.h123
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.c1050
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.h137
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_devcmd.h627
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_enet.h57
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_intr.c68
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_intr.h111
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_nic.h72
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_resource.h76
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.c216
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.h212
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rss.h40
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_stats.h70
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_vic.c79
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_vic.h83
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_wq.c195
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_wq.h175
-rw-r--r--drivers/net/ethernet/cisco/enic/wq_enet_desc.h98
-rw-r--r--drivers/net/ethernet/davicom/Kconfig23
-rw-r--r--drivers/net/ethernet/davicom/Makefile5
-rw-r--r--drivers/net/ethernet/davicom/dm9000.c1766
-rw-r--r--drivers/net/ethernet/davicom/dm9000.h180
-rw-r--r--drivers/net/ethernet/dec/Kconfig21
-rw-r--r--drivers/net/ethernet/dec/Makefile5
-rw-r--r--drivers/net/ethernet/dec/tulip/21142.c257
-rw-r--r--drivers/net/ethernet/dec/tulip/Kconfig172
-rw-r--r--drivers/net/ethernet/dec/tulip/Makefile19
-rw-r--r--drivers/net/ethernet/dec/tulip/de2104x.c2207
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.c5590
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.h1017
-rw-r--r--drivers/net/ethernet/dec/tulip/dmfe.c2273
-rw-r--r--drivers/net/ethernet/dec/tulip/eeprom.c382
-rw-r--r--drivers/net/ethernet/dec/tulip/interrupt.c814
-rw-r--r--drivers/net/ethernet/dec/tulip/media.c552
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic.c170
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic2.c403
-rw-r--r--drivers/net/ethernet/dec/tulip/timer.c176
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip.h570
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip_core.c2001
-rw-r--r--drivers/net/ethernet/dec/tulip/uli526x.c1845
-rw-r--r--drivers/net/ethernet/dec/tulip/winbond-840.c1665
-rw-r--r--drivers/net/ethernet/dec/tulip/xircom_cb.c1171
-rw-r--r--drivers/net/ethernet/dlink/Kconfig55
-rw-r--r--drivers/net/ethernet/dlink/Makefile6
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c1768
-rw-r--r--drivers/net/ethernet/dlink/dl2k.h425
-rw-r--r--drivers/net/ethernet/dlink/sundance.c2027
-rw-r--r--drivers/net/ethernet/dnet.c975
-rw-r--r--drivers/net/ethernet/dnet.h225
-rw-r--r--drivers/net/ethernet/ec_bhf.c706
-rw-r--r--drivers/net/ethernet/emulex/Kconfig23
-rw-r--r--drivers/net/ethernet/emulex/Makefile5
-rw-r--r--drivers/net/ethernet/emulex/benet/Kconfig14
-rw-r--r--drivers/net/ethernet/emulex/benet/Makefile7
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h894
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c3957
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h2170
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c1188
-rw-r--r--drivers/net/ethernet/emulex/benet/be_hw.h560
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c5115
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.c185
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.h78
-rw-r--r--drivers/net/ethernet/ethoc.c1327
-rw-r--r--drivers/net/ethernet/faraday/Kconfig39
-rw-r--r--drivers/net/ethernet/faraday/Makefile6
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c1346
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.h246
-rw-r--r--drivers/net/ethernet/faraday/ftmac100.c1203
-rw-r--r--drivers/net/ethernet/faraday/ftmac100.h180
-rw-r--r--drivers/net/ethernet/fealnx.c1971
-rw-r--r--drivers/net/ethernet/freescale/Kconfig97
-rw-r--r--drivers/net/ethernet/freescale/Makefile19
-rw-r--r--drivers/net/ethernet/freescale/fec.h353
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c2726
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.c1118
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.h294
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx_phy.c158
-rw-r--r--drivers/net/ethernet/freescale/fec_ptp.c400
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/Kconfig34
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/Makefile14
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fec.h44
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c1212
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet.h244
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fcc.c585
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fec.c502
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-scc.c485
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c234
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c235
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c488
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c3481
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h1274
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c1906
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c576
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c3977
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.h1238
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth_ethtool.c421
-rw-r--r--drivers/net/ethernet/freescale/xgmac_mdio.c277
-rw-r--r--drivers/net/ethernet/fujitsu/Kconfig31
-rw-r--r--drivers/net/ethernet/fujitsu/Makefile5
-rw-r--r--drivers/net/ethernet/fujitsu/fmvj18x_cs.c1172
-rw-r--r--drivers/net/ethernet/hisilicon/Kconfig27
-rw-r--r--drivers/net/ethernet/hisilicon/Makefile5
-rw-r--r--drivers/net/ethernet/hisilicon/hix5hd2_gmac.c1066
-rw-r--r--drivers/net/ethernet/hp/Kconfig32
-rw-r--r--drivers/net/ethernet/hp/Makefile5
-rw-r--r--drivers/net/ethernet/hp/hp100.c3066
-rw-r--r--drivers/net/ethernet/hp/hp100.h615
-rw-r--r--drivers/net/ethernet/i825xx/82596.c1548
-rw-r--r--drivers/net/ethernet/i825xx/Kconfig69
-rw-r--r--drivers/net/ethernet/i825xx/Makefile10
-rw-r--r--drivers/net/ethernet/i825xx/ether1.c1087
-rw-r--r--drivers/net/ethernet/i825xx/ether1.h280
-rw-r--r--drivers/net/ethernet/i825xx/lasi_82596.c237
-rw-r--r--drivers/net/ethernet/i825xx/lib82596.c1409
-rw-r--r--drivers/net/ethernet/i825xx/sni_82596.c185
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.c1188
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.h318
-rw-r--r--drivers/net/ethernet/ibm/Kconfig42
-rw-r--r--drivers/net/ethernet/ibm/Makefile7
-rw-r--r--drivers/net/ethernet/ibm/ehea/Makefile6
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea.h490
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_ethtool.c282
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_hw.h267
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c3582
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.c626
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.h447
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.c1022
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.h404
-rw-r--r--drivers/net/ethernet/ibm/emac/Kconfig76
-rw-r--r--drivers/net/ethernet/ibm/emac/Makefile11
-rw-r--r--drivers/net/ethernet/ibm/emac/core.c3121
-rw-r--r--drivers/net/ethernet/ibm/emac/core.h470
-rw-r--r--drivers/net/ethernet/ibm/emac/debug.c270
-rw-r--r--drivers/net/ethernet/ibm/emac/debug.h83
-rw-r--r--drivers/net/ethernet/ibm/emac/emac.h314
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.c794
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.h312
-rw-r--r--drivers/net/ethernet/ibm/emac/phy.c541
-rw-r--r--drivers/net/ethernet/ibm/emac/phy.h87
-rw-r--r--drivers/net/ethernet/ibm/emac/rgmii.c337
-rw-r--r--drivers/net/ethernet/ibm/emac/rgmii.h82
-rw-r--r--drivers/net/ethernet/ibm/emac/tah.c181
-rw-r--r--drivers/net/ethernet/ibm/emac/tah.h95
-rw-r--r--drivers/net/ethernet/ibm/emac/zmii.c328
-rw-r--r--drivers/net/ethernet/ibm/emac/zmii.h78
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c1631
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.h206
-rw-r--r--drivers/net/ethernet/icplus/Kconfig13
-rw-r--r--drivers/net/ethernet/icplus/Makefile5
-rw-r--r--drivers/net/ethernet/icplus/ipg.c2300
-rw-r--r--drivers/net/ethernet/icplus/ipg.h748
-rw-r--r--drivers/net/ethernet/intel/Kconfig303
-rw-r--r--drivers/net/ethernet/intel/Makefile14
-rw-r--r--drivers/net/ethernet/intel/e100.c3202
-rw-r--r--drivers/net/ethernet/intel/e1000/Makefile35
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h367
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_ethtool.c1910
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c5769
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.h3112
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c5192
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_osdep.h109
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_param.c754
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.c1418
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.h88
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c2067
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.h53
-rw-r--r--drivers/net/ethernet/intel/e1000e/Makefile37
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h798
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h591
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c2322
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h692
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c5155
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.h290
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.c1801
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.h68
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.c344
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.h65
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c7247
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.c631
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.h40
-rw-r--r--drivers/net/ethernet/intel/e1000e/param.c533
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.c3237
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.h236
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c275
-rw-r--r--drivers/net/ethernet/intel/e1000e/regs.h247
-rw-r--r--drivers/net/ethernet/intel/i40e/Makefile46
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h647
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.c1024
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.h110
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h2173
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_alloc.h58
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c2764
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.c472
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.h107
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c316
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c2248
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.c154
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.h51
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c1765
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.c360
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.h236
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c1000
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h169
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c9044
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c355
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_osdep.h81
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h265
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ptp.c625
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_register.h4674
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_status.h100
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c2344
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.h290
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h1165
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl.h363
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c2441
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h130
-rw-r--r--drivers/net/ethernet/intel/i40evf/Makefile36
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq.c978
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq.h110
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h2197
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_alloc.h58
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_common.c624
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_hmc.h236
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h169
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_osdep.h75
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_prototype.h94
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_register.h4674
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_status.h100
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c1667
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.h287
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_type.h1165
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h363
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h298
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c704
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c2475
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c781
-rw-r--r--drivers/net/ethernet/intel/igb/Makefile36
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c2865
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.h279
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h1016
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h573
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c902
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.h93
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.c1606
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.h87
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.c443
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.h73
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.c801
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.h56
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c2511
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h174
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h425
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h576
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c3042
-rw-r--r--drivers/net/ethernet/intel/igb/igb_hwmon.c249
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c8184
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c944
-rw-r--r--drivers/net/ethernet/intel/igbvf/Makefile38
-rw-r--r--drivers/net/ethernet/intel/igbvf/defines.h122
-rw-r--r--drivers/net/ethernet/intel/igbvf/ethtool.c479
-rw-r--r--drivers/net/ethernet/intel/igbvf/igbvf.h329
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.c350
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.h75
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c2914
-rw-r--r--drivers/net/ethernet/intel/igbvf/regs.h108
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.c400
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.h266
-rw-r--r--drivers/net/ethernet/intel/ixgb/Makefile35
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb.h206
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.c605
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.h104
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c660
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.c1263
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.h792
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ids.h48
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c2367
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_osdep.h64
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_param.c469
-rw-r--r--drivers/net/ethernet/intel/ixgbe/Makefile44
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h977
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c1358
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c2543
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c3877
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h206
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c404
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h171
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c288
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h97
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c363
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h125
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c814
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c276
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c3093
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c1007
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h87
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c1199
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c8830
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c471
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h121
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c2057
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h160
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c1007
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c1301
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h69
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c230
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h3102
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c897
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/Makefile38
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/defines.h298
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c817
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h470
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c3809
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.c349
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.h126
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/regs.h75
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.c604
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.h211
-rw-r--r--drivers/net/ethernet/jme.c3372
-rw-r--r--drivers/net/ethernet/jme.h1278
-rw-r--r--drivers/net/ethernet/korina.c1230
-rw-r--r--drivers/net/ethernet/lantiq_etop.c818
-rw-r--r--drivers/net/ethernet/marvell/Kconfig134
-rw-r--r--drivers/net/ethernet/marvell/Makefile10
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c3220
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c301
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c3121
-rw-r--r--drivers/net/ethernet/marvell/pxa168_eth.c1623
-rw-r--r--drivers/net/ethernet/marvell/skge.c4227
-rw-r--r--drivers/net/ethernet/marvell/skge.h2584
-rw-r--r--drivers/net/ethernet/marvell/sky2.c5248
-rw-r--r--drivers/net/ethernet/marvell/sky2.h2432
-rw-r--r--drivers/net/ethernet/mellanox/Kconfig24
-rw-r--r--drivers/net/ethernet/mellanox/Makefile6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Kconfig46
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Makefile10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/alloc.c419
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/catas.c171
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c2610
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cq.c359
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c334
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c232
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c263
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c1244
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c369
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c2664
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c223
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.h560
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_resources.c115
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c1165
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_selftest.c177
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c919
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c1400
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c2018
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h228
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c461
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h129
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/intf.c188
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c2855
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c1474
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h1315
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h860
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mr.c995
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/pd.c286
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c1292
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/profile.c266
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/qp.c625
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/reset.c179
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c4876
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/sense.c143
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/srq.c313
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Kconfig8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Makefile5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/alloc.c238
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c1577
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cq.c237
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/debugfs.c610
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c526
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw.c185
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/health.c200
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mad.c78
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c548
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mcg.c106
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h75
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mr.c248
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c531
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pd.c101
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/port.c104
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qp.c302
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/srq.c223
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/uar.c224
-rw-r--r--drivers/net/ethernet/micrel/Kconfig66
-rw-r--r--drivers/net/ethernet/micrel/Makefile9
-rw-r--r--drivers/net/ethernet/micrel/ks8695net.c1631
-rw-r--r--drivers/net/ethernet/micrel/ks8695net.h107
-rw-r--r--drivers/net/ethernet/micrel/ks8842.c1272
-rw-r--r--drivers/net/ethernet/micrel/ks8851.c1623
-rw-r--r--drivers/net/ethernet/micrel/ks8851.h298
-rw-r--r--drivers/net/ethernet/micrel/ks8851_mll.c1713
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c7264
-rw-r--r--drivers/net/ethernet/microchip/Kconfig38
-rw-r--r--drivers/net/ethernet/microchip/Makefile5
-rw-r--r--drivers/net/ethernet/microchip/enc28j60.c1663
-rw-r--r--drivers/net/ethernet/microchip/enc28j60_hw.h309
-rw-r--r--drivers/net/ethernet/moxa/Kconfig30
-rw-r--r--drivers/net/ethernet/moxa/Makefile5
-rw-r--r--drivers/net/ethernet/moxa/moxart_ether.c567
-rw-r--r--drivers/net/ethernet/moxa/moxart_ether.h330
-rw-r--r--drivers/net/ethernet/myricom/Kconfig46
-rw-r--r--drivers/net/ethernet/myricom/Makefile5
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/Makefile5
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c4284
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp.h435
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h60
-rw-r--r--drivers/net/ethernet/natsemi/Kconfig67
-rw-r--r--drivers/net/ethernet/natsemi/Makefile9
-rw-r--r--drivers/net/ethernet/natsemi/jazzsonic.c294
-rw-r--r--drivers/net/ethernet/natsemi/macsonic.c641
-rw-r--r--drivers/net/ethernet/natsemi/natsemi.c3374
-rw-r--r--drivers/net/ethernet/natsemi/ns83820.c2307
-rw-r--r--drivers/net/ethernet/natsemi/sonic.c741
-rw-r--r--drivers/net/ethernet/natsemi/sonic.h450
-rw-r--r--drivers/net/ethernet/natsemi/xtsonic.c321
-rw-r--r--drivers/net/ethernet/neterion/Kconfig55
-rw-r--r--drivers/net/ethernet/neterion/Makefile6
-rw-r--r--drivers/net/ethernet/neterion/s2io-regs.h958
-rw-r--r--drivers/net/ethernet/neterion/s2io.c8656
-rw-r--r--drivers/net/ethernet/neterion/s2io.h1147
-rw-r--r--drivers/net/ethernet/neterion/vxge/Makefile7
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.c5114
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.h2111
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-ethtool.c1132
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-ethtool.h67
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c4871
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.h520
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-reg.h4636
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-traffic.c2480
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-traffic.h2290
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-version.h49
-rw-r--r--drivers/net/ethernet/netx-eth.c508
-rw-r--r--drivers/net/ethernet/nuvoton/Kconfig30
-rw-r--r--drivers/net/ethernet/nuvoton/Makefile5
-rw-r--r--drivers/net/ethernet/nuvoton/w90p910_ether.c1095
-rw-r--r--drivers/net/ethernet/nvidia/Kconfig32
-rw-r--r--drivers/net/ethernet/nvidia/Makefile5
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c6385
-rw-r--r--drivers/net/ethernet/nxp/Kconfig8
-rw-r--r--drivers/net/ethernet/nxp/Makefile1
-rw-r--r--drivers/net/ethernet/nxp/lpc_eth.c1609
-rw-r--r--drivers/net/ethernet/octeon/Kconfig14
-rw-r--r--drivers/net/ethernet/octeon/Makefile5
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c1619
-rw-r--r--drivers/net/ethernet/oki-semi/Kconfig23
-rw-r--r--drivers/net/ethernet/oki-semi/Makefile5
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/Kconfig22
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/Makefile4
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h689
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c262
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.h35
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c512
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c2845
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c521
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c377
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h37
-rw-r--r--drivers/net/ethernet/packetengines/Kconfig47
-rw-r--r--drivers/net/ethernet/packetengines/Makefile6
-rw-r--r--drivers/net/ethernet/packetengines/hamachi.c1943
-rw-r--r--drivers/net/ethernet/packetengines/yellowfin.c1425
-rw-r--r--drivers/net/ethernet/pasemi/Kconfig30
-rw-r--r--drivers/net/ethernet/pasemi/Makefile6
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c1906
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.h215
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac_ethtool.c159
-rw-r--r--drivers/net/ethernet/qlogic/Kconfig96
-rw-r--r--drivers/net/ethernet/qlogic/Makefile8
-rw-r--r--drivers/net/ethernet/qlogic/netxen/Makefile27
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic.h1889
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c943
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c959
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h1079
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c2593
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.h285
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c1931
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c3527
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.c3951
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.h1189
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/Makefile15
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h2378
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c4065
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h661
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c2448
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c284
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c1428
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c1147
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h122
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c1874
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h948
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c1686
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h225
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c1310
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c2208
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c4300
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c1358
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h276
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c2209
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c2047
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c1424
-rw-r--r--drivers/net/ethernet/qlogic/qlge/Makefile7
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge.h2338
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_dbg.c2042
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c735
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c5010
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_mpi.c1284
-rw-r--r--drivers/net/ethernet/rdc/Kconfig34
-rw-r--r--drivers/net/ethernet/rdc/Makefile5
-rw-r--r--drivers/net/ethernet/rdc/r6040.c1270
-rw-r--r--drivers/net/ethernet/realtek/8139cp.c2138
-rw-r--r--drivers/net/ethernet/realtek/8139too.c2682
-rw-r--r--drivers/net/ethernet/realtek/Kconfig115
-rw-r--r--drivers/net/ethernet/realtek/Makefile8
-rw-r--r--drivers/net/ethernet/realtek/atp.c883
-rw-r--r--drivers/net/ethernet/realtek/atp.h257
-rw-r--r--drivers/net/ethernet/realtek/r8169.c7245
-rw-r--r--drivers/net/ethernet/renesas/Kconfig16
-rw-r--r--drivers/net/ethernet/renesas/Makefile5
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c3000
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h573
-rw-r--r--drivers/net/ethernet/s6gmac.c1059
-rw-r--r--drivers/net/ethernet/samsung/Kconfig32
-rw-r--r--drivers/net/ethernet/samsung/Makefile5
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/Makefile4
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h537
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_core.c284
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.c522
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.h296
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.c368
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.h50
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c524
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c2319
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c254
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.c254
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.h104
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c259
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h491
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.c91
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.h38
-rw-r--r--drivers/net/ethernet/seeq/Kconfig35
-rw-r--r--drivers/net/ethernet/seeq/Makefile6
-rw-r--r--drivers/net/ethernet/seeq/ether3.c899
-rw-r--r--drivers/net/ethernet/seeq/ether3.h176
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.c838
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.h103
-rw-r--r--drivers/net/ethernet/sfc/Kconfig38
-rw-r--r--drivers/net/ethernet/sfc/Makefile8
-rw-r--r--drivers/net/ethernet/sfc/bitfield.h542
-rw-r--r--drivers/net/ethernet/sfc/ef10.c3699
-rw-r--r--drivers/net/ethernet/sfc/ef10_regs.h355
-rw-r--r--drivers/net/ethernet/sfc/efx.c3288
-rw-r--r--drivers/net/ethernet/sfc/efx.h250
-rw-r--r--drivers/net/ethernet/sfc/enum.h181
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c1133
-rw-r--r--drivers/net/ethernet/sfc/falcon.c2875
-rw-r--r--drivers/net/ethernet/sfc/falcon_boards.c764
-rw-r--r--drivers/net/ethernet/sfc/farch.c2959
-rw-r--r--drivers/net/ethernet/sfc/farch_regs.h2932
-rw-r--r--drivers/net/ethernet/sfc/filter.h272
-rw-r--r--drivers/net/ethernet/sfc/io.h302
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c1891
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h361
-rw-r--r--drivers/net/ethernet/sfc/mcdi_mon.c534
-rw-r--r--drivers/net/ethernet/sfc/mcdi_pcol.h7907
-rw-r--r--drivers/net/ethernet/sfc/mcdi_port.c1024
-rw-r--r--drivers/net/ethernet/sfc/mdio_10g.c323
-rw-r--r--drivers/net/ethernet/sfc/mdio_10g.h110
-rw-r--r--drivers/net/ethernet/sfc/mtd.c133
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h1359
-rw-r--r--drivers/net/ethernet/sfc/nic.c534
-rw-r--r--drivers/net/ethernet/sfc/nic.h798
-rw-r--r--drivers/net/ethernet/sfc/phy.h50
-rw-r--r--drivers/net/ethernet/sfc/ptp.c1939
-rw-r--r--drivers/net/ethernet/sfc/qt202x_phy.c495
-rw-r--r--drivers/net/ethernet/sfc/rx.c981
-rw-r--r--drivers/net/ethernet/sfc/selftest.c789
-rw-r--r--drivers/net/ethernet/sfc/selftest.h55
-rw-r--r--drivers/net/ethernet/sfc/siena.c1017
-rw-r--r--drivers/net/ethernet/sfc/siena_sriov.c1645
-rw-r--r--drivers/net/ethernet/sfc/tenxpress.c494
-rw-r--r--drivers/net/ethernet/sfc/tx.c1347
-rw-r--r--drivers/net/ethernet/sfc/txc43128_phy.c560
-rw-r--r--drivers/net/ethernet/sfc/vfdi.h255
-rw-r--r--drivers/net/ethernet/sfc/workarounds.h53
-rw-r--r--drivers/net/ethernet/sgi/Kconfig35
-rw-r--r--drivers/net/ethernet/sgi/Makefile6
-rw-r--r--drivers/net/ethernet/sgi/ioc3-eth.c1672
-rw-r--r--drivers/net/ethernet/sgi/meth.c882
-rw-r--r--drivers/net/ethernet/sgi/meth.h243
-rw-r--r--drivers/net/ethernet/silan/Kconfig33
-rw-r--r--drivers/net/ethernet/silan/Makefile5
-rw-r--r--drivers/net/ethernet/silan/sc92031.c1584
-rw-r--r--drivers/net/ethernet/sis/Kconfig51
-rw-r--r--drivers/net/ethernet/sis/Makefile6
-rw-r--r--drivers/net/ethernet/sis/sis190.c1933
-rw-r--r--drivers/net/ethernet/sis/sis900.c2517
-rw-r--r--drivers/net/ethernet/sis/sis900.h329
-rw-r--r--drivers/net/ethernet/smsc/Kconfig131
-rw-r--r--drivers/net/ethernet/smsc/Makefile11
-rw-r--r--drivers/net/ethernet/smsc/epic100.c1596
-rw-r--r--drivers/net/ethernet/smsc/smc911x.c2183
-rw-r--r--drivers/net/ethernet/smsc/smc911x.h923
-rw-r--r--drivers/net/ethernet/smsc/smc9194.c1559
-rw-r--r--drivers/net/ethernet/smsc/smc9194.h241
-rw-r--r--drivers/net/ethernet/smsc/smc91c92_cs.c2055
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c2422
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h1163
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c2634
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.h405
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c1751
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.h275
-rw-r--r--drivers/net/ethernet/stmicro/Kconfig23
-rw-r--r--drivers/net/ethernet/stmicro/Makefile5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig90
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c160
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h458
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h219
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs_com.h134
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c130
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c330
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c140
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100.h126
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h266
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c391
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c201
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c189
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c146
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h116
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c267
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/enh_desc.c402
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc.h135
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc_core.c267
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/norm_desc.c273
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c135
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h201
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c780
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c148
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c3014
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c318
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c195
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c340
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c212
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h74
-rw-r--r--drivers/net/ethernet/sun/Kconfig88
-rw-r--r--drivers/net/ethernet/sun/Makefile11
-rw-r--r--drivers/net/ethernet/sun/cassini.c5292
-rw-r--r--drivers/net/ethernet/sun/cassini.h2912
-rw-r--r--drivers/net/ethernet/sun/niu.c10230
-rw-r--r--drivers/net/ethernet/sun/niu.h3306
-rw-r--r--drivers/net/ethernet/sun/sunbmac.c1283
-rw-r--r--drivers/net/ethernet/sun/sunbmac.h338
-rw-r--r--drivers/net/ethernet/sun/sungem.c3028
-rw-r--r--drivers/net/ethernet/sun/sungem.h1027
-rw-r--r--drivers/net/ethernet/sun/sunhme.c3340
-rw-r--r--drivers/net/ethernet/sun/sunhme.h513
-rw-r--r--drivers/net/ethernet/sun/sunqe.c996
-rw-r--r--drivers/net/ethernet/sun/sunqe.h350
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c1304
-rw-r--r--drivers/net/ethernet/sun/sunvnet.h83
-rw-r--r--drivers/net/ethernet/tehuti/Kconfig27
-rw-r--r--drivers/net/ethernet/tehuti/Makefile5
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.c2492
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.h561
-rw-r--r--drivers/net/ethernet/ti/Kconfig105
-rw-r--r--drivers/net/ethernet/ti/Makefile12
-rw-r--r--drivers/net/ethernet/ti/cpmac.c1286
-rw-r--r--drivers/net/ethernet/ti/cpsw-phy-sel.c221
-rw-r--r--drivers/net/ethernet/ti/cpsw.c2364
-rw-r--r--drivers/net/ethernet/ti/cpsw.h44
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c765
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.h108
-rw-r--r--drivers/net/ethernet/ti/cpts.c416
-rw-r--r--drivers/net/ethernet/ti/cpts.h144
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c1043
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.h117
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c2121
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c488
-rw-r--r--drivers/net/ethernet/ti/tlan.c3253
-rw-r--r--drivers/net/ethernet/ti/tlan.h545
-rw-r--r--drivers/net/ethernet/tile/Kconfig18
-rw-r--r--drivers/net/ethernet/tile/Makefile10
-rw-r--r--drivers/net/ethernet/tile/tilegx.c2281
-rw-r--r--drivers/net/ethernet/tile/tilepro.c2425
-rw-r--r--drivers/net/ethernet/toshiba/Kconfig57
-rw-r--r--drivers/net/ethernet/toshiba/Makefile10
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.c1894
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.h384
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.c2680
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.h326
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.c2604
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.h489
-rw-r--r--drivers/net/ethernet/toshiba/spider_net_ethtool.c181
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c2208
-rw-r--r--drivers/net/ethernet/tundra/Kconfig29
-rw-r--r--drivers/net/ethernet/tundra/Makefile5
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.c1695
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.h354
-rw-r--r--drivers/net/ethernet/via/Kconfig56
-rw-r--r--drivers/net/ethernet/via/Makefile6
-rw-r--r--drivers/net/ethernet/via/via-rhine.c2568
-rw-r--r--drivers/net/ethernet/via/via-velocity.c3766
-rw-r--r--drivers/net/ethernet/via/via-velocity.h1581
-rw-r--r--drivers/net/ethernet/wiznet/Kconfig74
-rw-r--r--drivers/net/ethernet/wiznet/Makefile2
-rw-r--r--drivers/net/ethernet/wiznet/w5100.c801
-rw-r--r--drivers/net/ethernet/wiznet/w5300.c713
-rw-r--r--drivers/net/ethernet/xilinx/Kconfig44
-rw-r--r--drivers/net/ethernet/xilinx/Makefile9
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac.h385
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c1184
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_mdio.c122
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet.h506
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_main.c1659
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c239
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c1259
-rw-r--r--drivers/net/ethernet/xircom/Kconfig31
-rw-r--r--drivers/net/ethernet/xircom/Makefile5
-rw-r--r--drivers/net/ethernet/xircom/xirc2ps_cs.c1798
-rw-r--r--drivers/net/ethernet/xscale/Kconfig31
-rw-r--r--drivers/net/ethernet/xscale/Makefile5
-rw-r--r--drivers/net/ethernet/xscale/ixp4xx_eth.c1537
1051 files changed, 1042152 insertions, 0 deletions
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
new file mode 100644
index 00000000000..a968654b631
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -0,0 +1,1454 @@
+/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
+/*
+ Written 1993-2000 by Donald Becker.
+
+ Copyright 1994-2000 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+
+ This driver is for the 3Com EtherLinkIII series.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Known limitations:
+ Because of the way 3c509 ISA detection works it's difficult to predict
+ a priori which of several ISA-mode cards will be detected first.
+
+ This driver does not use predictive interrupt mode, resulting in higher
+ packet latency but lower overhead. If interrupts are disabled for an
+ unusually long time it could also result in missed packets, but in
+ practice this rarely happens.
+
+
+ FIXES:
+ Alan Cox: Removed the 'Unexpected interrupt' bug.
+ Michael Meskes: Upgraded to Donald Becker's version 1.07.
+ Alan Cox: Increased the eeprom delay. Regardless of
+ what the docs say some people definitely
+ get problems with lower (but in card spec)
+ delays
+ v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
+ other cleanups. -djb
+ Andrea Arcangeli: Upgraded to Donald Becker's version 1.12.
+ Rick Payne: Fixed SMP race condition
+ v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
+ v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
+ v1.15 1/31/98 Faster recovery for Tx errors. -djb
+ v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
+ v1.18 12Mar2001 Andrew Morton
+ - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
+ - Reviewed against 1.18 from scyld.com
+ v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
+ - ethtool support
+ v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ - Power Management support
+ v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
+ - Full duplex support
+ v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
+ - Additional ethtool features
+ v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
+ - Increase *read_eeprom udelay to workaround oops with 2 cards.
+ v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
+ - Introduce driver model for EISA cards.
+ v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org>
+ - convert to isa_driver and pnp_driver and some cleanups
+*/
+
+#define DRV_NAME "3c509"
+#define DRV_VERSION "1.20"
+#define DRV_RELDATE "04Feb2008"
+
+/* A few values that may be tweaked. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (400*HZ/1000)
+
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/pm.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h> /* for udelay() */
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/device.h>
+#include <linux/eisa.h>
+#include <linux/bitops.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+static char version[] = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE " becker@scyld.com\n";
+
+#ifdef EL3_DEBUG
+static int el3_debug = EL3_DEBUG;
+#else
+static int el3_debug = 2;
+#endif
+
+/* Used to do a global count of all the cards in the system. Must be
+ * a global variable so that the eisa probe routines can increment
+ * it */
+static int el3_cards = 0;
+#define EL3_MAX_CARDS 8
+
+/* To minimize the size of the driver source I only define operating
+ constants if they are used several times. You'll need the manual
+ anyway if you want to understand driver details. */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define EEPROM_READ 0x80
+
+#define EL3_IO_EXTENT 16
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable. */
+enum c509cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
+ PowerDown = 28<<11, PowerAuto = 29<<11};
+
+enum c509status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+
+#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */
+#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */
+#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */
+
+/*
+ * Must be a power of two (we use a binary and in the
+ * circular queue)
+ */
+#define SKB_QUEUE_SIZE 64
+
+enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
+
+struct el3_private {
+ spinlock_t lock;
+ /* skb send-queue */
+ int head, size;
+ struct sk_buff *queue[SKB_QUEUE_SIZE];
+ enum el3_cardtype type;
+};
+static int id_port;
+static int current_tag;
+static struct net_device *el3_devs[EL3_MAX_CARDS];
+
+/* Parameters that may be passed into the module. */
+static int debug = -1;
+static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 10;
+#ifdef CONFIG_PNP
+static int nopnp;
+#endif
+
+static int el3_common_init(struct net_device *dev);
+static void el3_common_remove(struct net_device *dev);
+static ushort id_read_eeprom(int index);
+static ushort read_eeprom(int ioaddr, int index);
+static int el3_open(struct net_device *dev);
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev);
+static int el3_close(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void el3_tx_timeout (struct net_device *dev);
+static void el3_down(struct net_device *dev);
+static void el3_up(struct net_device *dev);
+static const struct ethtool_ops ethtool_ops;
+#ifdef CONFIG_PM
+static int el3_suspend(struct device *, pm_message_t);
+static int el3_resume(struct device *);
+#else
+#define el3_suspend NULL
+#define el3_resume NULL
+#endif
+
+
+/* generic device remove for all device types */
+static int el3_device_remove (struct device *device);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void el3_poll_controller(struct net_device *dev);
+#endif
+
+/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
+static int el3_isa_id_sequence(__be16 *phys_addr)
+{
+ short lrs_state = 0xff;
+ int i;
+
+ /* ISA boards are detected by sending the ID sequence to the
+ ID_PORT. We find cards past the first by setting the 'current_tag'
+ on cards as they are found. Cards with their tag set will not
+ respond to subsequent ID sequences. */
+
+ outb(0x00, id_port);
+ outb(0x00, id_port);
+ for (i = 0; i < 255; i++) {
+ outb(lrs_state, id_port);
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+ /* For the first probe, clear all board's tag registers. */
+ if (current_tag == 0)
+ outb(0xd0, id_port);
+ else /* Otherwise kill off already-found boards. */
+ outb(0xd8, id_port);
+ if (id_read_eeprom(7) != 0x6d50)
+ return 1;
+ /* Read in EEPROM data, which does contention-select.
+ Only the lowest address board will stay "on-line".
+ 3Com got the byte order backwards. */
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(id_read_eeprom(i));
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+ /* The ISA PnP 3c509 cards respond to the ID sequence too.
+ This check is needed in order not to register them twice. */
+ for (i = 0; i < el3_cards; i++) {
+ struct el3_private *lp = netdev_priv(el3_devs[i]);
+ if (lp->type == EL3_PNP &&
+ ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) {
+ if (el3_debug > 3)
+ pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
+ phys_addr[0] & 0xff, phys_addr[0] >> 8,
+ phys_addr[1] & 0xff, phys_addr[1] >> 8,
+ phys_addr[2] & 0xff, phys_addr[2] >> 8);
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, id_port);
+ return 2;
+ }
+ }
+ }
+#endif /* CONFIG_PNP */
+ return 0;
+
+}
+
+static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
+ int irq, int if_port, enum el3_cardtype type)
+{
+ struct el3_private *lp = netdev_priv(dev);
+
+ memcpy(dev->dev_addr, phys_addr, ETH_ALEN);
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->if_port = if_port;
+ lp->type = type;
+}
+
+static int el3_isa_match(struct device *pdev, unsigned int ndev)
+{
+ struct net_device *dev;
+ int ioaddr, isa_irq, if_port, err;
+ unsigned int iobase;
+ __be16 phys_addr[3];
+
+ while ((err = el3_isa_id_sequence(phys_addr)) == 2)
+ ; /* Skip to next card when PnP card found */
+ if (err == 1)
+ return 0;
+
+ iobase = id_read_eeprom(8);
+ if_port = iobase >> 14;
+ ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+ if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
+ isa_irq = irq[el3_cards];
+ else
+ isa_irq = id_read_eeprom(9) >> 12;
+
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(dev, pdev);
+ netdev_boot_setup_check(dev);
+
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, id_port);
+
+ /* Activate the adaptor at the EEPROM location. */
+ outb((ioaddr >> 4) | 0xe0, id_port);
+
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
+ dev_set_drvdata(pdev, dev);
+ if (el3_common_init(dev)) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 1;
+}
+
+static int el3_isa_remove(struct device *pdev,
+ unsigned int ndev)
+{
+ el3_device_remove(pdev);
+ dev_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int el3_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ current_tag = 0;
+ return el3_suspend(dev, state);
+}
+
+static int el3_isa_resume(struct device *dev, unsigned int n)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ int ioaddr = ndev->base_addr, err;
+ __be16 phys_addr[3];
+
+ while ((err = el3_isa_id_sequence(phys_addr)) == 2)
+ ; /* Skip to next card when PnP card found */
+ if (err == 1)
+ return 0;
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, id_port);
+ /* Enable the card */
+ outb((ioaddr >> 4) | 0xe0, id_port);
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50)
+ return 1;
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ return el3_resume(dev);
+}
+#endif
+
+static struct isa_driver el3_isa_driver = {
+ .match = el3_isa_match,
+ .remove = el3_isa_remove,
+#ifdef CONFIG_PM
+ .suspend = el3_isa_suspend,
+ .resume = el3_isa_resume,
+#endif
+ .driver = {
+ .name = "3c509"
+ },
+};
+static int isa_registered;
+
+#ifdef CONFIG_PNP
+static struct pnp_device_id el3_pnp_ids[] = {
+ { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
+ { .id = "TCM5091" }, /* 3Com Etherlink III */
+ { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
+ { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
+ { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
+ { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
+ { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
+
+static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
+{
+ short i;
+ int ioaddr, irq, if_port;
+ __be16 phys_addr[3];
+ struct net_device *dev = NULL;
+ int err;
+
+ ioaddr = pnp_port_start(pdev, 0);
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
+ return -EBUSY;
+ irq = pnp_irq(pdev, 0);
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+ if_port = read_eeprom(ioaddr, 8) >> 14;
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev) {
+ release_region(ioaddr, EL3_IO_EXTENT);
+ return -ENOMEM;
+ }
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ netdev_boot_setup_check(dev);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
+ pnp_set_drvdata(pdev, dev);
+ err = el3_common_init(dev);
+
+ if (err) {
+ pnp_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return err;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 0;
+}
+
+static void el3_pnp_remove(struct pnp_dev *pdev)
+{
+ el3_common_remove(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ return el3_suspend(&pdev->dev, state);
+}
+
+static int el3_pnp_resume(struct pnp_dev *pdev)
+{
+ return el3_resume(&pdev->dev);
+}
+#endif
+
+static struct pnp_driver el3_pnp_driver = {
+ .name = "3c509",
+ .id_table = el3_pnp_ids,
+ .probe = el3_pnp_probe,
+ .remove = el3_pnp_remove,
+#ifdef CONFIG_PM
+ .suspend = el3_pnp_suspend,
+ .resume = el3_pnp_resume,
+#endif
+};
+static int pnp_registered;
+#endif /* CONFIG_PNP */
+
+#ifdef CONFIG_EISA
+static struct eisa_device_id el3_eisa_ids[] = {
+ { "TCM5090" },
+ { "TCM5091" },
+ { "TCM5092" },
+ { "TCM5093" },
+ { "TCM5094" },
+ { "TCM5095" },
+ { "TCM5098" },
+ { "" }
+};
+MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
+
+static int el3_eisa_probe (struct device *device);
+
+static struct eisa_driver el3_eisa_driver = {
+ .id_table = el3_eisa_ids,
+ .driver = {
+ .name = "3c579",
+ .probe = el3_eisa_probe,
+ .remove = el3_device_remove,
+ .suspend = el3_suspend,
+ .resume = el3_resume,
+ }
+};
+static int eisa_registered;
+#endif
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = el3_open,
+ .ndo_stop = el3_close,
+ .ndo_start_xmit = el3_start_xmit,
+ .ndo_get_stats = el3_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = el3_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = el3_poll_controller,
+#endif
+};
+
+static int el3_common_init(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int err;
+ const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+
+ spin_lock_init(&lp->lock);
+
+ if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
+ dev->if_port = (dev->mem_start & 0x0f);
+ } else { /* xcvr codes 0/8 */
+ /* use eeprom value, but save user's full-duplex selection */
+ dev->if_port |= (dev->mem_start & 0x08);
+ }
+
+ /* The EL3-specific entries in the device structure. */
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->ethtool_ops = &ethtool_ops;
+
+ err = register_netdev(dev);
+ if (err) {
+ pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
+ dev->base_addr, dev->irq);
+ release_region(dev->base_addr, EL3_IO_EXTENT);
+ return err;
+ }
+
+ pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
+ dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
+ dev->dev_addr, dev->irq);
+
+ if (el3_debug > 0)
+ pr_info("%s", version);
+ return 0;
+
+}
+
+static void el3_common_remove (struct net_device *dev)
+{
+ unregister_netdev (dev);
+ release_region(dev->base_addr, EL3_IO_EXTENT);
+ free_netdev (dev);
+}
+
+#ifdef CONFIG_EISA
+static int __init el3_eisa_probe (struct device *device)
+{
+ short i;
+ int ioaddr, irq, if_port;
+ __be16 phys_addr[3];
+ struct net_device *dev = NULL;
+ struct eisa_device *edev;
+ int err;
+
+ /* Yeepee, The driver framework is calling us ! */
+ edev = to_eisa_device (device);
+ ioaddr = edev->base_addr;
+
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
+ return -EBUSY;
+
+ /* Change the register set to the configuration window 0. */
+ outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
+
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6)>>14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ /* Restore the "Product ID" to the EEPROM read register. */
+ read_eeprom(ioaddr, 3);
+
+ dev = alloc_etherdev(sizeof (struct el3_private));
+ if (dev == NULL) {
+ release_region(ioaddr, EL3_IO_EXTENT);
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, device);
+ netdev_boot_setup_check(dev);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
+ eisa_set_drvdata (edev, dev);
+ err = el3_common_init(dev);
+
+ if (err) {
+ eisa_set_drvdata (edev, NULL);
+ free_netdev(dev);
+ return err;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 0;
+}
+#endif
+
+/* This remove works for all device types.
+ *
+ * The net dev must be stored in the driver data field */
+static int el3_device_remove(struct device *device)
+{
+ struct net_device *dev;
+
+ dev = dev_get_drvdata(device);
+
+ el3_common_remove (dev);
+ return 0;
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ Assume that we are in register window zero.
+ */
+static ushort read_eeprom(int ioaddr, int index)
+{
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Pause for at least 162 us. for the read to take place.
+ Some chips seem to require much longer */
+ mdelay(2);
+ return inw(ioaddr + 12);
+}
+
+/* Read a word from the EEPROM when in the ISA ID probe state. */
+static ushort id_read_eeprom(int index)
+{
+ int bit, word = 0;
+
+ /* Issue read command, and pause for at least 162 us. for it to complete.
+ Assume extra-fast 16Mhz bus. */
+ outb(EEPROM_READ + index, id_port);
+
+ /* Pause for at least 162 us. for the read to take place. */
+ /* Some chips seem to require much longer */
+ mdelay(4);
+
+ for (bit = 15; bit >= 0; bit--)
+ word = (word << 1) + (inb(id_port) & 0x01);
+
+ if (el3_debug > 3)
+ pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+ return word;
+}
+
+
+static int
+el3_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(RxReset, ioaddr + EL3_CMD);
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+ i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
+ if (i)
+ return i;
+
+ EL3WINDOW(0);
+ if (el3_debug > 3)
+ pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
+ dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
+
+ el3_up(dev);
+
+ if (el3_debug > 3)
+ pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+
+ return 0;
+}
+
+static void
+el3_tx_timeout (struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ pr_warning("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d.\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
+ inw(ioaddr + TX_FREE));
+ dev->stats.tx_errors++;
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+}
+
+
+static netdev_tx_t
+el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ netif_stop_queue (dev);
+
+ dev->stats.tx_bytes += skb->len;
+
+ if (el3_debug > 4) {
+ pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ }
+ /*
+ * We lock the driver against other processors. Note
+ * we don't need to lock versus the IRQ as we suspended
+ * that. This means that we lose the ability to take
+ * an RX during a TX upload. That sucks a bit with SMP
+ * on an original 3c509 (2K buffer)
+ *
+ * Using disable_irq stops us crapping on other
+ * time sensitive devices.
+ */
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+ if (inw(ioaddr + TX_FREE) > 1536)
+ netif_start_queue(dev);
+ else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ dev_consume_skb_any (skb);
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+ }
+ return NETDEV_TX_OK;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t
+el3_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct el3_private *lp;
+ int ioaddr, status;
+ int i = max_interrupt_work;
+
+ lp = netdev_priv(dev);
+ spin_lock(&lp->lock);
+
+ ioaddr = dev->base_addr;
+
+ if (el3_debug > 4) {
+ status = inw(ioaddr + EL3_STATUS);
+ pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ while ((status = inw(ioaddr + EL3_STATUS)) &
+ (IntLatch | RxComplete | StatsFull)) {
+
+ if (status & RxComplete)
+ el3_rx(dev);
+
+ if (status & TxAvailable) {
+ if (el3_debug > 5)
+ pr_debug(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue (dev);
+ }
+ if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
+ /* Handle all uncommon interrupts. */
+ if (status & StatsFull) /* Empty statistics. */
+ update_stats(dev);
+ if (status & RxEarly) { /* Rx early is unused. */
+ el3_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & TxComplete) { /* Really Tx error. */
+ short tx_status;
+ int i = 4;
+
+ while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+ }
+ if (status & AdapterFailure) {
+ /* Adapter failure requires Rx reset and reinit. */
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ outw(SetRxFilter | RxStation | RxBroadcast
+ | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+ | (dev->flags & IFF_PROMISC ? RxProm : 0),
+ ioaddr + EL3_CMD);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (--i < 0) {
+ pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupts. */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
+ }
+
+ if (el3_debug > 4) {
+ pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ }
+ spin_unlock(&lp->lock);
+ return IRQ_HANDLED;
+}
+
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void el3_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ el3_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+static struct net_device_stats *
+el3_get_stats(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ /*
+ * This is fast enough not to bother with disable IRQ
+ * stuff.
+ */
+
+ spin_lock_irqsave(&lp->lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return &dev->stats;
+}
+
+/* Update statistics. We change to register window 6, 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 1 is always valid rather than use a special window-state variable.
+ */
+static void update_stats(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 5)
+ pr_debug(" Updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+}
+
+static int
+el3_rx(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ short rx_status;
+
+ if (el3_debug > 5)
+ pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ dev->stats.rx_errors++;
+ switch (error) {
+ case 0x0000: dev->stats.rx_over_errors++; break;
+ case 0x0800: dev->stats.rx_length_errors++; break;
+ case 0x1000: dev->stats.rx_frame_errors++; break;
+ case 0x1800: dev->stats.rx_length_errors++; break;
+ case 0x2000: dev->stats.rx_frame_errors++; break;
+ case 0x2800: dev->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+ if (el3_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb_reserve(skb, 2); /* Align IP on 16 byte */
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
+ (pkt_len + 3) >> 2);
+
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_bytes += pkt_len;
+ dev->stats.rx_packets++;
+ continue;
+ }
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ dev->stats.rx_dropped++;
+ if (el3_debug)
+ pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ inw(ioaddr + EL3_STATUS); /* Delay. */
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS) );
+ }
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct net_device *dev)
+{
+ unsigned long flags;
+ struct el3_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int mc_count = netdev_mc_count(dev);
+
+ if (el3_debug > 1) {
+ static int old;
+ if (old != mc_count) {
+ old = mc_count;
+ pr_debug("%s: Setting Rx mode to %d addresses.\n",
+ dev->name, mc_count);
+ }
+ }
+ spin_lock_irqsave(&lp->lock, flags);
+ if (dev->flags&IFF_PROMISC) {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ }
+ else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
+ }
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static int
+el3_close(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct el3_private *lp = netdev_priv(dev);
+
+ if (el3_debug > 2)
+ pr_debug("%s: Shutting down ethercard.\n", dev->name);
+
+ el3_down(dev);
+
+ free_irq(dev->irq, dev);
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ if (lp->type != EL3_EISA) {
+ /* But we explicitly zero the IRQ line select anyway. Don't do
+ * it on EISA cards, it prevents the module from getting an
+ * IRQ after unload+reload... */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ }
+
+ return 0;
+}
+
+static int
+el3_link_ok(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ u16 tmp;
+
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ return tmp & (1<<11);
+}
+
+static int
+el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ u16 tmp;
+ int ioaddr = dev->base_addr;
+
+ EL3WINDOW(0);
+ /* obtain current transceiver via WN4_MEDIA? */
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ ecmd->transceiver = XCVR_INTERNAL;
+ switch (tmp >> 14) {
+ case 0:
+ ecmd->port = PORT_TP;
+ break;
+ case 1:
+ ecmd->port = PORT_AUI;
+ ecmd->transceiver = XCVR_EXTERNAL;
+ break;
+ case 3:
+ ecmd->port = PORT_BNC;
+ default:
+ break;
+ }
+
+ ecmd->duplex = DUPLEX_HALF;
+ ecmd->supported = 0;
+ tmp = inw(ioaddr + WN0_CONF_CTRL);
+ if (tmp & (1<<13))
+ ecmd->supported |= SUPPORTED_AUI;
+ if (tmp & (1<<12))
+ ecmd->supported |= SUPPORTED_BNC;
+ if (tmp & (1<<9)) {
+ ecmd->supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full; /* hmm... */
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_NETDIAG);
+ if (tmp & FD_ENABLE)
+ ecmd->duplex = DUPLEX_FULL;
+ }
+
+ ethtool_cmd_speed_set(ecmd, SPEED_10);
+ EL3WINDOW(1);
+ return 0;
+}
+
+static int
+el3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ u16 tmp;
+ int ioaddr = dev->base_addr;
+
+ if (ecmd->speed != SPEED_10)
+ return -EINVAL;
+ if ((ecmd->duplex != DUPLEX_HALF) && (ecmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if ((ecmd->transceiver != XCVR_INTERNAL) && (ecmd->transceiver != XCVR_EXTERNAL))
+ return -EINVAL;
+
+ /* change XCVR type */
+ EL3WINDOW(0);
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ switch (ecmd->port) {
+ case PORT_TP:
+ tmp &= ~(3<<14);
+ dev->if_port = 0;
+ break;
+ case PORT_AUI:
+ tmp |= (1<<14);
+ dev->if_port = 1;
+ break;
+ case PORT_BNC:
+ tmp |= (3<<14);
+ dev->if_port = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ outw(tmp, ioaddr + WN0_ADDR_CONF);
+ if (dev->if_port == 3) {
+ /* fire up the DC-DC convertor if BNC gets enabled */
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ if (tmp & (3 << 14)) {
+ outw(StartCoax, ioaddr + EL3_CMD);
+ udelay(800);
+ } else
+ return -EIO;
+ }
+
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_NETDIAG);
+ if (ecmd->duplex == DUPLEX_FULL)
+ tmp |= FD_ENABLE;
+ else
+ tmp &= ~FD_ENABLE;
+ outw(tmp, ioaddr + WN4_NETDIAG);
+ EL3WINDOW(1);
+
+ return 0;
+}
+
+static void el3_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));
+}
+
+static int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ret;
+
+ spin_lock_irq(&lp->lock);
+ ret = el3_netdev_get_ecmd(dev, ecmd);
+ spin_unlock_irq(&lp->lock);
+ return ret;
+}
+
+static int el3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ret;
+
+ spin_lock_irq(&lp->lock);
+ ret = el3_netdev_set_ecmd(dev, ecmd);
+ spin_unlock_irq(&lp->lock);
+ return ret;
+}
+
+static u32 el3_get_link(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ u32 ret;
+
+ spin_lock_irq(&lp->lock);
+ ret = el3_link_ok(dev);
+ spin_unlock_irq(&lp->lock);
+ return ret;
+}
+
+static u32 el3_get_msglevel(struct net_device *dev)
+{
+ return el3_debug;
+}
+
+static void el3_set_msglevel(struct net_device *dev, u32 v)
+{
+ el3_debug = v;
+}
+
+static const struct ethtool_ops ethtool_ops = {
+ .get_drvinfo = el3_get_drvinfo,
+ .get_settings = el3_get_settings,
+ .set_settings = el3_set_settings,
+ .get_link = el3_get_link,
+ .get_msglevel = el3_get_msglevel,
+ .set_msglevel = el3_set_msglevel,
+};
+
+static void
+el3_down(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change here next open(). */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
+
+ update_stats(dev);
+}
+
+static void
+el3_up(struct net_device *dev)
+{
+ int i, sw_info, net_diag;
+ int ioaddr = dev->base_addr;
+
+ /* Activating the board required and does no harm otherwise */
+ outw(0x0001, ioaddr + 4);
+
+ /* Set the IRQ line. */
+ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+
+ /* Set the station address in window 2 each time opened. */
+ EL3WINDOW(2);
+
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ if ((dev->if_port & 0x03) == 3) /* BNC interface */
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
+ /* Combine secondary sw_info word (the adapter level) and primary
+ sw_info word (duplex setting plus other useless bits) */
+ EL3WINDOW(0);
+ sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
+ (read_eeprom(ioaddr, 0x0d) & 0xBff0);
+
+ EL3WINDOW(4);
+ net_diag = inw(ioaddr + WN4_NETDIAG);
+ net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
+ pr_info("%s: ", dev->name);
+ switch (dev->if_port & 0x0c) {
+ case 12:
+ /* force full-duplex mode if 3c5x9b */
+ if (sw_info & 0x000f) {
+ pr_cont("Forcing 3c5x9b full-duplex mode");
+ break;
+ }
+ case 8:
+ /* set full-duplex mode based on eeprom config setting */
+ if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
+ pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
+ break;
+ }
+ default:
+ /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
+ pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
+ net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
+ }
+
+ outw(net_diag, ioaddr + WN4_NETDIAG);
+ pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
+ if (el3_debug > 3)
+ pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
+ /* Enable link beat and jabber check. */
+ outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ /* Accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
+ ioaddr + EL3_CMD);
+
+ netif_start_queue(dev);
+}
+
+/* Power Management support functions */
+#ifdef CONFIG_PM
+
+static int
+el3_suspend(struct device *pdev, pm_message_t state)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct el3_private *lp;
+ int ioaddr;
+
+ dev = dev_get_drvdata(pdev);
+ lp = netdev_priv(dev);
+ ioaddr = dev->base_addr;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ if (netif_running(dev))
+ netif_device_detach(dev);
+
+ el3_down(dev);
+ outw(PowerDown, ioaddr + EL3_CMD);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 0;
+}
+
+static int
+el3_resume(struct device *pdev)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct el3_private *lp;
+ int ioaddr;
+
+ dev = dev_get_drvdata(pdev);
+ lp = netdev_priv(dev);
+ ioaddr = dev->base_addr;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ outw(PowerUp, ioaddr + EL3_CMD);
+ EL3WINDOW(0);
+ el3_up(dev);
+
+ if (netif_running(dev))
+ netif_device_attach(dev);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+module_param(debug,int, 0);
+module_param_array(irq, int, NULL, 0);
+module_param(max_interrupt_work, int, 0);
+MODULE_PARM_DESC(debug, "debug level (0-6)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
+#ifdef CONFIG_PNP
+module_param(nopnp, int, 0);
+MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
+#endif /* CONFIG_PNP */
+MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
+MODULE_LICENSE("GPL");
+
+static int __init el3_init_module(void)
+{
+ int ret = 0;
+
+ if (debug >= 0)
+ el3_debug = debug;
+
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+ ret = pnp_register_driver(&el3_pnp_driver);
+ if (!ret)
+ pnp_registered = 1;
+ }
+#endif
+ /* Select an open I/O location at 0x1*0 to do ISA contention select. */
+ /* Start with 0x110 to avoid some sound cards.*/
+ for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
+ if (!request_region(id_port, 1, "3c509-control"))
+ continue;
+ outb(0x00, id_port);
+ outb(0xff, id_port);
+ if (inb(id_port) & 0x01)
+ break;
+ else
+ release_region(id_port, 1);
+ }
+ if (id_port >= 0x200) {
+ id_port = 0;
+ pr_err("No I/O port available for 3c509 activation.\n");
+ } else {
+ ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
+ if (!ret)
+ isa_registered = 1;
+ }
+#ifdef CONFIG_EISA
+ ret = eisa_driver_register(&el3_eisa_driver);
+ if (!ret)
+ eisa_registered = 1;
+#endif
+
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ ret = 0;
+#endif
+ if (isa_registered)
+ ret = 0;
+#ifdef CONFIG_EISA
+ if (eisa_registered)
+ ret = 0;
+#endif
+ return ret;
+}
+
+static void __exit el3_cleanup_module(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_driver(&el3_pnp_driver);
+#endif
+ if (isa_registered)
+ isa_unregister_driver(&el3_isa_driver);
+ if (id_port)
+ release_region(id_port, 1);
+#ifdef CONFIG_EISA
+ if (eisa_registered)
+ eisa_driver_unregister(&el3_eisa_driver);
+#endif
+}
+
+module_init (el3_init_module);
+module_exit (el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
new file mode 100644
index 00000000000..94c656f5a05
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c515.c
@@ -0,0 +1,1583 @@
+/*
+ Written 1997-1998 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+
+ 2000/2/2- Added support for kernel-level ISAPnP
+ by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
+ Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
+
+ 2001/11/17 - Added ethtool support (jgarzik)
+
+ 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
+
+*/
+
+#define DRV_NAME "3c515"
+#define DRV_VERSION "0.99t-ac"
+#define DRV_RELDATE "28-Oct-2002"
+
+static char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " becker@scyld.com and others\n";
+
+#define CORKSCREW 1
+
+/* "Knobs" that adjust features and parameters. */
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1512 effectively disables this feature. */
+static int rx_copybreak = 200;
+
+/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
+static const int mtu = 1500;
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+
+/* Enable the automatic media selection code -- usually set. */
+#define AUTOMEDIA 1
+
+/* Allow the use of fragment bus master transfers instead of only
+ programmed-I/O for Vortex cards. Full-bus-master transfers are always
+ enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
+ the feature may be turned on using 'options'. */
+#define VORTEX_BUS_MASTER
+
+/* A few values that may be tweaked. */
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+
+#include <linux/module.h>
+#include <linux/isapnp.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define NEW_MULTICAST
+#include <linux/delay.h>
+
+#define MAX_UNITS 8
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+/* "Knobs" for adjusting internal parameters. */
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define DRIVER_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+ debugging. */
+static int rx_nocopy, rx_copy, queued_packet;
+
+/* Number of times to check to see if the Tx FIFO has space, used in some
+ limited cases. */
+#define WAIT_TX_AVAIL 200
+
+/* Operational parameter that usually are not changed. */
+#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */
+
+/* The size here is somewhat misleading: the Corkscrew also uses the ISA
+ aliased registers at <base>+0x400.
+ */
+#define CORKSCREW_TOTAL_SIZE 0x20
+
+#ifdef DRIVER_DEBUG
+static int corkscrew_debug = DRIVER_DEBUG;
+#else
+static int corkscrew_debug = 1;
+#endif
+
+#define CORKSCREW_ID 10
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
+3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout,
+it's not practical to integrate this driver with the other EtherLink drivers.
+
+II. Board-specific settings
+
+The Corkscrew has an EEPROM for configuration, but no special settings are
+needed for Linux.
+
+III. Driver operation
+
+The 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
+PCI cards, with the bus master interface extensively modified to work with
+the ISA bus.
+
+The card is capable of full-bus-master transfers with separate
+lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
+DEC Tulip and Intel Speedo3.
+
+This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
+receive buffer. This scheme allocates full-sized skbuffs as receive
+buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is
+chosen to trade-off the memory wasted by passing the full-sized skbuff to
+the queue layer for all frames vs. the copying cost of copying a frame to a
+correctly-sized skbuff.
+
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the netif
+layer. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+IV. Notes
+
+Thanks to Terry Murphy of 3Com for providing documentation and a development
+board.
+
+The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
+project names. I use these names to eliminate confusion -- 3Com product
+numbers and names are very similar and often confused.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
+This driver only supports ethernet frames because of the recent MTU limit
+of 1.5K, but the changes to support 4.5K are minimal.
+*/
+
+/* Operational definitions.
+ These are not used by other compilation units and thus are not
+ exported in a ".h" file.
+
+ First the windows. There are eight register windows, with the command
+ and status registers available in each.
+ */
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable.
+ Note that 11 parameters bits was fine for ethernet, but the new chips
+ can handle FDDI length frames (~4500 octets) and now parameters count
+ 32-bit 'Dwords' rather than octets. */
+
+enum corkscrew_cmd {
+ TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
+ RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
+ UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
+ DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
+ TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
+ AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
+ SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
+ SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
+ StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
+ StatsDisable = 22 << 11, StopCoax = 23 << 11,
+};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+/* Bits in the general status register. */
+enum corkscrew_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080,
+ DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
+ DMAInProgress = 1 << 11, /* DMA controller is still busy. */
+ CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0IRQ = 0x08,
+#if defined(CORKSCREW)
+ Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
+ Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
+#else
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+ Wn0EepromData = 12, /* Window 0: EEPROM results register. */
+#endif
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+
+/* EEPROM locations. */
+enum eeprom_offset {
+ PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
+ EtherLink3ID = 7,
+};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
+};
+enum wn3_config {
+ Ram_size = 7,
+ Ram_width = 8,
+ Ram_speed = 0x30,
+ Rom_size = 0xc0,
+ Ram_split_shift = 16,
+ Ram_split = 3 << Ram_split_shift,
+ Xcvr_shift = 20,
+ Xcvr = 7 << Xcvr_shift,
+ Autoselect = 0x1000000,
+};
+
+enum Window4 {
+ Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
+};
+enum Win4_Media_bits {
+ Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
+ Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
+ Media_LnkBeat = 0x0800,
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+/* Boomerang-style bus master control registers. Note ISA aliases! */
+enum MasterCtrl {
+ PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
+ 0x40c,
+ TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
+};
+
+/* The Rx and Tx descriptor lists.
+ Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
+ alignment contraint on tx_ring[] and rx_ring[]. */
+struct boom_rx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+ RxDComplete = 0x00008000, RxDError = 0x4000,
+ /* See boomerang_rx() for actual error bits */
+};
+
+struct boom_tx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+struct corkscrew_private {
+ const char *product_name;
+ struct list_head list;
+ struct net_device *our_dev;
+ /* The Rx and Tx rings are here to keep them quad-word-aligned. */
+ struct boom_rx_desc rx_ring[RX_RING_SIZE];
+ struct boom_tx_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of transmit- and receive-in-place skbuffs. */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+ struct timer_list timer; /* Media selection timer. */
+ int capabilities ; /* Adapter capabilities word. */
+ int options; /* User-settable misc. driver options. */
+ int last_rx_packets; /* For media autoselection. */
+ unsigned int available_media:8, /* From Wn3_Options */
+ media_override:3, /* Passed-in media type. */
+ default_media:3, /* Read from the EEPROM. */
+ full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
+ tx_full:1;
+ spinlock_t lock;
+ struct device *dev;
+};
+
+/* The action to take with a media selection timer tick.
+ Note that we deviate from the 3Com order by checking 10base2 before AUI.
+ */
+enum xcvr_types {
+ XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+ XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
+};
+
+static struct media_table {
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config. */
+ next:8; /* The media type to try next. */
+ short wait; /* Time before we check media status. */
+} media_tbl[] = {
+ { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
+ { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
+ { "undefined", 0, 0x80, XCVR_10baseT, 10000},
+ { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
+ { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
+ { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
+ { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
+ { "undefined", 0, 0x01, XCVR_10baseT, 10000},
+ { "Default", 0, 0xFF, XCVR_10baseT, 10000},
+};
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id corkscrew_isapnp_adapters[] = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
+ (long) "3Com Fast EtherLink ISA" },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
+
+static int nopnp;
+#endif /* __ISAPNP__ */
+
+static struct net_device *corkscrew_scan(int unit);
+static int corkscrew_setup(struct net_device *dev, int ioaddr,
+ struct pnp_dev *idev, int card_number);
+static int corkscrew_open(struct net_device *dev);
+static void corkscrew_timer(unsigned long arg);
+static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static int corkscrew_rx(struct net_device *dev);
+static void corkscrew_timeout(struct net_device *dev);
+static int boomerang_rx(struct net_device *dev);
+static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
+static int corkscrew_close(struct net_device *dev);
+static void update_stats(int addr, struct net_device *dev);
+static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static const struct ethtool_ops netdev_ethtool_ops;
+
+
+/*
+ Unfortunately maximizing the shared code between the integrated and
+ module version of the driver results in a complicated set of initialization
+ procedures.
+ init_module() -- modules / tc59x_init() -- built-in
+ The wrappers for corkscrew_scan()
+ corkscrew_scan() The common routine that scans for PCI and EISA cards
+ corkscrew_found_device() Allocate a device structure when we find a card.
+ Different versions exist for modules and built-in.
+ corkscrew_probe1() Fill in the device structure -- this is separated
+ so that the modules code can put it in dev->init.
+*/
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
+
+#ifdef MODULE
+static int debug = -1;
+
+module_param(debug, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param(rx_copybreak, int, 0);
+module_param(max_interrupt_work, int, 0);
+MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
+MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
+MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
+
+/* A list of all installed Vortex devices, for removing the driver module. */
+/* we will need locking (and refcounting) if we ever use it for more */
+static LIST_HEAD(root_corkscrew_dev);
+
+int init_module(void)
+{
+ int found = 0;
+ if (debug >= 0)
+ corkscrew_debug = debug;
+ if (corkscrew_debug)
+ pr_debug("%s", version);
+ while (corkscrew_scan(-1))
+ found++;
+ return found ? 0 : -ENODEV;
+}
+
+#else
+struct net_device *tc515_probe(int unit)
+{
+ struct net_device *dev = corkscrew_scan(unit);
+ static int printed;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ if (corkscrew_debug > 0 && !printed) {
+ printed = 1;
+ pr_debug("%s", version);
+ }
+
+ return dev;
+}
+#endif /* not MODULE */
+
+static int check_device(unsigned ioaddr)
+{
+ int timer;
+
+ if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
+ return 0;
+ /* Check the resource configuration for a matching ioaddr. */
+ if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
+ release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
+ return 0;
+ }
+ /* Verify by reading the device ID from the EEPROM. */
+ outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ udelay(162);
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
+ break;
+ }
+ if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
+ release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
+ return 0;
+ }
+ return 1;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ list_del_init(&vp->list);
+ if (dev->dma)
+ free_dma(dev->dma);
+ outw(TotalReset, dev->base_addr + EL3_CMD);
+ release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
+ if (vp->dev)
+ pnp_device_detach(to_pnp_dev(vp->dev));
+}
+
+static struct net_device *corkscrew_scan(int unit)
+{
+ struct net_device *dev;
+ static int cards_found = 0;
+ static int ioaddr;
+ int err;
+#ifdef __ISAPNP__
+ short i;
+ static int pnp_cards;
+#endif
+
+ dev = alloc_etherdev(sizeof(struct corkscrew_private));
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+#ifdef __ISAPNP__
+ if(nopnp == 1)
+ goto no_pnp;
+ for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
+ struct pnp_dev *idev = NULL;
+ int irq;
+ while((idev = pnp_find_dev(NULL,
+ corkscrew_isapnp_adapters[i].vendor,
+ corkscrew_isapnp_adapters[i].function,
+ idev))) {
+
+ if (pnp_device_attach(idev) < 0)
+ continue;
+ if (pnp_activate_dev(idev) < 0) {
+ pr_warning("pnp activate failed (out of resources?)\n");
+ pnp_device_detach(idev);
+ continue;
+ }
+ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ ioaddr = pnp_port_start(idev, 0);
+ irq = pnp_irq(idev, 0);
+ if (!check_device(ioaddr)) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ if(corkscrew_debug)
+ pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
+ (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
+ pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
+ inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
+ /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
+ SET_NETDEV_DEV(dev, &idev->dev);
+ pnp_cards++;
+ err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
+ if (!err)
+ return dev;
+ cleanup_card(dev);
+ }
+ }
+no_pnp:
+#endif /* __ISAPNP__ */
+
+ /* Check all locations on the ISA bus -- evil! */
+ for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
+ if (!check_device(ioaddr))
+ continue;
+
+ pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
+ inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
+ err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
+ if (!err)
+ return dev;
+ cleanup_card(dev);
+ }
+ free_netdev(dev);
+ return NULL;
+}
+
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = corkscrew_open,
+ .ndo_stop = corkscrew_close,
+ .ndo_start_xmit = corkscrew_start_xmit,
+ .ndo_tx_timeout = corkscrew_timeout,
+ .ndo_get_stats = corkscrew_get_stats,
+ .ndo_set_rx_mode = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+
+static int corkscrew_setup(struct net_device *dev, int ioaddr,
+ struct pnp_dev *idev, int card_number)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
+ int i;
+ int irq;
+
+#ifdef __ISAPNP__
+ if (idev) {
+ irq = pnp_irq(idev, 0);
+ vp->dev = &idev->dev;
+ } else {
+ irq = inw(ioaddr + 0x2002) & 15;
+ }
+#else
+ irq = inw(ioaddr + 0x2002) & 15;
+#endif
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->dma = inw(ioaddr + 0x2000) & 7;
+ vp->product_name = "3c515";
+ vp->options = dev->mem_start;
+ vp->our_dev = dev;
+
+ if (!vp->options) {
+ if (card_number >= MAX_UNITS)
+ vp->options = -1;
+ else
+ vp->options = options[card_number];
+ }
+
+ if (vp->options >= 0) {
+ vp->media_override = vp->options & 7;
+ if (vp->media_override == 2)
+ vp->media_override = 0;
+ vp->full_duplex = (vp->options & 8) ? 1 : 0;
+ vp->bus_master = (vp->options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+#ifdef MODULE
+ list_add(&vp->list, &root_corkscrew_dev);
+#endif
+
+ pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
+
+ spin_lock_init(&vp->lock);
+
+ /* Read the station address from the EEPROM. */
+ EL3WINDOW(0);
+ for (i = 0; i < 0x18; i++) {
+ __be16 *phys_addr = (__be16 *) dev->dev_addr;
+ int timer;
+ outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ udelay(162);
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
+ break;
+ }
+ eeprom[i] = inw(ioaddr + Wn0EepromData);
+ checksum ^= eeprom[i];
+ if (i < 3)
+ phys_addr[i] = htons(eeprom[i]);
+ }
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00)
+ pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
+ pr_cont(" %pM", dev->dev_addr);
+ if (eeprom[16] == 0x11c7) { /* Corkscrew */
+ if (request_dma(dev->dma, "3c515")) {
+ pr_cont(", DMA %d allocation failed", dev->dma);
+ dev->dma = 0;
+ } else
+ pr_cont(", DMA %d", dev->dma);
+ }
+ pr_cont(", IRQ %d\n", dev->irq);
+ /* Tell them about an invalid IRQ. */
+ if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
+ pr_warning(" *** Warning: this IRQ is unlikely to work! ***\n");
+
+ {
+ static const char * const ram_split[] = {
+ "5:3", "3:1", "1:1", "3:5"
+ };
+ __u32 config;
+ EL3WINDOW(3);
+ vp->available_media = inw(ioaddr + Wn3_Options);
+ config = inl(ioaddr + Wn3_Config);
+ if (corkscrew_debug > 1)
+ pr_info(" Internal config register is %4.4x, transceivers %#x.\n",
+ config, inw(ioaddr + Wn3_Options));
+ pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << config & Ram_size,
+ config & Ram_width ? "word" : "byte",
+ ram_split[(config & Ram_split) >> Ram_split_shift],
+ config & Autoselect ? "autoselect/" : "",
+ media_tbl[(config & Xcvr) >> Xcvr_shift].name);
+ vp->default_media = (config & Xcvr) >> Xcvr_shift;
+ vp->autoselect = config & Autoselect ? 1 : 0;
+ dev->if_port = vp->default_media;
+ }
+ if (vp->media_override != 7) {
+ pr_info(" Media override to transceiver type %d (%s).\n",
+ vp->media_override,
+ media_tbl[vp->media_override].name);
+ dev->if_port = vp->media_override;
+ }
+
+ vp->capabilities = eeprom[16];
+ vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
+ /* Rx is broken at 10mbps, so we always disable it. */
+ /* vp->full_bus_master_rx = 0; */
+ vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
+
+ /* The 3c51x-specific entries in the device structure. */
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = (400 * HZ) / 1000;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+
+ return register_netdev(dev);
+}
+
+
+static int corkscrew_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct corkscrew_private *vp = netdev_priv(dev);
+ __u32 config;
+ int i;
+
+ /* Before initializing select the active media port. */
+ EL3WINDOW(3);
+ if (vp->full_duplex)
+ outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+ config = inl(ioaddr + Wn3_Config);
+
+ if (vp->media_override != 7) {
+ if (corkscrew_debug > 1)
+ pr_info("%s: Media override to transceiver %d (%s).\n",
+ dev->name, vp->media_override,
+ media_tbl[vp->media_override].name);
+ dev->if_port = vp->media_override;
+ } else if (vp->autoselect) {
+ /* Find first available media type, starting with 100baseTx. */
+ dev->if_port = 4;
+ while (!(vp->available_media & media_tbl[dev->if_port].mask))
+ dev->if_port = media_tbl[dev->if_port].next;
+
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Initial media type %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ init_timer(&vp->timer);
+ vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
+ vp->timer.data = (unsigned long) dev;
+ vp->timer.function = corkscrew_timer; /* timer handler */
+ add_timer(&vp->timer);
+ } else
+ dev->if_port = vp->default_media;
+
+ config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
+ outl(config, ioaddr + Wn3_Config);
+
+ if (corkscrew_debug > 1) {
+ pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
+ dev->name, config);
+ }
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Wait a few ticks for the RxReset command to complete. */
+ for (i = 20; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+ /* Use the now-standard shared IRQ implementation. */
+ if (vp->capabilities == 0x11c7) {
+ /* Corkscrew: Cannot share ISA resources. */
+ if (dev->irq == 0 ||
+ dev->dma == 0 ||
+ request_irq(dev->irq, corkscrew_interrupt, 0,
+ vp->product_name, dev))
+ return -EAGAIN;
+ enable_dma(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_CASCADE);
+ } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
+ vp->product_name, dev)) {
+ return -EAGAIN;
+ }
+
+ if (corkscrew_debug > 1) {
+ EL3WINDOW(4);
+ pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + Wn4_Media));
+ }
+
+ /* Set the station address and mask in window 2 each time opened. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+ for (; i < 12; i += 2)
+ outw(0, ioaddr + i);
+
+ if (dev->if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms... */
+ outw(StartCoax, ioaddr + EL3_CMD);
+ EL3WINDOW(4);
+ outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
+ media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+ /* ..and on the Boomerang we enable the extra statistics bits. */
+ outw(0x0040, ioaddr + Wn4_NetDiag);
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ vp->cur_rx = vp->dirty_rx = 0;
+ if (corkscrew_debug > 2)
+ pr_debug("%s: Filling in the Rx ring.\n", dev->name);
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+ if (i < (RX_RING_SIZE - 1))
+ vp->rx_ring[i].next =
+ isa_virt_to_bus(&vp->rx_ring[i + 1]);
+ else
+ vp->rx_ring[i].next = 0;
+ vp->rx_ring[i].status = 0; /* Clear complete bit. */
+ vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
+ skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
+ vp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break; /* Bad news! */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
+ }
+ if (i != 0)
+ vp->rx_ring[i - 1].next =
+ isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+ outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
+ }
+ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
+ vp->cur_tx = vp->dirty_tx = 0;
+ outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+ /* Clear the Tx ring. */
+ for (i = 0; i < TX_RING_SIZE; i++)
+ vp->tx_skbuff[i] = NULL;
+ outl(0, ioaddr + DownListPtr);
+ }
+ /* Set receiver mode: presumably accept b-case and phys addr only. */
+ set_rx_mode(dev);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ netif_start_queue(dev);
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+ ioaddr + EL3_CMD);
+
+ return 0;
+}
+
+static void corkscrew_timer(unsigned long data)
+{
+#ifdef AUTOMEDIA
+ struct net_device *dev = (struct net_device *) data;
+ struct corkscrew_private *vp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ int ok = 0;
+
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media selection timer tick happened, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ spin_lock_irqsave(&vp->lock, flags);
+
+ {
+ int old_window = inw(ioaddr + EL3_CMD) >> 13;
+ int media_status;
+ EL3WINDOW(4);
+ media_status = inw(ioaddr + Wn4_Media);
+ switch (dev->if_port) {
+ case 0:
+ case 4:
+ case 5: /* 10baseT, 100baseTX, 100baseFX */
+ if (media_status & Media_LnkBeat) {
+ ok = 1;
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media %s has link beat, %x.\n",
+ dev->name,
+ media_tbl[dev->if_port].name,
+ media_status);
+ } else if (corkscrew_debug > 1)
+ pr_debug("%s: Media %s is has no link beat, %x.\n",
+ dev->name,
+ media_tbl[dev->if_port].name,
+ media_status);
+
+ break;
+ default: /* Other media types handled by Tx timeouts. */
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media %s is has no indication, %x.\n",
+ dev->name,
+ media_tbl[dev->if_port].name,
+ media_status);
+ ok = 1;
+ }
+ if (!ok) {
+ __u32 config;
+
+ do {
+ dev->if_port =
+ media_tbl[dev->if_port].next;
+ }
+ while (!(vp->available_media & media_tbl[dev->if_port].mask));
+
+ if (dev->if_port == 8) { /* Go back to default. */
+ dev->if_port = vp->default_media;
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media selection failing, using default %s port.\n",
+ dev->name,
+ media_tbl[dev->if_port].name);
+ } else {
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media selection failed, now trying %s port.\n",
+ dev->name,
+ media_tbl[dev->if_port].name);
+ vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
+ add_timer(&vp->timer);
+ }
+ outw((media_status & ~(Media_10TP | Media_SQE)) |
+ media_tbl[dev->if_port].media_bits,
+ ioaddr + Wn4_Media);
+
+ EL3WINDOW(3);
+ config = inl(ioaddr + Wn3_Config);
+ config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
+ outl(config, ioaddr + Wn3_Config);
+
+ outw(dev->if_port == 3 ? StartCoax : StopCoax,
+ ioaddr + EL3_CMD);
+ }
+ EL3WINDOW(old_window);
+ }
+
+ spin_unlock_irqrestore(&vp->lock, flags);
+ if (corkscrew_debug > 1)
+ pr_debug("%s: Media selection timer finished, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+#endif /* AUTOMEDIA */
+}
+
+static void corkscrew_timeout(struct net_device *dev)
+{
+ int i;
+ struct corkscrew_private *vp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ pr_warning("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TxStatus),
+ inw(ioaddr + EL3_STATUS));
+ /* Slight code bloat to be user friendly. */
+ if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
+ pr_warning("%s: Transmitter encountered 16 collisions --"
+ " network cable problem?\n", dev->name);
+#ifndef final_version
+ pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n",
+ vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
+ vp->cur_tx);
+ pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
+ &vp->tx_ring[0]);
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ pr_debug(" %d: %p length %8.8x status %8.8x\n", i,
+ &vp->tx_ring[i],
+ vp->tx_ring[i].length, vp->tx_ring[i].status);
+ }
+#endif
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ dev->stats.tx_errors++;
+ dev->stats.tx_dropped++;
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ /* Block a timer-based transmit from overlapping. */
+
+ netif_stop_queue(dev);
+
+ if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */
+ /* Calculate the next Tx descriptor entry. */
+ int entry = vp->cur_tx % TX_RING_SIZE;
+ struct boom_tx_desc *prev_entry;
+ unsigned long flags;
+ int i;
+
+ if (vp->tx_full) /* No room to transmit with */
+ return NETDEV_TX_BUSY;
+ if (vp->cur_tx != 0)
+ prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
+ else
+ prev_entry = NULL;
+ if (corkscrew_debug > 3)
+ pr_debug("%s: Trying to send a packet, Tx index %d.\n",
+ dev->name, vp->cur_tx);
+ /* vp->tx_full = 1; */
+ vp->tx_skbuff[entry] = skb;
+ vp->tx_ring[entry].next = 0;
+ vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
+ vp->tx_ring[entry].length = skb->len | 0x80000000;
+ vp->tx_ring[entry].status = skb->len | 0x80000000;
+
+ spin_lock_irqsave(&vp->lock, flags);
+ outw(DownStall, ioaddr + EL3_CMD);
+ /* Wait for the stall to complete. */
+ for (i = 20; i >= 0; i--)
+ if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
+ break;
+ if (prev_entry)
+ prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
+ if (inl(ioaddr + DownListPtr) == 0) {
+ outl(isa_virt_to_bus(&vp->tx_ring[entry]),
+ ioaddr + DownListPtr);
+ queued_packet++;
+ }
+ outw(DownUnstall, ioaddr + EL3_CMD);
+ spin_unlock_irqrestore(&vp->lock, flags);
+
+ vp->cur_tx++;
+ if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
+ vp->tx_full = 1;
+ else { /* Clear previous interrupt enable. */
+ if (prev_entry)
+ prev_entry->status &= ~0x80000000;
+ netif_wake_queue(dev);
+ }
+ return NETDEV_TX_OK;
+ }
+ /* Put out the doubleword header... */
+ outl(skb->len, ioaddr + TX_FIFO);
+ dev->stats.tx_bytes += skb->len;
+#ifdef VORTEX_BUS_MASTER
+ if (vp->bus_master) {
+ /* Set the bus-master controller to transfer the packet. */
+ outl((int) (skb->data), ioaddr + Wn7_MasterAddr);
+ outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+ vp->tx_skb = skb;
+ outw(StartDMADown, ioaddr + EL3_CMD);
+ /* queue will be woken at the DMADone interrupt. */
+ } else {
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb(skb);
+ if (inw(ioaddr + TxFree) > 1536) {
+ netif_wake_queue(dev);
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + (1536 >> 2),
+ ioaddr + EL3_CMD);
+ }
+#else
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb(skb);
+ if (inw(ioaddr + TxFree) > 1536) {
+ netif_wake_queue(dev);
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
+#endif /* bus master */
+
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
+ if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
+ if (corkscrew_debug > 2)
+ pr_debug("%s: Tx error, status %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x04)
+ dev->stats.tx_fifo_errors++;
+ if (tx_status & 0x38)
+ dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) {
+ int j;
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 20; j >= 0; j--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ }
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+ }
+ return NETDEV_TX_OK;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+
+static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
+{
+ /* Use the now-standard shared IRQ implementation. */
+ struct net_device *dev = dev_id;
+ struct corkscrew_private *lp = netdev_priv(dev);
+ int ioaddr, status;
+ int latency;
+ int i = max_interrupt_work;
+
+ ioaddr = dev->base_addr;
+ latency = inb(ioaddr + Timer);
+
+ spin_lock(&lp->lock);
+
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (corkscrew_debug > 4)
+ pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
+ dev->name, status, latency);
+ if ((status & 0xE000) != 0xE000) {
+ static int donedidthis;
+ /* Some interrupt controllers store a bogus interrupt from boot-time.
+ Ignore a single early interrupt, but don't hang the machine for
+ other interrupt problems. */
+ if (donedidthis++ > 100) {
+ pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
+ dev->name, status, netif_running(dev));
+ free_irq(dev->irq, dev);
+ dev->irq = -1;
+ }
+ }
+
+ do {
+ if (corkscrew_debug > 5)
+ pr_debug("%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+ if (status & RxComplete)
+ corkscrew_rx(dev);
+
+ if (status & TxAvailable) {
+ if (corkscrew_debug > 5)
+ pr_debug(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+ }
+ if (status & DownComplete) {
+ unsigned int dirty_tx = lp->dirty_tx;
+
+ while (lp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
+ break; /* It still hasn't been processed. */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb_irq(lp->tx_skbuff[entry]);
+ lp->tx_skbuff[entry] = NULL;
+ }
+ dirty_tx++;
+ }
+ lp->dirty_tx = dirty_tx;
+ outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
+ if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
+ lp->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ }
+#ifdef VORTEX_BUS_MASTER
+ if (status & DMADone) {
+ outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+ dev_kfree_skb_irq(lp->tx_skb); /* Release the transferred buffer */
+ netif_wake_queue(dev);
+ }
+#endif
+ if (status & UpComplete) {
+ boomerang_rx(dev);
+ outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ }
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts at once. */
+ if (status & RxEarly) { /* Rx early is unused. */
+ corkscrew_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & StatsFull) { /* Empty statistics. */
+ static int DoneDidThat;
+ if (corkscrew_debug > 4)
+ pr_debug("%s: Updating stats.\n", dev->name);
+ update_stats(ioaddr, dev);
+ /* DEBUG HACK: Disable statistics as an interrupt source. */
+ /* This occurs when we have the wrong media type! */
+ if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
+ int win, reg;
+ pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
+ dev->name);
+ for (win = 0; win < 8; win++) {
+ EL3WINDOW(win);
+ pr_notice("Vortex window %d:", win);
+ for (reg = 0; reg < 16; reg++)
+ pr_cont(" %2.2x", inb(ioaddr + reg));
+ pr_cont("\n");
+ }
+ EL3WINDOW(7);
+ outw(SetIntrEnb | TxAvailable |
+ RxComplete | AdapterFailure |
+ UpComplete | DownComplete |
+ TxComplete, ioaddr + EL3_CMD);
+ DoneDidThat++;
+ }
+ }
+ if (status & AdapterFailure) {
+ /* Adapter failure requires Rx reset and reinit. */
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | AdapterFailure,
+ ioaddr + EL3_CMD);
+ }
+ }
+
+ if (--i < 0) {
+ pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
+ dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
+ /* Disable all pending interrupts. */
+ outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
+ outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+
+ } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+ spin_unlock(&lp->lock);
+
+ if (corkscrew_debug > 4)
+ pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+ return IRQ_HANDLED;
+}
+
+static int corkscrew_rx(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+ short rx_status;
+
+ if (corkscrew_debug > 5)
+ pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
+ while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ unsigned char rx_error = inb(ioaddr + RxErrors);
+ if (corkscrew_debug > 2)
+ pr_debug(" Rx error: status %2.2x.\n",
+ rx_error);
+ dev->stats.rx_errors++;
+ if (rx_error & 0x01)
+ dev->stats.rx_over_errors++;
+ if (rx_error & 0x02)
+ dev->stats.rx_length_errors++;
+ if (rx_error & 0x04)
+ dev->stats.rx_frame_errors++;
+ if (rx_error & 0x08)
+ dev->stats.rx_crc_errors++;
+ if (rx_error & 0x10)
+ dev->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ short pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
+ if (corkscrew_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ insl(ioaddr + RX_FIFO,
+ skb_put(skb, pkt_len),
+ (pkt_len + 3) >> 2);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ /* Wait a limited time to go to next packet. */
+ for (i = 200; i >= 0; i--)
+ if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ continue;
+ } else if (corkscrew_debug)
+ pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
+ }
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ dev->stats.rx_dropped++;
+ /* Wait a limited time to skip this packet. */
+ for (i = 200; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ }
+ return 0;
+}
+
+static int boomerang_rx(struct net_device *dev)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ int entry = vp->cur_rx % RX_RING_SIZE;
+ int ioaddr = dev->base_addr;
+ int rx_status;
+
+ if (corkscrew_debug > 5)
+ pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
+ while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
+ if (rx_status & RxDError) { /* Error, update stats. */
+ unsigned char rx_error = rx_status >> 16;
+ if (corkscrew_debug > 2)
+ pr_debug(" Rx error: status %2.2x.\n",
+ rx_error);
+ dev->stats.rx_errors++;
+ if (rx_error & 0x01)
+ dev->stats.rx_over_errors++;
+ if (rx_error & 0x02)
+ dev->stats.rx_length_errors++;
+ if (rx_error & 0x04)
+ dev->stats.rx_frame_errors++;
+ if (rx_error & 0x08)
+ dev->stats.rx_crc_errors++;
+ if (rx_error & 0x10)
+ dev->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ short pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ dev->stats.rx_bytes += pkt_len;
+ if (corkscrew_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+
+ /* Check if the packet is long enough to just accept without
+ copying to a properly sized skbuff. */
+ if (pkt_len < rx_copybreak &&
+ (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ memcpy(skb_put(skb, pkt_len),
+ isa_bus_to_virt(vp->rx_ring[entry].
+ addr), pkt_len);
+ rx_copy++;
+ } else {
+ void *temp;
+ /* Pass up the skbuff already on the Rx ring. */
+ skb = vp->rx_skbuff[entry];
+ vp->rx_skbuff[entry] = NULL;
+ temp = skb_put(skb, pkt_len);
+ /* Remove this checking code for final release. */
+ if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
+ pr_warning("%s: Warning -- the skbuff addresses do not match"
+ " in boomerang_rx: %p vs. %p / %p.\n",
+ dev->name,
+ isa_bus_to_virt(vp->
+ rx_ring[entry].
+ addr), skb->head,
+ temp);
+ rx_nocopy++;
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+ entry = (++vp->cur_rx) % RX_RING_SIZE;
+ }
+ /* Refill the Rx ring buffers. */
+ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = vp->dirty_rx % RX_RING_SIZE;
+ if (vp->rx_skbuff[entry] == NULL) {
+ skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
+ if (skb == NULL)
+ break; /* Bad news! */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
+ vp->rx_skbuff[entry] = skb;
+ }
+ vp->rx_ring[entry].status = 0; /* Clear complete bit. */
+ }
+ return 0;
+}
+
+static int corkscrew_close(struct net_device *dev)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int i;
+
+ netif_stop_queue(dev);
+
+ if (corkscrew_debug > 1) {
+ pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS),
+ inb(ioaddr + TxStatus));
+ pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
+ dev->name, rx_nocopy, rx_copy, queued_packet);
+ }
+
+ del_timer(&vp->timer);
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == XCVR_10base2)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+
+ free_irq(dev->irq, dev);
+
+ outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
+
+ update_stats(ioaddr, dev);
+ if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
+ outl(0, ioaddr + UpListPtr);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (vp->rx_skbuff[i]) {
+ dev_kfree_skb(vp->rx_skbuff[i]);
+ vp->rx_skbuff[i] = NULL;
+ }
+ }
+ if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
+ outl(0, ioaddr + DownListPtr);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ if (vp->tx_skbuff[i]) {
+ dev_kfree_skb(vp->tx_skbuff[i]);
+ vp->tx_skbuff[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
+{
+ struct corkscrew_private *vp = netdev_priv(dev);
+ unsigned long flags;
+
+ if (netif_running(dev)) {
+ spin_lock_irqsave(&vp->lock, flags);
+ update_stats(dev->base_addr, dev);
+ spin_unlock_irqrestore(&vp->lock, flags);
+ }
+ return &dev->stats;
+}
+
+/* Update statistics.
+ Unlike with the EL3 we need not worry about interrupts changing
+ the window setting from underneath us, but we must still guard
+ against a race condition with a StatsUpdate interrupt updating the
+ table. This is done by checking that the ASM (!) code generated uses
+ atomic updates with '+='.
+ */
+static void update_stats(int ioaddr, struct net_device *dev)
+{
+ /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Must read to clear */
+ /* Tx deferrals */ inb(ioaddr + 8);
+ /* Don't bother with register 9, an extension of registers 6&7.
+ If we do use the 6&7 values the atomic update assumption above
+ is invalid. */
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ /* We change back to window 7 (not 1) with the Vortex. */
+ EL3WINDOW(7);
+}
+
+/* This new version of set_rx_mode() supports v1.4 kernels.
+ The Vortex chip has no documented multicast filter, so the only
+ multicast setting is to receive all multicast frames. At least
+ the chip has a very clean way to set the mode, unlike many others. */
+static void set_rx_mode(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ short new_mode;
+
+ if (dev->flags & IFF_PROMISC) {
+ if (corkscrew_debug > 3)
+ pr_debug("%s: Setting promiscuous mode.\n",
+ dev->name);
+ new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
+ } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
+ new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
+ } else
+ new_mode = SetRxFilter | RxStation | RxBroadcast;
+
+ outw(new_mode, ioaddr + EL3_CMD);
+}
+
+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), "ISA 0x%lx",
+ dev->base_addr);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ return corkscrew_debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+ corkscrew_debug = level;
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+};
+
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ while (!list_empty(&root_corkscrew_dev)) {
+ struct net_device *dev;
+ struct corkscrew_private *vp;
+
+ vp = list_entry(root_corkscrew_dev.next,
+ struct corkscrew_private, list);
+ dev = vp->our_dev;
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
new file mode 100644
index 00000000000..b9948f00c5e
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c574_cs.c
@@ -0,0 +1,1167 @@
+/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
+
+ Written 1993-1998 by
+ Donald Becker, becker@scyld.com, (driver core) and
+ David Hinds, dahinds@users.sourceforge.net (from his PC card code).
+ Locking fixes (C) Copyright 2003 Red Hat Inc
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+
+ This driver derives from Donald Becker's 3c509 core, which has the
+ following copyright:
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+
+*/
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
+Adapter.
+
+II. Board-specific settings
+
+None -- PC cards are autoconfigured.
+
+III. Driver operation
+
+The 3c574 uses a Boomerang-style interface, without the bus-master capability.
+See the Boomerang driver and documentation for most details.
+
+IV. Notes and chip documentation.
+
+Two added registers are used to enhance PIO performance, RunnerRdCtrl and
+RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the
+count of word (16 bits) reads or writes the driver is about to do to the Rx
+or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card
+translation latency by buffering the I/O operations with an 8 word FIFO.
+Note: No other chip accesses are permitted when this buffer is used.
+
+A second enhancement is that both attribute and common memory space
+0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster
+with *some* PCcard bridges) may be used instead of I/O operations.
+This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
+
+Some slow PC card bridges work better if they never see a WAIT signal.
+This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
+Only do this after testing that it is reliable and improves performance.
+
+The upper five bits of RunnerRdCtrl are used to window into PCcard
+configuration space registers. Window 0 is the regular Boomerang/Odie
+register set, 1-5 are various PC card control registers, and 16-31 are
+the (reversed!) CIS table.
+
+A final note: writing the InternalConfig register in window 3 with an
+invalid ramWidth is Very Bad.
+
+V. References
+
+http://www.scyld.com/expert/NWay.html
+http://www.national.com/opf/DP/DP83840A.html
+
+Thanks to Terry Murphy of 3Com for providing development information for
+earlier 3Com products.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.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/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/bitops.h>
+#include <linux/mii.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+INT_MODULE_PARM(max_interrupt_work, 32);
+
+/* Force full duplex modes? */
+INT_MODULE_PARM(full_duplex, 0);
+
+/* Autodetect link polarity reversal? */
+INT_MODULE_PARM(auto_polarity, 1);
+
+
+/*====================================================================*/
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((800*HZ)/1000)
+
+/* To minimize the size of the driver source and make the driver more
+ readable not all constants are symbolically defined.
+ You'll need the manual if you want to understand driver details anyway. */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable. */
+enum el3_cmds {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,
+};
+
+enum elxl_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+enum Window0 {
+ Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
+ IntrStatus=0x0E, /* Valid in all windows. */
+};
+/* These assumes the larger EEPROM. */
+enum Win0_EEPROM_cmds {
+ EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the "Odie" this window is always mapped at offsets 0x10-0x1f.
+ Except for TxFree, which is overlapped by RunnerWrCtrl. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
+ TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
+ RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
+};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+enum wn3_config {
+ Ram_size = 7,
+ Ram_width = 8,
+ Ram_speed = 0x30,
+ Rom_size = 0xc0,
+ Ram_split_shift = 16,
+ Ram_split = 3 << Ram_split_shift,
+ Xcvr_shift = 20,
+ Xcvr = 7 << Xcvr_shift,
+ Autoselect = 0x1000000,
+};
+
+enum Window4 { /* Window 4: Xcvr/media bits. */
+ Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
+};
+
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+ struct pcmcia_device *p_dev;
+ u16 advertising, partner; /* NWay media advertisement */
+ unsigned char phys; /* MII device address */
+ unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
+ /* for transceiver monitoring */
+ struct timer_list media;
+ unsigned short media_status;
+ unsigned short fast_poll;
+ unsigned long last_irq;
+ spinlock_t window_lock; /* Guards the Window selection */
+};
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with the original DP83840 on older 3c905 boards, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+/* Index of functions. */
+
+static int tc574_config(struct pcmcia_device *link);
+static void tc574_release(struct pcmcia_device *link);
+
+static void mdio_sync(unsigned int ioaddr, int bits);
+static int mdio_read(unsigned int ioaddr, int phy_id, int location);
+static void mdio_write(unsigned int ioaddr, int phy_id, int location,
+ int value);
+static unsigned short read_eeprom(unsigned int ioaddr, int index);
+static void tc574_wait_for_completion(struct net_device *dev, int cmd);
+
+static void tc574_reset(struct net_device *dev);
+static void media_check(unsigned long arg);
+static int el3_open(struct net_device *dev);
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev, int worklimit);
+static int el3_close(struct net_device *dev);
+static void el3_tx_timeout(struct net_device *dev);
+static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void set_rx_mode(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+
+static void tc574_detach(struct pcmcia_device *p_dev);
+
+/*
+ tc574_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+*/
+static const struct net_device_ops el3_netdev_ops = {
+ .ndo_open = el3_open,
+ .ndo_stop = el3_close,
+ .ndo_start_xmit = el3_start_xmit,
+ .ndo_tx_timeout = el3_tx_timeout,
+ .ndo_get_stats = el3_get_stats,
+ .ndo_do_ioctl = el3_ioctl,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int tc574_probe(struct pcmcia_device *link)
+{
+ struct el3_private *lp;
+ struct net_device *dev;
+
+ dev_dbg(&link->dev, "3c574_attach()\n");
+
+ /* Create the PC card device object. */
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev)
+ return -ENOMEM;
+ lp = netdev_priv(dev);
+ link->priv = dev;
+ lp->p_dev = link;
+
+ spin_lock_init(&lp->window_lock);
+ link->resource[0]->end = 32;
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
+ link->config_flags |= CONF_ENABLE_IRQ;
+ link->config_index = 1;
+
+ dev->netdev_ops = &el3_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ return tc574_config(link);
+}
+
+static void tc574_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "3c574_detach()\n");
+
+ unregister_netdev(dev);
+
+ tc574_release(link);
+
+ free_netdev(dev);
+} /* tc574_detach */
+
+static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+
+static int tc574_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct el3_private *lp = netdev_priv(dev);
+ int ret, i, j;
+ unsigned int ioaddr;
+ __be16 *phys_addr;
+ char *cardname;
+ __u32 config;
+ u8 *buf;
+ size_t len;
+
+ phys_addr = (__be16 *)dev->dev_addr;
+
+ dev_dbg(&link->dev, "3c574_config()\n");
+
+ link->io_lines = 16;
+
+ for (i = j = 0; j < 0x400; j += 0x20) {
+ link->resource[0]->start = j ^ 0x300;
+ i = pcmcia_request_io(link);
+ if (i == 0)
+ break;
+ }
+ if (i != 0)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, el3_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;
+
+ /* The 3c574 normally uses an EEPROM for configuration info, including
+ the hardware address. The future products may include a modem chip
+ and put the address in the CIS. */
+
+ len = pcmcia_get_tuple(link, 0x88, &buf);
+ if (buf && len >= 6) {
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(le16_to_cpu(buf[i * 2]));
+ kfree(buf);
+ } else {
+ kfree(buf); /* 0 < len < 6 */
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i + 10));
+ if (phys_addr[0] == htons(0x6060)) {
+ pr_notice("IO port conflict at 0x%03lx-0x%03lx\n",
+ dev->base_addr, dev->base_addr+15);
+ goto failed;
+ }
+ }
+ if (link->prod_id[1])
+ cardname = link->prod_id[1];
+ else
+ cardname = "3Com 3c574";
+
+ {
+ u_char mcr;
+ outw(2<<11, ioaddr + RunnerRdCtrl);
+ mcr = inb(ioaddr + 2);
+ outw(0<<11, ioaddr + RunnerRdCtrl);
+ pr_info(" ASIC rev %d,", mcr>>3);
+ EL3WINDOW(3);
+ config = inl(ioaddr + Wn3_Config);
+ lp->default_media = (config & Xcvr) >> Xcvr_shift;
+ lp->autoselect = config & Autoselect ? 1 : 0;
+ }
+
+ init_timer(&lp->media);
+
+ {
+ int phy;
+
+ /* Roadrunner only: Turn on the MII transceiver */
+ outw(0x8040, ioaddr + Wn3_Options);
+ mdelay(1);
+ outw(0xc040, ioaddr + Wn3_Options);
+ tc574_wait_for_completion(dev, TxReset);
+ tc574_wait_for_completion(dev, RxReset);
+ mdelay(1);
+ outw(0x8040, ioaddr + Wn3_Options);
+
+ EL3WINDOW(4);
+ for (phy = 1; phy <= 32; phy++) {
+ int mii_status;
+ mdio_sync(ioaddr, 32);
+ mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
+ if (mii_status != 0xffff) {
+ lp->phys = phy & 0x1f;
+ dev_dbg(&link->dev, " MII transceiver at "
+ "index %d, status %x.\n",
+ phy, mii_status);
+ if ((mii_status & 0x0040) == 0)
+ mii_preamble_required = 1;
+ break;
+ }
+ }
+ if (phy > 32) {
+ pr_notice(" No MII transceivers found!\n");
+ goto failed;
+ }
+ i = mdio_read(ioaddr, lp->phys, 16) | 0x40;
+ mdio_write(ioaddr, lp->phys, 16, i);
+ lp->advertising = mdio_read(ioaddr, lp->phys, 4);
+ if (full_duplex) {
+ /* Only advertise the FD media types. */
+ lp->advertising &= ~0x02a0;
+ mdio_write(ioaddr, lp->phys, 4, lp->advertising);
+ }
+ }
+
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ pr_notice("register_netdev() failed\n");
+ goto failed;
+ }
+
+ netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n",
+ cardname, dev->base_addr, dev->irq, dev->dev_addr);
+ netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n",
+ 8 << (config & Ram_size),
+ ram_split[(config & Ram_split) >> Ram_split_shift],
+ config & Autoselect ? "autoselect " : "");
+
+ return 0;
+
+failed:
+ tc574_release(link);
+ return -ENODEV;
+
+} /* tc574_config */
+
+static void tc574_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static int tc574_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int tc574_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open) {
+ tc574_reset(dev);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+static void dump_status(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ EL3WINDOW(1);
+ netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n",
+ inw(ioaddr+EL3_STATUS),
+ inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
+ inw(ioaddr+TxFree));
+ EL3WINDOW(4);
+ netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
+ inw(ioaddr+0x04), inw(ioaddr+0x06),
+ inw(ioaddr+0x08), inw(ioaddr+0x0a));
+ EL3WINDOW(1);
+}
+
+/*
+ Use this for commands that may take time to finish
+*/
+static void tc574_wait_for_completion(struct net_device *dev, int cmd)
+{
+ int i = 1500;
+ outw(cmd, dev->base_addr + EL3_CMD);
+ while (--i > 0)
+ if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
+ if (i == 0)
+ netdev_notice(dev, "command 0x%04x did not complete!\n", cmd);
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ Assume that we are in register window zero.
+ */
+static unsigned short read_eeprom(unsigned int ioaddr, int index)
+{
+ int timer;
+ outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 usec for the read to take place. */
+ for (timer = 1620; timer >= 0; timer--) {
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+ break;
+ }
+ return inw(ioaddr + Wn0EepromData);
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+ The maxium data clock rate is 2.5 Mhz. The timing is easily met by the
+ slow PC card interface. */
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN 0x00
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(unsigned int ioaddr, int bits)
+{
+ unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (-- bits >= 0) {
+ outw(MDIO_DATA_WRITE1, mdio_addr);
+ outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ }
+}
+
+static int mdio_read(unsigned int ioaddr, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ unsigned int retval = 0;
+ unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the read command bits out. */
+ for (i = 14; i >= 0; i--) {
+ int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value)
+{
+ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+ unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int i;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ /* Leave the interface idle. */
+ for (i = 1; i >= 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ }
+}
+
+/* Reset and restore all of the 3c574 registers. */
+static void tc574_reset(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int i;
+ unsigned int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ tc574_wait_for_completion(dev, TotalReset|0x10);
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ /* Clear any transactions in progress. */
+ outw(0, ioaddr + RunnerWrCtrl);
+ outw(0, ioaddr + RunnerRdCtrl);
+
+ /* Set the station address and mask. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+ for (; i < 12; i+=2)
+ outw(0, ioaddr + i);
+
+ /* Reset config options */
+ EL3WINDOW(3);
+ outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+ outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
+ ioaddr + Wn3_Config);
+ /* Roadrunner only: Turn on the MII transceiver. */
+ outw(0x8040, ioaddr + Wn3_Options);
+ mdelay(1);
+ outw(0xc040, ioaddr + Wn3_Options);
+ EL3WINDOW(1);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+
+ tc574_wait_for_completion(dev, TxReset);
+ tc574_wait_for_completion(dev, RxReset);
+ mdelay(1);
+ spin_lock_irqsave(&lp->window_lock, flags);
+ EL3WINDOW(3);
+ outw(0x8040, ioaddr + Wn3_Options);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+ inb(ioaddr + 13);
+
+ /* .. enable any extra statistics bits.. */
+ outw(0x0040, ioaddr + Wn4_NetDiag);
+
+ EL3WINDOW(1);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+
+ /* .. re-sync MII and re-fill what NWay is advertising. */
+ mdio_sync(ioaddr, 32);
+ mdio_write(ioaddr, lp->phys, 4, lp->advertising);
+ if (!auto_polarity) {
+ /* works for TDK 78Q2120 series MII's */
+ i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
+ mdio_write(ioaddr, lp->phys, 16, i);
+ }
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ /* Switch to register set 1 for normal use, just for TxFree. */
+ set_rx_mode(dev);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
+}
+
+static int el3_open(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ link->open++;
+ netif_start_queue(dev);
+
+ tc574_reset(dev);
+ lp->media.function = media_check;
+ lp->media.data = (unsigned long) dev;
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+
+ dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
+ dev->name, inw(dev->base_addr + EL3_STATUS));
+
+ return 0;
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_notice(dev, "Transmit timed out!\n");
+ dump_status(dev);
+ dev->stats.tx_errors++;
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ /* Issue TX_RESET and TX_START commands. */
+ tc574_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ int i;
+
+ /* Clear the Tx status stack. */
+ for (i = 32; i > 0; i--) {
+ u_char tx_status = inb(ioaddr + TxStatus);
+ if (!(tx_status & 0x84))
+ break;
+ /* reset transmitter on jabber error or underrun */
+ if (tx_status & 0x30)
+ tc574_wait_for_completion(dev, TxReset);
+ if (tx_status & 0x38) {
+ pr_debug("%s: transmit error: status 0x%02x\n",
+ dev->name, tx_status);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->stats.tx_aborted_errors++;
+ }
+ outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+}
+
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ pr_debug("%s: el3_start_xmit(length = %ld) called, "
+ "status %4.4x.\n", dev->name, (long)skb->len,
+ inw(ioaddr + EL3_STATUS));
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+
+ dev->stats.tx_bytes += skb->len;
+
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+
+ /* TxFree appears only in Window 1, not offset 0x1c. */
+ if (inw(ioaddr + TxFree) <= 1536) {
+ netif_stop_queue(dev);
+ /* Interrupt us when the FIFO has room for max-sized packet.
+ The threshold is in units of dwords. */
+ outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ }
+
+ pop_tx_status(dev);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t el3_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr;
+ unsigned status;
+ int work_budget = max_interrupt_work;
+ int handled = 0;
+
+ if (!netif_device_present(dev))
+ return IRQ_NONE;
+ ioaddr = dev->base_addr;
+
+ pr_debug("%s: interrupt, status %4.4x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS));
+
+ spin_lock(&lp->window_lock);
+
+ while ((status = inw(ioaddr + EL3_STATUS)) &
+ (IntLatch | RxComplete | RxEarly | StatsFull)) {
+ if (!netif_device_present(dev) ||
+ ((status & 0xe000) != 0x2000)) {
+ pr_debug("%s: Interrupt from dead card\n", dev->name);
+ break;
+ }
+
+ handled = 1;
+
+ if (status & RxComplete)
+ work_budget = el3_rx(dev, work_budget);
+
+ if (status & TxAvailable) {
+ pr_debug(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+ }
+
+ if (status & TxComplete)
+ pop_tx_status(dev);
+
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts. */
+ if (status & StatsFull)
+ update_stats(dev);
+ if (status & RxEarly) {
+ work_budget = el3_rx(dev, work_budget);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & AdapterFailure) {
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+ EL3WINDOW(1);
+ netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n",
+ fifo_diag);
+ if (fifo_diag & 0x0400) {
+ /* Tx overrun */
+ tc574_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (fifo_diag & 0x2000) {
+ /* Rx underrun */
+ tc574_wait_for_completion(dev, RxReset);
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD);
+ }
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (--work_budget < 0) {
+ pr_debug("%s: Too much work in interrupt, "
+ "status %4.4x.\n", dev->name, status);
+ /* Clear all interrupts */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ }
+
+ pr_debug("%s: exiting interrupt, status %4.4x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS));
+
+ spin_unlock(&lp->window_lock);
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ This timer serves two purposes: to check for missed interrupts
+ (and as a last resort, poll the NIC for events), and to monitor
+ the MII, reporting changes in cable status.
+*/
+static void media_check(unsigned long arg)
+{
+ struct net_device *dev = (struct net_device *) arg;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ unsigned long flags;
+ unsigned short /* cable, */ media, partner;
+
+ if (!netif_device_present(dev))
+ goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {
+ if (!lp->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+
+ local_irq_save(flags);
+ el3_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+
+ lp->fast_poll = HZ;
+ }
+ if (lp->fast_poll) {
+ lp->fast_poll--;
+ lp->media.expires = jiffies + 2*HZ/100;
+ add_timer(&lp->media);
+ return;
+ }
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ EL3WINDOW(4);
+ media = mdio_read(ioaddr, lp->phys, 1);
+ partner = mdio_read(ioaddr, lp->phys, 5);
+ EL3WINDOW(1);
+
+ if (media != lp->media_status) {
+ if ((media ^ lp->media_status) & 0x0004)
+ netdev_info(dev, "%s link beat\n",
+ (lp->media_status & 0x0004) ? "lost" : "found");
+ if ((media ^ lp->media_status) & 0x0020) {
+ lp->partner = 0;
+ if (lp->media_status & 0x0020) {
+ netdev_info(dev, "autonegotiation restarted\n");
+ } else if (partner) {
+ partner &= lp->advertising;
+ lp->partner = partner;
+ netdev_info(dev, "autonegotiation complete: "
+ "%dbaseT-%cD selected\n",
+ (partner & 0x0180) ? 100 : 10,
+ (partner & 0x0140) ? 'F' : 'H');
+ } else {
+ netdev_info(dev, "link partner did not autonegotiate\n");
+ }
+
+ EL3WINDOW(3);
+ outb((partner & 0x0140 ? 0x20 : 0) |
+ (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+ EL3WINDOW(1);
+
+ }
+ if (media & 0x0010)
+ netdev_info(dev, "remote fault detected\n");
+ if (media & 0x0002)
+ netdev_info(dev, "jabber detected\n");
+ lp->media_status = media;
+ }
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+
+reschedule:
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+
+ if (netif_device_present(dev)) {
+ unsigned long flags;
+ spin_lock_irqsave(&lp->window_lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+ }
+ return &dev->stats;
+}
+
+/* Update statistics.
+ Surprisingly this need not be run single-threaded, but it effectively is.
+ The counters clear when read, so the adds must merely be atomic.
+ */
+static void update_stats(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ u8 rx, tx, up;
+
+ pr_debug("%s: updating the statistics.\n", dev->name);
+
+ if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
+ return;
+
+ /* Unlike the 3c509 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ up = inb(ioaddr + 9);
+ dev->stats.tx_packets += (up&0x30) << 4;
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ rx = inw(ioaddr + 10);
+ tx = inw(ioaddr + 12);
+
+ EL3WINDOW(4);
+ /* BadSSD */ inb(ioaddr + 12);
+ up = inb(ioaddr + 13);
+
+ EL3WINDOW(1);
+}
+
+static int el3_rx(struct net_device *dev, int worklimit)
+{
+ unsigned int ioaddr = dev->base_addr;
+ short rx_status;
+
+ pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+ while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
+ worklimit > 0) {
+ worklimit--;
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ dev->stats.rx_errors++;
+ switch (error) {
+ case 0x0000: dev->stats.rx_over_errors++; break;
+ case 0x0800: dev->stats.rx_length_errors++; break;
+ case 0x1000: dev->stats.rx_frame_errors++; break;
+ case 0x1800: dev->stats.rx_length_errors++; break;
+ case 0x2000: dev->stats.rx_frame_errors++; break;
+ case 0x2800: dev->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+
+ pr_debug(" Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb_reserve(skb, 2);
+ insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+ ((pkt_len+3)>>2));
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ } else {
+ pr_debug("%s: couldn't allocate a sk_buff of"
+ " size %d.\n", dev->name, pkt_len);
+ dev->stats.rx_dropped++;
+ }
+ }
+ tc574_wait_for_completion(dev, RxDiscard);
+ }
+
+ return worklimit;
+}
+
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ struct mii_ioctl_data *data = if_mii(rq);
+ int phy = lp->phys & 0x1f;
+
+ pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+ dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+ data->phy_id, data->reg_num, data->val_in, data->val_out);
+
+ switch(cmd) {
+ case SIOCGMIIPHY: /* Get the address of the PHY in use. */
+ data->phy_id = phy;
+ case SIOCGMIIREG: /* Read the specified MII register. */
+ {
+ int saved_window;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ saved_window = inw(ioaddr + EL3_CMD) >> 13;
+ EL3WINDOW(4);
+ data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f,
+ data->reg_num & 0x1f);
+ EL3WINDOW(saved_window);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+ return 0;
+ }
+ case SIOCSMIIREG: /* Write the specified MII register */
+ {
+ int saved_window;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ saved_window = inw(ioaddr + EL3_CMD) >> 13;
+ EL3WINDOW(4);
+ mdio_write(ioaddr, data->phy_id & 0x1f,
+ data->reg_num & 0x1f, data->val_in);
+ EL3WINDOW(saved_window);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+ return 0;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
+ documented. Until it is we revert to receiving all multicast frames when
+ any multicast reception is desired.
+ Note: My other drivers emit a log message whenever promiscuous mode is
+ entered to help detect password sniffers. This is less desirable on
+ typical PC card machines, so we omit the message.
+ */
+
+static void set_rx_mode(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+
+ if (dev->flags & IFF_PROMISC)
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static void set_multicast_list(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->window_lock, flags);
+ set_rx_mode(dev);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+}
+
+static int el3_close(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
+
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
+
+ if (pcmcia_dev_present(link)) {
+ unsigned long flags;
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ /* Note: Switching to window 0 may disable the IRQ. */
+ EL3WINDOW(0);
+ spin_lock_irqsave(&lp->window_lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->window_lock, flags);
+
+ /* force interrupts off */
+ outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
+ }
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->media);
+
+ return 0;
+}
+
+static const struct pcmcia_device_id tc574_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574),
+ PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, tc574_ids);
+
+static struct pcmcia_driver tc574_driver = {
+ .owner = THIS_MODULE,
+ .name = "3c574_cs",
+ .probe = tc574_probe,
+ .remove = tc574_detach,
+ .id_table = tc574_ids,
+ .suspend = tc574_suspend,
+ .resume = tc574_resume,
+};
+module_pcmcia_driver(tc574_driver);
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
new file mode 100644
index 00000000000..f18647c2355
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c589_cs.c
@@ -0,0 +1,971 @@
+/* ======================================================================
+ *
+ * A PCMCIA ethernet driver for the 3com 3c589 card.
+ *
+ * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+ *
+ * 3c589_cs.c 1.162 2001/10/13 00:08:50
+ *
+ * The network driver code is based on Donald Becker's 3c589 code:
+ *
+ * Written 1994 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU General Public License,
+ * incorporated herein by reference.
+ * Donald Becker may be reached at becker@scyld.com
+ *
+ * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * ======================================================================
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DRV_NAME "3c589_cs"
+#define DRV_VERSION "1.162-ac"
+
+#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 <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+
+/* To minimize the size of the driver source I only define operating
+ * constants if they are used several times. You'll need the manual
+ * if you want to understand driver details.
+ */
+
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_TIMER 0x0a
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+#define EEPROM_READ 0x0080
+#define EEPROM_BUSY 0x8000
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ * 11 bits are the parameter, if applicable.
+ */
+
+enum c509cmd {
+ TotalReset = 0<<11,
+ SelectWindow = 1<<11,
+ StartCoax = 2<<11,
+ RxDisable = 3<<11,
+ RxEnable = 4<<11,
+ RxReset = 5<<11,
+ RxDiscard = 8<<11,
+ TxEnable = 9<<11,
+ TxDisable = 10<<11,
+ TxReset = 11<<11,
+ FakeIntr = 12<<11,
+ AckIntr = 13<<11,
+ SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11,
+ SetRxFilter = 16<<11,
+ SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11,
+ SetTxStart = 19<<11,
+ StatsEnable = 21<<11,
+ StatsDisable = 22<<11,
+ StopCoax = 23<<11
+};
+
+enum c509status {
+ IntLatch = 0x0001,
+ AdapterFailure = 0x0002,
+ TxComplete = 0x0004,
+ TxAvailable = 0x0008,
+ RxComplete = 0x0010,
+ RxEarly = 0x0020,
+ IntReq = 0x0040,
+ StatsFull = 0x0080,
+ CmdBusy = 0x1000
+};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1,
+ RxMulticast = 2,
+ RxBroadcast = 4,
+ RxProm = 8
+};
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+struct el3_private {
+ struct pcmcia_device *p_dev;
+ /* For transceiver monitoring */
+ struct timer_list media;
+ u16 media_status;
+ u16 fast_poll;
+ unsigned long last_irq;
+ spinlock_t lock;
+};
+
+static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* Special hook for setting if_port when module is loaded */
+INT_MODULE_PARM(if_port, 0);
+
+
+/*====================================================================*/
+
+static int tc589_config(struct pcmcia_device *link);
+static void tc589_release(struct pcmcia_device *link);
+
+static u16 read_eeprom(unsigned int ioaddr, int index);
+static void tc589_reset(struct net_device *dev);
+static void media_check(unsigned long arg);
+static int el3_config(struct net_device *dev, struct ifmap *map);
+static int el3_open(struct net_device *dev);
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev);
+static int el3_close(struct net_device *dev);
+static void el3_tx_timeout(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static const struct ethtool_ops netdev_ethtool_ops;
+
+static void tc589_detach(struct pcmcia_device *p_dev);
+
+static const struct net_device_ops el3_netdev_ops = {
+ .ndo_open = el3_open,
+ .ndo_stop = el3_close,
+ .ndo_start_xmit = el3_start_xmit,
+ .ndo_tx_timeout = el3_tx_timeout,
+ .ndo_set_config = el3_config,
+ .ndo_get_stats = el3_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int tc589_probe(struct pcmcia_device *link)
+{
+ struct el3_private *lp;
+ struct net_device *dev;
+
+ dev_dbg(&link->dev, "3c589_attach()\n");
+
+ /* Create new ethernet device */
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev)
+ return -ENOMEM;
+ lp = netdev_priv(dev);
+ link->priv = dev;
+ lp->p_dev = link;
+
+ spin_lock_init(&lp->lock);
+ link->resource[0]->end = 16;
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
+
+ link->config_flags |= CONF_ENABLE_IRQ;
+ link->config_index = 1;
+
+ dev->netdev_ops = &el3_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ dev->ethtool_ops = &netdev_ethtool_ops;
+
+ return tc589_config(link);
+}
+
+static void tc589_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "3c589_detach\n");
+
+ unregister_netdev(dev);
+
+ tc589_release(link);
+
+ free_netdev(dev);
+} /* tc589_detach */
+
+static int tc589_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ __be16 *phys_addr;
+ int ret, i, j, multi = 0, fifo;
+ unsigned int ioaddr;
+ static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+ u8 *buf;
+ size_t len;
+
+ dev_dbg(&link->dev, "3c589_config\n");
+
+ phys_addr = (__be16 *)dev->dev_addr;
+ /* Is this a 3c562? */
+ if (link->manf_id != MANFID_3COM)
+ dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
+ multi = (link->card_id == PRODID_3COM_3C562);
+
+ link->io_lines = 16;
+
+ /* For the 3c562, the base address must be xx00-xx7f */
+ for (i = j = 0; j < 0x400; j += 0x10) {
+ if (multi && (j & 0x80))
+ continue;
+ link->resource[0]->start = j ^ 0x300;
+ i = pcmcia_request_io(link);
+ if (i == 0)
+ break;
+ }
+ if (i != 0)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, el3_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;
+ EL3WINDOW(0);
+
+ /* The 3c589 has an extra EEPROM for configuration info, including
+ * the hardware address. The 3c562 puts the address in the CIS.
+ */
+ len = pcmcia_get_tuple(link, 0x88, &buf);
+ if (buf && len >= 6) {
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
+ kfree(buf);
+ } else {
+ kfree(buf); /* 0 < len < 6 */
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+ if (phys_addr[0] == htons(0x6060)) {
+ dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
+ dev->base_addr, dev->base_addr+15);
+ goto failed;
+ }
+ }
+
+ /* The address and resource configuration register aren't loaded from
+ * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
+ */
+
+ outw(0x3f00, ioaddr + 8);
+ fifo = inl(ioaddr);
+
+ /* The if_port symbol can be set when the module is loaded */
+ if ((if_port >= 0) && (if_port <= 3))
+ dev->if_port = if_port;
+ else
+ dev_err(&link->dev, "invalid if_port requested\n");
+
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ dev_err(&link->dev, "register_netdev() failed\n");
+ goto failed;
+ }
+
+ netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
+ (multi ? "562" : "589"), dev->base_addr, dev->irq,
+ dev->dev_addr);
+ netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
+ (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
+ if_names[dev->if_port]);
+ return 0;
+
+failed:
+ tc589_release(link);
+ return -ENODEV;
+} /* tc589_config */
+
+static void tc589_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static int tc589_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int tc589_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open) {
+ tc589_reset(dev);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+/*====================================================================*/
+
+/* Use this for commands that may take time to finish */
+
+static void tc589_wait_for_completion(struct net_device *dev, int cmd)
+{
+ int i = 100;
+ outw(cmd, dev->base_addr + EL3_CMD);
+ while (--i > 0)
+ if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
+ break;
+ if (i == 0)
+ netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ * Assume that we are in register window zero.
+ */
+
+static u16 read_eeprom(unsigned int ioaddr, int index)
+{
+ int i;
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Reading the eeprom takes 162 us */
+ for (i = 1620; i >= 0; i--)
+ if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
+ break;
+ return inw(ioaddr + 12);
+}
+
+/* Set transceiver type, perhaps to something other than what the user
+ * specified in dev->if_port.
+ */
+
+static void tc589_set_xcvr(struct net_device *dev, int if_port)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+
+ EL3WINDOW(0);
+ switch (if_port) {
+ case 0:
+ case 1:
+ outw(0, ioaddr + 6);
+ break;
+ case 2:
+ outw(3<<14, ioaddr + 6);
+ break;
+ case 3:
+ outw(1<<14, ioaddr + 6);
+ break;
+ }
+ /* On PCMCIA, this just turns on the LED */
+ outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+ /* 10baseT interface, enable link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ if (if_port == 2)
+ lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
+ else
+ lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
+}
+
+static void dump_status(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ EL3WINDOW(1);
+ netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
+ inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
+ EL3WINDOW(4);
+ netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
+ inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
+ inw(ioaddr+0x0a));
+ EL3WINDOW(1);
+}
+
+/* Reset and restore all of the 3c589 registers. */
+static void tc589_reset(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ int i;
+
+ EL3WINDOW(0);
+ outw(0x0001, ioaddr + 4); /* Activate board. */
+ outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
+
+ /* Set the station address in window 2. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ tc589_set_xcvr(dev, dev->if_port);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr+i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ set_rx_mode(dev);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | AdapterFailure, ioaddr + EL3_CMD);
+}
+
+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,
+};
+
+static int el3_config(struct net_device *dev, struct ifmap *map)
+{
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (map->port <= 3) {
+ dev->if_port = map->port;
+ netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+ tc589_set_xcvr(dev, dev->if_port);
+ } else {
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int el3_open(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ link->open++;
+ netif_start_queue(dev);
+
+ tc589_reset(dev);
+ init_timer(&lp->media);
+ lp->media.function = media_check;
+ lp->media.data = (unsigned long) dev;
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+
+ dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
+ dev->name, inw(dev->base_addr + EL3_STATUS));
+
+ return 0;
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_warn(dev, "Transmit timed out!\n");
+ dump_status(dev);
+ dev->stats.tx_errors++;
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ /* Issue TX_RESET and TX_START commands. */
+ tc589_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ int i;
+
+ /* Clear the Tx status stack. */
+ for (i = 32; i > 0; i--) {
+ u_char tx_status = inb(ioaddr + TX_STATUS);
+ if (!(tx_status & 0x84))
+ break;
+ /* reset transmitter on jabber error or underrun */
+ if (tx_status & 0x30)
+ tc589_wait_for_completion(dev, TxReset);
+ if (tx_status & 0x38) {
+ netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->stats.tx_aborted_errors++;
+ }
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+}
+
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct el3_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
+ (long)skb->len, inw(ioaddr + EL3_STATUS));
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ dev->stats.tx_bytes += skb->len;
+
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+ if (inw(ioaddr + TX_FREE) <= 1536) {
+ netif_stop_queue(dev);
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
+
+ pop_tx_status(dev);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t el3_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr;
+ __u16 status;
+ int i = 0, handled = 1;
+
+ if (!netif_device_present(dev))
+ return IRQ_NONE;
+
+ ioaddr = dev->base_addr;
+
+ netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
+
+ spin_lock(&lp->lock);
+ while ((status = inw(ioaddr + EL3_STATUS)) &
+ (IntLatch | RxComplete | StatsFull)) {
+ if ((status & 0xe000) != 0x2000) {
+ netdev_dbg(dev, "interrupt from dead card\n");
+ handled = 0;
+ break;
+ }
+ if (status & RxComplete)
+ el3_rx(dev);
+ if (status & TxAvailable) {
+ netdev_dbg(dev, " TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+ }
+ if (status & TxComplete)
+ pop_tx_status(dev);
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts. */
+ if (status & StatsFull) /* Empty statistics. */
+ update_stats(dev);
+ if (status & RxEarly) {
+ /* Rx early is unused. */
+ el3_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & AdapterFailure) {
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + 4);
+ EL3WINDOW(1);
+ netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
+ fifo_diag);
+ if (fifo_diag & 0x0400) {
+ /* Tx overrun */
+ tc589_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (fifo_diag & 0x2000) {
+ /* Rx underrun */
+ tc589_wait_for_completion(dev, RxReset);
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD);
+ }
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+ if (++i > 10) {
+ netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
+ status);
+ /* Clear all interrupts */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ }
+ lp->last_irq = jiffies;
+ spin_unlock(&lp->lock);
+ netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
+ inw(ioaddr + EL3_STATUS));
+ return IRQ_RETVAL(handled);
+}
+
+static void media_check(unsigned long arg)
+{
+ struct net_device *dev = (struct net_device *)(arg);
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u16 media, errs;
+ unsigned long flags;
+
+ if (!netif_device_present(dev))
+ goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ * this, we can limp along even if the interrupt is blocked
+ */
+ if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
+ (inb(ioaddr + EL3_TIMER) == 0xff)) {
+ if (!lp->fast_poll)
+ netdev_warn(dev, "interrupt(s) dropped!\n");
+
+ local_irq_save(flags);
+ el3_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+
+ lp->fast_poll = HZ;
+ }
+ if (lp->fast_poll) {
+ lp->fast_poll--;
+ lp->media.expires = jiffies + HZ/100;
+ add_timer(&lp->media);
+ return;
+ }
+
+ /* lp->lock guards the EL3 window. Window should always be 1 except
+ * when the lock is held
+ */
+
+ spin_lock_irqsave(&lp->lock, flags);
+ EL3WINDOW(4);
+ media = inw(ioaddr+WN4_MEDIA) & 0xc810;
+
+ /* Ignore collisions unless we've had no irq's recently */
+ if (time_before(jiffies, lp->last_irq + HZ)) {
+ media &= ~0x0010;
+ } else {
+ /* Try harder to detect carrier errors */
+ EL3WINDOW(6);
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ errs = inb(ioaddr + 0);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+ dev->stats.tx_carrier_errors += errs;
+ if (errs || (lp->media_status & 0x0010))
+ media |= 0x0010;
+ }
+
+ if (media != lp->media_status) {
+ if ((media & lp->media_status & 0x8000) &&
+ ((lp->media_status ^ media) & 0x0800))
+ netdev_info(dev, "%s link beat\n",
+ (lp->media_status & 0x0800 ? "lost" : "found"));
+ else if ((media & lp->media_status & 0x4000) &&
+ ((lp->media_status ^ media) & 0x0010))
+ netdev_info(dev, "coax cable %s\n",
+ (lp->media_status & 0x0010 ? "ok" : "problem"));
+ if (dev->if_port == 0) {
+ if (media & 0x8000) {
+ if (media & 0x0800)
+ netdev_info(dev, "flipped to 10baseT\n");
+ else
+ tc589_set_xcvr(dev, 2);
+ } else if (media & 0x4000) {
+ if (media & 0x0010)
+ tc589_set_xcvr(dev, 1);
+ else
+ netdev_info(dev, "flipped to 10base2\n");
+ }
+ }
+ lp->media_status = media;
+ }
+
+ EL3WINDOW(1);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+reschedule:
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ struct pcmcia_device *link = lp->p_dev;
+
+ if (pcmcia_dev_present(link)) {
+ spin_lock_irqsave(&lp->lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return &dev->stats;
+}
+
+/* Update statistics. We change to register window 6, 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 1 is always valid rather than use a special window-state variable.
+*
+* Caller must hold the lock for this
+*/
+
+static void update_stats(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_dbg(dev, "updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */
+ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */
+ inb(ioaddr + 7);
+ /* Tx deferrals */
+ inb(ioaddr + 8);
+ /* Rx octets */
+ inw(ioaddr + 10);
+ /* Tx octets */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+}
+
+static int el3_rx(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ int worklimit = 32;
+ short rx_status;
+
+ netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
+ worklimit > 0) {
+ worklimit--;
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ dev->stats.rx_errors++;
+ switch (error) {
+ case 0x0000:
+ dev->stats.rx_over_errors++;
+ break;
+ case 0x0800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x1000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x1800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x2000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x2800:
+ dev->stats.rx_crc_errors++;
+ break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+
+ netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb_reserve(skb, 2);
+ insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+ (pkt_len+3)>>2);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ } else {
+ netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
+ pkt_len);
+ dev->stats.rx_dropped++;
+ }
+ }
+ /* Pop the top of the Rx FIFO */
+ tc589_wait_for_completion(dev, RxDiscard);
+ }
+ if (worklimit == 0)
+ netdev_warn(dev, "too much work in el3_rx!\n");
+ return 0;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ u16 opts = SetRxFilter | RxStation | RxBroadcast;
+
+ if (dev->flags & IFF_PROMISC)
+ opts |= RxMulticast | RxProm;
+ else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
+ opts |= RxMulticast;
+ outw(opts, ioaddr + EL3_CMD);
+}
+
+static void set_multicast_list(struct net_device *dev)
+{
+ struct el3_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ set_rx_mode(dev);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int el3_close(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
+ unsigned int ioaddr = dev->base_addr;
+
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
+
+ if (pcmcia_dev_present(link)) {
+ /* Turn off statistics ASAP. We update dev->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 2)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 1) {
+ /* Disable link beat and jabber */
+ EL3WINDOW(4);
+ outw(0, ioaddr + WN4_MEDIA);
+ }
+
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ /* But we explicitly zero the IRQ line select anyway. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+
+ /* Check if the card still exists */
+ if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
+ update_stats(dev);
+ }
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->media);
+
+ return 0;
+}
+
+static const struct pcmcia_device_id tc589_ids[] = {
+ PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
+ PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
+ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
+ PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202),
+ PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"),
+ PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, tc589_ids);
+
+static struct pcmcia_driver tc589_driver = {
+ .owner = THIS_MODULE,
+ .name = "3c589_cs",
+ .probe = tc589_probe,
+ .remove = tc589_detach,
+ .id_table = tc589_ids,
+ .suspend = tc589_suspend,
+ .resume = tc589_resume,
+};
+module_pcmcia_driver(tc589_driver);
diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
new file mode 100644
index 00000000000..61477b8e8d2
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -0,0 +1,3328 @@
+/* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */
+/*
+ Written 1996-1999 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ This driver is for the 3Com "Vortex" and "Boomerang" series ethercards.
+ Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597
+ and the EtherLink XL 3c900 and 3c905 cards.
+
+ Problem reports and questions should be directed to
+ vortex@scyld.com
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+*/
+
+/*
+ * FIXME: This driver _could_ support MTU changing, but doesn't. See Don's hamachi.c implementation
+ * as well as other drivers
+ *
+ * NOTE: If you make 'vortex_debug' a constant (#define vortex_debug 0) the driver shrinks by 2k
+ * due to dead code elimination. There will be some performance benefits from this due to
+ * elimination of all the tests and reduced cache footprint.
+ */
+
+
+#define DRV_NAME "3c59x"
+
+
+
+/* A few values that may be tweaked. */
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 32
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/* "Knobs" that adjust features and parameters. */
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1512 effectively disables this feature. */
+#ifndef __arm__
+static int rx_copybreak = 200;
+#else
+/* ARM systems perform better by disregarding the bus-master
+ transfer capability of these cards. -- rmk */
+static int rx_copybreak = 1513;
+#endif
+/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
+static const int mtu = 1500;
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 32;
+/* Tx timeout interval (millisecs) */
+static int watchdog = 5000;
+
+/* Allow aggregation of Tx interrupts. Saves CPU load at the cost
+ * of possible Tx stalls if the system is blocking interrupts
+ * somewhere else. Undefine this to disable.
+ */
+#define tx_interrupt_mitigation 1
+
+/* Put out somewhat more debugging messages. (0: no msg, 1 minimal .. 6). */
+#define vortex_debug debug
+#ifdef VORTEX_DEBUG
+static int vortex_debug = VORTEX_DEBUG;
+#else
+static int vortex_debug = 1;
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/mii.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/highmem.h>
+#include <linux/eisa.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/gfp.h>
+#include <asm/irq.h> /* For nr_irqs only. */
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
+ This is only in the support-all-kernels source code. */
+
+#define RUN_AT(x) (jiffies + (x))
+
+#include <linux/delay.h>
+
+
+static const char version[] =
+ DRV_NAME ": Donald Becker and others.\n";
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("3Com 3c59x/3c9xx ethernet driver ");
+MODULE_LICENSE("GPL");
+
+
+/* Operational parameter that usually are not changed. */
+
+/* The Vortex size is twice that of the original EtherLinkIII series: the
+ runtime register window, window 1, is now always mapped in.
+ The Boomerang size is twice as large as the Vortex -- it has additional
+ bus master control registers. */
+#define VORTEX_TOTAL_SIZE 0x20
+#define BOOMERANG_TOTAL_SIZE 0x40
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with the original DP83840 on older 3c905 boards, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required;
+
+#define PFX DRV_NAME ": "
+
+
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com FastEtherLink and FastEtherLink
+XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs
+versions of the FastEtherLink cards. The supported product IDs are
+ 3c590, 3c592, 3c595, 3c597, 3c900, 3c905
+
+The related ISA 3c515 is supported with a separate driver, 3c515.c, included
+with the kernel source or available from
+ cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+
+The EEPROM settings for media type and forced-full-duplex are observed.
+The EEPROM media type should be left at the default "autoselect" unless using
+10base2 or AUI connections which cannot be reliably detected.
+
+III. Driver operation
+
+The 3c59x series use an interface that's very similar to the previous 3c5x9
+series. The primary interface is two programmed-I/O FIFOs, with an
+alternate single-contiguous-region bus-master transfer (see next).
+
+The 3c900 "Boomerang" series uses a full-bus-master interface with separate
+lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
+DEC Tulip and Intel Speedo3. The first chip version retains a compatible
+programmed-I/O interface that has been removed in 'B' and subsequent board
+revisions.
+
+One extension that is advertised in a very large font is that the adapters
+are capable of being bus masters. On the Vortex chip this capability was
+only for a single contiguous region making it far less useful than the full
+bus master capability. There is a significant performance impact of taking
+an extra interrupt or polling for the completion of each transfer, as well
+as difficulty sharing the single transfer engine between the transmit and
+receive threads. Using DMA transfers is a win only with large blocks or
+with the flawed versions of the Intel Orion motherboard PCI controller.
+
+The Boomerang chip's full-bus-master interface is useful, and has the
+currently-unused advantages over other similar chips that queued transmit
+packets may be reordered and receive buffer groups are associated with a
+single frame.
+
+With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
+Rather than a fixed intermediate receive buffer, this scheme allocates
+full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as
+the copying breakpoint: it is chosen to trade-off the memory wasted by
+passing the full-sized skbuff to the queue layer for all frames vs. the
+copying cost of copying a frame to a correctly-sized skbuff.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+IV. Notes
+
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development
+3c590, 3c595, and 3c900 boards.
+The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
+the EISA version is called "Demon". According to Terry these names come
+from rides at the local amusement park.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
+This driver only supports ethernet packets because of the skbuff allocation
+limit of 4K.
+*/
+
+/* This table drives the PCI probe routines. It's mostly boilerplate in all
+ of the drivers, and will likely be provided by some future kernel.
+*/
+enum pci_flags_bit {
+ PCI_USES_MASTER=4,
+};
+
+enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, IS_TORNADO=8,
+ EEPROM_8BIT=0x10, /* AKPM: Uses 0x230 as the base bitmaps for EEPROM reads */
+ HAS_PWR_CTRL=0x20, HAS_MII=0x40, HAS_NWAY=0x80, HAS_CB_FNS=0x100,
+ INVERT_MII_PWR=0x200, INVERT_LED_PWR=0x400, MAX_COLLISION_RESET=0x800,
+ EEPROM_OFFSET=0x1000, HAS_HWCKSM=0x2000, WNO_XCVR_PWR=0x4000,
+ EXTRA_PREAMBLE=0x8000, EEPROM_RESET=0x10000, };
+
+enum vortex_chips {
+ CH_3C590 = 0,
+ CH_3C592,
+ CH_3C597,
+ CH_3C595_1,
+ CH_3C595_2,
+
+ CH_3C595_3,
+ CH_3C900_1,
+ CH_3C900_2,
+ CH_3C900_3,
+ CH_3C900_4,
+
+ CH_3C900_5,
+ CH_3C900B_FL,
+ CH_3C905_1,
+ CH_3C905_2,
+ CH_3C905B_TX,
+ CH_3C905B_1,
+
+ CH_3C905B_2,
+ CH_3C905B_FX,
+ CH_3C905C,
+ CH_3C9202,
+ CH_3C980,
+ CH_3C9805,
+
+ CH_3CSOHO100_TX,
+ CH_3C555,
+ CH_3C556,
+ CH_3C556B,
+ CH_3C575,
+
+ CH_3C575_1,
+ CH_3CCFE575,
+ CH_3CCFE575CT,
+ CH_3CCFE656,
+ CH_3CCFEM656,
+
+ CH_3CCFEM656_1,
+ CH_3C450,
+ CH_3C920,
+ CH_3C982A,
+ CH_3C982B,
+
+ CH_905BT4,
+ CH_920B_EMB_WNM,
+};
+
+
+/* note: this array directly indexed by above enums, and MUST
+ * be kept in sync with both the enums above, and the PCI device
+ * table below
+ */
+static struct vortex_chip_info {
+ const char *name;
+ int flags;
+ int drv_flags;
+ int io_size;
+} vortex_info_tbl[] = {
+ {"3c590 Vortex 10Mbps",
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+ {"3c592 EISA 10Mbps Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+ {"3c597 EISA Fast Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+ {"3c595 Vortex 100baseTx",
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+ {"3c595 Vortex 100baseT4",
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+
+ {"3c595 Vortex 100base-MII",
+ PCI_USES_MASTER, IS_VORTEX, 32, },
+ {"3c900 Boomerang 10baseT",
+ PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
+ {"3c900 Boomerang 10Mbps Combo",
+ PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
+ {"3c900 Cyclone 10Mbps TPO", /* AKPM: from Don's 0.99M */
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ {"3c900 Cyclone 10Mbps Combo",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+
+ {"3c900 Cyclone 10Mbps TPC", /* AKPM: from Don's 0.99M */
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ {"3c900B-FL Cyclone 10base-FL",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ {"3c905 Boomerang 100baseTx",
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
+ {"3c905 Boomerang 100baseT4",
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
+ {"3C905B-TX Fast Etherlink XL PCI",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ {"3c905B Cyclone 100baseTx",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+
+ {"3c905B Cyclone 10/100/BNC",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
+ {"3c905B-FX Cyclone 100baseFx",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ {"3c905C Tornado",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ {"3c920B-EMB-WNM (ATI Radeon 9100 IGP)",
+ PCI_USES_MASTER, IS_TORNADO|HAS_MII|HAS_HWCKSM, 128, },
+ {"3c980 Cyclone",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+
+ {"3c980C Python-T",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
+ {"3cSOHO100-TX Hurricane",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ {"3c555 Laptop Hurricane",
+ PCI_USES_MASTER, IS_CYCLONE|EEPROM_8BIT|HAS_HWCKSM, 128, },
+ {"3c556 Laptop Tornado",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_8BIT|HAS_CB_FNS|INVERT_MII_PWR|
+ HAS_HWCKSM, 128, },
+ {"3c556B Laptop Hurricane",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR|
+ WNO_XCVR_PWR|HAS_HWCKSM, 128, },
+
+ {"3c575 [Megahertz] 10/100 LAN CardBus",
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
+ {"3c575 Boomerang CardBus",
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
+ {"3CCFE575BT Cyclone CardBus",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|
+ INVERT_LED_PWR|HAS_HWCKSM, 128, },
+ {"3CCFE575CT Tornado CardBus",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
+ {"3CCFE656 Cyclone CardBus",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ INVERT_LED_PWR|HAS_HWCKSM, 128, },
+
+ {"3CCFEM656B Cyclone+Winmodem CardBus",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ INVERT_LED_PWR|HAS_HWCKSM, 128, },
+ {"3CXFEM656C Tornado+Winmodem CardBus", /* From pcmcia-cs-3.1.5 */
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
+ {"3c450 HomePNA Tornado", /* AKPM: from Don's 0.99Q */
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+ {"3c920 Tornado",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+ {"3c982 Hydra Dual Port A",
+ PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
+
+ {"3c982 Hydra Dual Port B",
+ PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
+ {"3c905B-T4",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ {"3c920B-EMB-WNM Tornado",
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+
+ {NULL,}, /* NULL terminated list. */
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(vortex_pci_tbl) = {
+ { 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C590 },
+ { 0x10B7, 0x5920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C592 },
+ { 0x10B7, 0x5970, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C597 },
+ { 0x10B7, 0x5950, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_1 },
+ { 0x10B7, 0x5951, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_2 },
+
+ { 0x10B7, 0x5952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_3 },
+ { 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_1 },
+ { 0x10B7, 0x9001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_2 },
+ { 0x10B7, 0x9004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_3 },
+ { 0x10B7, 0x9005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_4 },
+
+ { 0x10B7, 0x9006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_5 },
+ { 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL },
+ { 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 },
+ { 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 },
+ { 0x10B7, 0x9054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_TX },
+ { 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 },
+
+ { 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 },
+ { 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX },
+ { 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C },
+ { 0x10B7, 0x9202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9202 },
+ { 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 },
+ { 0x10B7, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9805 },
+
+ { 0x10B7, 0x7646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CSOHO100_TX },
+ { 0x10B7, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C555 },
+ { 0x10B7, 0x6055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C556 },
+ { 0x10B7, 0x6056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C556B },
+ { 0x10B7, 0x5b57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575 },
+
+ { 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_1 },
+ { 0x10B7, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575 },
+ { 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT },
+ { 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 },
+ { 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 },
+
+ { 0x10B7, 0x6564, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656_1 },
+ { 0x10B7, 0x4500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C450 },
+ { 0x10B7, 0x9201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C920 },
+ { 0x10B7, 0x1201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982A },
+ { 0x10B7, 0x1202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982B },
+
+ { 0x10B7, 0x9056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_905BT4 },
+ { 0x10B7, 0x9210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_920B_EMB_WNM },
+
+ {0,} /* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, vortex_pci_tbl);
+
+
+/* Operational definitions.
+ These are not used by other compilation units and thus are not
+ exported in a ".h" file.
+
+ First the windows. There are eight register windows, with the command
+ and status registers available in each.
+ */
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable.
+ Note that 11 parameters bits was fine for ethernet, but the new chip
+ can handle FDDI length frames (~4500 octets) and now parameters count
+ 32-bit 'Dwords' rather than octets. */
+
+enum vortex_cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11,
+ UpStall = 6<<11, UpUnstall = (6<<11)+1,
+ DownStall = (6<<11)+2, DownUnstall = (6<<11)+3,
+ RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11,
+ StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11, SetFilterBit = 25<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Bits in the general status register. */
+enum vortex_status {
+ IntLatch = 0x0001, HostError = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080,
+ DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10,
+ DMAInProgress = 1<<11, /* DMA controller is still busy.*/
+ CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Vortex this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+ Wn0EepromData = 12, /* Window 0: EEPROM results register. */
+ IntrStatus=0x0E, /* Valid in all windows. */
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+/* EEPROM locations. */
+enum eeprom_offset {
+ PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
+ EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
+ NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
+ DriverTune=13, Checksum=15};
+
+enum Window2 { /* Window 2. */
+ Wn2_ResetOptions=12,
+};
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config=0, Wn3_MaxPktSize=4, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+
+#define BFEXT(value, offset, bitcount) \
+ ((((unsigned long)(value)) >> (offset)) & ((1 << (bitcount)) - 1))
+
+#define BFINS(lhs, rhs, offset, bitcount) \
+ (((lhs) & ~((((1 << (bitcount)) - 1)) << (offset))) | \
+ (((rhs) & ((1 << (bitcount)) - 1)) << (offset)))
+
+#define RAM_SIZE(v) BFEXT(v, 0, 3)
+#define RAM_WIDTH(v) BFEXT(v, 3, 1)
+#define RAM_SPEED(v) BFEXT(v, 4, 2)
+#define ROM_SIZE(v) BFEXT(v, 6, 2)
+#define RAM_SPLIT(v) BFEXT(v, 16, 2)
+#define XCVR(v) BFEXT(v, 20, 4)
+#define AUTOSELECT(v) BFEXT(v, 24, 1)
+
+enum Window4 { /* Window 4: Xcvr/media bits. */
+ Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
+};
+enum Win4_Media_bits {
+ Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
+ Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
+ Media_LnkBeat = 0x0800,
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_VlanEtherType=4, Wn7_MasterLen = 6,
+ Wn7_MasterStatus = 12,
+};
+/* Boomerang bus master control registers. */
+enum MasterCtrl {
+ PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c,
+ TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38,
+};
+
+/* The Rx and Tx descriptor lists.
+ Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
+ alignment contraint on tx_ring[] and rx_ring[]. */
+#define LAST_FRAG 0x80000000 /* Last Addr/Len pair in descriptor. */
+#define DN_COMPLETE 0x00010000 /* This packet has been downloaded */
+struct boom_rx_desc {
+ __le32 next; /* Last entry points to 0. */
+ __le32 status;
+ __le32 addr; /* Up to 63 addr/len pairs possible. */
+ __le32 length; /* Set LAST_FRAG to indicate last pair. */
+};
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+ RxDComplete=0x00008000, RxDError=0x4000,
+ /* See boomerang_rx() for actual error bits */
+ IPChksumErr=1<<25, TCPChksumErr=1<<26, UDPChksumErr=1<<27,
+ IPChksumValid=1<<29, TCPChksumValid=1<<30, UDPChksumValid=1<<31,
+};
+
+#ifdef MAX_SKB_FRAGS
+#define DO_ZEROCOPY 1
+#else
+#define DO_ZEROCOPY 0
+#endif
+
+struct boom_tx_desc {
+ __le32 next; /* Last entry points to 0. */
+ __le32 status; /* bits 0:12 length, others see below. */
+#if DO_ZEROCOPY
+ struct {
+ __le32 addr;
+ __le32 length;
+ } frag[1+MAX_SKB_FRAGS];
+#else
+ __le32 addr;
+ __le32 length;
+#endif
+};
+
+/* Values for the Tx status entry. */
+enum tx_desc_status {
+ CRCDisable=0x2000, TxDComplete=0x8000,
+ AddIPChksum=0x02000000, AddTCPChksum=0x04000000, AddUDPChksum=0x08000000,
+ TxIntrUploaded=0x80000000, /* IRQ when in FIFO, but maybe not sent. */
+};
+
+/* Chip features we care about in vp->capabilities, read from the EEPROM. */
+enum ChipCaps { CapBusMaster=0x20, CapPwrMgmt=0x2000 };
+
+struct vortex_extra_stats {
+ unsigned long tx_deferred;
+ unsigned long tx_max_collisions;
+ unsigned long tx_multiple_collisions;
+ unsigned long tx_single_collisions;
+ unsigned long rx_bad_ssd;
+};
+
+struct vortex_private {
+ /* The Rx and Tx rings should be quad-word-aligned. */
+ struct boom_rx_desc* rx_ring;
+ struct boom_tx_desc* tx_ring;
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_ring_dma;
+ /* The addresses of transmit- and receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct vortex_extra_stats xstats; /* NIC-specific extra stats */
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+ dma_addr_t tx_skb_dma; /* Allocated DMA address for bus master ctrl DMA. */
+
+ /* PCI configuration space information. */
+ struct device *gendev;
+ void __iomem *ioaddr; /* IO address space */
+ void __iomem *cb_fn_base; /* CardBus function status addr space. */
+
+ /* Some values here only for performance evaluation and path-coverage */
+ int rx_nocopy, rx_copy, queued_packet, rx_csumhits;
+ int card_idx;
+
+ /* The remainder are related to chip state, mostly media selection. */
+ struct timer_list timer; /* Media selection timer. */
+ struct timer_list rx_oom_timer; /* Rx skb allocation retry timer */
+ int options; /* User-settable misc. driver options. */
+ unsigned int media_override:4, /* Passed-in media type. */
+ default_media:4, /* Read from the EEPROM/Wn3_Config. */
+ full_duplex:1, autoselect:1,
+ bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
+ flow_ctrl:1, /* Use 802.3x flow control (PAUSE only) */
+ partner_flow_ctrl:1, /* Partner supports flow control */
+ has_nway:1,
+ enable_wol:1, /* Wake-on-LAN is enabled */
+ pm_state_valid:1, /* pci_dev->saved_config_space has sane contents */
+ open:1,
+ medialock:1,
+ large_frames:1, /* accept large frames */
+ handling_irq:1; /* private in_irq indicator */
+ /* {get|set}_wol operations are already serialized by rtnl.
+ * no additional locking is required for the enable_wol and acpi_set_WOL()
+ */
+ int drv_flags;
+ u16 status_enable;
+ u16 intr_enable;
+ u16 available_media; /* From Wn3_Options. */
+ u16 capabilities, info1, info2; /* Various, from EEPROM. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+ u16 deferred; /* Resend these interrupts when we
+ * bale from the ISR */
+ u16 io_size; /* Size of PCI region (for release_region) */
+
+ /* Serialises access to hardware other than MII and variables below.
+ * The lock hierarchy is rtnl_lock > {lock, mii_lock} > window_lock. */
+ spinlock_t lock;
+
+ spinlock_t mii_lock; /* Serialises access to MII */
+ struct mii_if_info mii; /* MII lib hooks/info */
+ spinlock_t window_lock; /* Serialises access to windowed regs */
+ int window; /* Register window */
+};
+
+static void window_set(struct vortex_private *vp, int window)
+{
+ if (window != vp->window) {
+ iowrite16(SelectWindow + window, vp->ioaddr + EL3_CMD);
+ vp->window = window;
+ }
+}
+
+#define DEFINE_WINDOW_IO(size) \
+static u ## size \
+window_read ## size(struct vortex_private *vp, int window, int addr) \
+{ \
+ unsigned long flags; \
+ u ## size ret; \
+ spin_lock_irqsave(&vp->window_lock, flags); \
+ window_set(vp, window); \
+ ret = ioread ## size(vp->ioaddr + addr); \
+ spin_unlock_irqrestore(&vp->window_lock, flags); \
+ return ret; \
+} \
+static void \
+window_write ## size(struct vortex_private *vp, u ## size value, \
+ int window, int addr) \
+{ \
+ unsigned long flags; \
+ spin_lock_irqsave(&vp->window_lock, flags); \
+ window_set(vp, window); \
+ iowrite ## size(value, vp->ioaddr + addr); \
+ spin_unlock_irqrestore(&vp->window_lock, flags); \
+}
+DEFINE_WINDOW_IO(8)
+DEFINE_WINDOW_IO(16)
+DEFINE_WINDOW_IO(32)
+
+#ifdef CONFIG_PCI
+#define DEVICE_PCI(dev) ((dev_is_pci(dev)) ? to_pci_dev((dev)) : NULL)
+#else
+#define DEVICE_PCI(dev) NULL
+#endif
+
+#define VORTEX_PCI(vp) \
+ ((struct pci_dev *) (((vp)->gendev) ? DEVICE_PCI((vp)->gendev) : NULL))
+
+#ifdef CONFIG_EISA
+#define DEVICE_EISA(dev) (((dev)->bus == &eisa_bus_type) ? to_eisa_device((dev)) : NULL)
+#else
+#define DEVICE_EISA(dev) NULL
+#endif
+
+#define VORTEX_EISA(vp) \
+ ((struct eisa_device *) (((vp)->gendev) ? DEVICE_EISA((vp)->gendev) : NULL))
+
+/* The action to take with a media selection timer tick.
+ Note that we deviate from the 3Com order by checking 10base2 before AUI.
+ */
+enum xcvr_types {
+ XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+ XCVR_100baseFx, XCVR_MII=6, XCVR_NWAY=8, XCVR_ExtMII=9, XCVR_Default=10,
+};
+
+static const struct media_table {
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config.*/
+ next:8; /* The media type to try next. */
+ int wait; /* Time before we check media status. */
+} media_tbl[] = {
+ { "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10},
+ { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10},
+ { "undefined", 0, 0x80, XCVR_10baseT, 10000},
+ { "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10},
+ { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10},
+ { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10},
+ { "MII", 0, 0x41, XCVR_10baseT, 3*HZ },
+ { "undefined", 0, 0x01, XCVR_10baseT, 10000},
+ { "Autonegotiate", 0, 0x41, XCVR_10baseT, 3*HZ},
+ { "MII-External", 0, 0x41, XCVR_10baseT, 3*HZ },
+ { "Default", 0, 0xFF, XCVR_10baseT, 10000},
+};
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "tx_deferred" },
+ { "tx_max_collisions" },
+ { "tx_multiple_collisions" },
+ { "tx_single_collisions" },
+ { "rx_bad_ssd" },
+};
+
+/* number of ETHTOOL_GSTATS u64's */
+#define VORTEX_NUM_STATS 5
+
+static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
+ int chip_idx, int card_idx);
+static int vortex_up(struct net_device *dev);
+static void vortex_down(struct net_device *dev, int final);
+static int vortex_open(struct net_device *dev);
+static void mdio_sync(struct vortex_private *vp, int bits);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *vp, int phy_id, int location, int value);
+static void vortex_timer(unsigned long arg);
+static void rx_oom_timer(unsigned long arg);
+static netdev_tx_t vortex_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static netdev_tx_t boomerang_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static int vortex_rx(struct net_device *dev);
+static int boomerang_rx(struct net_device *dev);
+static irqreturn_t vortex_interrupt(int irq, void *dev_id);
+static irqreturn_t boomerang_interrupt(int irq, void *dev_id);
+static int vortex_close(struct net_device *dev);
+static void dump_tx_ring(struct net_device *dev);
+static void update_stats(void __iomem *ioaddr, struct net_device *dev);
+static struct net_device_stats *vortex_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+#ifdef CONFIG_PCI
+static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#endif
+static void vortex_tx_timeout(struct net_device *dev);
+static void acpi_set_WOL(struct net_device *dev);
+static const struct ethtool_ops vortex_ethtool_ops;
+static void set_8021q_mode(struct net_device *dev, int enable);
+
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Option count limit only -- unlimited interfaces are supported. */
+#define MAX_UNITS 8
+static int options[MAX_UNITS] = { [0 ... MAX_UNITS-1] = -1 };
+static int full_duplex[MAX_UNITS] = {[0 ... MAX_UNITS-1] = -1 };
+static int hw_checksums[MAX_UNITS] = {[0 ... MAX_UNITS-1] = -1 };
+static int flow_ctrl[MAX_UNITS] = {[0 ... MAX_UNITS-1] = -1 };
+static int enable_wol[MAX_UNITS] = {[0 ... MAX_UNITS-1] = -1 };
+static int use_mmio[MAX_UNITS] = {[0 ... MAX_UNITS-1] = -1 };
+static int global_options = -1;
+static int global_full_duplex = -1;
+static int global_enable_wol = -1;
+static int global_use_mmio = -1;
+
+/* Variables to work-around the Compaq PCI BIOS32 problem. */
+static int compaq_ioaddr, compaq_irq, compaq_device_id = 0x5900;
+static struct net_device *compaq_net_device;
+
+static int vortex_cards_found;
+
+module_param(debug, int, 0);
+module_param(global_options, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param(global_full_duplex, int, 0);
+module_param_array(full_duplex, int, NULL, 0);
+module_param_array(hw_checksums, int, NULL, 0);
+module_param_array(flow_ctrl, int, NULL, 0);
+module_param(global_enable_wol, int, 0);
+module_param_array(enable_wol, int, NULL, 0);
+module_param(rx_copybreak, int, 0);
+module_param(max_interrupt_work, int, 0);
+module_param(compaq_ioaddr, int, 0);
+module_param(compaq_irq, int, 0);
+module_param(compaq_device_id, int, 0);
+module_param(watchdog, int, 0);
+module_param(global_use_mmio, int, 0);
+module_param_array(use_mmio, int, NULL, 0);
+MODULE_PARM_DESC(debug, "3c59x debug level (0-6)");
+MODULE_PARM_DESC(options, "3c59x: Bits 0-3: media type, bit 4: bus mastering, bit 9: full duplex");
+MODULE_PARM_DESC(global_options, "3c59x: same as options, but applies to all NICs if options is unset");
+MODULE_PARM_DESC(full_duplex, "3c59x full duplex setting(s) (1)");
+MODULE_PARM_DESC(global_full_duplex, "3c59x: same as full_duplex, but applies to all NICs if full_duplex is unset");
+MODULE_PARM_DESC(hw_checksums, "3c59x Hardware checksum checking by adapter(s) (0-1)");
+MODULE_PARM_DESC(flow_ctrl, "3c59x 802.3x flow control usage (PAUSE only) (0-1)");
+MODULE_PARM_DESC(enable_wol, "3c59x: Turn on Wake-on-LAN for adapter(s) (0-1)");
+MODULE_PARM_DESC(global_enable_wol, "3c59x: same as enable_wol, but applies to all NICs if enable_wol is unset");
+MODULE_PARM_DESC(rx_copybreak, "3c59x copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(max_interrupt_work, "3c59x maximum events handled per interrupt");
+MODULE_PARM_DESC(compaq_ioaddr, "3c59x PCI I/O base address (Compaq BIOS problem workaround)");
+MODULE_PARM_DESC(compaq_irq, "3c59x PCI IRQ number (Compaq BIOS problem workaround)");
+MODULE_PARM_DESC(compaq_device_id, "3c59x PCI device ID (Compaq BIOS problem workaround)");
+MODULE_PARM_DESC(watchdog, "3c59x transmit timeout in milliseconds");
+MODULE_PARM_DESC(global_use_mmio, "3c59x: same as use_mmio, but applies to all NICs if options is unset");
+MODULE_PARM_DESC(use_mmio, "3c59x: use memory-mapped PCI I/O resource (0-1)");
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void poll_vortex(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ unsigned long flags;
+ local_irq_save(flags);
+ (vp->full_bus_master_rx ? boomerang_interrupt:vortex_interrupt)(dev->irq,dev);
+ local_irq_restore(flags);
+}
+#endif
+
+#ifdef CONFIG_PM
+
+static int vortex_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *ndev = pci_get_drvdata(pdev);
+
+ if (!ndev || !netif_running(ndev))
+ return 0;
+
+ netif_device_detach(ndev);
+ vortex_down(ndev, 1);
+
+ return 0;
+}
+
+static int vortex_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct net_device *ndev = pci_get_drvdata(pdev);
+ int err;
+
+ if (!ndev || !netif_running(ndev))
+ return 0;
+
+ err = vortex_up(ndev);
+ if (err)
+ return err;
+
+ netif_device_attach(ndev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops vortex_pm_ops = {
+ .suspend = vortex_suspend,
+ .resume = vortex_resume,
+ .freeze = vortex_suspend,
+ .thaw = vortex_resume,
+ .poweroff = vortex_suspend,
+ .restore = vortex_resume,
+};
+
+#define VORTEX_PM_OPS (&vortex_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define VORTEX_PM_OPS NULL
+
+#endif /* !CONFIG_PM */
+
+#ifdef CONFIG_EISA
+static struct eisa_device_id vortex_eisa_ids[] = {
+ { "TCM5920", CH_3C592 },
+ { "TCM5970", CH_3C597 },
+ { "" }
+};
+MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids);
+
+static int __init vortex_eisa_probe(struct device *device)
+{
+ void __iomem *ioaddr;
+ struct eisa_device *edev;
+
+ edev = to_eisa_device(device);
+
+ if (!request_region(edev->base_addr, VORTEX_TOTAL_SIZE, DRV_NAME))
+ return -EBUSY;
+
+ ioaddr = ioport_map(edev->base_addr, VORTEX_TOTAL_SIZE);
+
+ if (vortex_probe1(device, ioaddr, ioread16(ioaddr + 0xC88) >> 12,
+ edev->id.driver_data, vortex_cards_found)) {
+ release_region(edev->base_addr, VORTEX_TOTAL_SIZE);
+ return -ENODEV;
+ }
+
+ vortex_cards_found++;
+
+ return 0;
+}
+
+static int vortex_eisa_remove(struct device *device)
+{
+ struct eisa_device *edev;
+ struct net_device *dev;
+ struct vortex_private *vp;
+ void __iomem *ioaddr;
+
+ edev = to_eisa_device(device);
+ dev = eisa_get_drvdata(edev);
+
+ if (!dev) {
+ pr_err("vortex_eisa_remove called for Compaq device!\n");
+ BUG();
+ }
+
+ vp = netdev_priv(dev);
+ ioaddr = vp->ioaddr;
+
+ unregister_netdev(dev);
+ iowrite16(TotalReset|0x14, ioaddr + EL3_CMD);
+ release_region(edev->base_addr, VORTEX_TOTAL_SIZE);
+
+ free_netdev(dev);
+ return 0;
+}
+
+static struct eisa_driver vortex_eisa_driver = {
+ .id_table = vortex_eisa_ids,
+ .driver = {
+ .name = "3c59x",
+ .probe = vortex_eisa_probe,
+ .remove = vortex_eisa_remove
+ }
+};
+
+#endif /* CONFIG_EISA */
+
+/* returns count found (>= 0), or negative on error */
+static int __init vortex_eisa_init(void)
+{
+ int eisa_found = 0;
+ int orig_cards_found = vortex_cards_found;
+
+#ifdef CONFIG_EISA
+ int err;
+
+ err = eisa_driver_register (&vortex_eisa_driver);
+ if (!err) {
+ /*
+ * Because of the way EISA bus is probed, we cannot assume
+ * any device have been found when we exit from
+ * eisa_driver_register (the bus root driver may not be
+ * initialized yet). So we blindly assume something was
+ * found, and let the sysfs magic happened...
+ */
+ eisa_found = 1;
+ }
+#endif
+
+ /* Special code to work-around the Compaq PCI BIOS32 problem. */
+ if (compaq_ioaddr) {
+ vortex_probe1(NULL, ioport_map(compaq_ioaddr, VORTEX_TOTAL_SIZE),
+ compaq_irq, compaq_device_id, vortex_cards_found++);
+ }
+
+ return vortex_cards_found - orig_cards_found + eisa_found;
+}
+
+/* returns count (>= 0), or negative on error */
+static int vortex_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int rc, unit, pci_bar;
+ struct vortex_chip_info *vci;
+ void __iomem *ioaddr;
+
+ /* wake up and enable device */
+ rc = pci_enable_device(pdev);
+ if (rc < 0)
+ goto out;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc < 0)
+ goto out_disable;
+
+ unit = vortex_cards_found;
+
+ if (global_use_mmio < 0 && (unit >= MAX_UNITS || use_mmio[unit] < 0)) {
+ /* Determine the default if the user didn't override us */
+ vci = &vortex_info_tbl[ent->driver_data];
+ pci_bar = vci->drv_flags & (IS_CYCLONE | IS_TORNADO) ? 1 : 0;
+ } else if (unit < MAX_UNITS && use_mmio[unit] >= 0)
+ pci_bar = use_mmio[unit] ? 1 : 0;
+ else
+ pci_bar = global_use_mmio ? 1 : 0;
+
+ ioaddr = pci_iomap(pdev, pci_bar, 0);
+ if (!ioaddr) /* If mapping fails, fall-back to BAR 0... */
+ ioaddr = pci_iomap(pdev, 0, 0);
+ if (!ioaddr) {
+ rc = -ENOMEM;
+ goto out_release;
+ }
+
+ rc = vortex_probe1(&pdev->dev, ioaddr, pdev->irq,
+ ent->driver_data, unit);
+ if (rc < 0)
+ goto out_iounmap;
+
+ vortex_cards_found++;
+ goto out;
+
+out_iounmap:
+ pci_iounmap(pdev, ioaddr);
+out_release:
+ pci_release_regions(pdev);
+out_disable:
+ pci_disable_device(pdev);
+out:
+ return rc;
+}
+
+static const struct net_device_ops boomrang_netdev_ops = {
+ .ndo_open = vortex_open,
+ .ndo_stop = vortex_close,
+ .ndo_start_xmit = boomerang_start_xmit,
+ .ndo_tx_timeout = vortex_tx_timeout,
+ .ndo_get_stats = vortex_get_stats,
+#ifdef CONFIG_PCI
+ .ndo_do_ioctl = vortex_ioctl,
+#endif
+ .ndo_set_rx_mode = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = poll_vortex,
+#endif
+};
+
+static const struct net_device_ops vortex_netdev_ops = {
+ .ndo_open = vortex_open,
+ .ndo_stop = vortex_close,
+ .ndo_start_xmit = vortex_start_xmit,
+ .ndo_tx_timeout = vortex_tx_timeout,
+ .ndo_get_stats = vortex_get_stats,
+#ifdef CONFIG_PCI
+ .ndo_do_ioctl = vortex_ioctl,
+#endif
+ .ndo_set_rx_mode = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = poll_vortex,
+#endif
+};
+
+/*
+ * Start up the PCI/EISA device which is described by *gendev.
+ * Return 0 on success.
+ *
+ * NOTE: pdev can be NULL, for the case of a Compaq device
+ */
+static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
+ int chip_idx, int card_idx)
+{
+ struct vortex_private *vp;
+ int option;
+ unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
+ int i, step;
+ struct net_device *dev;
+ static int printed_version;
+ int retval, print_info;
+ struct vortex_chip_info * const vci = &vortex_info_tbl[chip_idx];
+ const char *print_name = "3c59x";
+ struct pci_dev *pdev = NULL;
+ struct eisa_device *edev = NULL;
+
+ if (!printed_version) {
+ pr_info("%s", version);
+ printed_version = 1;
+ }
+
+ if (gendev) {
+ if ((pdev = DEVICE_PCI(gendev))) {
+ print_name = pci_name(pdev);
+ }
+
+ if ((edev = DEVICE_EISA(gendev))) {
+ print_name = dev_name(&edev->dev);
+ }
+ }
+
+ dev = alloc_etherdev(sizeof(*vp));
+ retval = -ENOMEM;
+ if (!dev)
+ goto out;
+
+ SET_NETDEV_DEV(dev, gendev);
+ vp = netdev_priv(dev);
+
+ option = global_options;
+
+ /* The lower four bits are the media type. */
+ if (dev->mem_start) {
+ /*
+ * The 'options' param is passed in as the third arg to the
+ * LILO 'ether=' argument for non-modular use
+ */
+ option = dev->mem_start;
+ }
+ else if (card_idx < MAX_UNITS) {
+ if (options[card_idx] >= 0)
+ option = options[card_idx];
+ }
+
+ if (option > 0) {
+ if (option & 0x8000)
+ vortex_debug = 7;
+ if (option & 0x4000)
+ vortex_debug = 2;
+ if (option & 0x0400)
+ vp->enable_wol = 1;
+ }
+
+ print_info = (vortex_debug > 1);
+ if (print_info)
+ pr_info("See Documentation/networking/vortex.txt\n");
+
+ pr_info("%s: 3Com %s %s at %p.\n",
+ print_name,
+ pdev ? "PCI" : "EISA",
+ vci->name,
+ ioaddr);
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = irq;
+ dev->mtu = mtu;
+ vp->ioaddr = ioaddr;
+ vp->large_frames = mtu > 1500;
+ vp->drv_flags = vci->drv_flags;
+ vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
+ vp->io_size = vci->io_size;
+ vp->card_idx = card_idx;
+ vp->window = -1;
+
+ /* module list only for Compaq device */
+ if (gendev == NULL) {
+ compaq_net_device = dev;
+ }
+
+ /* PCI-only startup logic */
+ if (pdev) {
+ /* enable bus-mastering if necessary */
+ if (vci->flags & PCI_USES_MASTER)
+ pci_set_master(pdev);
+
+ if (vci->drv_flags & IS_VORTEX) {
+ u8 pci_latency;
+ u8 new_latency = 248;
+
+ /* Check the PCI latency value. On the 3c590 series the latency timer
+ must be set to the maximum value to avoid data corruption that occurs
+ when the timer expires during a transfer. This bug exists the Vortex
+ chip only. */
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < new_latency) {
+ pr_info("%s: Overriding PCI latency timer (CFLT) setting of %d, new value is %d.\n",
+ print_name, pci_latency, new_latency);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency);
+ }
+ }
+ }
+
+ spin_lock_init(&vp->lock);
+ spin_lock_init(&vp->mii_lock);
+ spin_lock_init(&vp->window_lock);
+ vp->gendev = gendev;
+ vp->mii.dev = dev;
+ vp->mii.mdio_read = mdio_read;
+ vp->mii.mdio_write = mdio_write;
+ vp->mii.phy_id_mask = 0x1f;
+ vp->mii.reg_num_mask = 0x1f;
+
+ /* Makes sure rings are at least 16 byte aligned. */
+ vp->rx_ring = pci_alloc_consistent(pdev, sizeof(struct boom_rx_desc) * RX_RING_SIZE
+ + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
+ &vp->rx_ring_dma);
+ retval = -ENOMEM;
+ if (!vp->rx_ring)
+ goto free_device;
+
+ vp->tx_ring = (struct boom_tx_desc *)(vp->rx_ring + RX_RING_SIZE);
+ vp->tx_ring_dma = vp->rx_ring_dma + sizeof(struct boom_rx_desc) * RX_RING_SIZE;
+
+ /* if we are a PCI driver, we store info in pdev->driver_data
+ * instead of a module list */
+ if (pdev)
+ pci_set_drvdata(pdev, dev);
+ if (edev)
+ eisa_set_drvdata(edev, dev);
+
+ vp->media_override = 7;
+ if (option >= 0) {
+ vp->media_override = ((option & 7) == 2) ? 0 : option & 15;
+ if (vp->media_override != 7)
+ vp->medialock = 1;
+ vp->full_duplex = (option & 0x200) ? 1 : 0;
+ vp->bus_master = (option & 16) ? 1 : 0;
+ }
+
+ if (global_full_duplex > 0)
+ vp->full_duplex = 1;
+ if (global_enable_wol > 0)
+ vp->enable_wol = 1;
+
+ if (card_idx < MAX_UNITS) {
+ if (full_duplex[card_idx] > 0)
+ vp->full_duplex = 1;
+ if (flow_ctrl[card_idx] > 0)
+ vp->flow_ctrl = 1;
+ if (enable_wol[card_idx] > 0)
+ vp->enable_wol = 1;
+ }
+
+ vp->mii.force_media = vp->full_duplex;
+ vp->options = option;
+ /* Read the station address from the EEPROM. */
+ {
+ int base;
+
+ if (vci->drv_flags & EEPROM_8BIT)
+ base = 0x230;
+ else if (vci->drv_flags & EEPROM_OFFSET)
+ base = EEPROM_Read + 0x30;
+ else
+ base = EEPROM_Read;
+
+ for (i = 0; i < 0x40; i++) {
+ int timer;
+ window_write16(vp, base + i, 0, Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 10; timer >= 0; timer--) {
+ udelay(162);
+ if ((window_read16(vp, 0, Wn0EepromCmd) &
+ 0x8000) == 0)
+ break;
+ }
+ eeprom[i] = window_read16(vp, 0, Wn0EepromData);
+ }
+ }
+ for (i = 0; i < 0x18; i++)
+ checksum ^= eeprom[i];
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00) { /* Grrr, needless incompatible change 3Com. */
+ while (i < 0x21)
+ checksum ^= eeprom[i++];
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ }
+ if ((checksum != 0x00) && !(vci->drv_flags & IS_TORNADO))
+ pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
+ for (i = 0; i < 3; i++)
+ ((__be16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]);
+ if (print_info)
+ pr_cont(" %pM", dev->dev_addr);
+ /* Unfortunately an all zero eeprom passes the checksum and this
+ gets found in the wild in failure cases. Crypto is hard 8) */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ retval = -EINVAL;
+ pr_err("*** EEPROM MAC address is invalid.\n");
+ goto free_ring; /* With every pack */
+ }
+ for (i = 0; i < 6; i++)
+ window_write8(vp, dev->dev_addr[i], 2, i);
+
+ if (print_info)
+ pr_cont(", IRQ %d\n", dev->irq);
+ /* Tell them about an invalid IRQ. */
+ if (dev->irq <= 0 || dev->irq >= nr_irqs)
+ pr_warning(" *** Warning: IRQ %d is unlikely to work! ***\n",
+ dev->irq);
+
+ step = (window_read8(vp, 4, Wn4_NetDiag) & 0x1e) >> 1;
+ if (print_info) {
+ pr_info(" product code %02x%02x rev %02x.%d date %02d-%02d-%02d\n",
+ eeprom[6]&0xff, eeprom[6]>>8, eeprom[0x14],
+ step, (eeprom[4]>>5) & 15, eeprom[4] & 31, eeprom[4]>>9);
+ }
+
+
+ if (pdev && vci->drv_flags & HAS_CB_FNS) {
+ unsigned short n;
+
+ vp->cb_fn_base = pci_iomap(pdev, 2, 0);
+ if (!vp->cb_fn_base) {
+ retval = -ENOMEM;
+ goto free_ring;
+ }
+
+ if (print_info) {
+ pr_info("%s: CardBus functions mapped %16.16llx->%p\n",
+ print_name,
+ (unsigned long long)pci_resource_start(pdev, 2),
+ vp->cb_fn_base);
+ }
+
+ n = window_read16(vp, 2, Wn2_ResetOptions) & ~0x4010;
+ if (vp->drv_flags & INVERT_LED_PWR)
+ n |= 0x10;
+ if (vp->drv_flags & INVERT_MII_PWR)
+ n |= 0x4000;
+ window_write16(vp, n, 2, Wn2_ResetOptions);
+ if (vp->drv_flags & WNO_XCVR_PWR) {
+ window_write16(vp, 0x0800, 0, 0);
+ }
+ }
+
+ /* Extract our information from the EEPROM data. */
+ vp->info1 = eeprom[13];
+ vp->info2 = eeprom[15];
+ vp->capabilities = eeprom[16];
+
+ if (vp->info1 & 0x8000) {
+ vp->full_duplex = 1;
+ if (print_info)
+ pr_info("Full duplex capable\n");
+ }
+
+ {
+ static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+ unsigned int config;
+ vp->available_media = window_read16(vp, 3, Wn3_Options);
+ if ((vp->available_media & 0xff) == 0) /* Broken 3c916 */
+ vp->available_media = 0x40;
+ config = window_read32(vp, 3, Wn3_Config);
+ if (print_info) {
+ pr_debug(" Internal config register is %4.4x, transceivers %#x.\n",
+ config, window_read16(vp, 3, Wn3_Options));
+ pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << RAM_SIZE(config),
+ RAM_WIDTH(config) ? "word" : "byte",
+ ram_split[RAM_SPLIT(config)],
+ AUTOSELECT(config) ? "autoselect/" : "",
+ XCVR(config) > XCVR_ExtMII ? "<invalid transceiver>" :
+ media_tbl[XCVR(config)].name);
+ }
+ vp->default_media = XCVR(config);
+ if (vp->default_media == XCVR_NWAY)
+ vp->has_nway = 1;
+ vp->autoselect = AUTOSELECT(config);
+ }
+
+ if (vp->media_override != 7) {
+ pr_info("%s: Media override to transceiver type %d (%s).\n",
+ print_name, vp->media_override,
+ media_tbl[vp->media_override].name);
+ dev->if_port = vp->media_override;
+ } else
+ dev->if_port = vp->default_media;
+
+ if ((vp->available_media & 0x40) || (vci->drv_flags & HAS_NWAY) ||
+ dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
+ int phy, phy_idx = 0;
+ mii_preamble_required++;
+ if (vp->drv_flags & EXTRA_PREAMBLE)
+ mii_preamble_required++;
+ mdio_sync(vp, 32);
+ mdio_read(dev, 24, MII_BMSR);
+ for (phy = 0; phy < 32 && phy_idx < 1; phy++) {
+ int mii_status, phyx;
+
+ /*
+ * For the 3c905CX we look at index 24 first, because it bogusly
+ * reports an external PHY at all indices
+ */
+ if (phy == 0)
+ phyx = 24;
+ else if (phy <= 24)
+ phyx = phy - 1;
+ else
+ phyx = phy;
+ mii_status = mdio_read(dev, phyx, MII_BMSR);
+ if (mii_status && mii_status != 0xffff) {
+ vp->phys[phy_idx++] = phyx;
+ if (print_info) {
+ pr_info(" MII transceiver found at address %d, status %4x.\n",
+ phyx, mii_status);
+ }
+ if ((mii_status & 0x0040) == 0)
+ mii_preamble_required++;
+ }
+ }
+ mii_preamble_required--;
+ if (phy_idx == 0) {
+ pr_warning(" ***WARNING*** No MII transceivers found!\n");
+ vp->phys[0] = 24;
+ } else {
+ vp->advertising = mdio_read(dev, vp->phys[0], MII_ADVERTISE);
+ if (vp->full_duplex) {
+ /* Only advertise the FD media types. */
+ vp->advertising &= ~0x02A0;
+ mdio_write(dev, vp->phys[0], 4, vp->advertising);
+ }
+ }
+ vp->mii.phy_id = vp->phys[0];
+ }
+
+ if (vp->capabilities & CapBusMaster) {
+ vp->full_bus_master_tx = 1;
+ if (print_info) {
+ pr_info(" Enabling bus-master transmits and %s receives.\n",
+ (vp->info2 & 1) ? "early" : "whole-frame" );
+ }
+ vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
+ vp->bus_master = 0; /* AKPM: vortex only */
+ }
+
+ /* The 3c59x-specific entries in the device structure. */
+ if (vp->full_bus_master_tx) {
+ dev->netdev_ops = &boomrang_netdev_ops;
+ /* Actually, it still should work with iommu. */
+ if (card_idx < MAX_UNITS &&
+ ((hw_checksums[card_idx] == -1 && (vp->drv_flags & HAS_HWCKSM)) ||
+ hw_checksums[card_idx] == 1)) {
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
+ }
+ } else
+ dev->netdev_ops = &vortex_netdev_ops;
+
+ if (print_info) {
+ pr_info("%s: scatter/gather %sabled. h/w checksums %sabled\n",
+ print_name,
+ (dev->features & NETIF_F_SG) ? "en":"dis",
+ (dev->features & NETIF_F_IP_CSUM) ? "en":"dis");
+ }
+
+ dev->ethtool_ops = &vortex_ethtool_ops;
+ dev->watchdog_timeo = (watchdog * HZ) / 1000;
+
+ if (pdev) {
+ vp->pm_state_valid = 1;
+ pci_save_state(pdev);
+ acpi_set_WOL(dev);
+ }
+ retval = register_netdev(dev);
+ if (retval == 0)
+ return 0;
+
+free_ring:
+ pci_free_consistent(pdev,
+ sizeof(struct boom_rx_desc) * RX_RING_SIZE
+ + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
+ vp->rx_ring,
+ vp->rx_ring_dma);
+free_device:
+ free_netdev(dev);
+ pr_err(PFX "vortex_probe1 fails. Returns %d\n", retval);
+out:
+ return retval;
+}
+
+static void
+issue_and_wait(struct net_device *dev, int cmd)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int i;
+
+ iowrite16(cmd, ioaddr + EL3_CMD);
+ for (i = 0; i < 2000; i++) {
+ if (!(ioread16(ioaddr + EL3_STATUS) & CmdInProgress))
+ return;
+ }
+
+ /* OK, that didn't work. Do it the slow way. One second */
+ for (i = 0; i < 100000; i++) {
+ if (!(ioread16(ioaddr + EL3_STATUS) & CmdInProgress)) {
+ if (vortex_debug > 1)
+ pr_info("%s: command 0x%04x took %d usecs\n",
+ dev->name, cmd, i * 10);
+ return;
+ }
+ udelay(10);
+ }
+ pr_err("%s: command 0x%04x did not complete! Status=0x%x\n",
+ dev->name, cmd, ioread16(ioaddr + EL3_STATUS));
+}
+
+static void
+vortex_set_duplex(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ pr_info("%s: setting %s-duplex.\n",
+ dev->name, (vp->full_duplex) ? "full" : "half");
+
+ /* Set the full-duplex bit. */
+ window_write16(vp,
+ ((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+ (vp->large_frames ? 0x40 : 0) |
+ ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ?
+ 0x100 : 0),
+ 3, Wn3_MAC_Ctrl);
+}
+
+static void vortex_check_media(struct net_device *dev, unsigned int init)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ unsigned int ok_to_print = 0;
+
+ if (vortex_debug > 3)
+ ok_to_print = 1;
+
+ if (mii_check_media(&vp->mii, ok_to_print, init)) {
+ vp->full_duplex = vp->mii.full_duplex;
+ vortex_set_duplex(dev);
+ } else if (init) {
+ vortex_set_duplex(dev);
+ }
+}
+
+static int
+vortex_up(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ unsigned int config;
+ int i, mii_reg1, mii_reg5, err = 0;
+
+ if (VORTEX_PCI(vp)) {
+ pci_set_power_state(VORTEX_PCI(vp), PCI_D0); /* Go active */
+ if (vp->pm_state_valid)
+ pci_restore_state(VORTEX_PCI(vp));
+ err = pci_enable_device(VORTEX_PCI(vp));
+ if (err) {
+ pr_warning("%s: Could not enable device\n",
+ dev->name);
+ goto err_out;
+ }
+ }
+
+ /* Before initializing select the active media port. */
+ config = window_read32(vp, 3, Wn3_Config);
+
+ if (vp->media_override != 7) {
+ pr_info("%s: Media override to transceiver %d (%s).\n",
+ dev->name, vp->media_override,
+ media_tbl[vp->media_override].name);
+ dev->if_port = vp->media_override;
+ } else if (vp->autoselect) {
+ if (vp->has_nway) {
+ if (vortex_debug > 1)
+ pr_info("%s: using NWAY device table, not %d\n",
+ dev->name, dev->if_port);
+ dev->if_port = XCVR_NWAY;
+ } else {
+ /* Find first available media type, starting with 100baseTx. */
+ dev->if_port = XCVR_100baseTx;
+ while (! (vp->available_media & media_tbl[dev->if_port].mask))
+ dev->if_port = media_tbl[dev->if_port].next;
+ if (vortex_debug > 1)
+ pr_info("%s: first available media type: %s\n",
+ dev->name, media_tbl[dev->if_port].name);
+ }
+ } else {
+ dev->if_port = vp->default_media;
+ if (vortex_debug > 1)
+ pr_info("%s: using default media %s\n",
+ dev->name, media_tbl[dev->if_port].name);
+ }
+
+ init_timer(&vp->timer);
+ vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+ vp->timer.data = (unsigned long)dev;
+ vp->timer.function = vortex_timer; /* timer handler */
+ add_timer(&vp->timer);
+
+ init_timer(&vp->rx_oom_timer);
+ vp->rx_oom_timer.data = (unsigned long)dev;
+ vp->rx_oom_timer.function = rx_oom_timer;
+
+ if (vortex_debug > 1)
+ pr_debug("%s: Initial media type %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ vp->full_duplex = vp->mii.force_media;
+ config = BFINS(config, dev->if_port, 20, 4);
+ if (vortex_debug > 6)
+ pr_debug("vortex_up(): writing 0x%x to InternalConfig\n", config);
+ window_write32(vp, config, 3, Wn3_Config);
+
+ if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
+ mii_reg1 = mdio_read(dev, vp->phys[0], MII_BMSR);
+ mii_reg5 = mdio_read(dev, vp->phys[0], MII_LPA);
+ vp->partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0);
+ vp->mii.full_duplex = vp->full_duplex;
+
+ vortex_check_media(dev, 1);
+ }
+ else
+ vortex_set_duplex(dev);
+
+ issue_and_wait(dev, TxReset);
+ /*
+ * Don't reset the PHY - that upsets autonegotiation during DHCP operations.
+ */
+ issue_and_wait(dev, RxReset|0x04);
+
+
+ iowrite16(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+ if (vortex_debug > 1) {
+ pr_debug("%s: vortex_up() irq %d media status %4.4x.\n",
+ dev->name, dev->irq, window_read16(vp, 4, Wn4_Media));
+ }
+
+ /* Set the station address and mask in window 2 each time opened. */
+ for (i = 0; i < 6; i++)
+ window_write8(vp, dev->dev_addr[i], 2, i);
+ for (; i < 12; i+=2)
+ window_write16(vp, 0, 2, i);
+
+ if (vp->cb_fn_base) {
+ unsigned short n = window_read16(vp, 2, Wn2_ResetOptions) & ~0x4010;
+ if (vp->drv_flags & INVERT_LED_PWR)
+ n |= 0x10;
+ if (vp->drv_flags & INVERT_MII_PWR)
+ n |= 0x4000;
+ window_write16(vp, n, 2, Wn2_ResetOptions);
+ }
+
+ if (dev->if_port == XCVR_10base2)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ iowrite16(StartCoax, ioaddr + EL3_CMD);
+ if (dev->if_port != XCVR_NWAY) {
+ window_write16(vp,
+ (window_read16(vp, 4, Wn4_Media) &
+ ~(Media_10TP|Media_SQE)) |
+ media_tbl[dev->if_port].media_bits,
+ 4, Wn4_Media);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ iowrite16(StatsDisable, ioaddr + EL3_CMD);
+ for (i = 0; i < 10; i++)
+ window_read8(vp, 6, i);
+ window_read16(vp, 6, 10);
+ window_read16(vp, 6, 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ window_read8(vp, 4, 12);
+ /* ..and on the Boomerang we enable the extra statistics bits. */
+ window_write16(vp, 0x0040, 4, Wn4_NetDiag);
+
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ vp->cur_rx = vp->dirty_rx = 0;
+ /* Initialize the RxEarly register as recommended. */
+ iowrite16(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ iowrite32(0x0020, ioaddr + PktStatus);
+ iowrite32(vp->rx_ring_dma, ioaddr + UpListPtr);
+ }
+ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
+ vp->cur_tx = vp->dirty_tx = 0;
+ if (vp->drv_flags & IS_BOOMERANG)
+ iowrite8(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+ /* Clear the Rx, Tx rings. */
+ for (i = 0; i < RX_RING_SIZE; i++) /* AKPM: this is done in vortex_open, too */
+ vp->rx_ring[i].status = 0;
+ for (i = 0; i < TX_RING_SIZE; i++)
+ vp->tx_skbuff[i] = NULL;
+ iowrite32(0, ioaddr + DownListPtr);
+ }
+ /* Set receiver mode: presumably accept b-case and phys addr only. */
+ set_rx_mode(dev);
+ /* enable 802.1q tagged frames */
+ set_8021q_mode(dev, 1);
+ iowrite16(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ iowrite16(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ iowrite16(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ vp->status_enable = SetStatusEnb | HostError|IntReq|StatsFull|TxComplete|
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0);
+ vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable |
+ (vp->full_bus_master_rx ? 0 : RxComplete) |
+ StatsFull | HostError | TxComplete | IntReq
+ | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete;
+ iowrite16(vp->status_enable, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ iowrite16(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ iowrite16(vp->intr_enable, ioaddr + EL3_CMD);
+ if (vp->cb_fn_base) /* The PCMCIA people are idiots. */
+ iowrite32(0x8000, vp->cb_fn_base + 4);
+ netif_start_queue (dev);
+err_out:
+ return err;
+}
+
+static int
+vortex_open(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ int i;
+ int retval;
+
+ /* Use the now-standard shared IRQ implementation. */
+ if ((retval = request_irq(dev->irq, vp->full_bus_master_rx ?
+ boomerang_interrupt : vortex_interrupt, IRQF_SHARED, dev->name, dev))) {
+ pr_err("%s: Could not reserve IRQ %d\n", dev->name, dev->irq);
+ goto err;
+ }
+
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ if (vortex_debug > 2)
+ pr_debug("%s: Filling in the Rx ring.\n", dev->name);
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+ vp->rx_ring[i].next = cpu_to_le32(vp->rx_ring_dma + sizeof(struct boom_rx_desc) * (i+1));
+ vp->rx_ring[i].status = 0; /* Clear complete bit. */
+ vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG);
+
+ skb = __netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN,
+ GFP_KERNEL);
+ vp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break; /* Bad news! */
+
+ skb_reserve(skb, NET_IP_ALIGN); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
+ }
+ if (i != RX_RING_SIZE) {
+ int j;
+ pr_emerg("%s: no memory for rx ring\n", dev->name);
+ for (j = 0; j < i; j++) {
+ if (vp->rx_skbuff[j]) {
+ dev_kfree_skb(vp->rx_skbuff[j]);
+ vp->rx_skbuff[j] = NULL;
+ }
+ }
+ retval = -ENOMEM;
+ goto err_free_irq;
+ }
+ /* Wrap the ring. */
+ vp->rx_ring[i-1].next = cpu_to_le32(vp->rx_ring_dma);
+ }
+
+ retval = vortex_up(dev);
+ if (!retval)
+ goto out;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err:
+ if (vortex_debug > 1)
+ pr_err("%s: vortex_open() fails: returning %d\n", dev->name, retval);
+out:
+ return retval;
+}
+
+static void
+vortex_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int next_tick = 60*HZ;
+ int ok = 0;
+ int media_status;
+
+ if (vortex_debug > 2) {
+ pr_debug("%s: Media selection timer tick happened, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+ pr_debug("dev->watchdog_timeo=%d\n", dev->watchdog_timeo);
+ }
+
+ media_status = window_read16(vp, 4, Wn4_Media);
+ switch (dev->if_port) {
+ case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx:
+ if (media_status & Media_LnkBeat) {
+ netif_carrier_on(dev);
+ ok = 1;
+ if (vortex_debug > 1)
+ pr_debug("%s: Media %s has link beat, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+ } else {
+ netif_carrier_off(dev);
+ if (vortex_debug > 1) {
+ pr_debug("%s: Media %s has no link beat, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+ }
+ }
+ break;
+ case XCVR_MII: case XCVR_NWAY:
+ {
+ ok = 1;
+ vortex_check_media(dev, 0);
+ }
+ break;
+ default: /* Other media types handled by Tx timeouts. */
+ if (vortex_debug > 1)
+ pr_debug("%s: Media %s has no indication, %x.\n",
+ dev->name, media_tbl[dev->if_port].name, media_status);
+ ok = 1;
+ }
+
+ if (dev->flags & IFF_SLAVE || !netif_carrier_ok(dev))
+ next_tick = 5*HZ;
+
+ if (vp->medialock)
+ goto leave_media_alone;
+
+ if (!ok) {
+ unsigned int config;
+
+ spin_lock_irq(&vp->lock);
+
+ do {
+ dev->if_port = media_tbl[dev->if_port].next;
+ } while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
+ if (dev->if_port == XCVR_Default) { /* Go back to default. */
+ dev->if_port = vp->default_media;
+ if (vortex_debug > 1)
+ pr_debug("%s: Media selection failing, using default %s port.\n",
+ dev->name, media_tbl[dev->if_port].name);
+ } else {
+ if (vortex_debug > 1)
+ pr_debug("%s: Media selection failed, now trying %s port.\n",
+ dev->name, media_tbl[dev->if_port].name);
+ next_tick = media_tbl[dev->if_port].wait;
+ }
+ window_write16(vp,
+ (media_status & ~(Media_10TP|Media_SQE)) |
+ media_tbl[dev->if_port].media_bits,
+ 4, Wn4_Media);
+
+ config = window_read32(vp, 3, Wn3_Config);
+ config = BFINS(config, dev->if_port, 20, 4);
+ window_write32(vp, config, 3, Wn3_Config);
+
+ iowrite16(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
+ ioaddr + EL3_CMD);
+ if (vortex_debug > 1)
+ pr_debug("wrote 0x%08x to Wn3_Config\n", config);
+ /* AKPM: FIXME: Should reset Rx & Tx here. P60 of 3c90xc.pdf */
+
+ spin_unlock_irq(&vp->lock);
+ }
+
+leave_media_alone:
+ if (vortex_debug > 2)
+ pr_debug("%s: Media selection timer finished, %s.\n",
+ dev->name, media_tbl[dev->if_port].name);
+
+ mod_timer(&vp->timer, RUN_AT(next_tick));
+ if (vp->deferred)
+ iowrite16(FakeIntr, ioaddr + EL3_CMD);
+}
+
+static void vortex_tx_timeout(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+
+ pr_err("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, ioread8(ioaddr + TxStatus),
+ ioread16(ioaddr + EL3_STATUS));
+ pr_err(" diagnostics: net %04x media %04x dma %08x fifo %04x\n",
+ window_read16(vp, 4, Wn4_NetDiag),
+ window_read16(vp, 4, Wn4_Media),
+ ioread32(ioaddr + PktStatus),
+ window_read16(vp, 4, Wn4_FIFODiag));
+ /* Slight code bloat to be user friendly. */
+ if ((ioread8(ioaddr + TxStatus) & 0x88) == 0x88)
+ pr_err("%s: Transmitter encountered 16 collisions --"
+ " network cable problem?\n", dev->name);
+ if (ioread16(ioaddr + EL3_STATUS) & IntLatch) {
+ pr_err("%s: Interrupt posted but not delivered --"
+ " IRQ blocked by another device?\n", dev->name);
+ /* Bad idea here.. but we might as well handle a few events. */
+ {
+ /*
+ * Block interrupts because vortex_interrupt does a bare spin_lock()
+ */
+ unsigned long flags;
+ local_irq_save(flags);
+ if (vp->full_bus_master_tx)
+ boomerang_interrupt(dev->irq, dev);
+ else
+ vortex_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+ }
+ }
+
+ if (vortex_debug > 0)
+ dump_tx_ring(dev);
+
+ issue_and_wait(dev, TxReset);
+
+ dev->stats.tx_errors++;
+ if (vp->full_bus_master_tx) {
+ pr_debug("%s: Resetting the Tx ring pointer.\n", dev->name);
+ if (vp->cur_tx - vp->dirty_tx > 0 && ioread32(ioaddr + DownListPtr) == 0)
+ iowrite32(vp->tx_ring_dma + (vp->dirty_tx % TX_RING_SIZE) * sizeof(struct boom_tx_desc),
+ ioaddr + DownListPtr);
+ if (vp->cur_tx - vp->dirty_tx < TX_RING_SIZE)
+ netif_wake_queue (dev);
+ if (vp->drv_flags & IS_BOOMERANG)
+ iowrite8(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+ iowrite16(DownUnstall, ioaddr + EL3_CMD);
+ } else {
+ dev->stats.tx_dropped++;
+ netif_wake_queue(dev);
+ }
+
+ /* Issue Tx Enable */
+ iowrite16(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+}
+
+/*
+ * Handle uncommon interrupt sources. This is a separate routine to minimize
+ * the cache impact.
+ */
+static void
+vortex_error(struct net_device *dev, int status)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int do_tx_reset = 0, reset_mask = 0;
+ unsigned char tx_status = 0;
+
+ if (vortex_debug > 2) {
+ pr_err("%s: vortex_error(), status=0x%x\n", dev->name, status);
+ }
+
+ if (status & TxComplete) { /* Really "TxError" for us. */
+ tx_status = ioread8(ioaddr + TxStatus);
+ /* Presumably a tx-timeout. We must merely re-enable. */
+ if (vortex_debug > 2 ||
+ (tx_status != 0x88 && vortex_debug > 0)) {
+ pr_err("%s: Transmit error, Tx status register %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status == 0x82) {
+ pr_err("Probably a duplex mismatch. See "
+ "Documentation/networking/vortex.txt\n");
+ }
+ dump_tx_ring(dev);
+ }
+ if (tx_status & 0x14) dev->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x08) vp->xstats.tx_max_collisions++;
+ iowrite8(0, ioaddr + TxStatus);
+ if (tx_status & 0x30) { /* txJabber or txUnderrun */
+ do_tx_reset = 1;
+ } else if ((tx_status & 0x08) && (vp->drv_flags & MAX_COLLISION_RESET)) { /* maxCollisions */
+ do_tx_reset = 1;
+ reset_mask = 0x0108; /* Reset interface logic, but not download logic */
+ } else { /* Merely re-enable the transmitter. */
+ iowrite16(TxEnable, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (status & RxEarly) /* Rx early is unused. */
+ iowrite16(AckIntr | RxEarly, ioaddr + EL3_CMD);
+
+ if (status & StatsFull) { /* Empty statistics. */
+ static int DoneDidThat;
+ if (vortex_debug > 4)
+ pr_debug("%s: Updating stats.\n", dev->name);
+ update_stats(ioaddr, dev);
+ /* HACK: Disable statistics as an interrupt source. */
+ /* This occurs when we have the wrong media type! */
+ if (DoneDidThat == 0 &&
+ ioread16(ioaddr + EL3_STATUS) & StatsFull) {
+ pr_warning("%s: Updating statistics failed, disabling "
+ "stats as an interrupt source.\n", dev->name);
+ iowrite16(SetIntrEnb |
+ (window_read16(vp, 5, 10) & ~StatsFull),
+ ioaddr + EL3_CMD);
+ vp->intr_enable &= ~StatsFull;
+ DoneDidThat++;
+ }
+ }
+ if (status & IntReq) { /* Restore all interrupt sources. */
+ iowrite16(vp->status_enable, ioaddr + EL3_CMD);
+ iowrite16(vp->intr_enable, ioaddr + EL3_CMD);
+ }
+ if (status & HostError) {
+ u16 fifo_diag;
+ fifo_diag = window_read16(vp, 4, Wn4_FIFODiag);
+ pr_err("%s: Host error, FIFO diagnostic register %4.4x.\n",
+ dev->name, fifo_diag);
+ /* Adapter failure requires Tx/Rx reset and reinit. */
+ if (vp->full_bus_master_tx) {
+ int bus_status = ioread32(ioaddr + PktStatus);
+ /* 0x80000000 PCI master abort. */
+ /* 0x40000000 PCI target abort. */
+ if (vortex_debug)
+ pr_err("%s: PCI bus error, bus status %8.8x\n", dev->name, bus_status);
+
+ /* In this case, blow the card away */
+ /* Must not enter D3 or we can't legally issue the reset! */
+ vortex_down(dev, 0);
+ issue_and_wait(dev, TotalReset | 0xff);
+ vortex_up(dev); /* AKPM: bug. vortex_up() assumes that the rx ring is full. It may not be. */
+ } else if (fifo_diag & 0x0400)
+ do_tx_reset = 1;
+ if (fifo_diag & 0x3000) {
+ /* Reset Rx fifo and upload logic */
+ issue_and_wait(dev, RxReset|0x07);
+ /* Set the Rx filter to the current state. */
+ set_rx_mode(dev);
+ /* enable 802.1q VLAN tagged frames */
+ set_8021q_mode(dev, 1);
+ iowrite16(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ iowrite16(AckIntr | HostError, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (do_tx_reset) {
+ issue_and_wait(dev, TxReset|reset_mask);
+ iowrite16(TxEnable, ioaddr + EL3_CMD);
+ if (!vp->full_bus_master_tx)
+ netif_wake_queue(dev);
+ }
+}
+
+static netdev_tx_t
+vortex_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+
+ /* Put out the doubleword header... */
+ iowrite32(skb->len, ioaddr + TX_FIFO);
+ if (vp->bus_master) {
+ /* Set the bus-master controller to transfer the packet. */
+ int len = (skb->len + 3) & ~3;
+ vp->tx_skb_dma = pci_map_single(VORTEX_PCI(vp), skb->data, len,
+ PCI_DMA_TODEVICE);
+ spin_lock_irq(&vp->window_lock);
+ window_set(vp, 7);
+ iowrite32(vp->tx_skb_dma, ioaddr + Wn7_MasterAddr);
+ iowrite16(len, ioaddr + Wn7_MasterLen);
+ spin_unlock_irq(&vp->window_lock);
+ vp->tx_skb = skb;
+ skb_tx_timestamp(skb);
+ iowrite16(StartDMADown, ioaddr + EL3_CMD);
+ /* netif_wake_queue() will be called at the DMADone interrupt. */
+ } else {
+ /* ... and the packet rounded to a doubleword. */
+ skb_tx_timestamp(skb);
+ iowrite32_rep(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_consume_skb_any (skb);
+ if (ioread16(ioaddr + TxFree) > 1536) {
+ netif_start_queue (dev); /* AKPM: redundant? */
+ } else {
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ netif_stop_queue(dev);
+ iowrite16(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ }
+ }
+
+
+ /* Clear the Tx status stack. */
+ {
+ int tx_status;
+ int i = 32;
+
+ while (--i > 0 && (tx_status = ioread8(ioaddr + TxStatus)) > 0) {
+ if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
+ if (vortex_debug > 2)
+ pr_debug("%s: Tx error, status %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x04) dev->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) {
+ issue_and_wait(dev, TxReset);
+ }
+ iowrite16(TxEnable, ioaddr + EL3_CMD);
+ }
+ iowrite8(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+ }
+ return NETDEV_TX_OK;
+}
+
+static netdev_tx_t
+boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ /* Calculate the next Tx descriptor entry. */
+ int entry = vp->cur_tx % TX_RING_SIZE;
+ struct boom_tx_desc *prev_entry = &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE];
+ unsigned long flags;
+
+ if (vortex_debug > 6) {
+ pr_debug("boomerang_start_xmit()\n");
+ pr_debug("%s: Trying to send a packet, Tx index %d.\n",
+ dev->name, vp->cur_tx);
+ }
+
+ /*
+ * We can't allow a recursion from our interrupt handler back into the
+ * tx routine, as they take the same spin lock, and that causes
+ * deadlock. Just return NETDEV_TX_BUSY and let the stack try again in
+ * a bit
+ */
+ if (vp->handling_irq)
+ return NETDEV_TX_BUSY;
+
+ if (vp->cur_tx - vp->dirty_tx >= TX_RING_SIZE) {
+ if (vortex_debug > 0)
+ pr_warning("%s: BUG! Tx Ring full, refusing to send buffer.\n",
+ dev->name);
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+
+ vp->tx_skbuff[entry] = skb;
+
+ vp->tx_ring[entry].next = 0;
+#if DO_ZEROCOPY
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
+ else
+ vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded | AddTCPChksum | AddUDPChksum);
+
+ if (!skb_shinfo(skb)->nr_frags) {
+ vp->tx_ring[entry].frag[0].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data,
+ skb->len, PCI_DMA_TODEVICE));
+ vp->tx_ring[entry].frag[0].length = cpu_to_le32(skb->len | LAST_FRAG);
+ } else {
+ int i;
+
+ vp->tx_ring[entry].frag[0].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data,
+ skb_headlen(skb), PCI_DMA_TODEVICE));
+ vp->tx_ring[entry].frag[0].length = cpu_to_le32(skb_headlen(skb));
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ vp->tx_ring[entry].frag[i+1].addr =
+ cpu_to_le32(pci_map_single(
+ VORTEX_PCI(vp),
+ (void *)skb_frag_address(frag),
+ skb_frag_size(frag), PCI_DMA_TODEVICE));
+
+ if (i == skb_shinfo(skb)->nr_frags-1)
+ vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(skb_frag_size(frag)|LAST_FRAG);
+ else
+ vp->tx_ring[entry].frag[i+1].length = cpu_to_le32(skb_frag_size(frag));
+ }
+ }
+#else
+ vp->tx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, skb->len, PCI_DMA_TODEVICE));
+ vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
+ vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
+#endif
+
+ spin_lock_irqsave(&vp->lock, flags);
+ /* Wait for the stall to complete. */
+ issue_and_wait(dev, DownStall);
+ prev_entry->next = cpu_to_le32(vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc));
+ if (ioread32(ioaddr + DownListPtr) == 0) {
+ iowrite32(vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc), ioaddr + DownListPtr);
+ vp->queued_packet++;
+ }
+
+ vp->cur_tx++;
+ if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) {
+ netif_stop_queue (dev);
+ } else { /* Clear previous interrupt enable. */
+#if defined(tx_interrupt_mitigation)
+ /* Dubious. If in boomeang_interrupt "faster" cyclone ifdef
+ * were selected, this would corrupt DN_COMPLETE. No?
+ */
+ prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
+#endif
+ }
+ skb_tx_timestamp(skb);
+ iowrite16(DownUnstall, ioaddr + EL3_CMD);
+ spin_unlock_irqrestore(&vp->lock, flags);
+ return NETDEV_TX_OK;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+
+/*
+ * This is the ISR for the vortex series chips.
+ * full_bus_master_tx == 0 && full_bus_master_rx == 0
+ */
+
+static irqreturn_t
+vortex_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr;
+ int status;
+ int work_done = max_interrupt_work;
+ int handled = 0;
+
+ ioaddr = vp->ioaddr;
+ spin_lock(&vp->lock);
+
+ status = ioread16(ioaddr + EL3_STATUS);
+
+ if (vortex_debug > 6)
+ pr_debug("vortex_interrupt(). status=0x%4x\n", status);
+
+ if ((status & IntLatch) == 0)
+ goto handler_exit; /* No interrupt: shared IRQs cause this */
+ handled = 1;
+
+ if (status & IntReq) {
+ status |= vp->deferred;
+ vp->deferred = 0;
+ }
+
+ if (status == 0xffff) /* h/w no longer present (hotplug)? */
+ goto handler_exit;
+
+ if (vortex_debug > 4)
+ pr_debug("%s: interrupt, status %4.4x, latency %d ticks.\n",
+ dev->name, status, ioread8(ioaddr + Timer));
+
+ spin_lock(&vp->window_lock);
+ window_set(vp, 7);
+
+ do {
+ if (vortex_debug > 5)
+ pr_debug("%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+ if (status & RxComplete)
+ vortex_rx(dev);
+
+ if (status & TxAvailable) {
+ if (vortex_debug > 5)
+ pr_debug(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ iowrite16(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue (dev);
+ }
+
+ if (status & DMADone) {
+ if (ioread16(ioaddr + Wn7_MasterStatus) & 0x1000) {
+ iowrite16(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+ pci_unmap_single(VORTEX_PCI(vp), vp->tx_skb_dma, (vp->tx_skb->len + 3) & ~3, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(vp->tx_skb); /* Release the transferred buffer */
+ if (ioread16(ioaddr + TxFree) > 1536) {
+ /*
+ * AKPM: FIXME: I don't think we need this. If the queue was stopped due to
+ * insufficient FIFO room, the TxAvailable test will succeed and call
+ * netif_wake_queue()
+ */
+ netif_wake_queue(dev);
+ } else { /* Interrupt when FIFO has room for max-sized packet. */
+ iowrite16(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ netif_stop_queue(dev);
+ }
+ }
+ }
+ /* Check for all uncommon interrupts at once. */
+ if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) {
+ if (status == 0xffff)
+ break;
+ if (status & RxEarly)
+ vortex_rx(dev);
+ spin_unlock(&vp->window_lock);
+ vortex_error(dev, status);
+ spin_lock(&vp->window_lock);
+ window_set(vp, 7);
+ }
+
+ if (--work_done < 0) {
+ pr_warning("%s: Too much work in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Disable all pending interrupts. */
+ do {
+ vp->deferred |= status;
+ iowrite16(SetStatusEnb | (~vp->deferred & vp->status_enable),
+ ioaddr + EL3_CMD);
+ iowrite16(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
+ } while ((status = ioread16(ioaddr + EL3_CMD)) & IntLatch);
+ /* The timer will reenable interrupts. */
+ mod_timer(&vp->timer, jiffies + 1*HZ);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ iowrite16(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ } while ((status = ioread16(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+ spin_unlock(&vp->window_lock);
+
+ if (vortex_debug > 4)
+ pr_debug("%s: exiting interrupt, status %4.4x.\n",
+ dev->name, status);
+handler_exit:
+ spin_unlock(&vp->lock);
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * This is the ISR for the boomerang series chips.
+ * full_bus_master_tx == 1 && full_bus_master_rx == 1
+ */
+
+static irqreturn_t
+boomerang_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr;
+ int status;
+ int work_done = max_interrupt_work;
+
+ ioaddr = vp->ioaddr;
+
+
+ /*
+ * It seems dopey to put the spinlock this early, but we could race against vortex_tx_timeout
+ * and boomerang_start_xmit
+ */
+ spin_lock(&vp->lock);
+ vp->handling_irq = 1;
+
+ status = ioread16(ioaddr + EL3_STATUS);
+
+ if (vortex_debug > 6)
+ pr_debug("boomerang_interrupt. status=0x%4x\n", status);
+
+ if ((status & IntLatch) == 0)
+ goto handler_exit; /* No interrupt: shared IRQs can cause this */
+
+ if (status == 0xffff) { /* h/w no longer present (hotplug)? */
+ if (vortex_debug > 1)
+ pr_debug("boomerang_interrupt(1): status = 0xffff\n");
+ goto handler_exit;
+ }
+
+ if (status & IntReq) {
+ status |= vp->deferred;
+ vp->deferred = 0;
+ }
+
+ if (vortex_debug > 4)
+ pr_debug("%s: interrupt, status %4.4x, latency %d ticks.\n",
+ dev->name, status, ioread8(ioaddr + Timer));
+ do {
+ if (vortex_debug > 5)
+ pr_debug("%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+ if (status & UpComplete) {
+ iowrite16(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ if (vortex_debug > 5)
+ pr_debug("boomerang_interrupt->boomerang_rx\n");
+ boomerang_rx(dev);
+ }
+
+ if (status & DownComplete) {
+ unsigned int dirty_tx = vp->dirty_tx;
+
+ iowrite16(AckIntr | DownComplete, ioaddr + EL3_CMD);
+ while (vp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+#if 1 /* AKPM: the latter is faster, but cyclone-only */
+ if (ioread32(ioaddr + DownListPtr) ==
+ vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc))
+ break; /* It still hasn't been processed. */
+#else
+ if ((vp->tx_ring[entry].status & DN_COMPLETE) == 0)
+ break; /* It still hasn't been processed. */
+#endif
+
+ if (vp->tx_skbuff[entry]) {
+ struct sk_buff *skb = vp->tx_skbuff[entry];
+#if DO_ZEROCOPY
+ int i;
+ for (i=0; i<=skb_shinfo(skb)->nr_frags; i++)
+ pci_unmap_single(VORTEX_PCI(vp),
+ le32_to_cpu(vp->tx_ring[entry].frag[i].addr),
+ le32_to_cpu(vp->tx_ring[entry].frag[i].length)&0xFFF,
+ PCI_DMA_TODEVICE);
+#else
+ pci_unmap_single(VORTEX_PCI(vp),
+ le32_to_cpu(vp->tx_ring[entry].addr), skb->len, PCI_DMA_TODEVICE);
+#endif
+ dev_kfree_skb_irq(skb);
+ vp->tx_skbuff[entry] = NULL;
+ } else {
+ pr_debug("boomerang_interrupt: no skb!\n");
+ }
+ /* dev->stats.tx_packets++; Counted below. */
+ dirty_tx++;
+ }
+ vp->dirty_tx = dirty_tx;
+ if (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1) {
+ if (vortex_debug > 6)
+ pr_debug("boomerang_interrupt: wake queue\n");
+ netif_wake_queue (dev);
+ }
+ }
+
+ /* Check for all uncommon interrupts at once. */
+ if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq))
+ vortex_error(dev, status);
+
+ if (--work_done < 0) {
+ pr_warning("%s: Too much work in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Disable all pending interrupts. */
+ do {
+ vp->deferred |= status;
+ iowrite16(SetStatusEnb | (~vp->deferred & vp->status_enable),
+ ioaddr + EL3_CMD);
+ iowrite16(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
+ } while ((status = ioread16(ioaddr + EL3_CMD)) & IntLatch);
+ /* The timer will reenable interrupts. */
+ mod_timer(&vp->timer, jiffies + 1*HZ);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ iowrite16(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ if (vp->cb_fn_base) /* The PCMCIA people are idiots. */
+ iowrite32(0x8000, vp->cb_fn_base + 4);
+
+ } while ((status = ioread16(ioaddr + EL3_STATUS)) & IntLatch);
+
+ if (vortex_debug > 4)
+ pr_debug("%s: exiting interrupt, status %4.4x.\n",
+ dev->name, status);
+handler_exit:
+ vp->handling_irq = 0;
+ spin_unlock(&vp->lock);
+ return IRQ_HANDLED;
+}
+
+static int vortex_rx(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int i;
+ short rx_status;
+
+ if (vortex_debug > 5)
+ pr_debug("vortex_rx(): status %4.4x, rx_status %4.4x.\n",
+ ioread16(ioaddr+EL3_STATUS), ioread16(ioaddr+RxStatus));
+ while ((rx_status = ioread16(ioaddr + RxStatus)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ unsigned char rx_error = ioread8(ioaddr + RxErrors);
+ if (vortex_debug > 2)
+ pr_debug(" Rx error: status %2.2x.\n", rx_error);
+ dev->stats.rx_errors++;
+ if (rx_error & 0x01) dev->stats.rx_over_errors++;
+ if (rx_error & 0x02) dev->stats.rx_length_errors++;
+ if (rx_error & 0x04) dev->stats.rx_frame_errors++;
+ if (rx_error & 0x08) dev->stats.rx_crc_errors++;
+ if (rx_error & 0x10) dev->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ int pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+ if (vortex_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ if (vp->bus_master &&
+ ! (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)) {
+ dma_addr_t dma = pci_map_single(VORTEX_PCI(vp), skb_put(skb, pkt_len),
+ pkt_len, PCI_DMA_FROMDEVICE);
+ iowrite32(dma, ioaddr + Wn7_MasterAddr);
+ iowrite16((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+ iowrite16(StartDMAUp, ioaddr + EL3_CMD);
+ while (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)
+ ;
+ pci_unmap_single(VORTEX_PCI(vp), dma, pkt_len, PCI_DMA_FROMDEVICE);
+ } else {
+ ioread32_rep(ioaddr + RX_FIFO,
+ skb_put(skb, pkt_len),
+ (pkt_len + 3) >> 2);
+ }
+ iowrite16(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ /* Wait a limited time to go to next packet. */
+ for (i = 200; i >= 0; i--)
+ if ( ! (ioread16(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ continue;
+ } else if (vortex_debug > 0)
+ pr_notice("%s: No memory to allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ dev->stats.rx_dropped++;
+ }
+ issue_and_wait(dev, RxDiscard);
+ }
+
+ return 0;
+}
+
+static int
+boomerang_rx(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ int entry = vp->cur_rx % RX_RING_SIZE;
+ void __iomem *ioaddr = vp->ioaddr;
+ int rx_status;
+ int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
+
+ if (vortex_debug > 5)
+ pr_debug("boomerang_rx(): status %4.4x\n", ioread16(ioaddr+EL3_STATUS));
+
+ while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete){
+ if (--rx_work_limit < 0)
+ break;
+ if (rx_status & RxDError) { /* Error, update stats. */
+ unsigned char rx_error = rx_status >> 16;
+ if (vortex_debug > 2)
+ pr_debug(" Rx error: status %2.2x.\n", rx_error);
+ dev->stats.rx_errors++;
+ if (rx_error & 0x01) dev->stats.rx_over_errors++;
+ if (rx_error & 0x02) dev->stats.rx_length_errors++;
+ if (rx_error & 0x04) dev->stats.rx_frame_errors++;
+ if (rx_error & 0x08) dev->stats.rx_crc_errors++;
+ if (rx_error & 0x10) dev->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ int pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+ dma_addr_t dma = le32_to_cpu(vp->rx_ring[entry].addr);
+
+ if (vortex_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+
+ /* Check if the packet is long enough to just accept without
+ copying to a properly sized skbuff. */
+ if (pkt_len < rx_copybreak &&
+ (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ memcpy(skb_put(skb, pkt_len),
+ vp->rx_skbuff[entry]->data,
+ pkt_len);
+ pci_dma_sync_single_for_device(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ vp->rx_copy++;
+ } else {
+ /* Pass up the skbuff already on the Rx ring. */
+ skb = vp->rx_skbuff[entry];
+ vp->rx_skbuff[entry] = NULL;
+ skb_put(skb, pkt_len);
+ pci_unmap_single(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ vp->rx_nocopy++;
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ { /* Use hardware checksum info. */
+ int csum_bits = rx_status & 0xee000000;
+ if (csum_bits &&
+ (csum_bits == (IPChksumValid | TCPChksumValid) ||
+ csum_bits == (IPChksumValid | UDPChksumValid))) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ vp->rx_csumhits++;
+ }
+ }
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+ entry = (++vp->cur_rx) % RX_RING_SIZE;
+ }
+ /* Refill the Rx ring buffers. */
+ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = vp->dirty_rx % RX_RING_SIZE;
+ if (vp->rx_skbuff[entry] == NULL) {
+ skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ);
+ if (skb == NULL) {
+ static unsigned long last_jif;
+ if (time_after(jiffies, last_jif + 10 * HZ)) {
+ pr_warning("%s: memory shortage\n", dev->name);
+ last_jif = jiffies;
+ }
+ if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE)
+ mod_timer(&vp->rx_oom_timer, RUN_AT(HZ * 1));
+ break; /* Bad news! */
+ }
+
+ vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
+ vp->rx_skbuff[entry] = skb;
+ }
+ vp->rx_ring[entry].status = 0; /* Clear complete bit. */
+ iowrite16(UpUnstall, ioaddr + EL3_CMD);
+ }
+ return 0;
+}
+
+/*
+ * If we've hit a total OOM refilling the Rx ring we poll once a second
+ * for some memory. Otherwise there is no way to restart the rx process.
+ */
+static void
+rx_oom_timer(unsigned long arg)
+{
+ struct net_device *dev = (struct net_device *)arg;
+ struct vortex_private *vp = netdev_priv(dev);
+
+ spin_lock_irq(&vp->lock);
+ if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE) /* This test is redundant, but makes me feel good */
+ boomerang_rx(dev);
+ if (vortex_debug > 1) {
+ pr_debug("%s: rx_oom_timer %s\n", dev->name,
+ ((vp->cur_rx - vp->dirty_rx) != RX_RING_SIZE) ? "succeeded" : "retrying");
+ }
+ spin_unlock_irq(&vp->lock);
+}
+
+static void
+vortex_down(struct net_device *dev, int final_down)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+
+ netif_stop_queue (dev);
+
+ del_timer_sync(&vp->rx_oom_timer);
+ del_timer_sync(&vp->timer);
+
+ /* Turn off statistics ASAP. We update dev->stats below. */
+ iowrite16(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ iowrite16(RxDisable, ioaddr + EL3_CMD);
+ iowrite16(TxDisable, ioaddr + EL3_CMD);
+
+ /* Disable receiving 802.1q tagged frames */
+ set_8021q_mode(dev, 0);
+
+ if (dev->if_port == XCVR_10base2)
+ /* Turn off thinnet power. Green! */
+ iowrite16(StopCoax, ioaddr + EL3_CMD);
+
+ iowrite16(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
+
+ update_stats(ioaddr, dev);
+ if (vp->full_bus_master_rx)
+ iowrite32(0, ioaddr + UpListPtr);
+ if (vp->full_bus_master_tx)
+ iowrite32(0, ioaddr + DownListPtr);
+
+ if (final_down && VORTEX_PCI(vp)) {
+ vp->pm_state_valid = 1;
+ pci_save_state(VORTEX_PCI(vp));
+ acpi_set_WOL(dev);
+ }
+}
+
+static int
+vortex_close(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int i;
+
+ if (netif_device_present(dev))
+ vortex_down(dev, 1);
+
+ if (vortex_debug > 1) {
+ pr_debug("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+ dev->name, ioread16(ioaddr + EL3_STATUS), ioread8(ioaddr + TxStatus));
+ pr_debug("%s: vortex close stats: rx_nocopy %d rx_copy %d"
+ " tx_queued %d Rx pre-checksummed %d.\n",
+ dev->name, vp->rx_nocopy, vp->rx_copy, vp->queued_packet, vp->rx_csumhits);
+ }
+
+#if DO_ZEROCOPY
+ if (vp->rx_csumhits &&
+ (vp->drv_flags & HAS_HWCKSM) == 0 &&
+ (vp->card_idx >= MAX_UNITS || hw_checksums[vp->card_idx] == -1)) {
+ pr_warning("%s supports hardware checksums, and we're not using them!\n", dev->name);
+ }
+#endif
+
+ free_irq(dev->irq, dev);
+
+ if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (vp->rx_skbuff[i]) {
+ pci_unmap_single( VORTEX_PCI(vp), le32_to_cpu(vp->rx_ring[i].addr),
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(vp->rx_skbuff[i]);
+ vp->rx_skbuff[i] = NULL;
+ }
+ }
+ if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (vp->tx_skbuff[i]) {
+ struct sk_buff *skb = vp->tx_skbuff[i];
+#if DO_ZEROCOPY
+ int k;
+
+ for (k=0; k<=skb_shinfo(skb)->nr_frags; k++)
+ pci_unmap_single(VORTEX_PCI(vp),
+ le32_to_cpu(vp->tx_ring[i].frag[k].addr),
+ le32_to_cpu(vp->tx_ring[i].frag[k].length)&0xFFF,
+ PCI_DMA_TODEVICE);
+#else
+ pci_unmap_single(VORTEX_PCI(vp), le32_to_cpu(vp->tx_ring[i].addr), skb->len, PCI_DMA_TODEVICE);
+#endif
+ dev_kfree_skb(skb);
+ vp->tx_skbuff[i] = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+dump_tx_ring(struct net_device *dev)
+{
+ if (vortex_debug > 0) {
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+
+ if (vp->full_bus_master_tx) {
+ int i;
+ int stalled = ioread32(ioaddr + PktStatus) & 0x04; /* Possible racy. But it's only debug stuff */
+
+ pr_err(" Flags; bus-master %d, dirty %d(%d) current %d(%d)\n",
+ vp->full_bus_master_tx,
+ vp->dirty_tx, vp->dirty_tx % TX_RING_SIZE,
+ vp->cur_tx, vp->cur_tx % TX_RING_SIZE);
+ pr_err(" Transmit list %8.8x vs. %p.\n",
+ ioread32(ioaddr + DownListPtr),
+ &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
+ issue_and_wait(dev, DownStall);
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ unsigned int length;
+
+#if DO_ZEROCOPY
+ length = le32_to_cpu(vp->tx_ring[i].frag[0].length);
+#else
+ length = le32_to_cpu(vp->tx_ring[i].length);
+#endif
+ pr_err(" %d: @%p length %8.8x status %8.8x\n",
+ i, &vp->tx_ring[i], length,
+ le32_to_cpu(vp->tx_ring[i].status));
+ }
+ if (!stalled)
+ iowrite16(DownUnstall, ioaddr + EL3_CMD);
+ }
+ }
+}
+
+static struct net_device_stats *vortex_get_stats(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ unsigned long flags;
+
+ if (netif_device_present(dev)) { /* AKPM: Used to be netif_running */
+ spin_lock_irqsave (&vp->lock, flags);
+ update_stats(ioaddr, dev);
+ spin_unlock_irqrestore (&vp->lock, flags);
+ }
+ return &dev->stats;
+}
+
+/* Update statistics.
+ Unlike with the EL3 we need not worry about interrupts changing
+ the window setting from underneath us, but we must still guard
+ against a race condition with a StatsUpdate interrupt updating the
+ table. This is done by checking that the ASM (!) code generated uses
+ atomic updates with '+='.
+ */
+static void update_stats(void __iomem *ioaddr, struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ dev->stats.tx_carrier_errors += window_read8(vp, 6, 0);
+ dev->stats.tx_heartbeat_errors += window_read8(vp, 6, 1);
+ dev->stats.tx_window_errors += window_read8(vp, 6, 4);
+ dev->stats.rx_fifo_errors += window_read8(vp, 6, 5);
+ dev->stats.tx_packets += window_read8(vp, 6, 6);
+ dev->stats.tx_packets += (window_read8(vp, 6, 9) &
+ 0x30) << 4;
+ /* Rx packets */ window_read8(vp, 6, 7); /* Must read to clear */
+ /* Don't bother with register 9, an extension of registers 6&7.
+ If we do use the 6&7 values the atomic update assumption above
+ is invalid. */
+ dev->stats.rx_bytes += window_read16(vp, 6, 10);
+ dev->stats.tx_bytes += window_read16(vp, 6, 12);
+ /* Extra stats for get_ethtool_stats() */
+ vp->xstats.tx_multiple_collisions += window_read8(vp, 6, 2);
+ vp->xstats.tx_single_collisions += window_read8(vp, 6, 3);
+ vp->xstats.tx_deferred += window_read8(vp, 6, 8);
+ vp->xstats.rx_bad_ssd += window_read8(vp, 4, 12);
+
+ dev->stats.collisions = vp->xstats.tx_multiple_collisions
+ + vp->xstats.tx_single_collisions
+ + vp->xstats.tx_max_collisions;
+
+ {
+ u8 up = window_read8(vp, 4, 13);
+ dev->stats.rx_bytes += (up & 0x0f) << 16;
+ dev->stats.tx_bytes += (up & 0xf0) << 12;
+ }
+}
+
+static int vortex_nway_reset(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ return mii_nway_restart(&vp->mii);
+}
+
+static int vortex_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ return mii_ethtool_gset(&vp->mii, cmd);
+}
+
+static int vortex_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ return mii_ethtool_sset(&vp->mii, cmd);
+}
+
+static u32 vortex_get_msglevel(struct net_device *dev)
+{
+ return vortex_debug;
+}
+
+static void vortex_set_msglevel(struct net_device *dev, u32 dbg)
+{
+ vortex_debug = dbg;
+}
+
+static int vortex_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return VORTEX_NUM_STATS;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void vortex_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vp->lock, flags);
+ update_stats(ioaddr, dev);
+ spin_unlock_irqrestore(&vp->lock, flags);
+
+ data[0] = vp->xstats.tx_deferred;
+ data[1] = vp->xstats.tx_max_collisions;
+ data[2] = vp->xstats.tx_multiple_collisions;
+ data[3] = vp->xstats.tx_single_collisions;
+ data[4] = vp->xstats.rx_bad_ssd;
+}
+
+
+static void vortex_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static void vortex_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ if (VORTEX_PCI(vp)) {
+ strlcpy(info->bus_info, pci_name(VORTEX_PCI(vp)),
+ sizeof(info->bus_info));
+ } else {
+ if (VORTEX_EISA(vp))
+ strlcpy(info->bus_info, dev_name(vp->gendev),
+ sizeof(info->bus_info));
+ else
+ snprintf(info->bus_info, sizeof(info->bus_info),
+ "EISA 0x%lx %d", dev->base_addr, dev->irq);
+ }
+}
+
+static void vortex_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ if (!VORTEX_PCI(vp))
+ return;
+
+ wol->supported = WAKE_MAGIC;
+
+ wol->wolopts = 0;
+ if (vp->enable_wol)
+ wol->wolopts |= WAKE_MAGIC;
+}
+
+static int vortex_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+
+ if (!VORTEX_PCI(vp))
+ return -EOPNOTSUPP;
+
+ if (wol->wolopts & ~WAKE_MAGIC)
+ return -EINVAL;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ vp->enable_wol = 1;
+ else
+ vp->enable_wol = 0;
+ acpi_set_WOL(dev);
+
+ return 0;
+}
+
+static const struct ethtool_ops vortex_ethtool_ops = {
+ .get_drvinfo = vortex_get_drvinfo,
+ .get_strings = vortex_get_strings,
+ .get_msglevel = vortex_get_msglevel,
+ .set_msglevel = vortex_set_msglevel,
+ .get_ethtool_stats = vortex_get_ethtool_stats,
+ .get_sset_count = vortex_get_sset_count,
+ .get_settings = vortex_get_settings,
+ .set_settings = vortex_set_settings,
+ .get_link = ethtool_op_get_link,
+ .nway_reset = vortex_nway_reset,
+ .get_wol = vortex_get_wol,
+ .set_wol = vortex_set_wol,
+ .get_ts_info = ethtool_op_get_ts_info,
+};
+
+#ifdef CONFIG_PCI
+/*
+ * Must power the device up to do MDIO operations
+ */
+static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ int err;
+ struct vortex_private *vp = netdev_priv(dev);
+ pci_power_t state = 0;
+
+ if(VORTEX_PCI(vp))
+ state = VORTEX_PCI(vp)->current_state;
+
+ /* The kernel core really should have pci_get_power_state() */
+
+ if(state != 0)
+ pci_set_power_state(VORTEX_PCI(vp), PCI_D0);
+ err = generic_mii_ioctl(&vp->mii, if_mii(rq), cmd, NULL);
+ if(state != 0)
+ pci_set_power_state(VORTEX_PCI(vp), state);
+
+ return err;
+}
+#endif
+
+
+/* Pre-Cyclone chips have no documented multicast filter, so the only
+ multicast setting is to receive all multicast frames. At least
+ the chip has a very clean way to set the mode, unlike many others. */
+static void set_rx_mode(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+ int new_mode;
+
+ if (dev->flags & IFF_PROMISC) {
+ if (vortex_debug > 3)
+ pr_notice("%s: Setting promiscuous mode.\n", dev->name);
+ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
+ } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
+ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
+ } else
+ new_mode = SetRxFilter | RxStation | RxBroadcast;
+
+ iowrite16(new_mode, ioaddr + EL3_CMD);
+}
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+/* Setup the card so that it can receive frames with an 802.1q VLAN tag.
+ Note that this must be done after each RxReset due to some backwards
+ compatibility logic in the Cyclone and Tornado ASICs */
+
+/* The Ethernet Type used for 802.1q tagged frames */
+#define VLAN_ETHER_TYPE 0x8100
+
+static void set_8021q_mode(struct net_device *dev, int enable)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ int mac_ctrl;
+
+ if ((vp->drv_flags&IS_CYCLONE) || (vp->drv_flags&IS_TORNADO)) {
+ /* cyclone and tornado chipsets can recognize 802.1q
+ * tagged frames and treat them correctly */
+
+ int max_pkt_size = dev->mtu+14; /* MTU+Ethernet header */
+ if (enable)
+ max_pkt_size += 4; /* 802.1Q VLAN tag */
+
+ window_write16(vp, max_pkt_size, 3, Wn3_MaxPktSize);
+
+ /* set VlanEtherType to let the hardware checksumming
+ treat tagged frames correctly */
+ window_write16(vp, VLAN_ETHER_TYPE, 7, Wn7_VlanEtherType);
+ } else {
+ /* on older cards we have to enable large frames */
+
+ vp->large_frames = dev->mtu > 1500 || enable;
+
+ mac_ctrl = window_read16(vp, 3, Wn3_MAC_Ctrl);
+ if (vp->large_frames)
+ mac_ctrl |= 0x40;
+ else
+ mac_ctrl &= ~0x40;
+ window_write16(vp, mac_ctrl, 3, Wn3_MAC_Ctrl);
+ }
+}
+#else
+
+static void set_8021q_mode(struct net_device *dev, int enable)
+{
+}
+
+
+#endif
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+static void mdio_delay(struct vortex_private *vp)
+{
+ window_read32(vp, 4, Wn4_PhysicalMgmt);
+}
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN 0x00
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(struct vortex_private *vp, int bits)
+{
+ /* Establish sync by sending at least 32 logic ones. */
+ while (-- bits >= 0) {
+ window_write16(vp, MDIO_DATA_WRITE1, 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ window_write16(vp, MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK,
+ 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ int i;
+ struct vortex_private *vp = netdev_priv(dev);
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ unsigned int retval = 0;
+
+ spin_lock_bh(&vp->mii_lock);
+
+ if (mii_preamble_required)
+ mdio_sync(vp, 32);
+
+ /* Shift the read command bits out. */
+ for (i = 14; i >= 0; i--) {
+ int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ window_write16(vp, dataval, 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ window_write16(vp, dataval | MDIO_SHIFT_CLK,
+ 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ window_write16(vp, MDIO_ENB_IN, 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ retval = (retval << 1) |
+ ((window_read16(vp, 4, Wn4_PhysicalMgmt) &
+ MDIO_DATA_READ) ? 1 : 0);
+ window_write16(vp, MDIO_ENB_IN | MDIO_SHIFT_CLK,
+ 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ }
+
+ spin_unlock_bh(&vp->mii_lock);
+
+ return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+ int i;
+
+ spin_lock_bh(&vp->mii_lock);
+
+ if (mii_preamble_required)
+ mdio_sync(vp, 32);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ window_write16(vp, dataval, 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ window_write16(vp, dataval | MDIO_SHIFT_CLK,
+ 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ }
+ /* Leave the interface idle. */
+ for (i = 1; i >= 0; i--) {
+ window_write16(vp, MDIO_ENB_IN, 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ window_write16(vp, MDIO_ENB_IN | MDIO_SHIFT_CLK,
+ 4, Wn4_PhysicalMgmt);
+ mdio_delay(vp);
+ }
+
+ spin_unlock_bh(&vp->mii_lock);
+}
+
+/* ACPI: Advanced Configuration and Power Interface. */
+/* Set Wake-On-LAN mode and put the board into D3 (power-down) state. */
+static void acpi_set_WOL(struct net_device *dev)
+{
+ struct vortex_private *vp = netdev_priv(dev);
+ void __iomem *ioaddr = vp->ioaddr;
+
+ device_set_wakeup_enable(vp->gendev, vp->enable_wol);
+
+ if (vp->enable_wol) {
+ /* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */
+ window_write16(vp, 2, 7, 0x0c);
+ /* The RxFilter must accept the WOL frames. */
+ iowrite16(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ iowrite16(RxEnable, ioaddr + EL3_CMD);
+
+ if (pci_enable_wake(VORTEX_PCI(vp), PCI_D3hot, 1)) {
+ pr_info("%s: WOL not supported.\n", pci_name(VORTEX_PCI(vp)));
+
+ vp->enable_wol = 0;
+ return;
+ }
+
+ if (VORTEX_PCI(vp)->current_state < PCI_D3hot)
+ return;
+
+ /* Change the power state to D3; RxEnable doesn't take effect. */
+ pci_set_power_state(VORTEX_PCI(vp), PCI_D3hot);
+ }
+}
+
+
+static void vortex_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct vortex_private *vp;
+
+ if (!dev) {
+ pr_err("vortex_remove_one called for Compaq device!\n");
+ BUG();
+ }
+
+ vp = netdev_priv(dev);
+
+ if (vp->cb_fn_base)
+ pci_iounmap(pdev, vp->cb_fn_base);
+
+ unregister_netdev(dev);
+
+ pci_set_power_state(pdev, PCI_D0); /* Go active */
+ if (vp->pm_state_valid)
+ pci_restore_state(pdev);
+ pci_disable_device(pdev);
+
+ /* Should really use issue_and_wait() here */
+ iowrite16(TotalReset | ((vp->drv_flags & EEPROM_RESET) ? 0x04 : 0x14),
+ vp->ioaddr + EL3_CMD);
+
+ pci_iounmap(pdev, vp->ioaddr);
+
+ pci_free_consistent(pdev,
+ sizeof(struct boom_rx_desc) * RX_RING_SIZE
+ + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
+ vp->rx_ring,
+ vp->rx_ring_dma);
+
+ pci_release_regions(pdev);
+
+ free_netdev(dev);
+}
+
+
+static struct pci_driver vortex_driver = {
+ .name = "3c59x",
+ .probe = vortex_init_one,
+ .remove = vortex_remove_one,
+ .id_table = vortex_pci_tbl,
+ .driver.pm = VORTEX_PM_OPS,
+};
+
+
+static int vortex_have_pci;
+static int vortex_have_eisa;
+
+
+static int __init vortex_init(void)
+{
+ int pci_rc, eisa_rc;
+
+ pci_rc = pci_register_driver(&vortex_driver);
+ eisa_rc = vortex_eisa_init();
+
+ if (pci_rc == 0)
+ vortex_have_pci = 1;
+ if (eisa_rc > 0)
+ vortex_have_eisa = 1;
+
+ return (vortex_have_pci + vortex_have_eisa) ? 0 : -ENODEV;
+}
+
+
+static void __exit vortex_eisa_cleanup(void)
+{
+ void __iomem *ioaddr;
+
+#ifdef CONFIG_EISA
+ /* Take care of the EISA devices */
+ eisa_driver_unregister(&vortex_eisa_driver);
+#endif
+
+ if (compaq_net_device) {
+ ioaddr = ioport_map(compaq_net_device->base_addr,
+ VORTEX_TOTAL_SIZE);
+
+ unregister_netdev(compaq_net_device);
+ iowrite16(TotalReset, ioaddr + EL3_CMD);
+ release_region(compaq_net_device->base_addr,
+ VORTEX_TOTAL_SIZE);
+
+ free_netdev(compaq_net_device);
+ }
+}
+
+
+static void __exit vortex_cleanup(void)
+{
+ if (vortex_have_pci)
+ pci_unregister_driver(&vortex_driver);
+ if (vortex_have_eisa)
+ vortex_eisa_cleanup();
+}
+
+
+module_init(vortex_init);
+module_exit(vortex_cleanup);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
new file mode 100644
index 00000000000..afaab4b2333
--- /dev/null
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -0,0 +1,107 @@
+#
+# 3Com Ethernet device configuration
+#
+
+config NET_VENDOR_3COM
+ bool "3Com devices"
+ default y
+ depends on ISA || EISA || PCI || PCMCIA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about 3Com cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_3COM
+
+config EL3
+ tristate "3c509/3c579 \"EtherLink III\" support"
+ depends on (ISA || EISA)
+ ---help---
+ If you have a network (Ethernet) card belonging to the 3Com
+ EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
+ from <http://www.tldp.org/docs.html#howto>.
+
+ If your card is not working you may need to use the DOS
+ setup disk to disable Plug & Play mode, and to select the default
+ media type.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c509.
+
+config 3C515
+ tristate "3c515 ISA \"Fast EtherLink\""
+ depends on ISA && ISA_DMA_API
+ ---help---
+ If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
+ network card, say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c515.
+
+config PCMCIA_3C574
+ tristate "3Com 3c574 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
+ (PC-card) Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called 3c574_cs. If unsure, say N.
+
+config PCMCIA_3C589
+ tristate "3Com 3c589 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
+ (PC-card) Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called 3c589_cs. If unsure, say N.
+
+config VORTEX
+ tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
+ depends on (PCI || EISA) && HAS_IOPORT_MAP
+ select MII
+ ---help---
+ This option enables driver support for a large number of 10Mbps and
+ 10/100Mbps EISA, PCI and Cardbus 3Com network cards:
+
+ "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI
+ "Boomerang" (EtherLink XL 3c900 or 3c905) PCI
+ "Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus
+ "Tornado" (3c905) PCI
+ "Hurricane" (3c555/3cSOHO) PCI
+
+ If you have such a card, say Y and read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>. More
+ specific information is in
+ <file:Documentation/networking/vortex.txt> and in the comments at
+ the beginning of <file:drivers/net/ethernet/3com/3c59x.c>.
+
+ To compile this support as a module, choose M here.
+
+config TYPHOON
+ tristate "3cr990 series \"Typhoon\" support"
+ depends on PCI
+ select CRC32
+ ---help---
+ This option enables driver support for the 3cr990 series of cards:
+
+ 3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97,
+ 3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server,
+ 3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR
+
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called typhoon.
+
+endif # NET_VENDOR_3COM
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
new file mode 100644
index 00000000000..74046afab99
--- /dev/null
+++ b/drivers/net/ethernet/3com/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the 3Com Ethernet device drivers
+#
+
+obj-$(CONFIG_EL3) += 3c509.o
+obj-$(CONFIG_3C515) += 3c515.o
+obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
+obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
+obj-$(CONFIG_VORTEX) += 3c59x.o
+obj-$(CONFIG_TYPHOON) += typhoon.o
diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
new file mode 100644
index 00000000000..e13b04624de
--- /dev/null
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -0,0 +1,2556 @@
+/* typhoon.c: A Linux Ethernet device driver for 3Com 3CR990 family of NICs */
+/*
+ Written 2002-2004 by David Dillow <dave@thedillows.org>
+ Based on code written 1998-2000 by Donald Becker <becker@scyld.com> and
+ Linux 2.2.x driver by David P. McLean <davidpmclean@yahoo.com>.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This software is available on a public web site. It may enable
+ cryptographic capabilities of the 3Com hardware, and may be
+ exported from the United States under License Exception "TSU"
+ pursuant to 15 C.F.R. Section 740.13(e).
+
+ This work was funded by the National Library of Medicine under
+ the Department of Energy project number 0274DD06D1 and NLM project
+ number Y1-LM-2015-01.
+
+ This driver is designed for the 3Com 3CR990 Family of cards with the
+ 3XP Processor. It has been tested on x86 and sparc64.
+
+ KNOWN ISSUES:
+ *) Cannot DMA Rx packets to a 2 byte aligned address. Also firmware
+ issue. Hopefully 3Com will fix it.
+ *) Waiting for a command response takes 8ms due to non-preemptable
+ polling. Only significant for getting stats and creating
+ SAs, but an ugly wart never the less.
+
+ TODO:
+ *) Doesn't do IPSEC offloading. Yet. Keep yer pants on, it's coming.
+ *) Add more support for ethtool (especially for NIC stats)
+ *) Allow disabling of RX checksum offloading
+ *) Fix MAC changing to work while the interface is up
+ (Need to put commands on the TX ring, which changes
+ the locking)
+ *) Add in FCS to {rx,tx}_bytes, since the hardware doesn't. See
+ http://oss.sgi.com/cgi-bin/mesg.cgi?a=netdev&i=20031215152211.7003fe8e.rddunlap%40osdl.org
+*/
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ * Setting to > 1518 effectively disables this feature.
+ */
+static int rx_copybreak = 200;
+
+/* Should we use MMIO or Port IO?
+ * 0: Port IO
+ * 1: MMIO
+ * 2: Try MMIO, fallback to Port IO
+ */
+static unsigned int use_mmio = 2;
+
+/* end user-configurable values */
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ */
+static const int multicast_filter_limit = 32;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ * The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ * Making the Tx ring too large decreases the effectiveness of channel
+ * bonding and packet priority.
+ * There are no ill effects from too-large receive rings.
+ *
+ * We don't currently use the Hi Tx ring so, don't make it very big.
+ *
+ * Beware that if we start using the Hi Tx ring, we will need to change
+ * typhoon_num_free_tx() and typhoon_tx_complete() to account for that.
+ */
+#define TXHI_ENTRIES 2
+#define TXLO_ENTRIES 128
+#define RX_ENTRIES 32
+#define COMMAND_ENTRIES 16
+#define RESPONSE_ENTRIES 32
+
+#define COMMAND_RING_SIZE (COMMAND_ENTRIES * sizeof(struct cmd_desc))
+#define RESPONSE_RING_SIZE (RESPONSE_ENTRIES * sizeof(struct resp_desc))
+
+/* The 3XP will preload and remove 64 entries from the free buffer
+ * list, and we need one entry to keep the ring from wrapping, so
+ * to keep this a power of two, we use 128 entries.
+ */
+#define RXFREE_ENTRIES 128
+#define RXENT_ENTRIES (RXFREE_ENTRIES - 1)
+
+/* Operational parameters that usually are not changed. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+
+#define PKT_BUF_SZ 1536
+#define FIRMWARE_NAME "3com/typhoon.bin"
+
+#define pr_fmt(fmt) KBUILD_MODNAME " " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/in6.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+
+#include "typhoon.h"
+
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE_NAME);
+MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)");
+MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and "
+ "the buffer given back to the NIC. Default "
+ "is 200.");
+MODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. "
+ "Default is to try MMIO and fallback to PIO.");
+module_param(rx_copybreak, int, 0);
+module_param(use_mmio, int, 0);
+
+#if defined(NETIF_F_TSO) && MAX_SKB_FRAGS > 32
+#warning Typhoon only supports 32 entries in its SG list for TSO, disabling TSO
+#undef NETIF_F_TSO
+#endif
+
+#if TXLO_ENTRIES <= (2 * MAX_SKB_FRAGS)
+#error TX ring too small!
+#endif
+
+struct typhoon_card_info {
+ const char *name;
+ const int capabilities;
+};
+
+#define TYPHOON_CRYPTO_NONE 0x00
+#define TYPHOON_CRYPTO_DES 0x01
+#define TYPHOON_CRYPTO_3DES 0x02
+#define TYPHOON_CRYPTO_VARIABLE 0x04
+#define TYPHOON_FIBER 0x08
+#define TYPHOON_WAKEUP_NEEDS_RESET 0x10
+
+enum typhoon_cards {
+ TYPHOON_TX = 0, TYPHOON_TX95, TYPHOON_TX97, TYPHOON_SVR,
+ TYPHOON_SVR95, TYPHOON_SVR97, TYPHOON_TXM, TYPHOON_BSVR,
+ TYPHOON_FX95, TYPHOON_FX97, TYPHOON_FX95SVR, TYPHOON_FX97SVR,
+ TYPHOON_FXM,
+};
+
+/* directly indexed by enum typhoon_cards, above */
+static struct typhoon_card_info typhoon_card_info[] = {
+ { "3Com Typhoon (3C990-TX)",
+ TYPHOON_CRYPTO_NONE},
+ { "3Com Typhoon (3CR990-TX-95)",
+ TYPHOON_CRYPTO_DES},
+ { "3Com Typhoon (3CR990-TX-97)",
+ TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
+ { "3Com Typhoon (3C990SVR)",
+ TYPHOON_CRYPTO_NONE},
+ { "3Com Typhoon (3CR990SVR95)",
+ TYPHOON_CRYPTO_DES},
+ { "3Com Typhoon (3CR990SVR97)",
+ TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
+ { "3Com Typhoon2 (3C990B-TX-M)",
+ TYPHOON_CRYPTO_VARIABLE},
+ { "3Com Typhoon2 (3C990BSVR)",
+ TYPHOON_CRYPTO_VARIABLE},
+ { "3Com Typhoon (3CR990-FX-95)",
+ TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
+ { "3Com Typhoon (3CR990-FX-97)",
+ TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
+ { "3Com Typhoon (3CR990-FX-95 Server)",
+ TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
+ { "3Com Typhoon (3CR990-FX-97 Server)",
+ TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
+ { "3Com Typhoon2 (3C990B-FX-97)",
+ TYPHOON_CRYPTO_VARIABLE | TYPHOON_FIBER},
+};
+
+/* Notes on the new subsystem numbering scheme:
+ * bits 0-1 indicate crypto capabilities: (0) variable, (1) DES, or (2) 3DES
+ * bit 4 indicates if this card has secured firmware (we don't support it)
+ * bit 8 indicates if this is a (0) copper or (1) fiber card
+ * bits 12-16 indicate card type: (0) client and (1) server
+ */
+static DEFINE_PCI_DEVICE_TABLE(typhoon_pci_tbl) = {
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,TYPHOON_TX },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX95 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX97 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
+ PCI_ANY_ID, 0x1000, 0, 0, TYPHOON_TXM },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
+ PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FXM },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
+ PCI_ANY_ID, 0x2000, 0, 0, TYPHOON_BSVR },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
+ PCI_ANY_ID, 0x1101, 0, 0, TYPHOON_FX95 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
+ PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FX97 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
+ PCI_ANY_ID, 0x2101, 0, 0, TYPHOON_FX95SVR },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
+ PCI_ANY_ID, 0x2102, 0, 0, TYPHOON_FX97SVR },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR95,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR95 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR97,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR97 },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, typhoon_pci_tbl);
+
+/* Define the shared memory area
+ * Align everything the 3XP will normally be using.
+ * We'll need to move/align txHi if we start using that ring.
+ */
+#define __3xp_aligned ____cacheline_aligned
+struct typhoon_shared {
+ struct typhoon_interface iface;
+ struct typhoon_indexes indexes __3xp_aligned;
+ struct tx_desc txLo[TXLO_ENTRIES] __3xp_aligned;
+ struct rx_desc rxLo[RX_ENTRIES] __3xp_aligned;
+ struct rx_desc rxHi[RX_ENTRIES] __3xp_aligned;
+ struct cmd_desc cmd[COMMAND_ENTRIES] __3xp_aligned;
+ struct resp_desc resp[RESPONSE_ENTRIES] __3xp_aligned;
+ struct rx_free rxBuff[RXFREE_ENTRIES] __3xp_aligned;
+ u32 zeroWord;
+ struct tx_desc txHi[TXHI_ENTRIES];
+} __packed;
+
+struct rxbuff_ent {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+};
+
+struct typhoon {
+ /* Tx cache line section */
+ struct transmit_ring txLoRing ____cacheline_aligned;
+ struct pci_dev * tx_pdev;
+ void __iomem *tx_ioaddr;
+ u32 txlo_dma_addr;
+
+ /* Irq/Rx cache line section */
+ void __iomem *ioaddr ____cacheline_aligned;
+ struct typhoon_indexes *indexes;
+ u8 awaiting_resp;
+ u8 duplex;
+ u8 speed;
+ u8 card_state;
+ struct basic_ring rxLoRing;
+ struct pci_dev * pdev;
+ struct net_device * dev;
+ struct napi_struct napi;
+ struct basic_ring rxHiRing;
+ struct basic_ring rxBuffRing;
+ struct rxbuff_ent rxbuffers[RXENT_ENTRIES];
+
+ /* general section */
+ spinlock_t command_lock ____cacheline_aligned;
+ struct basic_ring cmdRing;
+ struct basic_ring respRing;
+ struct net_device_stats stats;
+ struct net_device_stats stats_saved;
+ struct typhoon_shared * shared;
+ dma_addr_t shared_dma;
+ __le16 xcvr_select;
+ __le16 wol_events;
+ __le32 offload;
+
+ /* unused stuff (future use) */
+ int capabilities;
+ struct transmit_ring txHiRing;
+};
+
+enum completion_wait_values {
+ NoWait = 0, WaitNoSleep, WaitSleep,
+};
+
+/* These are the values for the typhoon.card_state variable.
+ * These determine where the statistics will come from in get_stats().
+ * The sleep image does not support the statistics we need.
+ */
+enum state_values {
+ Sleeping = 0, Running,
+};
+
+/* PCI writes are not guaranteed to be posted in order, but outstanding writes
+ * cannot pass a read, so this forces current writes to post.
+ */
+#define typhoon_post_pci_writes(x) \
+ do { if(likely(use_mmio)) ioread32(x+TYPHOON_REG_HEARTBEAT); } while(0)
+
+/* We'll wait up to six seconds for a reset, and half a second normally.
+ */
+#define TYPHOON_UDELAY 50
+#define TYPHOON_RESET_TIMEOUT_SLEEP (6 * HZ)
+#define TYPHOON_RESET_TIMEOUT_NOSLEEP ((6 * 1000000) / TYPHOON_UDELAY)
+#define TYPHOON_WAIT_TIMEOUT ((1000000 / 2) / TYPHOON_UDELAY)
+
+#if defined(NETIF_F_TSO)
+#define skb_tso_size(x) (skb_shinfo(x)->gso_size)
+#define TSO_NUM_DESCRIPTORS 2
+#define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT
+#else
+#define NETIF_F_TSO 0
+#define skb_tso_size(x) 0
+#define TSO_NUM_DESCRIPTORS 0
+#define TSO_OFFLOAD_ON 0
+#endif
+
+static inline void
+typhoon_inc_index(u32 *index, const int count, const int num_entries)
+{
+ /* Increment a ring index -- we can use this for all rings execept
+ * the Rx rings, as they use different size descriptors
+ * otherwise, everything is the same size as a cmd_desc
+ */
+ *index += count * sizeof(struct cmd_desc);
+ *index %= num_entries * sizeof(struct cmd_desc);
+}
+
+static inline void
+typhoon_inc_cmd_index(u32 *index, const int count)
+{
+ typhoon_inc_index(index, count, COMMAND_ENTRIES);
+}
+
+static inline void
+typhoon_inc_resp_index(u32 *index, const int count)
+{
+ typhoon_inc_index(index, count, RESPONSE_ENTRIES);
+}
+
+static inline void
+typhoon_inc_rxfree_index(u32 *index, const int count)
+{
+ typhoon_inc_index(index, count, RXFREE_ENTRIES);
+}
+
+static inline void
+typhoon_inc_tx_index(u32 *index, const int count)
+{
+ /* if we start using the Hi Tx ring, this needs updating */
+ typhoon_inc_index(index, count, TXLO_ENTRIES);
+}
+
+static inline void
+typhoon_inc_rx_index(u32 *index, const int count)
+{
+ /* sizeof(struct rx_desc) != sizeof(struct cmd_desc) */
+ *index += count * sizeof(struct rx_desc);
+ *index %= RX_ENTRIES * sizeof(struct rx_desc);
+}
+
+static int
+typhoon_reset(void __iomem *ioaddr, int wait_type)
+{
+ int i, err = 0;
+ int timeout;
+
+ if(wait_type == WaitNoSleep)
+ timeout = TYPHOON_RESET_TIMEOUT_NOSLEEP;
+ else
+ timeout = TYPHOON_RESET_TIMEOUT_SLEEP;
+
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+
+ iowrite32(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
+ typhoon_post_pci_writes(ioaddr);
+ udelay(1);
+ iowrite32(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
+
+ if(wait_type != NoWait) {
+ for(i = 0; i < timeout; i++) {
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) ==
+ TYPHOON_STATUS_WAITING_FOR_HOST)
+ goto out;
+
+ if(wait_type == WaitSleep)
+ schedule_timeout_uninterruptible(1);
+ else
+ udelay(TYPHOON_UDELAY);
+ }
+
+ err = -ETIMEDOUT;
+ }
+
+out:
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+
+ /* The 3XP seems to need a little extra time to complete the load
+ * of the sleep image before we can reliably boot it. Failure to
+ * do this occasionally results in a hung adapter after boot in
+ * typhoon_init_one() while trying to read the MAC address or
+ * putting the card to sleep. 3Com's driver waits 5ms, but
+ * that seems to be overkill. However, if we can sleep, we might
+ * as well give it that much time. Otherwise, we'll give it 500us,
+ * which should be enough (I've see it work well at 100us, but still
+ * saw occasional problems.)
+ */
+ if(wait_type == WaitSleep)
+ msleep(5);
+ else
+ udelay(500);
+ return err;
+}
+
+static int
+typhoon_wait_status(void __iomem *ioaddr, u32 wait_value)
+{
+ int i, err = 0;
+
+ for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) == wait_value)
+ goto out;
+ udelay(TYPHOON_UDELAY);
+ }
+
+ err = -ETIMEDOUT;
+
+out:
+ return err;
+}
+
+static inline void
+typhoon_media_status(struct net_device *dev, struct resp_desc *resp)
+{
+ if(resp->parm1 & TYPHOON_MEDIA_STAT_NO_LINK)
+ netif_carrier_off(dev);
+ else
+ netif_carrier_on(dev);
+}
+
+static inline void
+typhoon_hello(struct typhoon *tp)
+{
+ struct basic_ring *ring = &tp->cmdRing;
+ struct cmd_desc *cmd;
+
+ /* We only get a hello request if we've not sent anything to the
+ * card in a long while. If the lock is held, then we're in the
+ * process of issuing a command, so we don't need to respond.
+ */
+ if(spin_trylock(&tp->command_lock)) {
+ cmd = (struct cmd_desc *)(ring->ringBase + ring->lastWrite);
+ typhoon_inc_cmd_index(&ring->lastWrite, 1);
+
+ INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);
+ wmb();
+ iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
+ spin_unlock(&tp->command_lock);
+ }
+}
+
+static int
+typhoon_process_response(struct typhoon *tp, int resp_size,
+ struct resp_desc *resp_save)
+{
+ struct typhoon_indexes *indexes = tp->indexes;
+ struct resp_desc *resp;
+ u8 *base = tp->respRing.ringBase;
+ int count, len, wrap_len;
+ u32 cleared;
+ u32 ready;
+
+ cleared = le32_to_cpu(indexes->respCleared);
+ ready = le32_to_cpu(indexes->respReady);
+ while(cleared != ready) {
+ resp = (struct resp_desc *)(base + cleared);
+ count = resp->numDesc + 1;
+ if(resp_save && resp->seqNo) {
+ if(count > resp_size) {
+ resp_save->flags = TYPHOON_RESP_ERROR;
+ goto cleanup;
+ }
+
+ wrap_len = 0;
+ len = count * sizeof(*resp);
+ if(unlikely(cleared + len > RESPONSE_RING_SIZE)) {
+ wrap_len = cleared + len - RESPONSE_RING_SIZE;
+ len = RESPONSE_RING_SIZE - cleared;
+ }
+
+ memcpy(resp_save, resp, len);
+ if(unlikely(wrap_len)) {
+ resp_save += len / sizeof(*resp);
+ memcpy(resp_save, base, wrap_len);
+ }
+
+ resp_save = NULL;
+ } else if(resp->cmd == TYPHOON_CMD_READ_MEDIA_STATUS) {
+ typhoon_media_status(tp->dev, resp);
+ } else if(resp->cmd == TYPHOON_CMD_HELLO_RESP) {
+ typhoon_hello(tp);
+ } else {
+ netdev_err(tp->dev,
+ "dumping unexpected response 0x%04x:%d:0x%02x:0x%04x:%08x:%08x\n",
+ le16_to_cpu(resp->cmd),
+ resp->numDesc, resp->flags,
+ le16_to_cpu(resp->parm1),
+ le32_to_cpu(resp->parm2),
+ le32_to_cpu(resp->parm3));
+ }
+
+cleanup:
+ typhoon_inc_resp_index(&cleared, count);
+ }
+
+ indexes->respCleared = cpu_to_le32(cleared);
+ wmb();
+ return resp_save == NULL;
+}
+
+static inline int
+typhoon_num_free(int lastWrite, int lastRead, int ringSize)
+{
+ /* this works for all descriptors but rx_desc, as they are a
+ * different size than the cmd_desc -- everyone else is the same
+ */
+ lastWrite /= sizeof(struct cmd_desc);
+ lastRead /= sizeof(struct cmd_desc);
+ return (ringSize + lastRead - lastWrite - 1) % ringSize;
+}
+
+static inline int
+typhoon_num_free_cmd(struct typhoon *tp)
+{
+ int lastWrite = tp->cmdRing.lastWrite;
+ int cmdCleared = le32_to_cpu(tp->indexes->cmdCleared);
+
+ return typhoon_num_free(lastWrite, cmdCleared, COMMAND_ENTRIES);
+}
+
+static inline int
+typhoon_num_free_resp(struct typhoon *tp)
+{
+ int respReady = le32_to_cpu(tp->indexes->respReady);
+ int respCleared = le32_to_cpu(tp->indexes->respCleared);
+
+ return typhoon_num_free(respReady, respCleared, RESPONSE_ENTRIES);
+}
+
+static inline int
+typhoon_num_free_tx(struct transmit_ring *ring)
+{
+ /* if we start using the Hi Tx ring, this needs updating */
+ return typhoon_num_free(ring->lastWrite, ring->lastRead, TXLO_ENTRIES);
+}
+
+static int
+typhoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,
+ int num_resp, struct resp_desc *resp)
+{
+ struct typhoon_indexes *indexes = tp->indexes;
+ struct basic_ring *ring = &tp->cmdRing;
+ struct resp_desc local_resp;
+ int i, err = 0;
+ int got_resp;
+ int freeCmd, freeResp;
+ int len, wrap_len;
+
+ spin_lock(&tp->command_lock);
+
+ freeCmd = typhoon_num_free_cmd(tp);
+ freeResp = typhoon_num_free_resp(tp);
+
+ if(freeCmd < num_cmd || freeResp < num_resp) {
+ netdev_err(tp->dev, "no descs for cmd, had (needed) %d (%d) cmd, %d (%d) resp\n",
+ freeCmd, num_cmd, freeResp, num_resp);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if(cmd->flags & TYPHOON_CMD_RESPOND) {
+ /* If we're expecting a response, but the caller hasn't given
+ * us a place to put it, we'll provide one.
+ */
+ tp->awaiting_resp = 1;
+ if(resp == NULL) {
+ resp = &local_resp;
+ num_resp = 1;
+ }
+ }
+
+ wrap_len = 0;
+ len = num_cmd * sizeof(*cmd);
+ if(unlikely(ring->lastWrite + len > COMMAND_RING_SIZE)) {
+ wrap_len = ring->lastWrite + len - COMMAND_RING_SIZE;
+ len = COMMAND_RING_SIZE - ring->lastWrite;
+ }
+
+ memcpy(ring->ringBase + ring->lastWrite, cmd, len);
+ if(unlikely(wrap_len)) {
+ struct cmd_desc *wrap_ptr = cmd;
+ wrap_ptr += len / sizeof(*cmd);
+ memcpy(ring->ringBase, wrap_ptr, wrap_len);
+ }
+
+ typhoon_inc_cmd_index(&ring->lastWrite, num_cmd);
+
+ /* "I feel a presence... another warrior is on the mesa."
+ */
+ wmb();
+ iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
+ typhoon_post_pci_writes(tp->ioaddr);
+
+ if((cmd->flags & TYPHOON_CMD_RESPOND) == 0)
+ goto out;
+
+ /* Ugh. We'll be here about 8ms, spinning our thumbs, unable to
+ * preempt or do anything other than take interrupts. So, don't
+ * wait for a response unless you have to.
+ *
+ * I've thought about trying to sleep here, but we're called
+ * from many contexts that don't allow that. Also, given the way
+ * 3Com has implemented irq coalescing, we would likely timeout --
+ * this has been observed in real life!
+ *
+ * The big killer is we have to wait to get stats from the card,
+ * though we could go to a periodic refresh of those if we don't
+ * mind them getting somewhat stale. The rest of the waiting
+ * commands occur during open/close/suspend/resume, so they aren't
+ * time critical. Creating SAs in the future will also have to
+ * wait here.
+ */
+ got_resp = 0;
+ for(i = 0; i < TYPHOON_WAIT_TIMEOUT && !got_resp; i++) {
+ if(indexes->respCleared != indexes->respReady)
+ got_resp = typhoon_process_response(tp, num_resp,
+ resp);
+ udelay(TYPHOON_UDELAY);
+ }
+
+ if(!got_resp) {
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* Collect the error response even if we don't care about the
+ * rest of the response
+ */
+ if(resp->flags & TYPHOON_RESP_ERROR)
+ err = -EIO;
+
+out:
+ if(tp->awaiting_resp) {
+ tp->awaiting_resp = 0;
+ smp_wmb();
+
+ /* Ugh. If a response was added to the ring between
+ * the call to typhoon_process_response() and the clearing
+ * of tp->awaiting_resp, we could have missed the interrupt
+ * and it could hang in the ring an indeterminate amount of
+ * time. So, check for it, and interrupt ourselves if this
+ * is the case.
+ */
+ if(indexes->respCleared != indexes->respReady)
+ iowrite32(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
+ }
+
+ spin_unlock(&tp->command_lock);
+ return err;
+}
+
+static inline void
+typhoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,
+ u32 ring_dma)
+{
+ struct tcpopt_desc *tcpd;
+ u32 tcpd_offset = ring_dma;
+
+ tcpd = (struct tcpopt_desc *) (txRing->ringBase + txRing->lastWrite);
+ tcpd_offset += txRing->lastWrite;
+ tcpd_offset += offsetof(struct tcpopt_desc, bytesTx);
+ typhoon_inc_tx_index(&txRing->lastWrite, 1);
+
+ tcpd->flags = TYPHOON_OPT_DESC | TYPHOON_OPT_TCP_SEG;
+ tcpd->numDesc = 1;
+ tcpd->mss_flags = cpu_to_le16(skb_tso_size(skb));
+ tcpd->mss_flags |= TYPHOON_TSO_FIRST | TYPHOON_TSO_LAST;
+ tcpd->respAddrLo = cpu_to_le32(tcpd_offset);
+ tcpd->bytesTx = cpu_to_le32(skb->len);
+ tcpd->status = 0;
+}
+
+static netdev_tx_t
+typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ struct transmit_ring *txRing;
+ struct tx_desc *txd, *first_txd;
+ dma_addr_t skb_dma;
+ int numDesc;
+
+ /* we have two rings to choose from, but we only use txLo for now
+ * If we start using the Hi ring as well, we'll need to update
+ * typhoon_stop_runtime(), typhoon_interrupt(), typhoon_num_free_tx(),
+ * and TXHI_ENTRIES to match, as well as update the TSO code below
+ * to get the right DMA address
+ */
+ txRing = &tp->txLoRing;
+
+ /* We need one descriptor for each fragment of the sk_buff, plus the
+ * one for the ->data area of it.
+ *
+ * The docs say a maximum of 16 fragment descriptors per TCP option
+ * descriptor, then make a new packet descriptor and option descriptor
+ * for the next 16 fragments. The engineers say just an option
+ * descriptor is needed. I've tested up to 26 fragments with a single
+ * packet descriptor/option descriptor combo, so I use that for now.
+ *
+ * If problems develop with TSO, check this first.
+ */
+ numDesc = skb_shinfo(skb)->nr_frags + 1;
+ if (skb_is_gso(skb))
+ numDesc++;
+
+ /* When checking for free space in the ring, we need to also
+ * account for the initial Tx descriptor, and we always must leave
+ * at least one descriptor unused in the ring so that it doesn't
+ * wrap and look empty.
+ *
+ * The only time we should loop here is when we hit the race
+ * between marking the queue awake and updating the cleared index.
+ * Just loop and it will appear. This comes from the acenic driver.
+ */
+ while(unlikely(typhoon_num_free_tx(txRing) < (numDesc + 2)))
+ smp_rmb();
+
+ first_txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
+ typhoon_inc_tx_index(&txRing->lastWrite, 1);
+
+ first_txd->flags = TYPHOON_TX_DESC | TYPHOON_DESC_VALID;
+ first_txd->numDesc = 0;
+ first_txd->len = 0;
+ first_txd->tx_addr = (u64)((unsigned long) skb);
+ first_txd->processFlags = 0;
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL) {
+ /* The 3XP will figure out if this is UDP/TCP */
+ first_txd->processFlags |= TYPHOON_TX_PF_TCP_CHKSUM;
+ first_txd->processFlags |= TYPHOON_TX_PF_UDP_CHKSUM;
+ first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM;
+ }
+
+ if(vlan_tx_tag_present(skb)) {
+ first_txd->processFlags |=
+ TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY;
+ first_txd->processFlags |=
+ cpu_to_le32(htons(vlan_tx_tag_get(skb)) <<
+ TYPHOON_TX_PF_VLAN_TAG_SHIFT);
+ }
+
+ if (skb_is_gso(skb)) {
+ first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
+ first_txd->numDesc++;
+
+ typhoon_tso_fill(skb, txRing, tp->txlo_dma_addr);
+ }
+
+ txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
+ typhoon_inc_tx_index(&txRing->lastWrite, 1);
+
+ /* No need to worry about padding packet -- the firmware pads
+ * it with zeros to ETH_ZLEN for us.
+ */
+ if(skb_shinfo(skb)->nr_frags == 0) {
+ skb_dma = pci_map_single(tp->tx_pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
+ txd->len = cpu_to_le16(skb->len);
+ txd->frag.addr = cpu_to_le32(skb_dma);
+ txd->frag.addrHi = 0;
+ first_txd->numDesc++;
+ } else {
+ int i, len;
+
+ len = skb_headlen(skb);
+ skb_dma = pci_map_single(tp->tx_pdev, skb->data, len,
+ PCI_DMA_TODEVICE);
+ txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
+ txd->len = cpu_to_le16(len);
+ txd->frag.addr = cpu_to_le32(skb_dma);
+ txd->frag.addrHi = 0;
+ first_txd->numDesc++;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ void *frag_addr;
+
+ txd = (struct tx_desc *) (txRing->ringBase +
+ txRing->lastWrite);
+ typhoon_inc_tx_index(&txRing->lastWrite, 1);
+
+ len = skb_frag_size(frag);
+ frag_addr = skb_frag_address(frag);
+ skb_dma = pci_map_single(tp->tx_pdev, frag_addr, len,
+ PCI_DMA_TODEVICE);
+ txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
+ txd->len = cpu_to_le16(len);
+ txd->frag.addr = cpu_to_le32(skb_dma);
+ txd->frag.addrHi = 0;
+ first_txd->numDesc++;
+ }
+ }
+
+ /* Kick the 3XP
+ */
+ wmb();
+ iowrite32(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
+
+ /* If we don't have room to put the worst case packet on the
+ * queue, then we must stop the queue. We need 2 extra
+ * descriptors -- one to prevent ring wrap, and one for the
+ * Tx header.
+ */
+ numDesc = MAX_SKB_FRAGS + TSO_NUM_DESCRIPTORS + 1;
+
+ if(typhoon_num_free_tx(txRing) < (numDesc + 2)) {
+ netif_stop_queue(dev);
+
+ /* A Tx complete IRQ could have gotten between, making
+ * the ring free again. Only need to recheck here, since
+ * Tx is serialized.
+ */
+ if(typhoon_num_free_tx(txRing) >= (numDesc + 2))
+ netif_wake_queue(dev);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static void
+typhoon_set_rx_mode(struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ struct cmd_desc xp_cmd;
+ u32 mc_filter[2];
+ __le16 filter;
+
+ filter = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
+ if(dev->flags & IFF_PROMISC) {
+ filter |= TYPHOON_RX_FILTER_PROMISCOUS;
+ } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to match, or accept all multicasts. */
+ filter |= TYPHOON_RX_FILTER_ALL_MCAST;
+ } else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ netdev_for_each_mc_addr(ha, dev) {
+ int bit = ether_crc(ETH_ALEN, ha->addr) & 0x3f;
+ mc_filter[bit >> 5] |= 1 << (bit & 0x1f);
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd,
+ TYPHOON_CMD_SET_MULTICAST_HASH);
+ xp_cmd.parm1 = TYPHOON_MCAST_HASH_SET;
+ xp_cmd.parm2 = cpu_to_le32(mc_filter[0]);
+ xp_cmd.parm3 = cpu_to_le32(mc_filter[1]);
+ typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+
+ filter |= TYPHOON_RX_FILTER_MCAST_HASH;
+ }
+
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
+ xp_cmd.parm1 = filter;
+ typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+}
+
+static int
+typhoon_do_get_stats(struct typhoon *tp)
+{
+ struct net_device_stats *stats = &tp->stats;
+ struct net_device_stats *saved = &tp->stats_saved;
+ struct cmd_desc xp_cmd;
+ struct resp_desc xp_resp[7];
+ struct stats_resp *s = (struct stats_resp *) xp_resp;
+ int err;
+
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_STATS);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 7, xp_resp);
+ if(err < 0)
+ return err;
+
+ /* 3Com's Linux driver uses txMultipleCollisions as it's
+ * collisions value, but there is some other collision info as well...
+ *
+ * The extra status reported would be a good candidate for
+ * ethtool_ops->get_{strings,stats}()
+ */
+ stats->tx_packets = le32_to_cpu(s->txPackets) +
+ saved->tx_packets;
+ stats->tx_bytes = le64_to_cpu(s->txBytes) +
+ saved->tx_bytes;
+ stats->tx_errors = le32_to_cpu(s->txCarrierLost) +
+ saved->tx_errors;
+ stats->tx_carrier_errors = le32_to_cpu(s->txCarrierLost) +
+ saved->tx_carrier_errors;
+ stats->collisions = le32_to_cpu(s->txMultipleCollisions) +
+ saved->collisions;
+ stats->rx_packets = le32_to_cpu(s->rxPacketsGood) +
+ saved->rx_packets;
+ stats->rx_bytes = le64_to_cpu(s->rxBytesGood) +
+ saved->rx_bytes;
+ stats->rx_fifo_errors = le32_to_cpu(s->rxFifoOverruns) +
+ saved->rx_fifo_errors;
+ stats->rx_errors = le32_to_cpu(s->rxFifoOverruns) +
+ le32_to_cpu(s->BadSSD) + le32_to_cpu(s->rxCrcErrors) +
+ saved->rx_errors;
+ stats->rx_crc_errors = le32_to_cpu(s->rxCrcErrors) +
+ saved->rx_crc_errors;
+ stats->rx_length_errors = le32_to_cpu(s->rxOversized) +
+ saved->rx_length_errors;
+ tp->speed = (s->linkStatus & TYPHOON_LINK_100MBPS) ?
+ SPEED_100 : SPEED_10;
+ tp->duplex = (s->linkStatus & TYPHOON_LINK_FULL_DUPLEX) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+
+ return 0;
+}
+
+static struct net_device_stats *
+typhoon_get_stats(struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ struct net_device_stats *stats = &tp->stats;
+ struct net_device_stats *saved = &tp->stats_saved;
+
+ smp_rmb();
+ if(tp->card_state == Sleeping)
+ return saved;
+
+ if(typhoon_do_get_stats(tp) < 0) {
+ netdev_err(dev, "error getting stats\n");
+ return saved;
+ }
+
+ return stats;
+}
+
+static void
+typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ struct pci_dev *pci_dev = tp->pdev;
+ struct cmd_desc xp_cmd;
+ struct resp_desc xp_resp[3];
+
+ smp_rmb();
+ if(tp->card_state == Sleeping) {
+ strlcpy(info->fw_version, "Sleep image",
+ sizeof(info->fw_version));
+ } else {
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
+ if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
+ strlcpy(info->fw_version, "Unknown runtime",
+ sizeof(info->fw_version));
+ } else {
+ u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2);
+ snprintf(info->fw_version, sizeof(info->fw_version),
+ "%02x.%03x.%03x", sleep_ver >> 24,
+ (sleep_ver >> 12) & 0xfff, sleep_ver & 0xfff);
+ }
+ }
+
+ strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info));
+}
+
+static int
+typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct typhoon *tp = netdev_priv(dev);
+
+ cmd->supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg;
+
+ switch (tp->xcvr_select) {
+ case TYPHOON_XCVR_10HALF:
+ cmd->advertising = ADVERTISED_10baseT_Half;
+ break;
+ case TYPHOON_XCVR_10FULL:
+ cmd->advertising = ADVERTISED_10baseT_Full;
+ break;
+ case TYPHOON_XCVR_100HALF:
+ cmd->advertising = ADVERTISED_100baseT_Half;
+ break;
+ case TYPHOON_XCVR_100FULL:
+ cmd->advertising = ADVERTISED_100baseT_Full;
+ break;
+ case TYPHOON_XCVR_AUTONEG:
+ cmd->advertising = ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_Autoneg;
+ break;
+ }
+
+ if(tp->capabilities & TYPHOON_FIBER) {
+ cmd->supported |= SUPPORTED_FIBRE;
+ cmd->advertising |= ADVERTISED_FIBRE;
+ cmd->port = PORT_FIBRE;
+ } else {
+ cmd->supported |= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP;
+ cmd->advertising |= ADVERTISED_TP;
+ cmd->port = PORT_TP;
+ }
+
+ /* need to get stats to make these link speed/duplex valid */
+ typhoon_do_get_stats(tp);
+ ethtool_cmd_speed_set(cmd, tp->speed);
+ cmd->duplex = tp->duplex;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ if(tp->xcvr_select == TYPHOON_XCVR_AUTONEG)
+ cmd->autoneg = AUTONEG_ENABLE;
+ else
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ return 0;
+}
+
+static int
+typhoon_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ u32 speed = ethtool_cmd_speed(cmd);
+ struct cmd_desc xp_cmd;
+ __le16 xcvr;
+ int err;
+
+ err = -EINVAL;
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ xcvr = TYPHOON_XCVR_AUTONEG;
+ } else {
+ if (cmd->duplex == DUPLEX_HALF) {
+ if (speed == SPEED_10)
+ xcvr = TYPHOON_XCVR_10HALF;
+ else if (speed == SPEED_100)
+ xcvr = TYPHOON_XCVR_100HALF;
+ else
+ goto out;
+ } else if (cmd->duplex == DUPLEX_FULL) {
+ if (speed == SPEED_10)
+ xcvr = TYPHOON_XCVR_10FULL;
+ else if (speed == SPEED_100)
+ xcvr = TYPHOON_XCVR_100FULL;
+ else
+ goto out;
+ } else
+ goto out;
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_XCVR_SELECT);
+ xp_cmd.parm1 = xcvr;
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto out;
+
+ tp->xcvr_select = xcvr;
+ if(cmd->autoneg == AUTONEG_ENABLE) {
+ tp->speed = 0xff; /* invalid */
+ tp->duplex = 0xff; /* invalid */
+ } else {
+ tp->speed = speed;
+ tp->duplex = cmd->duplex;
+ }
+
+out:
+ return err;
+}
+
+static void
+typhoon_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct typhoon *tp = netdev_priv(dev);
+
+ wol->supported = WAKE_PHY | WAKE_MAGIC;
+ wol->wolopts = 0;
+ if(tp->wol_events & TYPHOON_WAKE_LINK_EVENT)
+ wol->wolopts |= WAKE_PHY;
+ if(tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
+ wol->wolopts |= WAKE_MAGIC;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int
+typhoon_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct typhoon *tp = netdev_priv(dev);
+
+ if(wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
+ return -EINVAL;
+
+ tp->wol_events = 0;
+ if(wol->wolopts & WAKE_PHY)
+ tp->wol_events |= TYPHOON_WAKE_LINK_EVENT;
+ if(wol->wolopts & WAKE_MAGIC)
+ tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT;
+
+ return 0;
+}
+
+static void
+typhoon_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
+{
+ ering->rx_max_pending = RXENT_ENTRIES;
+ ering->tx_max_pending = TXLO_ENTRIES - 1;
+
+ ering->rx_pending = RXENT_ENTRIES;
+ ering->tx_pending = TXLO_ENTRIES - 1;
+}
+
+static const struct ethtool_ops typhoon_ethtool_ops = {
+ .get_settings = typhoon_get_settings,
+ .set_settings = typhoon_set_settings,
+ .get_drvinfo = typhoon_get_drvinfo,
+ .get_wol = typhoon_get_wol,
+ .set_wol = typhoon_set_wol,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = typhoon_get_ringparam,
+};
+
+static int
+typhoon_wait_interrupt(void __iomem *ioaddr)
+{
+ int i, err = 0;
+
+ for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
+ if(ioread32(ioaddr + TYPHOON_REG_INTR_STATUS) &
+ TYPHOON_INTR_BOOTCMD)
+ goto out;
+ udelay(TYPHOON_UDELAY);
+ }
+
+ err = -ETIMEDOUT;
+
+out:
+ iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
+ return err;
+}
+
+#define shared_offset(x) offsetof(struct typhoon_shared, x)
+
+static void
+typhoon_init_interface(struct typhoon *tp)
+{
+ struct typhoon_interface *iface = &tp->shared->iface;
+ dma_addr_t shared_dma;
+
+ memset(tp->shared, 0, sizeof(struct typhoon_shared));
+
+ /* The *Hi members of iface are all init'd to zero by the memset().
+ */
+ shared_dma = tp->shared_dma + shared_offset(indexes);
+ iface->ringIndex = cpu_to_le32(shared_dma);
+
+ shared_dma = tp->shared_dma + shared_offset(txLo);
+ iface->txLoAddr = cpu_to_le32(shared_dma);
+ iface->txLoSize = cpu_to_le32(TXLO_ENTRIES * sizeof(struct tx_desc));
+
+ shared_dma = tp->shared_dma + shared_offset(txHi);
+ iface->txHiAddr = cpu_to_le32(shared_dma);
+ iface->txHiSize = cpu_to_le32(TXHI_ENTRIES * sizeof(struct tx_desc));
+
+ shared_dma = tp->shared_dma + shared_offset(rxBuff);
+ iface->rxBuffAddr = cpu_to_le32(shared_dma);
+ iface->rxBuffSize = cpu_to_le32(RXFREE_ENTRIES *
+ sizeof(struct rx_free));
+
+ shared_dma = tp->shared_dma + shared_offset(rxLo);
+ iface->rxLoAddr = cpu_to_le32(shared_dma);
+ iface->rxLoSize = cpu_to_le32(RX_ENTRIES * sizeof(struct rx_desc));
+
+ shared_dma = tp->shared_dma + shared_offset(rxHi);
+ iface->rxHiAddr = cpu_to_le32(shared_dma);
+ iface->rxHiSize = cpu_to_le32(RX_ENTRIES * sizeof(struct rx_desc));
+
+ shared_dma = tp->shared_dma + shared_offset(cmd);
+ iface->cmdAddr = cpu_to_le32(shared_dma);
+ iface->cmdSize = cpu_to_le32(COMMAND_RING_SIZE);
+
+ shared_dma = tp->shared_dma + shared_offset(resp);
+ iface->respAddr = cpu_to_le32(shared_dma);
+ iface->respSize = cpu_to_le32(RESPONSE_RING_SIZE);
+
+ shared_dma = tp->shared_dma + shared_offset(zeroWord);
+ iface->zeroAddr = cpu_to_le32(shared_dma);
+
+ tp->indexes = &tp->shared->indexes;
+ tp->txLoRing.ringBase = (u8 *) tp->shared->txLo;
+ tp->txHiRing.ringBase = (u8 *) tp->shared->txHi;
+ tp->rxLoRing.ringBase = (u8 *) tp->shared->rxLo;
+ tp->rxHiRing.ringBase = (u8 *) tp->shared->rxHi;
+ tp->rxBuffRing.ringBase = (u8 *) tp->shared->rxBuff;
+ tp->cmdRing.ringBase = (u8 *) tp->shared->cmd;
+ tp->respRing.ringBase = (u8 *) tp->shared->resp;
+
+ tp->txLoRing.writeRegister = TYPHOON_REG_TX_LO_READY;
+ tp->txHiRing.writeRegister = TYPHOON_REG_TX_HI_READY;
+
+ tp->txlo_dma_addr = le32_to_cpu(iface->txLoAddr);
+ tp->card_state = Sleeping;
+
+ tp->offload = TYPHOON_OFFLOAD_IP_CHKSUM | TYPHOON_OFFLOAD_TCP_CHKSUM;
+ tp->offload |= TYPHOON_OFFLOAD_UDP_CHKSUM | TSO_OFFLOAD_ON;
+ tp->offload |= TYPHOON_OFFLOAD_VLAN;
+
+ spin_lock_init(&tp->command_lock);
+
+ /* Force the writes to the shared memory area out before continuing. */
+ wmb();
+}
+
+static void
+typhoon_init_rings(struct typhoon *tp)
+{
+ memset(tp->indexes, 0, sizeof(struct typhoon_indexes));
+
+ tp->txLoRing.lastWrite = 0;
+ tp->txHiRing.lastWrite = 0;
+ tp->rxLoRing.lastWrite = 0;
+ tp->rxHiRing.lastWrite = 0;
+ tp->rxBuffRing.lastWrite = 0;
+ tp->cmdRing.lastWrite = 0;
+ tp->respRing.lastWrite = 0;
+
+ tp->txLoRing.lastRead = 0;
+ tp->txHiRing.lastRead = 0;
+}
+
+static const struct firmware *typhoon_fw;
+
+static int
+typhoon_request_firmware(struct typhoon *tp)
+{
+ const struct typhoon_file_header *fHdr;
+ const struct typhoon_section_header *sHdr;
+ const u8 *image_data;
+ u32 numSections;
+ u32 section_len;
+ u32 remaining;
+ int err;
+
+ if (typhoon_fw)
+ return 0;
+
+ err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev);
+ if (err) {
+ netdev_err(tp->dev, "Failed to load firmware \"%s\"\n",
+ FIRMWARE_NAME);
+ return err;
+ }
+
+ image_data = (u8 *) typhoon_fw->data;
+ remaining = typhoon_fw->size;
+ if (remaining < sizeof(struct typhoon_file_header))
+ goto invalid_fw;
+
+ fHdr = (struct typhoon_file_header *) image_data;
+ if (memcmp(fHdr->tag, "TYPHOON", 8))
+ goto invalid_fw;
+
+ numSections = le32_to_cpu(fHdr->numSections);
+ image_data += sizeof(struct typhoon_file_header);
+ remaining -= sizeof(struct typhoon_file_header);
+
+ while (numSections--) {
+ if (remaining < sizeof(struct typhoon_section_header))
+ goto invalid_fw;
+
+ sHdr = (struct typhoon_section_header *) image_data;
+ image_data += sizeof(struct typhoon_section_header);
+ section_len = le32_to_cpu(sHdr->len);
+
+ if (remaining < section_len)
+ goto invalid_fw;
+
+ image_data += section_len;
+ remaining -= section_len;
+ }
+
+ return 0;
+
+invalid_fw:
+ netdev_err(tp->dev, "Invalid firmware image\n");
+ release_firmware(typhoon_fw);
+ typhoon_fw = NULL;
+ return -EINVAL;
+}
+
+static int
+typhoon_download_firmware(struct typhoon *tp)
+{
+ void __iomem *ioaddr = tp->ioaddr;
+ struct pci_dev *pdev = tp->pdev;
+ const struct typhoon_file_header *fHdr;
+ const struct typhoon_section_header *sHdr;
+ const u8 *image_data;
+ void *dpage;
+ dma_addr_t dpage_dma;
+ __sum16 csum;
+ u32 irqEnabled;
+ u32 irqMasked;
+ u32 numSections;
+ u32 section_len;
+ u32 len;
+ u32 load_addr;
+ u32 hmac;
+ int i;
+ int err;
+
+ image_data = (u8 *) typhoon_fw->data;
+ fHdr = (struct typhoon_file_header *) image_data;
+
+ /* Cannot just map the firmware image using pci_map_single() as
+ * the firmware is vmalloc()'d and may not be physically contiguous,
+ * so we allocate some consistent memory to copy the sections into.
+ */
+ err = -ENOMEM;
+ dpage = pci_alloc_consistent(pdev, PAGE_SIZE, &dpage_dma);
+ if(!dpage) {
+ netdev_err(tp->dev, "no DMA mem for firmware\n");
+ goto err_out;
+ }
+
+ irqEnabled = ioread32(ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(irqEnabled | TYPHOON_INTR_BOOTCMD,
+ ioaddr + TYPHOON_REG_INTR_ENABLE);
+ irqMasked = ioread32(ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(irqMasked | TYPHOON_INTR_BOOTCMD,
+ ioaddr + TYPHOON_REG_INTR_MASK);
+
+ err = -ETIMEDOUT;
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
+ netdev_err(tp->dev, "card ready timeout\n");
+ goto err_out_irq;
+ }
+
+ numSections = le32_to_cpu(fHdr->numSections);
+ load_addr = le32_to_cpu(fHdr->startAddr);
+
+ iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(load_addr, ioaddr + TYPHOON_REG_DOWNLOAD_BOOT_ADDR);
+ hmac = le32_to_cpu(fHdr->hmacDigest[0]);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_0);
+ hmac = le32_to_cpu(fHdr->hmacDigest[1]);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_1);
+ hmac = le32_to_cpu(fHdr->hmacDigest[2]);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_2);
+ hmac = le32_to_cpu(fHdr->hmacDigest[3]);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_3);
+ hmac = le32_to_cpu(fHdr->hmacDigest[4]);
+ iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_4);
+ typhoon_post_pci_writes(ioaddr);
+ iowrite32(TYPHOON_BOOTCMD_RUNTIME_IMAGE, ioaddr + TYPHOON_REG_COMMAND);
+
+ image_data += sizeof(struct typhoon_file_header);
+
+ /* The ioread32() in typhoon_wait_interrupt() will force the
+ * last write to the command register to post, so
+ * we don't need a typhoon_post_pci_writes() after it.
+ */
+ for(i = 0; i < numSections; i++) {
+ sHdr = (struct typhoon_section_header *) image_data;
+ image_data += sizeof(struct typhoon_section_header);
+ load_addr = le32_to_cpu(sHdr->startAddr);
+ section_len = le32_to_cpu(sHdr->len);
+
+ while(section_len) {
+ len = min_t(u32, section_len, PAGE_SIZE);
+
+ if(typhoon_wait_interrupt(ioaddr) < 0 ||
+ ioread32(ioaddr + TYPHOON_REG_STATUS) !=
+ TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
+ netdev_err(tp->dev, "segment ready timeout\n");
+ goto err_out_irq;
+ }
+
+ /* Do an pseudo IPv4 checksum on the data -- first
+ * need to convert each u16 to cpu order before
+ * summing. Fortunately, due to the properties of
+ * the checksum, we can do this once, at the end.
+ */
+ csum = csum_fold(csum_partial_copy_nocheck(image_data,
+ dpage, len,
+ 0));
+
+ iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
+ iowrite32(le16_to_cpu((__force __le16)csum),
+ ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
+ iowrite32(load_addr,
+ ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
+ iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
+ iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO);
+ typhoon_post_pci_writes(ioaddr);
+ iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE,
+ ioaddr + TYPHOON_REG_COMMAND);
+
+ image_data += len;
+ load_addr += len;
+ section_len -= len;
+ }
+ }
+
+ if(typhoon_wait_interrupt(ioaddr) < 0 ||
+ ioread32(ioaddr + TYPHOON_REG_STATUS) !=
+ TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
+ netdev_err(tp->dev, "final segment ready timeout\n");
+ goto err_out_irq;
+ }
+
+ iowrite32(TYPHOON_BOOTCMD_DNLD_COMPLETE, ioaddr + TYPHOON_REG_COMMAND);
+
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_BOOT) < 0) {
+ netdev_err(tp->dev, "boot ready timeout, status 0x%0x\n",
+ ioread32(ioaddr + TYPHOON_REG_STATUS));
+ goto err_out_irq;
+ }
+
+ err = 0;
+
+err_out_irq:
+ iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE);
+
+ pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma);
+
+err_out:
+ return err;
+}
+
+static int
+typhoon_boot_3XP(struct typhoon *tp, u32 initial_status)
+{
+ void __iomem *ioaddr = tp->ioaddr;
+
+ if(typhoon_wait_status(ioaddr, initial_status) < 0) {
+ netdev_err(tp->dev, "boot ready timeout\n");
+ goto out_timeout;
+ }
+
+ iowrite32(0, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_HI);
+ iowrite32(tp->shared_dma, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_LO);
+ typhoon_post_pci_writes(ioaddr);
+ iowrite32(TYPHOON_BOOTCMD_REG_BOOT_RECORD,
+ ioaddr + TYPHOON_REG_COMMAND);
+
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_RUNNING) < 0) {
+ netdev_err(tp->dev, "boot finish timeout (status 0x%x)\n",
+ ioread32(ioaddr + TYPHOON_REG_STATUS));
+ goto out_timeout;
+ }
+
+ /* Clear the Transmit and Command ready registers
+ */
+ iowrite32(0, ioaddr + TYPHOON_REG_TX_HI_READY);
+ iowrite32(0, ioaddr + TYPHOON_REG_CMD_READY);
+ iowrite32(0, ioaddr + TYPHOON_REG_TX_LO_READY);
+ typhoon_post_pci_writes(ioaddr);
+ iowrite32(TYPHOON_BOOTCMD_BOOT, ioaddr + TYPHOON_REG_COMMAND);
+
+ return 0;
+
+out_timeout:
+ return -ETIMEDOUT;
+}
+
+static u32
+typhoon_clean_tx(struct typhoon *tp, struct transmit_ring *txRing,
+ volatile __le32 * index)
+{
+ u32 lastRead = txRing->lastRead;
+ struct tx_desc *tx;
+ dma_addr_t skb_dma;
+ int dma_len;
+ int type;
+
+ while(lastRead != le32_to_cpu(*index)) {
+ tx = (struct tx_desc *) (txRing->ringBase + lastRead);
+ type = tx->flags & TYPHOON_TYPE_MASK;
+
+ if(type == TYPHOON_TX_DESC) {
+ /* This tx_desc describes a packet.
+ */
+ unsigned long ptr = tx->tx_addr;
+ struct sk_buff *skb = (struct sk_buff *) ptr;
+ dev_kfree_skb_irq(skb);
+ } else if(type == TYPHOON_FRAG_DESC) {
+ /* This tx_desc describes a memory mapping. Free it.
+ */
+ skb_dma = (dma_addr_t) le32_to_cpu(tx->frag.addr);
+ dma_len = le16_to_cpu(tx->len);
+ pci_unmap_single(tp->pdev, skb_dma, dma_len,
+ PCI_DMA_TODEVICE);
+ }
+
+ tx->flags = 0;
+ typhoon_inc_tx_index(&lastRead, 1);
+ }
+
+ return lastRead;
+}
+
+static void
+typhoon_tx_complete(struct typhoon *tp, struct transmit_ring *txRing,
+ volatile __le32 * index)
+{
+ u32 lastRead;
+ int numDesc = MAX_SKB_FRAGS + 1;
+
+ /* This will need changing if we start to use the Hi Tx ring. */
+ lastRead = typhoon_clean_tx(tp, txRing, index);
+ if(netif_queue_stopped(tp->dev) && typhoon_num_free(txRing->lastWrite,
+ lastRead, TXLO_ENTRIES) > (numDesc + 2))
+ netif_wake_queue(tp->dev);
+
+ txRing->lastRead = lastRead;
+ smp_wmb();
+}
+
+static void
+typhoon_recycle_rx_skb(struct typhoon *tp, u32 idx)
+{
+ struct typhoon_indexes *indexes = tp->indexes;
+ struct rxbuff_ent *rxb = &tp->rxbuffers[idx];
+ struct basic_ring *ring = &tp->rxBuffRing;
+ struct rx_free *r;
+
+ if((ring->lastWrite + sizeof(*r)) % (RXFREE_ENTRIES * sizeof(*r)) ==
+ le32_to_cpu(indexes->rxBuffCleared)) {
+ /* no room in ring, just drop the skb
+ */
+ dev_kfree_skb_any(rxb->skb);
+ rxb->skb = NULL;
+ return;
+ }
+
+ r = (struct rx_free *) (ring->ringBase + ring->lastWrite);
+ typhoon_inc_rxfree_index(&ring->lastWrite, 1);
+ r->virtAddr = idx;
+ r->physAddr = cpu_to_le32(rxb->dma_addr);
+
+ /* Tell the card about it */
+ wmb();
+ indexes->rxBuffReady = cpu_to_le32(ring->lastWrite);
+}
+
+static int
+typhoon_alloc_rx_skb(struct typhoon *tp, u32 idx)
+{
+ struct typhoon_indexes *indexes = tp->indexes;
+ struct rxbuff_ent *rxb = &tp->rxbuffers[idx];
+ struct basic_ring *ring = &tp->rxBuffRing;
+ struct rx_free *r;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+
+ rxb->skb = NULL;
+
+ if((ring->lastWrite + sizeof(*r)) % (RXFREE_ENTRIES * sizeof(*r)) ==
+ le32_to_cpu(indexes->rxBuffCleared))
+ return -ENOMEM;
+
+ skb = netdev_alloc_skb(tp->dev, PKT_BUF_SZ);
+ if(!skb)
+ return -ENOMEM;
+
+#if 0
+ /* Please, 3com, fix the firmware to allow DMA to a unaligned
+ * address! Pretty please?
+ */
+ skb_reserve(skb, 2);
+#endif
+
+ dma_addr = pci_map_single(tp->pdev, skb->data,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+
+ /* Since no card does 64 bit DAC, the high bits will never
+ * change from zero.
+ */
+ r = (struct rx_free *) (ring->ringBase + ring->lastWrite);
+ typhoon_inc_rxfree_index(&ring->lastWrite, 1);
+ r->virtAddr = idx;
+ r->physAddr = cpu_to_le32(dma_addr);
+ rxb->skb = skb;
+ rxb->dma_addr = dma_addr;
+
+ /* Tell the card about it */
+ wmb();
+ indexes->rxBuffReady = cpu_to_le32(ring->lastWrite);
+ return 0;
+}
+
+static int
+typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * ready,
+ volatile __le32 * cleared, int budget)
+{
+ struct rx_desc *rx;
+ struct sk_buff *skb, *new_skb;
+ struct rxbuff_ent *rxb;
+ dma_addr_t dma_addr;
+ u32 local_ready;
+ u32 rxaddr;
+ int pkt_len;
+ u32 idx;
+ __le32 csum_bits;
+ int received;
+
+ received = 0;
+ local_ready = le32_to_cpu(*ready);
+ rxaddr = le32_to_cpu(*cleared);
+ while(rxaddr != local_ready && budget > 0) {
+ rx = (struct rx_desc *) (rxRing->ringBase + rxaddr);
+ idx = rx->addr;
+ rxb = &tp->rxbuffers[idx];
+ skb = rxb->skb;
+ dma_addr = rxb->dma_addr;
+
+ typhoon_inc_rx_index(&rxaddr, 1);
+
+ if(rx->flags & TYPHOON_RX_ERROR) {
+ typhoon_recycle_rx_skb(tp, idx);
+ continue;
+ }
+
+ pkt_len = le16_to_cpu(rx->frameLen);
+
+ if(pkt_len < rx_copybreak &&
+ (new_skb = netdev_alloc_skb(tp->dev, pkt_len + 2)) != NULL) {
+ skb_reserve(new_skb, 2);
+ pci_dma_sync_single_for_cpu(tp->pdev, dma_addr,
+ PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
+ skb_copy_to_linear_data(new_skb, skb->data, pkt_len);
+ pci_dma_sync_single_for_device(tp->pdev, dma_addr,
+ PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
+ skb_put(new_skb, pkt_len);
+ typhoon_recycle_rx_skb(tp, idx);
+ } else {
+ new_skb = skb;
+ skb_put(new_skb, pkt_len);
+ pci_unmap_single(tp->pdev, dma_addr, PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
+ typhoon_alloc_rx_skb(tp, idx);
+ }
+ new_skb->protocol = eth_type_trans(new_skb, tp->dev);
+ csum_bits = rx->rxStatus & (TYPHOON_RX_IP_CHK_GOOD |
+ TYPHOON_RX_UDP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD);
+ if(csum_bits ==
+ (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD) ||
+ csum_bits ==
+ (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_UDP_CHK_GOOD)) {
+ new_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else
+ skb_checksum_none_assert(new_skb);
+
+ if (rx->rxStatus & TYPHOON_RX_VLAN)
+ __vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
+ ntohl(rx->vlanTag) & 0xffff);
+ netif_receive_skb(new_skb);
+
+ received++;
+ budget--;
+ }
+ *cleared = cpu_to_le32(rxaddr);
+
+ return received;
+}
+
+static void
+typhoon_fill_free_ring(struct typhoon *tp)
+{
+ u32 i;
+
+ for(i = 0; i < RXENT_ENTRIES; i++) {
+ struct rxbuff_ent *rxb = &tp->rxbuffers[i];
+ if(rxb->skb)
+ continue;
+ if(typhoon_alloc_rx_skb(tp, i) < 0)
+ break;
+ }
+}
+
+static int
+typhoon_poll(struct napi_struct *napi, int budget)
+{
+ struct typhoon *tp = container_of(napi, struct typhoon, napi);
+ struct typhoon_indexes *indexes = tp->indexes;
+ int work_done;
+
+ rmb();
+ if(!tp->awaiting_resp && indexes->respReady != indexes->respCleared)
+ typhoon_process_response(tp, 0, NULL);
+
+ if(le32_to_cpu(indexes->txLoCleared) != tp->txLoRing.lastRead)
+ typhoon_tx_complete(tp, &tp->txLoRing, &indexes->txLoCleared);
+
+ work_done = 0;
+
+ if(indexes->rxHiCleared != indexes->rxHiReady) {
+ work_done += typhoon_rx(tp, &tp->rxHiRing, &indexes->rxHiReady,
+ &indexes->rxHiCleared, budget);
+ }
+
+ if(indexes->rxLoCleared != indexes->rxLoReady) {
+ work_done += typhoon_rx(tp, &tp->rxLoRing, &indexes->rxLoReady,
+ &indexes->rxLoCleared, budget - work_done);
+ }
+
+ if(le32_to_cpu(indexes->rxBuffCleared) == tp->rxBuffRing.lastWrite) {
+ /* rxBuff ring is empty, try to fill it. */
+ typhoon_fill_free_ring(tp);
+ }
+
+ if (work_done < budget) {
+ napi_complete(napi);
+ iowrite32(TYPHOON_INTR_NONE,
+ tp->ioaddr + TYPHOON_REG_INTR_MASK);
+ typhoon_post_pci_writes(tp->ioaddr);
+ }
+
+ return work_done;
+}
+
+static irqreturn_t
+typhoon_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct typhoon *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->ioaddr;
+ u32 intr_status;
+
+ intr_status = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ if(!(intr_status & TYPHOON_INTR_HOST_INT))
+ return IRQ_NONE;
+
+ iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS);
+
+ if (napi_schedule_prep(&tp->napi)) {
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ typhoon_post_pci_writes(ioaddr);
+ __napi_schedule(&tp->napi);
+ } else {
+ netdev_err(dev, "Error, poll already scheduled\n");
+ }
+ return IRQ_HANDLED;
+}
+
+static void
+typhoon_free_rx_rings(struct typhoon *tp)
+{
+ u32 i;
+
+ for(i = 0; i < RXENT_ENTRIES; i++) {
+ struct rxbuff_ent *rxb = &tp->rxbuffers[i];
+ if(rxb->skb) {
+ pci_unmap_single(tp->pdev, rxb->dma_addr, PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(rxb->skb);
+ rxb->skb = NULL;
+ }
+ }
+}
+
+static int
+typhoon_sleep(struct typhoon *tp, pci_power_t state, __le16 events)
+{
+ struct pci_dev *pdev = tp->pdev;
+ void __iomem *ioaddr = tp->ioaddr;
+ struct cmd_desc xp_cmd;
+ int err;
+
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_ENABLE_WAKE_EVENTS);
+ xp_cmd.parm1 = events;
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0) {
+ netdev_err(tp->dev, "typhoon_sleep(): wake events cmd err %d\n",
+ err);
+ return err;
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_GOTO_SLEEP);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0) {
+ netdev_err(tp->dev, "typhoon_sleep(): sleep cmd err %d\n", err);
+ return err;
+ }
+
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_SLEEPING) < 0)
+ return -ETIMEDOUT;
+
+ /* Since we cannot monitor the status of the link while sleeping,
+ * tell the world it went away.
+ */
+ netif_carrier_off(tp->dev);
+
+ pci_enable_wake(tp->pdev, state, 1);
+ pci_disable_device(pdev);
+ return pci_set_power_state(pdev, state);
+}
+
+static int
+typhoon_wakeup(struct typhoon *tp, int wait_type)
+{
+ struct pci_dev *pdev = tp->pdev;
+ void __iomem *ioaddr = tp->ioaddr;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ /* Post 2.x.x versions of the Sleep Image require a reset before
+ * we can download the Runtime Image. But let's not make users of
+ * the old firmware pay for the reset.
+ */
+ iowrite32(TYPHOON_BOOTCMD_WAKEUP, ioaddr + TYPHOON_REG_COMMAND);
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_HOST) < 0 ||
+ (tp->capabilities & TYPHOON_WAKEUP_NEEDS_RESET))
+ return typhoon_reset(ioaddr, wait_type);
+
+ return 0;
+}
+
+static int
+typhoon_start_runtime(struct typhoon *tp)
+{
+ struct net_device *dev = tp->dev;
+ void __iomem *ioaddr = tp->ioaddr;
+ struct cmd_desc xp_cmd;
+ int err;
+
+ typhoon_init_rings(tp);
+ typhoon_fill_free_ring(tp);
+
+ err = typhoon_download_firmware(tp);
+ if(err < 0) {
+ netdev_err(tp->dev, "cannot load runtime on 3XP\n");
+ goto error_out;
+ }
+
+ if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_BOOT) < 0) {
+ netdev_err(tp->dev, "cannot boot 3XP\n");
+ err = -EIO;
+ goto error_out;
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAX_PKT_SIZE);
+ xp_cmd.parm1 = cpu_to_le16(PKT_BUF_SZ);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAC_ADDRESS);
+ xp_cmd.parm1 = cpu_to_le16(ntohs(*(__be16 *)&dev->dev_addr[0]));
+ xp_cmd.parm2 = cpu_to_le32(ntohl(*(__be32 *)&dev->dev_addr[2]));
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ /* Disable IRQ coalescing -- we can reenable it when 3Com gives
+ * us some more information on how to control it.
+ */
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_IRQ_COALESCE_CTRL);
+ xp_cmd.parm1 = 0;
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_XCVR_SELECT);
+ xp_cmd.parm1 = tp->xcvr_select;
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_VLAN_TYPE_WRITE);
+ xp_cmd.parm1 = cpu_to_le16(ETH_P_8021Q);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_OFFLOAD_TASKS);
+ xp_cmd.parm2 = tp->offload;
+ xp_cmd.parm3 = tp->offload;
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ typhoon_set_rx_mode(dev);
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_TX_ENABLE);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_RX_ENABLE);
+ err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+ if(err < 0)
+ goto error_out;
+
+ tp->card_state = Running;
+ smp_wmb();
+
+ iowrite32(TYPHOON_INTR_ENABLE_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_MASK);
+ typhoon_post_pci_writes(ioaddr);
+
+ return 0;
+
+error_out:
+ typhoon_reset(ioaddr, WaitNoSleep);
+ typhoon_free_rx_rings(tp);
+ typhoon_init_rings(tp);
+ return err;
+}
+
+static int
+typhoon_stop_runtime(struct typhoon *tp, int wait_type)
+{
+ struct typhoon_indexes *indexes = tp->indexes;
+ struct transmit_ring *txLo = &tp->txLoRing;
+ void __iomem *ioaddr = tp->ioaddr;
+ struct cmd_desc xp_cmd;
+ int i;
+
+ /* Disable interrupts early, since we can't schedule a poll
+ * when called with !netif_running(). This will be posted
+ * when we force the posting of the command.
+ */
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_RX_DISABLE);
+ typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+
+ /* Wait 1/2 sec for any outstanding transmits to occur
+ * We'll cleanup after the reset if this times out.
+ */
+ for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
+ if(indexes->txLoCleared == cpu_to_le32(txLo->lastWrite))
+ break;
+ udelay(TYPHOON_UDELAY);
+ }
+
+ if(i == TYPHOON_WAIT_TIMEOUT)
+ netdev_err(tp->dev, "halt timed out waiting for Tx to complete\n");
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_TX_DISABLE);
+ typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+
+ /* save the statistics so when we bring the interface up again,
+ * the values reported to userspace are correct.
+ */
+ tp->card_state = Sleeping;
+ smp_wmb();
+ typhoon_do_get_stats(tp);
+ memcpy(&tp->stats_saved, &tp->stats, sizeof(struct net_device_stats));
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_HALT);
+ typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
+
+ if(typhoon_wait_status(ioaddr, TYPHOON_STATUS_HALTED) < 0)
+ netdev_err(tp->dev, "timed out waiting for 3XP to halt\n");
+
+ if(typhoon_reset(ioaddr, wait_type) < 0) {
+ netdev_err(tp->dev, "unable to reset 3XP\n");
+ return -ETIMEDOUT;
+ }
+
+ /* cleanup any outstanding Tx packets */
+ if(indexes->txLoCleared != cpu_to_le32(txLo->lastWrite)) {
+ indexes->txLoCleared = cpu_to_le32(txLo->lastWrite);
+ typhoon_clean_tx(tp, &tp->txLoRing, &indexes->txLoCleared);
+ }
+
+ return 0;
+}
+
+static void
+typhoon_tx_timeout(struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+
+ if(typhoon_reset(tp->ioaddr, WaitNoSleep) < 0) {
+ netdev_warn(dev, "could not reset in tx timeout\n");
+ goto truly_dead;
+ }
+
+ /* If we ever start using the Hi ring, it will need cleaning too */
+ typhoon_clean_tx(tp, &tp->txLoRing, &tp->indexes->txLoCleared);
+ typhoon_free_rx_rings(tp);
+
+ if(typhoon_start_runtime(tp) < 0) {
+ netdev_err(dev, "could not start runtime in tx timeout\n");
+ goto truly_dead;
+ }
+
+ netif_wake_queue(dev);
+ return;
+
+truly_dead:
+ /* Reset the hardware, and turn off carrier to avoid more timeouts */
+ typhoon_reset(tp->ioaddr, NoWait);
+ netif_carrier_off(dev);
+}
+
+static int
+typhoon_open(struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+ int err;
+
+ err = typhoon_request_firmware(tp);
+ if (err)
+ goto out;
+
+ err = typhoon_wakeup(tp, WaitSleep);
+ if(err < 0) {
+ netdev_err(dev, "unable to wakeup device\n");
+ goto out_sleep;
+ }
+
+ err = request_irq(dev->irq, typhoon_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if(err < 0)
+ goto out_sleep;
+
+ napi_enable(&tp->napi);
+
+ err = typhoon_start_runtime(tp);
+ if(err < 0) {
+ napi_disable(&tp->napi);
+ goto out_irq;
+ }
+
+ netif_start_queue(dev);
+ return 0;
+
+out_irq:
+ free_irq(dev->irq, dev);
+
+out_sleep:
+ if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
+ netdev_err(dev, "unable to reboot into sleep img\n");
+ typhoon_reset(tp->ioaddr, NoWait);
+ goto out;
+ }
+
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0)
+ netdev_err(dev, "unable to go back to sleep\n");
+
+out:
+ return err;
+}
+
+static int
+typhoon_close(struct net_device *dev)
+{
+ struct typhoon *tp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ napi_disable(&tp->napi);
+
+ if(typhoon_stop_runtime(tp, WaitSleep) < 0)
+ netdev_err(dev, "unable to stop runtime\n");
+
+ /* Make sure there is no irq handler running on a different CPU. */
+ free_irq(dev->irq, dev);
+
+ typhoon_free_rx_rings(tp);
+ typhoon_init_rings(tp);
+
+ if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0)
+ netdev_err(dev, "unable to boot sleep image\n");
+
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0)
+ netdev_err(dev, "unable to put card to sleep\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+typhoon_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct typhoon *tp = netdev_priv(dev);
+
+ /* If we're down, resume when we are upped.
+ */
+ if(!netif_running(dev))
+ return 0;
+
+ if(typhoon_wakeup(tp, WaitNoSleep) < 0) {
+ netdev_err(dev, "critical: could not wake up in resume\n");
+ goto reset;
+ }
+
+ if(typhoon_start_runtime(tp) < 0) {
+ netdev_err(dev, "critical: could not start runtime in resume\n");
+ goto reset;
+ }
+
+ netif_device_attach(dev);
+ return 0;
+
+reset:
+ typhoon_reset(tp->ioaddr, NoWait);
+ return -EBUSY;
+}
+
+static int
+typhoon_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct typhoon *tp = netdev_priv(dev);
+ struct cmd_desc xp_cmd;
+
+ /* If we're down, we're already suspended.
+ */
+ if(!netif_running(dev))
+ return 0;
+
+ /* TYPHOON_OFFLOAD_VLAN is always on now, so this doesn't work */
+ if(tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
+ netdev_warn(dev, "cannot do WAKE_MAGIC with VLAN offloading\n");
+
+ netif_device_detach(dev);
+
+ if(typhoon_stop_runtime(tp, WaitNoSleep) < 0) {
+ netdev_err(dev, "unable to stop runtime\n");
+ goto need_resume;
+ }
+
+ typhoon_free_rx_rings(tp);
+ typhoon_init_rings(tp);
+
+ if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
+ netdev_err(dev, "unable to boot sleep image\n");
+ goto need_resume;
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAC_ADDRESS);
+ xp_cmd.parm1 = cpu_to_le16(ntohs(*(__be16 *)&dev->dev_addr[0]));
+ xp_cmd.parm2 = cpu_to_le32(ntohl(*(__be32 *)&dev->dev_addr[2]));
+ if(typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL) < 0) {
+ netdev_err(dev, "unable to set mac address in suspend\n");
+ goto need_resume;
+ }
+
+ INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
+ xp_cmd.parm1 = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
+ if(typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL) < 0) {
+ netdev_err(dev, "unable to set rx filter in suspend\n");
+ goto need_resume;
+ }
+
+ if(typhoon_sleep(tp, pci_choose_state(pdev, state), tp->wol_events) < 0) {
+ netdev_err(dev, "unable to put card to sleep\n");
+ goto need_resume;
+ }
+
+ return 0;
+
+need_resume:
+ typhoon_resume(pdev);
+ return -EBUSY;
+}
+#endif
+
+static int
+typhoon_test_mmio(struct pci_dev *pdev)
+{
+ void __iomem *ioaddr = pci_iomap(pdev, 1, 128);
+ int mode = 0;
+ u32 val;
+
+ if(!ioaddr)
+ goto out;
+
+ if(ioread32(ioaddr + TYPHOON_REG_STATUS) !=
+ TYPHOON_STATUS_WAITING_FOR_HOST)
+ goto out_unmap;
+
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
+
+ /* Ok, see if we can change our interrupt status register by
+ * sending ourselves an interrupt. If so, then MMIO works.
+ * The 50usec delay is arbitrary -- it could probably be smaller.
+ */
+ val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ if((val & TYPHOON_INTR_SELF) == 0) {
+ iowrite32(1, ioaddr + TYPHOON_REG_SELF_INTERRUPT);
+ ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ udelay(50);
+ val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+ if(val & TYPHOON_INTR_SELF)
+ mode = 1;
+ }
+
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
+ iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
+ iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
+ ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
+
+out_unmap:
+ pci_iounmap(pdev, ioaddr);
+
+out:
+ if(!mode)
+ pr_info("%s: falling back to port IO\n", pci_name(pdev));
+ return mode;
+}
+
+static const struct net_device_ops typhoon_netdev_ops = {
+ .ndo_open = typhoon_open,
+ .ndo_stop = typhoon_close,
+ .ndo_start_xmit = typhoon_start_tx,
+ .ndo_set_rx_mode = typhoon_set_rx_mode,
+ .ndo_tx_timeout = typhoon_tx_timeout,
+ .ndo_get_stats = typhoon_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int
+typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ struct typhoon *tp;
+ int card_id = (int) ent->driver_data;
+ void __iomem *ioaddr;
+ void *shared;
+ dma_addr_t shared_dma;
+ struct cmd_desc xp_cmd;
+ struct resp_desc xp_resp[3];
+ int err = 0;
+ const char *err_msg;
+
+ dev = alloc_etherdev(sizeof(*tp));
+ if(dev == NULL) {
+ err_msg = "unable to alloc new net device";
+ err = -ENOMEM;
+ goto error_out;
+ }
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = pci_enable_device(pdev);
+ if(err < 0) {
+ err_msg = "unable to enable device";
+ goto error_out_dev;
+ }
+
+ err = pci_set_mwi(pdev);
+ if(err < 0) {
+ err_msg = "unable to set MWI";
+ goto error_out_disable;
+ }
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if(err < 0) {
+ err_msg = "No usable DMA configuration";
+ goto error_out_mwi;
+ }
+
+ /* sanity checks on IO and MMIO BARs
+ */
+ if(!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
+ err_msg = "region #1 not a PCI IO resource, aborting";
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
+ if(pci_resource_len(pdev, 0) < 128) {
+ err_msg = "Invalid PCI IO region size, aborting";
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
+ if(!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
+ err_msg = "region #1 not a PCI MMIO resource, aborting";
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
+ if(pci_resource_len(pdev, 1) < 128) {
+ err_msg = "Invalid PCI MMIO region size, aborting";
+ err = -ENODEV;
+ goto error_out_mwi;
+ }
+
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if(err < 0) {
+ err_msg = "could not request regions";
+ goto error_out_mwi;
+ }
+
+ /* map our registers
+ */
+ if(use_mmio != 0 && use_mmio != 1)
+ use_mmio = typhoon_test_mmio(pdev);
+
+ ioaddr = pci_iomap(pdev, use_mmio, 128);
+ if (!ioaddr) {
+ err_msg = "cannot remap registers, aborting";
+ err = -EIO;
+ goto error_out_regions;
+ }
+
+ /* allocate pci dma space for rx and tx descriptor rings
+ */
+ shared = pci_alloc_consistent(pdev, sizeof(struct typhoon_shared),
+ &shared_dma);
+ if(!shared) {
+ err_msg = "could not allocate DMA memory";
+ err = -ENOMEM;
+ goto error_out_remap;
+ }
+
+ dev->irq = pdev->irq;
+ tp = netdev_priv(dev);
+ tp->shared = shared;
+ tp->shared_dma = shared_dma;
+ tp->pdev = pdev;
+ tp->tx_pdev = pdev;
+ tp->ioaddr = ioaddr;
+ tp->tx_ioaddr = ioaddr;
+ tp->dev = dev;
+
+ /* Init sequence:
+ * 1) Reset the adapter to clear any bad juju
+ * 2) Reload the sleep image
+ * 3) Boot the sleep image
+ * 4) Get the hardware address.
+ * 5) Put the card to sleep.
+ */
+ if (typhoon_reset(ioaddr, WaitSleep) < 0) {
+ err_msg = "could not reset 3XP";
+ err = -EIO;
+ goto error_out_dma;
+ }
+
+ /* Now that we've reset the 3XP and are sure it's not going to
+ * write all over memory, enable bus mastering, and save our
+ * state for resuming after a suspend.
+ */
+ pci_set_master(pdev);
+ pci_save_state(pdev);
+
+ typhoon_init_interface(tp);
+ typhoon_init_rings(tp);
+
+ if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
+ err_msg = "cannot boot 3XP sleep image";
+ err = -EIO;
+ goto error_out_reset;
+ }
+
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_MAC_ADDRESS);
+ if(typhoon_issue_command(tp, 1, &xp_cmd, 1, xp_resp) < 0) {
+ err_msg = "cannot read MAC address";
+ err = -EIO;
+ goto error_out_reset;
+ }
+
+ *(__be16 *)&dev->dev_addr[0] = htons(le16_to_cpu(xp_resp[0].parm1));
+ *(__be32 *)&dev->dev_addr[2] = htonl(le32_to_cpu(xp_resp[0].parm2));
+
+ if(!is_valid_ether_addr(dev->dev_addr)) {
+ err_msg = "Could not obtain valid ethernet address, aborting";
+ goto error_out_reset;
+ }
+
+ /* Read the Sleep Image version last, so the response is valid
+ * later when we print out the version reported.
+ */
+ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
+ if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
+ err_msg = "Could not get Sleep Image version";
+ goto error_out_reset;
+ }
+
+ tp->capabilities = typhoon_card_info[card_id].capabilities;
+ tp->xcvr_select = TYPHOON_XCVR_AUTONEG;
+
+ /* Typhoon 1.0 Sleep Images return one response descriptor to the
+ * READ_VERSIONS command. Those versions are OK after waking up
+ * from sleep without needing a reset. Typhoon 1.1+ Sleep Images
+ * seem to need a little extra help to get started. Since we don't
+ * know how to nudge it along, just kick it.
+ */
+ if(xp_resp[0].numDesc != 0)
+ tp->capabilities |= TYPHOON_WAKEUP_NEEDS_RESET;
+
+ if(typhoon_sleep(tp, PCI_D3hot, 0) < 0) {
+ err_msg = "cannot put adapter to sleep";
+ err = -EIO;
+ goto error_out_reset;
+ }
+
+ /* The chip-specific entries in the device structure. */
+ dev->netdev_ops = &typhoon_netdev_ops;
+ netif_napi_add(dev, &tp->napi, typhoon_poll, 16);
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ dev->ethtool_ops = &typhoon_ethtool_ops;
+
+ /* We can handle scatter gather, up to 16 entries, and
+ * we can do IP checksumming (only version 4, doh...)
+ *
+ * There's no way to turn off the RX VLAN offloading and stripping
+ * on the current 3XP firmware -- it does not respect the offload
+ * settings -- so we only allow the user to toggle the TX processing.
+ */
+ dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_CTAG_TX;
+ dev->features = dev->hw_features |
+ NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM;
+
+ if(register_netdev(dev) < 0) {
+ err_msg = "unable to register netdev";
+ goto error_out_reset;
+ }
+
+ pci_set_drvdata(pdev, dev);
+
+ netdev_info(dev, "%s at %s 0x%llx, %pM\n",
+ typhoon_card_info[card_id].name,
+ use_mmio ? "MMIO" : "IO",
+ (unsigned long long)pci_resource_start(pdev, use_mmio),
+ dev->dev_addr);
+
+ /* xp_resp still contains the response to the READ_VERSIONS command.
+ * For debugging, let the user know what version he has.
+ */
+ if(xp_resp[0].numDesc == 0) {
+ /* This is the Typhoon 1.0 type Sleep Image, last 16 bits
+ * of version is Month/Day of build.
+ */
+ u16 monthday = le32_to_cpu(xp_resp[0].parm2) & 0xffff;
+ netdev_info(dev, "Typhoon 1.0 Sleep Image built %02u/%02u/2000\n",
+ monthday >> 8, monthday & 0xff);
+ } else if(xp_resp[0].numDesc == 2) {
+ /* This is the Typhoon 1.1+ type Sleep Image
+ */
+ u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2);
+ u8 *ver_string = (u8 *) &xp_resp[1];
+ ver_string[25] = 0;
+ netdev_info(dev, "Typhoon 1.1+ Sleep Image version %02x.%03x.%03x %s\n",
+ sleep_ver >> 24, (sleep_ver >> 12) & 0xfff,
+ sleep_ver & 0xfff, ver_string);
+ } else {
+ netdev_warn(dev, "Unknown Sleep Image version (%u:%04x)\n",
+ xp_resp[0].numDesc, le32_to_cpu(xp_resp[0].parm2));
+ }
+
+ return 0;
+
+error_out_reset:
+ typhoon_reset(ioaddr, NoWait);
+
+error_out_dma:
+ pci_free_consistent(pdev, sizeof(struct typhoon_shared),
+ shared, shared_dma);
+error_out_remap:
+ pci_iounmap(pdev, ioaddr);
+error_out_regions:
+ pci_release_regions(pdev);
+error_out_mwi:
+ pci_clear_mwi(pdev);
+error_out_disable:
+ pci_disable_device(pdev);
+error_out_dev:
+ free_netdev(dev);
+error_out:
+ pr_err("%s: %s\n", pci_name(pdev), err_msg);
+ return err;
+}
+
+static void
+typhoon_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct typhoon *tp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ typhoon_reset(tp->ioaddr, NoWait);
+ pci_iounmap(pdev, tp->ioaddr);
+ pci_free_consistent(pdev, sizeof(struct typhoon_shared),
+ tp->shared, tp->shared_dma);
+ pci_release_regions(pdev);
+ pci_clear_mwi(pdev);
+ pci_disable_device(pdev);
+ free_netdev(dev);
+}
+
+static struct pci_driver typhoon_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = typhoon_pci_tbl,
+ .probe = typhoon_init_one,
+ .remove = typhoon_remove_one,
+#ifdef CONFIG_PM
+ .suspend = typhoon_suspend,
+ .resume = typhoon_resume,
+#endif
+};
+
+static int __init
+typhoon_init(void)
+{
+ return pci_register_driver(&typhoon_driver);
+}
+
+static void __exit
+typhoon_cleanup(void)
+{
+ release_firmware(typhoon_fw);
+ pci_unregister_driver(&typhoon_driver);
+}
+
+module_init(typhoon_init);
+module_exit(typhoon_cleanup);
diff --git a/drivers/net/ethernet/3com/typhoon.h b/drivers/net/ethernet/3com/typhoon.h
new file mode 100644
index 00000000000..88187fc84aa
--- /dev/null
+++ b/drivers/net/ethernet/3com/typhoon.h
@@ -0,0 +1,624 @@
+/* typhoon.h: chip info for the 3Com 3CR990 family of controllers */
+/*
+ Written 2002-2003 by David Dillow <dave@thedillows.org>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This software is available on a public web site. It may enable
+ cryptographic capabilities of the 3Com hardware, and may be
+ exported from the United States under License Exception "TSU"
+ pursuant to 15 C.F.R. Section 740.13(e).
+
+ This work was funded by the National Library of Medicine under
+ the Department of Energy project number 0274DD06D1 and NLM project
+ number Y1-LM-2015-01.
+*/
+
+/* All Typhoon ring positions are specificed in bytes, and point to the
+ * first "clean" entry in the ring -- ie the next entry we use for whatever
+ * purpose.
+ */
+
+/* The Typhoon basic ring
+ * ringBase: where this ring lives (our virtual address)
+ * lastWrite: the next entry we'll use
+ */
+struct basic_ring {
+ u8 *ringBase;
+ u32 lastWrite;
+};
+
+/* The Typoon transmit ring -- same as a basic ring, plus:
+ * lastRead: where we're at in regard to cleaning up the ring
+ * writeRegister: register to use for writing (different for Hi & Lo rings)
+ */
+struct transmit_ring {
+ u8 *ringBase;
+ u32 lastWrite;
+ u32 lastRead;
+ int writeRegister;
+};
+
+/* The host<->Typhoon ring index structure
+ * This indicates the current positions in the rings
+ *
+ * All values must be in little endian format for the 3XP
+ *
+ * rxHiCleared: entry we've cleared to in the Hi receive ring
+ * rxLoCleared: entry we've cleared to in the Lo receive ring
+ * rxBuffReady: next entry we'll put a free buffer in
+ * respCleared: entry we've cleared to in the response ring
+ *
+ * txLoCleared: entry the NIC has cleared to in the Lo transmit ring
+ * txHiCleared: entry the NIC has cleared to in the Hi transmit ring
+ * rxLoReady: entry the NIC has filled to in the Lo receive ring
+ * rxBuffCleared: entry the NIC has cleared in the free buffer ring
+ * cmdCleared: entry the NIC has cleared in the command ring
+ * respReady: entry the NIC has filled to in the response ring
+ * rxHiReady: entry the NIC has filled to in the Hi receive ring
+ */
+struct typhoon_indexes {
+ /* The first four are written by the host, and read by the NIC */
+ volatile __le32 rxHiCleared;
+ volatile __le32 rxLoCleared;
+ volatile __le32 rxBuffReady;
+ volatile __le32 respCleared;
+
+ /* The remaining are written by the NIC, and read by the host */
+ volatile __le32 txLoCleared;
+ volatile __le32 txHiCleared;
+ volatile __le32 rxLoReady;
+ volatile __le32 rxBuffCleared;
+ volatile __le32 cmdCleared;
+ volatile __le32 respReady;
+ volatile __le32 rxHiReady;
+} __packed;
+
+/* The host<->Typhoon interface
+ * Our means of communicating where things are
+ *
+ * All values must be in little endian format for the 3XP
+ *
+ * ringIndex: 64 bit bus address of the index structure
+ * txLoAddr: 64 bit bus address of the Lo transmit ring
+ * txLoSize: size (in bytes) of the Lo transmit ring
+ * txHi*: as above for the Hi priority transmit ring
+ * rxLo*: as above for the Lo priority receive ring
+ * rxBuff*: as above for the free buffer ring
+ * cmd*: as above for the command ring
+ * resp*: as above for the response ring
+ * zeroAddr: 64 bit bus address of a zero word (for DMA)
+ * rxHi*: as above for the Hi Priority receive ring
+ *
+ * While there is room for 64 bit addresses, current versions of the 3XP
+ * only do 32 bit addresses, so the *Hi for each of the above will always
+ * be zero.
+ */
+struct typhoon_interface {
+ __le32 ringIndex;
+ __le32 ringIndexHi;
+ __le32 txLoAddr;
+ __le32 txLoAddrHi;
+ __le32 txLoSize;
+ __le32 txHiAddr;
+ __le32 txHiAddrHi;
+ __le32 txHiSize;
+ __le32 rxLoAddr;
+ __le32 rxLoAddrHi;
+ __le32 rxLoSize;
+ __le32 rxBuffAddr;
+ __le32 rxBuffAddrHi;
+ __le32 rxBuffSize;
+ __le32 cmdAddr;
+ __le32 cmdAddrHi;
+ __le32 cmdSize;
+ __le32 respAddr;
+ __le32 respAddrHi;
+ __le32 respSize;
+ __le32 zeroAddr;
+ __le32 zeroAddrHi;
+ __le32 rxHiAddr;
+ __le32 rxHiAddrHi;
+ __le32 rxHiSize;
+} __packed;
+
+/* The Typhoon transmit/fragment descriptor
+ *
+ * A packet is described by a packet descriptor, followed by option descriptors,
+ * if any, then one or more fragment descriptors.
+ *
+ * Packet descriptor:
+ * flags: Descriptor type
+ * len:i zero, or length of this packet
+ * addr*: 8 bytes of opaque data to the firmware -- for skb pointer
+ * processFlags: Determine offload tasks to perform on this packet.
+ *
+ * Fragment descriptor:
+ * flags: Descriptor type
+ * len:i length of this fragment
+ * addr: low bytes of DMA address for this part of the packet
+ * addrHi: hi bytes of DMA address for this part of the packet
+ * processFlags: must be zero
+ *
+ * TYPHOON_DESC_VALID is not mentioned in their docs, but their Linux
+ * driver uses it.
+ */
+struct tx_desc {
+ u8 flags;
+#define TYPHOON_TYPE_MASK 0x07
+#define TYPHOON_FRAG_DESC 0x00
+#define TYPHOON_TX_DESC 0x01
+#define TYPHOON_CMD_DESC 0x02
+#define TYPHOON_OPT_DESC 0x03
+#define TYPHOON_RX_DESC 0x04
+#define TYPHOON_RESP_DESC 0x05
+#define TYPHOON_OPT_TYPE_MASK 0xf0
+#define TYPHOON_OPT_IPSEC 0x00
+#define TYPHOON_OPT_TCP_SEG 0x10
+#define TYPHOON_CMD_RESPOND 0x40
+#define TYPHOON_RESP_ERROR 0x40
+#define TYPHOON_RX_ERROR 0x40
+#define TYPHOON_DESC_VALID 0x80
+ u8 numDesc;
+ __le16 len;
+ union {
+ struct {
+ __le32 addr;
+ __le32 addrHi;
+ } frag;
+ u64 tx_addr; /* opaque for hardware, for TX_DESC */
+ };
+ __le32 processFlags;
+#define TYPHOON_TX_PF_NO_CRC cpu_to_le32(0x00000001)
+#define TYPHOON_TX_PF_IP_CHKSUM cpu_to_le32(0x00000002)
+#define TYPHOON_TX_PF_TCP_CHKSUM cpu_to_le32(0x00000004)
+#define TYPHOON_TX_PF_TCP_SEGMENT cpu_to_le32(0x00000008)
+#define TYPHOON_TX_PF_INSERT_VLAN cpu_to_le32(0x00000010)
+#define TYPHOON_TX_PF_IPSEC cpu_to_le32(0x00000020)
+#define TYPHOON_TX_PF_VLAN_PRIORITY cpu_to_le32(0x00000040)
+#define TYPHOON_TX_PF_UDP_CHKSUM cpu_to_le32(0x00000080)
+#define TYPHOON_TX_PF_PAD_FRAME cpu_to_le32(0x00000100)
+#define TYPHOON_TX_PF_RESERVED cpu_to_le32(0x00000e00)
+#define TYPHOON_TX_PF_VLAN_MASK cpu_to_le32(0x0ffff000)
+#define TYPHOON_TX_PF_INTERNAL cpu_to_le32(0xf0000000)
+#define TYPHOON_TX_PF_VLAN_TAG_SHIFT 12
+} __packed;
+
+/* The TCP Segmentation offload option descriptor
+ *
+ * flags: descriptor type
+ * numDesc: must be 1
+ * mss_flags: bits 0-11 (little endian) are MSS, 12 is first TSO descriptor
+ * 13 is list TSO descriptor, set both if only one TSO
+ * respAddrLo: low bytes of address of the bytesTx field of this descriptor
+ * bytesTx: total number of bytes in this TSO request
+ * status: 0 on completion
+ */
+struct tcpopt_desc {
+ u8 flags;
+ u8 numDesc;
+ __le16 mss_flags;
+#define TYPHOON_TSO_FIRST cpu_to_le16(0x1000)
+#define TYPHOON_TSO_LAST cpu_to_le16(0x2000)
+ __le32 respAddrLo;
+ __le32 bytesTx;
+ __le32 status;
+} __packed;
+
+/* The IPSEC Offload descriptor
+ *
+ * flags: descriptor type
+ * numDesc: must be 1
+ * ipsecFlags: bit 0: 0 -- generate IV, 1 -- use supplied IV
+ * sa1, sa2: Security Association IDs for this packet
+ * reserved: set to 0
+ */
+struct ipsec_desc {
+ u8 flags;
+ u8 numDesc;
+ __le16 ipsecFlags;
+#define TYPHOON_IPSEC_GEN_IV cpu_to_le16(0x0000)
+#define TYPHOON_IPSEC_USE_IV cpu_to_le16(0x0001)
+ __le32 sa1;
+ __le32 sa2;
+ __le32 reserved;
+} __packed;
+
+/* The Typhoon receive descriptor (Updated by NIC)
+ *
+ * flags: Descriptor type, error indication
+ * numDesc: Always zero
+ * frameLen: the size of the packet received
+ * addr: low 32 bytes of the virtual addr passed in for this buffer
+ * addrHi: high 32 bytes of the virtual addr passed in for this buffer
+ * rxStatus: Error if set in flags, otherwise result of offload processing
+ * filterResults: results of filtering on packet, not used
+ * ipsecResults: Results of IPSEC processing
+ * vlanTag: the 801.2q TCI from the packet
+ */
+struct rx_desc {
+ u8 flags;
+ u8 numDesc;
+ __le16 frameLen;
+ u32 addr; /* opaque, comes from virtAddr */
+ u32 addrHi; /* opaque, comes from virtAddrHi */
+ __le32 rxStatus;
+#define TYPHOON_RX_ERR_INTERNAL cpu_to_le32(0x00000000)
+#define TYPHOON_RX_ERR_FIFO_UNDERRUN cpu_to_le32(0x00000001)
+#define TYPHOON_RX_ERR_BAD_SSD cpu_to_le32(0x00000002)
+#define TYPHOON_RX_ERR_RUNT cpu_to_le32(0x00000003)
+#define TYPHOON_RX_ERR_CRC cpu_to_le32(0x00000004)
+#define TYPHOON_RX_ERR_OVERSIZE cpu_to_le32(0x00000005)
+#define TYPHOON_RX_ERR_ALIGN cpu_to_le32(0x00000006)
+#define TYPHOON_RX_ERR_DRIBBLE cpu_to_le32(0x00000007)
+#define TYPHOON_RX_PROTO_MASK cpu_to_le32(0x00000003)
+#define TYPHOON_RX_PROTO_UNKNOWN cpu_to_le32(0x00000000)
+#define TYPHOON_RX_PROTO_IP cpu_to_le32(0x00000001)
+#define TYPHOON_RX_PROTO_IPX cpu_to_le32(0x00000002)
+#define TYPHOON_RX_VLAN cpu_to_le32(0x00000004)
+#define TYPHOON_RX_IP_FRAG cpu_to_le32(0x00000008)
+#define TYPHOON_RX_IPSEC cpu_to_le32(0x00000010)
+#define TYPHOON_RX_IP_CHK_FAIL cpu_to_le32(0x00000020)
+#define TYPHOON_RX_TCP_CHK_FAIL cpu_to_le32(0x00000040)
+#define TYPHOON_RX_UDP_CHK_FAIL cpu_to_le32(0x00000080)
+#define TYPHOON_RX_IP_CHK_GOOD cpu_to_le32(0x00000100)
+#define TYPHOON_RX_TCP_CHK_GOOD cpu_to_le32(0x00000200)
+#define TYPHOON_RX_UDP_CHK_GOOD cpu_to_le32(0x00000400)
+ __le16 filterResults;
+#define TYPHOON_RX_FILTER_MASK cpu_to_le16(0x7fff)
+#define TYPHOON_RX_FILTERED cpu_to_le16(0x8000)
+ __le16 ipsecResults;
+#define TYPHOON_RX_OUTER_AH_GOOD cpu_to_le16(0x0001)
+#define TYPHOON_RX_OUTER_ESP_GOOD cpu_to_le16(0x0002)
+#define TYPHOON_RX_INNER_AH_GOOD cpu_to_le16(0x0004)
+#define TYPHOON_RX_INNER_ESP_GOOD cpu_to_le16(0x0008)
+#define TYPHOON_RX_OUTER_AH_FAIL cpu_to_le16(0x0010)
+#define TYPHOON_RX_OUTER_ESP_FAIL cpu_to_le16(0x0020)
+#define TYPHOON_RX_INNER_AH_FAIL cpu_to_le16(0x0040)
+#define TYPHOON_RX_INNER_ESP_FAIL cpu_to_le16(0x0080)
+#define TYPHOON_RX_UNKNOWN_SA cpu_to_le16(0x0100)
+#define TYPHOON_RX_ESP_FORMAT_ERR cpu_to_le16(0x0200)
+ __be32 vlanTag;
+} __packed;
+
+/* The Typhoon free buffer descriptor, used to give a buffer to the NIC
+ *
+ * physAddr: low 32 bits of the bus address of the buffer
+ * physAddrHi: high 32 bits of the bus address of the buffer, always zero
+ * virtAddr: low 32 bits of the skb address
+ * virtAddrHi: high 32 bits of the skb address, always zero
+ *
+ * the virt* address is basically two 32 bit cookies, just passed back
+ * from the NIC
+ */
+struct rx_free {
+ __le32 physAddr;
+ __le32 physAddrHi;
+ u32 virtAddr;
+ u32 virtAddrHi;
+} __packed;
+
+/* The Typhoon command descriptor, used for commands and responses
+ *
+ * flags: descriptor type
+ * numDesc: number of descriptors following in this command/response,
+ * ie, zero for a one descriptor command
+ * cmd: the command
+ * seqNo: sequence number (unused)
+ * parm1: use varies by command
+ * parm2: use varies by command
+ * parm3: use varies by command
+ */
+struct cmd_desc {
+ u8 flags;
+ u8 numDesc;
+ __le16 cmd;
+#define TYPHOON_CMD_TX_ENABLE cpu_to_le16(0x0001)
+#define TYPHOON_CMD_TX_DISABLE cpu_to_le16(0x0002)
+#define TYPHOON_CMD_RX_ENABLE cpu_to_le16(0x0003)
+#define TYPHOON_CMD_RX_DISABLE cpu_to_le16(0x0004)
+#define TYPHOON_CMD_SET_RX_FILTER cpu_to_le16(0x0005)
+#define TYPHOON_CMD_READ_STATS cpu_to_le16(0x0007)
+#define TYPHOON_CMD_XCVR_SELECT cpu_to_le16(0x0013)
+#define TYPHOON_CMD_SET_MAX_PKT_SIZE cpu_to_le16(0x001a)
+#define TYPHOON_CMD_READ_MEDIA_STATUS cpu_to_le16(0x001b)
+#define TYPHOON_CMD_GOTO_SLEEP cpu_to_le16(0x0023)
+#define TYPHOON_CMD_SET_MULTICAST_HASH cpu_to_le16(0x0025)
+#define TYPHOON_CMD_SET_MAC_ADDRESS cpu_to_le16(0x0026)
+#define TYPHOON_CMD_READ_MAC_ADDRESS cpu_to_le16(0x0027)
+#define TYPHOON_CMD_VLAN_TYPE_WRITE cpu_to_le16(0x002b)
+#define TYPHOON_CMD_CREATE_SA cpu_to_le16(0x0034)
+#define TYPHOON_CMD_DELETE_SA cpu_to_le16(0x0035)
+#define TYPHOON_CMD_READ_VERSIONS cpu_to_le16(0x0043)
+#define TYPHOON_CMD_IRQ_COALESCE_CTRL cpu_to_le16(0x0045)
+#define TYPHOON_CMD_ENABLE_WAKE_EVENTS cpu_to_le16(0x0049)
+#define TYPHOON_CMD_SET_OFFLOAD_TASKS cpu_to_le16(0x004f)
+#define TYPHOON_CMD_HELLO_RESP cpu_to_le16(0x0057)
+#define TYPHOON_CMD_HALT cpu_to_le16(0x005d)
+#define TYPHOON_CMD_READ_IPSEC_INFO cpu_to_le16(0x005e)
+#define TYPHOON_CMD_GET_IPSEC_ENABLE cpu_to_le16(0x0067)
+#define TYPHOON_CMD_GET_CMD_LVL cpu_to_le16(0x0069)
+ u16 seqNo;
+ __le16 parm1;
+ __le32 parm2;
+ __le32 parm3;
+} __packed;
+
+/* The Typhoon response descriptor, see command descriptor for details
+ */
+struct resp_desc {
+ u8 flags;
+ u8 numDesc;
+ __le16 cmd;
+ __le16 seqNo;
+ __le16 parm1;
+ __le32 parm2;
+ __le32 parm3;
+} __packed;
+
+#define INIT_COMMAND_NO_RESPONSE(x, command) \
+ do { struct cmd_desc *_ptr = (x); \
+ memset(_ptr, 0, sizeof(struct cmd_desc)); \
+ _ptr->flags = TYPHOON_CMD_DESC | TYPHOON_DESC_VALID; \
+ _ptr->cmd = command; \
+ } while(0)
+
+/* We set seqNo to 1 if we're expecting a response from this command */
+#define INIT_COMMAND_WITH_RESPONSE(x, command) \
+ do { struct cmd_desc *_ptr = (x); \
+ memset(_ptr, 0, sizeof(struct cmd_desc)); \
+ _ptr->flags = TYPHOON_CMD_RESPOND | TYPHOON_CMD_DESC; \
+ _ptr->flags |= TYPHOON_DESC_VALID; \
+ _ptr->cmd = command; \
+ _ptr->seqNo = 1; \
+ } while(0)
+
+/* TYPHOON_CMD_SET_RX_FILTER filter bits (cmd.parm1)
+ */
+#define TYPHOON_RX_FILTER_DIRECTED cpu_to_le16(0x0001)
+#define TYPHOON_RX_FILTER_ALL_MCAST cpu_to_le16(0x0002)
+#define TYPHOON_RX_FILTER_BROADCAST cpu_to_le16(0x0004)
+#define TYPHOON_RX_FILTER_PROMISCOUS cpu_to_le16(0x0008)
+#define TYPHOON_RX_FILTER_MCAST_HASH cpu_to_le16(0x0010)
+
+/* TYPHOON_CMD_READ_STATS response format
+ */
+struct stats_resp {
+ u8 flags;
+ u8 numDesc;
+ __le16 cmd;
+ __le16 seqNo;
+ __le16 unused;
+ __le32 txPackets;
+ __le64 txBytes;
+ __le32 txDeferred;
+ __le32 txLateCollisions;
+ __le32 txCollisions;
+ __le32 txCarrierLost;
+ __le32 txMultipleCollisions;
+ __le32 txExcessiveCollisions;
+ __le32 txFifoUnderruns;
+ __le32 txMulticastTxOverflows;
+ __le32 txFiltered;
+ __le32 rxPacketsGood;
+ __le64 rxBytesGood;
+ __le32 rxFifoOverruns;
+ __le32 BadSSD;
+ __le32 rxCrcErrors;
+ __le32 rxOversized;
+ __le32 rxBroadcast;
+ __le32 rxMulticast;
+ __le32 rxOverflow;
+ __le32 rxFiltered;
+ __le32 linkStatus;
+#define TYPHOON_LINK_STAT_MASK cpu_to_le32(0x00000001)
+#define TYPHOON_LINK_GOOD cpu_to_le32(0x00000001)
+#define TYPHOON_LINK_BAD cpu_to_le32(0x00000000)
+#define TYPHOON_LINK_SPEED_MASK cpu_to_le32(0x00000002)
+#define TYPHOON_LINK_100MBPS cpu_to_le32(0x00000002)
+#define TYPHOON_LINK_10MBPS cpu_to_le32(0x00000000)
+#define TYPHOON_LINK_DUPLEX_MASK cpu_to_le32(0x00000004)
+#define TYPHOON_LINK_FULL_DUPLEX cpu_to_le32(0x00000004)
+#define TYPHOON_LINK_HALF_DUPLEX cpu_to_le32(0x00000000)
+ __le32 unused2;
+ __le32 unused3;
+} __packed;
+
+/* TYPHOON_CMD_XCVR_SELECT xcvr values (resp.parm1)
+ */
+#define TYPHOON_XCVR_10HALF cpu_to_le16(0x0000)
+#define TYPHOON_XCVR_10FULL cpu_to_le16(0x0001)
+#define TYPHOON_XCVR_100HALF cpu_to_le16(0x0002)
+#define TYPHOON_XCVR_100FULL cpu_to_le16(0x0003)
+#define TYPHOON_XCVR_AUTONEG cpu_to_le16(0x0004)
+
+/* TYPHOON_CMD_READ_MEDIA_STATUS (resp.parm1)
+ */
+#define TYPHOON_MEDIA_STAT_CRC_STRIP_DISABLE cpu_to_le16(0x0004)
+#define TYPHOON_MEDIA_STAT_COLLISION_DETECT cpu_to_le16(0x0010)
+#define TYPHOON_MEDIA_STAT_CARRIER_SENSE cpu_to_le16(0x0020)
+#define TYPHOON_MEDIA_STAT_POLARITY_REV cpu_to_le16(0x0400)
+#define TYPHOON_MEDIA_STAT_NO_LINK cpu_to_le16(0x0800)
+
+/* TYPHOON_CMD_SET_MULTICAST_HASH enable values (cmd.parm1)
+ */
+#define TYPHOON_MCAST_HASH_DISABLE cpu_to_le16(0x0000)
+#define TYPHOON_MCAST_HASH_ENABLE cpu_to_le16(0x0001)
+#define TYPHOON_MCAST_HASH_SET cpu_to_le16(0x0002)
+
+/* TYPHOON_CMD_CREATE_SA descriptor and settings
+ */
+struct sa_descriptor {
+ u8 flags;
+ u8 numDesc;
+ u16 cmd;
+ u16 seqNo;
+ u16 mode;
+#define TYPHOON_SA_MODE_NULL cpu_to_le16(0x0000)
+#define TYPHOON_SA_MODE_AH cpu_to_le16(0x0001)
+#define TYPHOON_SA_MODE_ESP cpu_to_le16(0x0002)
+ u8 hashFlags;
+#define TYPHOON_SA_HASH_ENABLE 0x01
+#define TYPHOON_SA_HASH_SHA1 0x02
+#define TYPHOON_SA_HASH_MD5 0x04
+ u8 direction;
+#define TYPHOON_SA_DIR_RX 0x00
+#define TYPHOON_SA_DIR_TX 0x01
+ u8 encryptionFlags;
+#define TYPHOON_SA_ENCRYPT_ENABLE 0x01
+#define TYPHOON_SA_ENCRYPT_DES 0x02
+#define TYPHOON_SA_ENCRYPT_3DES 0x00
+#define TYPHOON_SA_ENCRYPT_3DES_2KEY 0x00
+#define TYPHOON_SA_ENCRYPT_3DES_3KEY 0x04
+#define TYPHOON_SA_ENCRYPT_CBC 0x08
+#define TYPHOON_SA_ENCRYPT_ECB 0x00
+ u8 specifyIndex;
+#define TYPHOON_SA_SPECIFY_INDEX 0x01
+#define TYPHOON_SA_GENERATE_INDEX 0x00
+ u32 SPI;
+ u32 destAddr;
+ u32 destMask;
+ u8 integKey[20];
+ u8 confKey[24];
+ u32 index;
+ u32 unused;
+ u32 unused2;
+} __packed;
+
+/* TYPHOON_CMD_SET_OFFLOAD_TASKS bits (cmd.parm2 (Tx) & cmd.parm3 (Rx))
+ * This is all for IPv4.
+ */
+#define TYPHOON_OFFLOAD_TCP_CHKSUM cpu_to_le32(0x00000002)
+#define TYPHOON_OFFLOAD_UDP_CHKSUM cpu_to_le32(0x00000004)
+#define TYPHOON_OFFLOAD_IP_CHKSUM cpu_to_le32(0x00000008)
+#define TYPHOON_OFFLOAD_IPSEC cpu_to_le32(0x00000010)
+#define TYPHOON_OFFLOAD_BCAST_THROTTLE cpu_to_le32(0x00000020)
+#define TYPHOON_OFFLOAD_DHCP_PREVENT cpu_to_le32(0x00000040)
+#define TYPHOON_OFFLOAD_VLAN cpu_to_le32(0x00000080)
+#define TYPHOON_OFFLOAD_FILTERING cpu_to_le32(0x00000100)
+#define TYPHOON_OFFLOAD_TCP_SEGMENT cpu_to_le32(0x00000200)
+
+/* TYPHOON_CMD_ENABLE_WAKE_EVENTS bits (cmd.parm1)
+ */
+#define TYPHOON_WAKE_MAGIC_PKT cpu_to_le16(0x01)
+#define TYPHOON_WAKE_LINK_EVENT cpu_to_le16(0x02)
+#define TYPHOON_WAKE_ICMP_ECHO cpu_to_le16(0x04)
+#define TYPHOON_WAKE_ARP cpu_to_le16(0x08)
+
+/* These are used to load the firmware image on the NIC
+ */
+struct typhoon_file_header {
+ u8 tag[8];
+ __le32 version;
+ __le32 numSections;
+ __le32 startAddr;
+ __le32 hmacDigest[5];
+} __packed;
+
+struct typhoon_section_header {
+ __le32 len;
+ u16 checksum;
+ u16 reserved;
+ __le32 startAddr;
+} __packed;
+
+/* The Typhoon Register offsets
+ */
+#define TYPHOON_REG_SOFT_RESET 0x00
+#define TYPHOON_REG_INTR_STATUS 0x04
+#define TYPHOON_REG_INTR_ENABLE 0x08
+#define TYPHOON_REG_INTR_MASK 0x0c
+#define TYPHOON_REG_SELF_INTERRUPT 0x10
+#define TYPHOON_REG_HOST2ARM7 0x14
+#define TYPHOON_REG_HOST2ARM6 0x18
+#define TYPHOON_REG_HOST2ARM5 0x1c
+#define TYPHOON_REG_HOST2ARM4 0x20
+#define TYPHOON_REG_HOST2ARM3 0x24
+#define TYPHOON_REG_HOST2ARM2 0x28
+#define TYPHOON_REG_HOST2ARM1 0x2c
+#define TYPHOON_REG_HOST2ARM0 0x30
+#define TYPHOON_REG_ARM2HOST3 0x34
+#define TYPHOON_REG_ARM2HOST2 0x38
+#define TYPHOON_REG_ARM2HOST1 0x3c
+#define TYPHOON_REG_ARM2HOST0 0x40
+
+#define TYPHOON_REG_BOOT_DATA_LO TYPHOON_REG_HOST2ARM5
+#define TYPHOON_REG_BOOT_DATA_HI TYPHOON_REG_HOST2ARM4
+#define TYPHOON_REG_BOOT_DEST_ADDR TYPHOON_REG_HOST2ARM3
+#define TYPHOON_REG_BOOT_CHECKSUM TYPHOON_REG_HOST2ARM2
+#define TYPHOON_REG_BOOT_LENGTH TYPHOON_REG_HOST2ARM1
+
+#define TYPHOON_REG_DOWNLOAD_BOOT_ADDR TYPHOON_REG_HOST2ARM1
+#define TYPHOON_REG_DOWNLOAD_HMAC_0 TYPHOON_REG_HOST2ARM2
+#define TYPHOON_REG_DOWNLOAD_HMAC_1 TYPHOON_REG_HOST2ARM3
+#define TYPHOON_REG_DOWNLOAD_HMAC_2 TYPHOON_REG_HOST2ARM4
+#define TYPHOON_REG_DOWNLOAD_HMAC_3 TYPHOON_REG_HOST2ARM5
+#define TYPHOON_REG_DOWNLOAD_HMAC_4 TYPHOON_REG_HOST2ARM6
+
+#define TYPHOON_REG_BOOT_RECORD_ADDR_HI TYPHOON_REG_HOST2ARM2
+#define TYPHOON_REG_BOOT_RECORD_ADDR_LO TYPHOON_REG_HOST2ARM1
+
+#define TYPHOON_REG_TX_LO_READY TYPHOON_REG_HOST2ARM3
+#define TYPHOON_REG_CMD_READY TYPHOON_REG_HOST2ARM2
+#define TYPHOON_REG_TX_HI_READY TYPHOON_REG_HOST2ARM1
+
+#define TYPHOON_REG_COMMAND TYPHOON_REG_HOST2ARM0
+#define TYPHOON_REG_HEARTBEAT TYPHOON_REG_ARM2HOST3
+#define TYPHOON_REG_STATUS TYPHOON_REG_ARM2HOST0
+
+/* 3XP Reset values (TYPHOON_REG_SOFT_RESET)
+ */
+#define TYPHOON_RESET_ALL 0x7f
+#define TYPHOON_RESET_NONE 0x00
+
+/* 3XP irq bits (TYPHOON_REG_INTR{STATUS,ENABLE,MASK})
+ *
+ * Some of these came from OpenBSD, as the 3Com docs have it wrong
+ * (INTR_SELF) or don't list it at all (INTR_*_ABORT)
+ *
+ * Enabling irqs on the Heartbeat reg (ArmToHost3) gets you an irq
+ * about every 8ms, so don't do it.
+ */
+#define TYPHOON_INTR_HOST_INT 0x00000001
+#define TYPHOON_INTR_ARM2HOST0 0x00000002
+#define TYPHOON_INTR_ARM2HOST1 0x00000004
+#define TYPHOON_INTR_ARM2HOST2 0x00000008
+#define TYPHOON_INTR_ARM2HOST3 0x00000010
+#define TYPHOON_INTR_DMA0 0x00000020
+#define TYPHOON_INTR_DMA1 0x00000040
+#define TYPHOON_INTR_DMA2 0x00000080
+#define TYPHOON_INTR_DMA3 0x00000100
+#define TYPHOON_INTR_MASTER_ABORT 0x00000200
+#define TYPHOON_INTR_TARGET_ABORT 0x00000400
+#define TYPHOON_INTR_SELF 0x00000800
+#define TYPHOON_INTR_RESERVED 0xfffff000
+
+#define TYPHOON_INTR_BOOTCMD TYPHOON_INTR_ARM2HOST0
+
+#define TYPHOON_INTR_ENABLE_ALL 0xffffffef
+#define TYPHOON_INTR_ALL 0xffffffff
+#define TYPHOON_INTR_NONE 0x00000000
+
+/* The commands for the 3XP chip (TYPHOON_REG_COMMAND)
+ */
+#define TYPHOON_BOOTCMD_BOOT 0x00
+#define TYPHOON_BOOTCMD_WAKEUP 0xfa
+#define TYPHOON_BOOTCMD_DNLD_COMPLETE 0xfb
+#define TYPHOON_BOOTCMD_SEG_AVAILABLE 0xfc
+#define TYPHOON_BOOTCMD_RUNTIME_IMAGE 0xfd
+#define TYPHOON_BOOTCMD_REG_BOOT_RECORD 0xff
+
+/* 3XP Status values (TYPHOON_REG_STATUS)
+ */
+#define TYPHOON_STATUS_WAITING_FOR_BOOT 0x07
+#define TYPHOON_STATUS_SECOND_INIT 0x08
+#define TYPHOON_STATUS_RUNNING 0x09
+#define TYPHOON_STATUS_WAITING_FOR_HOST 0x0d
+#define TYPHOON_STATUS_WAITING_FOR_SEGMENT 0x10
+#define TYPHOON_STATUS_SLEEPING 0x11
+#define TYPHOON_STATUS_HALTED 0x14
diff --git a/drivers/net/ethernet/8390/8390.c b/drivers/net/ethernet/8390/8390.c
new file mode 100644
index 00000000000..5db1f55abef
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.c
@@ -0,0 +1,103 @@
+/* 8390 core for usual drivers */
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+int ei_open(struct net_device *dev)
+{
+ return __ei_open(dev);
+}
+EXPORT_SYMBOL(ei_open);
+
+int ei_close(struct net_device *dev)
+{
+ return __ei_close(dev);
+}
+EXPORT_SYMBOL(ei_close);
+
+netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(ei_start_xmit);
+
+struct net_device_stats *ei_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(ei_get_stats);
+
+void ei_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(ei_set_multicast_list);
+
+void ei_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(ei_tx_timeout);
+
+irqreturn_t ei_interrupt(int irq, void *dev_id)
+{
+ return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(ei_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void ei_poll(struct net_device *dev)
+{
+ __ei_poll(dev);
+}
+EXPORT_SYMBOL(ei_poll);
+#endif
+
+const struct net_device_ops ei_netdev_ops = {
+ .ndo_open = ei_open,
+ .ndo_stop = ei_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+EXPORT_SYMBOL(ei_netdev_ops);
+
+struct net_device *__alloc_ei_netdev(int size)
+{
+ struct net_device *dev = ____alloc_ei_netdev(size);
+ if (dev)
+ dev->netdev_ops = &ei_netdev_ops;
+ return dev;
+}
+EXPORT_SYMBOL(__alloc_ei_netdev);
+
+void NS8390_init(struct net_device *dev, int startp)
+{
+ __NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390_init);
+
+#if defined(MODULE)
+
+static int __init ns8390_module_init(void)
+{
+ return 0;
+}
+
+static void __exit ns8390_module_exit(void)
+{
+}
+
+module_init(ns8390_module_init);
+module_exit(ns8390_module_exit);
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/8390.h b/drivers/net/ethernet/8390/8390.h
new file mode 100644
index 00000000000..3e2f2c2e7b5
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.h
@@ -0,0 +1,225 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+ under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
+
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/irqreturn.h>
+#include <linux/skbuff.h>
+
+#define TX_PAGES 12 /* Two Tx slots */
+
+/* The 8390 specific per-packet-header format. */
+struct e8390_pkt_hdr {
+ unsigned char status; /* status */
+ unsigned char next; /* pointer to next packet. */
+ unsigned short count; /* header + packet length in bytes */
+};
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void ei_poll(struct net_device *dev);
+void eip_poll(struct net_device *dev);
+#endif
+
+
+/* Without I/O delay - non ISA or later chips */
+void NS8390_init(struct net_device *dev, int startp);
+int ei_open(struct net_device *dev);
+int ei_close(struct net_device *dev);
+irqreturn_t ei_interrupt(int irq, void *dev_id);
+void ei_tx_timeout(struct net_device *dev);
+netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void ei_set_multicast_list(struct net_device *dev);
+struct net_device_stats *ei_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops ei_netdev_ops;
+
+struct net_device *__alloc_ei_netdev(int size);
+static inline struct net_device *alloc_ei_netdev(void)
+{
+ return __alloc_ei_netdev(0);
+}
+
+/* With I/O delay form */
+void NS8390p_init(struct net_device *dev, int startp);
+int eip_open(struct net_device *dev);
+int eip_close(struct net_device *dev);
+irqreturn_t eip_interrupt(int irq, void *dev_id);
+void eip_tx_timeout(struct net_device *dev);
+netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void eip_set_multicast_list(struct net_device *dev);
+struct net_device_stats *eip_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops eip_netdev_ops;
+
+struct net_device *__alloc_eip_netdev(int size);
+static inline struct net_device *alloc_eip_netdev(void)
+{
+ return __alloc_eip_netdev(0);
+}
+
+/* You have one of these per-board */
+struct ei_device {
+ const char *name;
+ void (*reset_8390)(struct net_device *);
+ void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int);
+ void (*block_output)(struct net_device *, int, const unsigned char *, int);
+ void (*block_input)(struct net_device *, int, struct sk_buff *, int);
+ unsigned long rmem_start;
+ unsigned long rmem_end;
+ void __iomem *mem;
+ unsigned char mcfilter[8];
+ unsigned open:1;
+ unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
+ unsigned bigendian:1; /* 16-bit big endian mode. Do NOT */
+ /* set this on random 8390 clones! */
+ unsigned txing:1; /* Transmit Active */
+ unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
+ unsigned dmaing:1; /* Remote DMA Active */
+ unsigned char tx_start_page, rx_start_page, stop_page;
+ unsigned char current_page; /* Read pointer in buffer */
+ unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
+ unsigned char txqueue; /* Tx Packet buffer queue length. */
+ short tx1, tx2; /* Packet lengths for ping-pong tx. */
+ short lasttx; /* Alpha version consistency check. */
+ unsigned char reg0; /* Register '0' in a WD8013 */
+ unsigned char reg5; /* Register '5' in a WD8013 */
+ unsigned char saved_irq; /* Original dev->irq value. */
+ u32 *reg_offset; /* Register mapping table */
+ spinlock_t page_lock; /* Page register locks */
+ unsigned long priv; /* Private field to store bus IDs etc. */
+ u32 msg_enable; /* debug message level */
+#ifdef AX88796_PLATFORM
+ unsigned char rxcr_base; /* default value for RXCR */
+#endif
+};
+
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
+#define TX_TIMEOUT (20*HZ/100)
+
+#define ei_status (*(struct ei_device *)netdev_priv(dev))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+
+#ifdef AX88796_PLATFORM
+#define E8390_RXCONFIG (ei_status.rxcr_base | 0x04)
+#define E8390_RXOFF (ei_status.rxcr_base | 0x20)
+#else
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#endif
+
+#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+/*
+ * Only generate indirect loads given a machine that needs them.
+ * - removed AMIGA_PCMCIA from this list, handled as ISA io now
+ * - the _p for generates no delay by default 8390p.c overrides this.
+ */
+
+#ifndef ei_inb
+#define ei_inb(_p) inb(_p)
+#define ei_outb(_v,_p) outb(_v,_p)
+#define ei_inb_p(_p) inb(_p)
+#define ei_outb_p(_v,_p) outb(_v,_p)
+#endif
+
+#ifndef EI_SHIFT
+#define EI_SHIFT(x) (x)
+#endif
+
+#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
+#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
+#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */
+#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */
+#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */
+#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */
+#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */
+#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */
+#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */
+#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */
+#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */
+#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */
+#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */
+#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */
+#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */
+#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */
+#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */
+#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */
+#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */
+#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */
+#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */
+#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */
+#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */
+#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */
+#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS 0x01 /* word transfer mode selection */
+#define ENDCFG_BOS 0x02 /* byte order selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */
+#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */
+#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */
+#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */
+#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicast address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+#endif /* _8390_h */
diff --git a/drivers/net/ethernet/8390/8390p.c b/drivers/net/ethernet/8390/8390p.c
new file mode 100644
index 00000000000..e8fc2e87e84
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390p.c
@@ -0,0 +1,105 @@
+/* 8390 core for ISA devices needing bus delays */
+
+static const char version[] =
+ "8390p.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#define ei_inb(_p) inb(_p)
+#define ei_outb(_v, _p) outb(_v, _p)
+#define ei_inb_p(_p) inb_p(_p)
+#define ei_outb_p(_v, _p) outb_p(_v, _p)
+
+#include "lib8390.c"
+
+int eip_open(struct net_device *dev)
+{
+ return __ei_open(dev);
+}
+EXPORT_SYMBOL(eip_open);
+
+int eip_close(struct net_device *dev)
+{
+ return __ei_close(dev);
+}
+EXPORT_SYMBOL(eip_close);
+
+netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(eip_start_xmit);
+
+struct net_device_stats *eip_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(eip_get_stats);
+
+void eip_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(eip_set_multicast_list);
+
+void eip_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(eip_tx_timeout);
+
+irqreturn_t eip_interrupt(int irq, void *dev_id)
+{
+ return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(eip_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void eip_poll(struct net_device *dev)
+{
+ __ei_poll(dev);
+}
+EXPORT_SYMBOL(eip_poll);
+#endif
+
+const struct net_device_ops eip_netdev_ops = {
+ .ndo_open = eip_open,
+ .ndo_stop = eip_close,
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_rx_mode = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+EXPORT_SYMBOL(eip_netdev_ops);
+
+struct net_device *__alloc_eip_netdev(int size)
+{
+ struct net_device *dev = ____alloc_ei_netdev(size);
+ if (dev)
+ dev->netdev_ops = &eip_netdev_ops;
+ return dev;
+}
+EXPORT_SYMBOL(__alloc_eip_netdev);
+
+void NS8390p_init(struct net_device *dev, int startp)
+{
+ __NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390p_init);
+
+static int __init NS8390p_init_module(void)
+{
+ return 0;
+}
+
+static void __exit NS8390p_cleanup_module(void)
+{
+}
+
+module_init(NS8390p_init_module);
+module_exit(NS8390p_cleanup_module);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
new file mode 100644
index 00000000000..0988811f4e4
--- /dev/null
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -0,0 +1,205 @@
+#
+# 8390 device configuration
+#
+
+config NET_VENDOR_8390
+ bool "National Semi-conductor 8390 devices"
+ default y
+ depends on NET_VENDOR_NATSEMI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Western Digital cards. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_8390
+
+config PCMCIA_AXNET
+ tristate "Asix AX88190 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach an Asix AX88190-based PCMCIA
+ (PC-card) Fast Ethernet card to your computer. These cards are
+ nearly NE2000 compatible but need a separate driver due to a few
+ misfeatures.
+
+ To compile this driver as a module, choose M here: the module will be
+ called axnet_cs. If unsure, say N.
+
+config AX88796
+ tristate "ASIX AX88796 NE2000 clone support"
+ depends on (ARM || MIPS || SUPERH)
+ select CRC32
+ select PHYLIB
+ select MDIO_BITBANG
+ ---help---
+ AX88796 driver, using platform bus to provide
+ chip detection and resources
+
+config AX88796_93CX6
+ bool "ASIX AX88796 external 93CX6 eeprom support"
+ depends on AX88796
+ select EEPROM_93CX6
+ ---help---
+ Select this if your platform comes with an external 93CX6 eeprom.
+
+config HYDRA
+ tristate "Hydra support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hydra.
+
+config ARM_ETHERH
+ tristate "I-cubed EtherH/ANT EtherM support"
+ depends on ARM && ARCH_ACORN
+ select CRC32
+ ---help---
+ If you have an Acorn system with one of these network cards, you
+ should say Y to this option if you wish to use it with Linux.
+
+config MAC8390
+ bool "Macintosh NS 8390 based ethernet cards"
+ depends on MAC
+ select CRC32
+ ---help---
+ If you want to include a driver to support Nubus or LC-PDS
+ Ethernet cards using an NS8390 chipset or its equivalent, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+config MCF8390
+ tristate "ColdFire NS8390 based Ethernet support"
+ depends on COLDFIRE
+ select CRC32
+ ---help---
+ This driver is for Ethernet devices using an NS8390-compatible
+ chipset on many common ColdFire CPU based boards. Many of the older
+ Freescale dev boards use this, and some other common boards like
+ some SnapGear routers do as well.
+
+ If you have one of these boards and want to use the network interface
+ on them then choose Y. To compile this driver as a module, choose M
+ here, the module will be called mcf8390.
+
+config NE2000
+ tristate "NE2000/NE1000 support"
+ depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX)
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Many Ethernet cards
+ without a specific driver are compatible with NE2000.
+
+ If you have a PCI NE2000 card however, say N here and Y to "PCI
+ NE2000 and clone support" below.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne.
+
+config NE2K_PCI
+ tristate "PCI NE2000 and clones support (see help)"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver is for NE2000 compatible PCI cards. It will not work
+ with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
+ support" below). If you have a PCI NE2000 network (Ethernet) card,
+ say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver also works for the following NE2000 clone cards:
+ RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2
+ NetVin NV5000SC Via 86C926 SureCom NE34 Winbond
+ Holtek HT80232 Holtek HT80229
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne2k-pci.
+
+config APNE
+ tristate "PCMCIA NE2000 support"
+ depends on AMIGA_PCMCIA
+ select CRC32
+ ---help---
+ If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called apne.
+
+config PCMCIA_PCNET
+ tristate "NE2000 compatible PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ ---help---
+ Say Y here if you intend to attach an NE2000 compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcnet_cs. If unsure, say N.
+
+config STNIC
+ tristate "National DP83902AV support"
+ depends on SUPERH
+ select CRC32
+ ---help---
+ Support for cards based on the National Semiconductor DP83902AV
+ ST-NIC Serial Network Interface Controller for Twisted Pair. This
+ is a 10Mbit/sec Ethernet controller. Product overview and specs at
+ <http://www.national.com/pf/DP/DP83902A.html>.
+
+ If unsure, say N.
+
+config ULTRA
+ tristate "SMC Ultra support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Important: There have been many reports that, with some motherboards
+ mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
+ such as some BusLogic models) causes corruption problems with many
+ operating systems. The Linux smc-ultra driver has a work-around for
+ this but keep it in mind if you have such a SCSI card and have
+ problems.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-ultra.
+
+config WD80x3
+ tristate "WD80*3 support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called wd.
+
+config ZORRO8390
+ tristate "Zorro NS8390-based Ethernet support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ This driver is for Zorro Ethernet cards using an NS8390-compatible
+ chipset, like the Village Tronic Ariadne II and the Individual
+ Computers X-Surf Ethernet cards. If you have such a card, say Y.
+ Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called zorro8390.
+
+endif # NET_VENDOR_8390
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
new file mode 100644
index 00000000000..ff3b3189418
--- /dev/null
+++ b/drivers/net/ethernet/8390/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the 8390 network device drivers.
+#
+
+obj-$(CONFIG_MAC8390) += mac8390.o
+obj-$(CONFIG_APNE) += apne.o 8390.o
+obj-$(CONFIG_ARM_ETHERH) += etherh.o
+obj-$(CONFIG_AX88796) += ax88796.o
+obj-$(CONFIG_HYDRA) += hydra.o 8390.o
+obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o
+obj-$(CONFIG_NE2000) += ne.o 8390p.o
+obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
+obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
+obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
+obj-$(CONFIG_STNIC) += stnic.o 8390.o
+obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
+obj-$(CONFIG_WD80x3) += wd.o 8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c
new file mode 100644
index 00000000000..c56ac9ebc08
--- /dev/null
+++ b/drivers/net/ethernet/8390/apne.c
@@ -0,0 +1,626 @@
+/*
+ * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
+ *
+ * (C) Copyright 1997 Alain Malek
+ * (Alain.Malek@cryogen.com)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ne.c: A general non-shared-memory NS8390 ethernet driver for linux
+ * Written 1992-94 by Donald Becker.
+ *
+ * 8390.c: A general NS8390 ethernet driver core for linux.
+ * Written 1992-94 by Donald Becker.
+ *
+ * cnetdevice: A Sana-II ethernet driver for AmigaOS
+ * Written by Bruce Abbott (bhabbott@inhb.co.nz)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/amigayle.h>
+#include <asm/amipcmcia.h>
+
+#include "8390.h"
+
+/* ---- No user-serviceable parts below ---- */
+
+#define DRV_NAME "apne"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NE_EN0_ISR 0x07
+#define NE_EN0_DCFG 0x0e
+
+#define NE_EN0_RSARLO 0x08
+#define NE_EN0_RSARHI 0x09
+#define NE_EN0_RCNTLO 0x0a
+#define NE_EN0_RXCR 0x0c
+#define NE_EN0_TXCR 0x0d
+#define NE_EN0_RCNTHI 0x0b
+#define NE_EN0_IMR 0x0f
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+
+struct net_device * __init apne_probe(int unit);
+static int apne_probe1(struct net_device *dev, int ioaddr);
+
+static void apne_reset_8390(struct net_device *dev);
+static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void apne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void apne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static irqreturn_t apne_interrupt(int irq, void *dev_id);
+
+static int init_pcmcia(void);
+
+/* IO base address used for nic */
+
+#define IOBASE 0x300
+
+/*
+ use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
+ you can find the values to use by looking at the cnet.device
+ config file example (the default values are for the CNET40BC card)
+*/
+
+/*
+#define MANUAL_CONFIG 0x20
+#define MANUAL_OFFSET 0x3f8
+
+#define MANUAL_HWADDR0 0x00
+#define MANUAL_HWADDR1 0x12
+#define MANUAL_HWADDR2 0x34
+#define MANUAL_HWADDR3 0x56
+#define MANUAL_HWADDR4 0x78
+#define MANUAL_HWADDR5 0x9a
+*/
+
+static const char version[] =
+ "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
+
+static int apne_owned; /* signal if card already owned */
+
+static u32 apne_msg_enable;
+module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+
+struct net_device * __init apne_probe(int unit)
+{
+ struct net_device *dev;
+ struct ei_device *ei_local;
+
+#ifndef MANUAL_CONFIG
+ char tuple[8];
+#endif
+ int err;
+
+ if (!MACH_IS_AMIGA)
+ return ERR_PTR(-ENODEV);
+
+ if (apne_owned)
+ return ERR_PTR(-ENODEV);
+
+ if ( !(AMIGAHW_PRESENT(PCMCIA)) )
+ return ERR_PTR(-ENODEV);
+
+ pr_info("Looking for PCMCIA ethernet card : ");
+
+ /* check if a card is inserted */
+ if (!(PCMCIA_INSERTED)) {
+ pr_cont("NO PCMCIA card inserted\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ dev = alloc_ei_netdev();
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = apne_msg_enable;
+
+ /* disable pcmcia irq for readtuple */
+ pcmcia_disable_irq();
+
+#ifndef MANUAL_CONFIG
+ if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
+ (tuple[2] != CISTPL_FUNCID_NETWORK)) {
+ pr_cont("not an ethernet card\n");
+ /* XXX: shouldn't we re-enable irq here? */
+ free_netdev(dev);
+ return ERR_PTR(-ENODEV);
+ }
+#endif
+
+ pr_cont("ethernet PCMCIA card inserted\n");
+
+ if (!init_pcmcia()) {
+ /* XXX: shouldn't we re-enable irq here? */
+ free_netdev(dev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (!request_region(IOBASE, 0x20, DRV_NAME)) {
+ free_netdev(dev);
+ return ERR_PTR(-EBUSY);
+ }
+
+ err = apne_probe1(dev, IOBASE);
+ if (err) {
+ release_region(IOBASE, 0x20);
+ free_netdev(dev);
+ return ERR_PTR(err);
+ }
+ err = register_netdev(dev);
+ if (!err)
+ return dev;
+
+ pcmcia_disable_irq();
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ pcmcia_reset();
+ release_region(IOBASE, 0x20);
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static int __init apne_probe1(struct net_device *dev, int ioaddr)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+#ifndef MANUAL_HWADDR0
+ int neX000, ctron;
+#endif
+ static unsigned version_printed;
+
+ if ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ netdev_info(dev, version);
+
+ netdev_info(dev, "PCMCIA NE*000 ethercard probe");
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ { unsigned long reset_start_time = jiffies;
+
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ pr_cont(" not found (no reset ack).\n");
+ return -ENODEV;
+ }
+
+ outb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
+ }
+
+#ifndef MANUAL_HWADDR0
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned long value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/
+ {0x48, NE_EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_IMR}, /* Mask completion irq. */
+ {0xFF, NE_EN0_ISR},
+ {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, NE_EN0_RCNTLO},
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, NE_EN0_RSARHI},
+ {E8390_RREAD+E8390_START, NE_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
+ outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+ }
+
+ }
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+ if (SA_prom[i] != SA_prom[i+1])
+ wordlength = 1;
+ }
+
+ /* At this point, wordlength *only* tells us if the SA_prom is doubled
+ up or not because some broken PCI cards don't respect the byte-wide
+ request in program_seq above, and hence don't have doubled up values.
+ These broken cards would otherwise be detected as an ne1000. */
+
+ if (wordlength == 2)
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+
+ if (wordlength == 2) {
+ /* We must set the 8390 for word mode. */
+ outb(0x49, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+
+ /* Set up the rest of the parameters. */
+ if (neX000) {
+ name = (wordlength == 2) ? "NE2000" : "NE1000";
+ } else if (ctron) {
+ name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+ start_page = 0x01;
+ stop_page = (wordlength == 2) ? 0x40 : 0x20;
+ } else {
+ pr_cont(" not found.\n");
+ return -ENXIO;
+
+ }
+
+#else
+ wordlength = 2;
+ /* We must set the 8390 for word mode. */
+ outb(0x49, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ SA_prom[0] = MANUAL_HWADDR0;
+ SA_prom[1] = MANUAL_HWADDR1;
+ SA_prom[2] = MANUAL_HWADDR2;
+ SA_prom[3] = MANUAL_HWADDR3;
+ SA_prom[4] = MANUAL_HWADDR4;
+ SA_prom[5] = MANUAL_HWADDR5;
+ name = "NE2000";
+#endif
+
+ dev->base_addr = ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+ dev->netdev_ops = &ei_netdev_ops;
+
+ /* Install the Interrupt handler */
+ i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
+ if (i) return i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ pr_cont(" %pM\n", dev->dev_addr);
+
+ netdev_info(dev, "%s found.\n", name);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = (wordlength == 2);
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = &apne_reset_8390;
+ ei_status.block_input = &apne_block_input;
+ ei_status.block_output = &apne_block_output;
+ ei_status.get_8390_hdr = &apne_get_8390_hdr;
+
+ NS8390_init(dev, 0);
+
+ pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */
+ pcmcia_enable_irq();
+
+ apne_owned = 1;
+
+ return 0;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+static void
+apne_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ init_pcmcia();
+
+ netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
+
+ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ netdev_err(dev, "ne_reset_8390() did not complete.\n");
+ break;
+ }
+ outb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int nic_base = dev->base_addr;
+ int cnt;
+ char *ptrc;
+ short *ptrs;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ ei_status.dmaing, ei_status.irqlock, dev->irq);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+ outb(0, nic_base + NE_EN0_RCNTHI);
+ outb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
+ outb(ring_page, nic_base + NE_EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.word16) {
+ ptrs = (short*)hdr;
+ for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
+ *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+ } else {
+ ptrc = (char*)hdr;
+ for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
+ *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+ }
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ are porting to a new ethercard, look at the packet driver source for hints.
+ The NEx000 doesn't share the on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static void
+apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+ char *ptrc;
+ short *ptrs;
+ int cnt;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ ei_status.dmaing, ei_status.irqlock, dev->irq);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ outb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+ outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ ptrs = (short*)buf;
+ for (cnt = 0; cnt < (count>>1); cnt++)
+ *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+ }
+ } else {
+ ptrc = buf;
+ for (cnt = 0; cnt < count; cnt++)
+ *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+ }
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void
+apne_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+ char *ptrc;
+ short *ptrs;
+ int cnt;
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+ ei_status.dmaing, ei_status.irqlock, dev->irq);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ outb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ outb(0x00, nic_base + NE_EN0_RSARLO);
+ outb(start_page, nic_base + NE_EN0_RSARHI);
+
+ outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ ptrs = (short*)buf;
+ for (cnt = 0; cnt < count>>1; cnt++)
+ outw(*ptrs++, NE_BASE+NE_DATAPORT);
+ } else {
+ ptrc = (char*)buf;
+ for (cnt = 0; cnt < count; cnt++)
+ outb(*ptrc++, NE_BASE + NE_DATAPORT);
+ }
+
+ dma_start = jiffies;
+
+ while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ apne_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static irqreturn_t apne_interrupt(int irq, void *dev_id)
+{
+ unsigned char pcmcia_intreq;
+
+ if (!(gayle.inten & GAYLE_IRQ_IRQ))
+ return IRQ_NONE;
+
+ pcmcia_intreq = pcmcia_get_intreq();
+
+ if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
+ pcmcia_ack_int(pcmcia_intreq);
+ return IRQ_NONE;
+ }
+ if (apne_msg_enable & NETIF_MSG_INTR)
+ pr_debug("pcmcia intreq = %x\n", pcmcia_intreq);
+ pcmcia_disable_irq(); /* to get rid of the sti() within ei_interrupt */
+ ei_interrupt(irq, dev_id);
+ pcmcia_ack_int(pcmcia_get_intreq());
+ pcmcia_enable_irq();
+ return IRQ_HANDLED;
+}
+
+#ifdef MODULE
+static struct net_device *apne_dev;
+
+static int __init apne_module_init(void)
+{
+ apne_dev = apne_probe(-1);
+ return PTR_ERR_OR_ZERO(apne_dev);
+}
+
+static void __exit apne_module_exit(void)
+{
+ unregister_netdev(apne_dev);
+
+ pcmcia_disable_irq();
+
+ free_irq(IRQ_AMIGA_PORTS, apne_dev);
+
+ pcmcia_reset();
+
+ release_region(IOBASE, 0x20);
+
+ free_netdev(apne_dev);
+}
+module_init(apne_module_init);
+module_exit(apne_module_exit);
+#endif
+
+static int init_pcmcia(void)
+{
+ u_char config;
+#ifndef MANUAL_CONFIG
+ u_char tuple[32];
+ int offset_len;
+#endif
+ u_long offset;
+
+ pcmcia_reset();
+ pcmcia_program_voltage(PCMCIA_0V);
+ pcmcia_access_speed(PCMCIA_SPEED_250NS);
+ pcmcia_write_enable();
+
+#ifdef MANUAL_CONFIG
+ config = MANUAL_CONFIG;
+#else
+ /* get and write config byte to enable IO port */
+
+ if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
+ return 0;
+
+ config = tuple[2] & 0x3f;
+#endif
+#ifdef MANUAL_OFFSET
+ offset = MANUAL_OFFSET;
+#else
+ if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
+ return 0;
+
+ offset_len = (tuple[2] & 0x3) + 1;
+ offset = 0;
+ while(offset_len--) {
+ offset = (offset << 8) | tuple[4+offset_len];
+ }
+#endif
+
+ out_8(GAYLE_ATTRIBUTE+offset, config);
+
+ return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
new file mode 100644
index 00000000000..1d162ccb473
--- /dev/null
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -0,0 +1,1016 @@
+/* drivers/net/ethernet/8390/ax88796.c
+ *
+ * Copyright 2005,2007 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Asix AX88796 10/100 Ethernet controller support
+ * Based on ne.c, by Donald Becker, et-al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mdio-bitbang.h>
+#include <linux/phy.h>
+#include <linux/eeprom_93cx6.h>
+#include <linux/slab.h>
+
+#include <net/ax88796.h>
+
+
+/* Rename the lib8390.c functions to show that they are in this driver */
+#define __ei_open ax_ei_open
+#define __ei_close ax_ei_close
+#define __ei_poll ax_ei_poll
+#define __ei_start_xmit ax_ei_start_xmit
+#define __ei_tx_timeout ax_ei_tx_timeout
+#define __ei_get_stats ax_ei_get_stats
+#define __ei_set_multicast_list ax_ei_set_multicast_list
+#define __ei_interrupt ax_ei_interrupt
+#define ____alloc_ei_netdev ax__alloc_ei_netdev
+#define __NS8390_init ax_NS8390_init
+
+/* force unsigned long back to 'void __iomem *' */
+#define ax_convert_addr(_a) ((void __force __iomem *)(_a))
+
+#define ei_inb(_a) readb(ax_convert_addr(_a))
+#define ei_outb(_v, _a) writeb(_v, ax_convert_addr(_a))
+
+#define ei_inb_p(_a) ei_inb(_a)
+#define ei_outb_p(_v, _a) ei_outb(_v, _a)
+
+/* define EI_SHIFT() to take into account our register offsets */
+#define EI_SHIFT(x) (ei_local->reg_offset[(x)])
+
+/* Ensure we have our RCR base value */
+#define AX88796_PLATFORM
+
+static unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electronics\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME "ax88796"
+#define DRV_VERSION "1.00"
+
+/* from ne.c */
+#define NE_CMD EI_SHIFT(0x00)
+#define NE_RESET EI_SHIFT(0x1f)
+#define NE_DATAPORT EI_SHIFT(0x10)
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define AX_GPOC_PPDSET BIT(6)
+
+static u32 ax_msg_enable;
+
+/* device private data */
+
+struct ax_device {
+ struct mii_bus *mii_bus;
+ struct mdiobb_ctrl bb_ctrl;
+ struct phy_device *phy_dev;
+ void __iomem *addr_memr;
+ u8 reg_memr;
+ int link;
+ int speed;
+ int duplex;
+
+ void __iomem *map2;
+ const struct ax_plat_data *plat;
+
+ unsigned char running;
+ unsigned char resume_open;
+ unsigned int irqflags;
+
+ u32 reg_offsets[0x20];
+};
+
+static inline struct ax_device *to_ax_dev(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ return (struct ax_device *)(ei_local + 1);
+}
+
+/*
+ * ax_initial_check
+ *
+ * do an initial probe for the card to check whether it exists
+ * and is functional
+ */
+static int ax_initial_check(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *ioaddr = ei_local->mem;
+ int reg0;
+ int regd;
+
+ reg0 = ei_inb(ioaddr);
+ if (reg0 == 0xFF)
+ return -ENODEV;
+
+ ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr + E8390_CMD);
+ regd = ei_inb(ioaddr + 0x0d);
+ ei_outb(0xff, ioaddr + 0x0d);
+ ei_outb(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD);
+ ei_inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (ei_inb(ioaddr + EN0_COUNTER0) != 0) {
+ ei_outb(reg0, ioaddr);
+ ei_outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void ax_reset_8390(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long reset_start_time = jiffies;
+ void __iomem *addr = (void __iomem *)dev->base_addr;
+
+ netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
+
+ ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
+
+ ei_local->txing = 0;
+ ei_local->dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete.\n", __func__);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RESET, addr + EN0_ISR); /* Ack intr. */
+}
+
+
+static void ax_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in %s "
+ "[DMAstat:%d][irqlock:%d].\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ ei_outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ ei_outb(0, nic_base + EN0_RCNTHI);
+ ei_outb(0, nic_base + EN0_RSARLO); /* On page boundary */
+ ei_outb(ring_page, nic_base + EN0_RSARHI);
+ ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_local->word16)
+ ioread16_rep(nic_base + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr) >> 1);
+ else
+ ioread8_rep(nic_base + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr));
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_local->dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+
+/*
+ * Block input and output, similar to the Crynwr packet driver. If
+ * you are porting to a new ethercard, look at the packet driver
+ * source for hints. The NEx000 doesn't share the on-board packet
+ * memory -- you have to put the packet out through the "remote DMA"
+ * dataport using ei_outb.
+ */
+static void ax_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+ char *buf = skb->data;
+
+ if (ei_local->dmaing) {
+ netdev_err(dev,
+ "DMAing conflict in %s "
+ "[DMAstat:%d][irqlock:%d].\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+
+ ei_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + NE_CMD);
+ ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+ ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+ ei_outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ ei_outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+ ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_local->word16) {
+ ioread16_rep(nic_base + NE_DATAPORT, buf, count >> 1);
+ if (count & 0x01)
+ buf[count-1] = ei_inb(nic_base + NE_DATAPORT);
+
+ } else {
+ ioread8_rep(nic_base + NE_DATAPORT, buf, count);
+ }
+
+ ei_local->dmaing &= ~1;
+}
+
+static void ax_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+ unsigned long dma_start;
+
+ /*
+ * Round the count up for word writes. Do we need to do this?
+ * What effect will an odd byte count have on the 8390? I
+ * should check someday.
+ */
+ if (ei_local->word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in %s."
+ "[DMAstat:%d][irqlock:%d]\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ ei_outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+ ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+ ei_outb(0x00, nic_base + EN0_RSARLO);
+ ei_outb(start_page, nic_base + EN0_RSARHI);
+
+ ei_outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_local->word16)
+ iowrite16_rep(nic_base + NE_DATAPORT, buf, count >> 1);
+ else
+ iowrite8_rep(nic_base + NE_DATAPORT, buf, count);
+
+ dma_start = jiffies;
+
+ while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ ax_reset_8390(dev);
+ ax_NS8390_init(dev, 1);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_local->dmaing &= ~0x01;
+}
+
+/* definitions for accessing MII/EEPROM interface */
+
+#define AX_MEMR EI_SHIFT(0x14)
+#define AX_MEMR_MDC BIT(0)
+#define AX_MEMR_MDIR BIT(1)
+#define AX_MEMR_MDI BIT(2)
+#define AX_MEMR_MDO BIT(3)
+#define AX_MEMR_EECS BIT(4)
+#define AX_MEMR_EEI BIT(5)
+#define AX_MEMR_EEO BIT(6)
+#define AX_MEMR_EECLK BIT(7)
+
+static void ax_handle_link_change(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+ int status_change = 0;
+
+ if (phy_dev->link && ((ax->speed != phy_dev->speed) ||
+ (ax->duplex != phy_dev->duplex))) {
+
+ ax->speed = phy_dev->speed;
+ ax->duplex = phy_dev->duplex;
+ status_change = 1;
+ }
+
+ if (phy_dev->link != ax->link) {
+ if (!phy_dev->link) {
+ ax->speed = 0;
+ ax->duplex = -1;
+ }
+ ax->link = phy_dev->link;
+
+ status_change = 1;
+ }
+
+ if (status_change)
+ phy_print_status(phy_dev);
+}
+
+static int ax_mii_probe(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = NULL;
+ int ret;
+
+ /* find the first phy */
+ phy_dev = phy_find_first(ax->mii_bus);
+ if (!phy_dev) {
+ netdev_err(dev, "no PHY found\n");
+ return -ENODEV;
+ }
+
+ ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change,
+ PHY_INTERFACE_MODE_MII);
+ if (ret) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ return ret;
+ }
+
+ /* mask with MAC supported features */
+ phy_dev->supported &= PHY_BASIC_FEATURES;
+ phy_dev->advertising = phy_dev->supported;
+
+ ax->phy_dev = phy_dev;
+
+ netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
+
+ return 0;
+}
+
+static void ax_phy_switch(struct net_device *dev, int on)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+
+ u8 reg_gpoc = ax->plat->gpoc_val;
+
+ if (!!on)
+ reg_gpoc &= ~AX_GPOC_PPDSET;
+ else
+ reg_gpoc |= AX_GPOC_PPDSET;
+
+ ei_outb(reg_gpoc, ei_local->mem + EI_SHIFT(0x17));
+}
+
+static int ax_open(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ int ret;
+
+ netdev_dbg(dev, "open\n");
+
+ ret = request_irq(dev->irq, ax_ei_interrupt, ax->irqflags,
+ dev->name, dev);
+ if (ret)
+ goto failed_request_irq;
+
+ /* turn the phy on (if turned off) */
+ ax_phy_switch(dev, 1);
+
+ ret = ax_mii_probe(dev);
+ if (ret)
+ goto failed_mii_probe;
+ phy_start(ax->phy_dev);
+
+ ret = ax_ei_open(dev);
+ if (ret)
+ goto failed_ax_ei_open;
+
+ ax->running = 1;
+
+ return 0;
+
+ failed_ax_ei_open:
+ phy_disconnect(ax->phy_dev);
+ failed_mii_probe:
+ ax_phy_switch(dev, 0);
+ free_irq(dev->irq, dev);
+ failed_request_irq:
+ return ret;
+}
+
+static int ax_close(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+
+ netdev_dbg(dev, "close\n");
+
+ ax->running = 0;
+ wmb();
+
+ ax_ei_close(dev);
+
+ /* turn the phy off */
+ ax_phy_switch(dev, 0);
+ phy_disconnect(ax->phy_dev);
+
+ free_irq(dev->irq, dev);
+ return 0;
+}
+
+static int ax_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phy_dev, req, cmd);
+}
+
+/* ethtool ops */
+
+static void ax_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct platform_device *pdev = to_platform_device(dev->dev.parent);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info));
+}
+
+static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phy_dev, cmd);
+}
+
+static int ax_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phy_dev, cmd);
+}
+
+static u32 ax_get_msglevel(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ return ei_local->msg_enable;
+}
+
+static void ax_set_msglevel(struct net_device *dev, u32 v)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ ei_local->msg_enable = v;
+}
+
+static const struct ethtool_ops ax_ethtool_ops = {
+ .get_drvinfo = ax_get_drvinfo,
+ .get_settings = ax_get_settings,
+ .set_settings = ax_set_settings,
+ .get_link = ethtool_op_get_link,
+ .get_ts_info = ethtool_op_get_ts_info,
+ .get_msglevel = ax_get_msglevel,
+ .set_msglevel = ax_set_msglevel,
+};
+
+#ifdef CONFIG_AX88796_93CX6
+static void ax_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+ struct ei_device *ei_local = eeprom->data;
+ u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+ eeprom->reg_data_in = reg & AX_MEMR_EEI;
+ eeprom->reg_data_out = reg & AX_MEMR_EEO; /* Input pin */
+ eeprom->reg_data_clock = reg & AX_MEMR_EECLK;
+ eeprom->reg_chip_select = reg & AX_MEMR_EECS;
+}
+
+static void ax_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+ struct ei_device *ei_local = eeprom->data;
+ u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+ reg &= ~(AX_MEMR_EEI | AX_MEMR_EECLK | AX_MEMR_EECS);
+
+ if (eeprom->reg_data_in)
+ reg |= AX_MEMR_EEI;
+ if (eeprom->reg_data_clock)
+ reg |= AX_MEMR_EECLK;
+ if (eeprom->reg_chip_select)
+ reg |= AX_MEMR_EECS;
+
+ ei_outb(reg, ei_local->mem + AX_MEMR);
+ udelay(10);
+}
+#endif
+
+static const struct net_device_ops ax_netdev_ops = {
+ .ndo_open = ax_open,
+ .ndo_stop = ax_close,
+ .ndo_do_ioctl = ax_ioctl,
+
+ .ndo_start_xmit = ax_ei_start_xmit,
+ .ndo_tx_timeout = ax_ei_tx_timeout,
+ .ndo_get_stats = ax_ei_get_stats,
+ .ndo_set_rx_mode = ax_ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ax_ei_poll,
+#endif
+};
+
+static void ax_bb_mdc(struct mdiobb_ctrl *ctrl, int level)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (level)
+ ax->reg_memr |= AX_MEMR_MDC;
+ else
+ ax->reg_memr &= ~AX_MEMR_MDC;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_dir(struct mdiobb_ctrl *ctrl, int output)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (output)
+ ax->reg_memr &= ~AX_MEMR_MDIR;
+ else
+ ax->reg_memr |= AX_MEMR_MDIR;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_set_data(struct mdiobb_ctrl *ctrl, int value)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (value)
+ ax->reg_memr |= AX_MEMR_MDO;
+ else
+ ax->reg_memr &= ~AX_MEMR_MDO;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static int ax_bb_get_data(struct mdiobb_ctrl *ctrl)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+ int reg_memr = ei_inb(ax->addr_memr);
+
+ return reg_memr & AX_MEMR_MDI ? 1 : 0;
+}
+
+static struct mdiobb_ops bb_ops = {
+ .owner = THIS_MODULE,
+ .set_mdc = ax_bb_mdc,
+ .set_mdio_dir = ax_bb_dir,
+ .set_mdio_data = ax_bb_set_data,
+ .get_mdio_data = ax_bb_get_data,
+};
+
+/* setup code */
+
+static int ax_mii_init(struct net_device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev->dev.parent);
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ int err, i;
+
+ ax->bb_ctrl.ops = &bb_ops;
+ ax->addr_memr = ei_local->mem + AX_MEMR;
+ ax->mii_bus = alloc_mdio_bitbang(&ax->bb_ctrl);
+ if (!ax->mii_bus) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ax->mii_bus->name = "ax88796_mii_bus";
+ ax->mii_bus->parent = dev->dev.parent;
+ snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ pdev->name, pdev->id);
+
+ ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!ax->mii_bus->irq) {
+ err = -ENOMEM;
+ goto out_free_mdio_bitbang;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ax->mii_bus->irq[i] = PHY_POLL;
+
+ err = mdiobus_register(ax->mii_bus);
+ if (err)
+ goto out_free_irq;
+
+ return 0;
+
+ out_free_irq:
+ kfree(ax->mii_bus->irq);
+ out_free_mdio_bitbang:
+ free_mdio_bitbang(ax->mii_bus);
+ out:
+ return err;
+}
+
+static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local)
+{
+ void __iomem *ioaddr = ei_local->mem;
+ struct ax_device *ax = to_ax_dev(dev);
+
+ /* Select page 0 */
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_STOP, ioaddr + E8390_CMD);
+
+ /* set to byte access */
+ ei_outb(ax->plat->dcr_val & ~1, ioaddr + EN0_DCFG);
+ ei_outb(ax->plat->gpoc_val, ioaddr + EI_SHIFT(0x17));
+}
+
+/*
+ * ax_init_dev
+ *
+ * initialise the specified device, taking care to note the MAC
+ * address it may already have (if configured), ensure
+ * the device is ready to be used by lib8390.c and registerd with
+ * the network layer.
+ */
+static int ax_init_dev(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ void __iomem *ioaddr = ei_local->mem;
+ unsigned int start_page;
+ unsigned int stop_page;
+ int ret;
+ int i;
+
+ ret = ax_initial_check(dev);
+ if (ret)
+ goto err_out;
+
+ /* setup goes here */
+
+ ax_initial_setup(dev, ei_local);
+
+ /* read the mac from the card prom if we need it */
+
+ if (ax->plat->flags & AXFLG_HAS_EEPROM) {
+ unsigned char SA_prom[32];
+
+ for (i = 0; i < sizeof(SA_prom); i += 2) {
+ SA_prom[i] = ei_inb(ioaddr + NE_DATAPORT);
+ SA_prom[i + 1] = ei_inb(ioaddr + NE_DATAPORT);
+ }
+
+ if (ax->plat->wordlength == 2)
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+
+ memcpy(dev->dev_addr, SA_prom, ETH_ALEN);
+ }
+
+#ifdef CONFIG_AX88796_93CX6
+ if (ax->plat->flags & AXFLG_HAS_93CX6) {
+ unsigned char mac_addr[ETH_ALEN];
+ struct eeprom_93cx6 eeprom;
+
+ eeprom.data = ei_local;
+ eeprom.register_read = ax_eeprom_register_read;
+ eeprom.register_write = ax_eeprom_register_write;
+ eeprom.width = PCI_EEPROM_WIDTH_93C56;
+
+ eeprom_93cx6_multiread(&eeprom, 0,
+ (__le16 __force *)mac_addr,
+ sizeof(mac_addr) >> 1);
+
+ memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
+ }
+#endif
+ if (ax->plat->wordlength == 2) {
+ /* We must set the 8390 for word mode. */
+ ei_outb(ax->plat->dcr_val, ei_local->mem + EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+ /* load the mac-address from the device */
+ if (ax->plat->flags & AXFLG_MAC_FROMDEV) {
+ ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+ ei_local->mem + E8390_CMD); /* 0x61 */
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] =
+ ei_inb(ioaddr + EN1_PHYS_SHIFT(i));
+ }
+
+ if ((ax->plat->flags & AXFLG_MAC_FROMPLATFORM) &&
+ ax->plat->mac_addr)
+ memcpy(dev->dev_addr, ax->plat->mac_addr, ETH_ALEN);
+
+ ax_reset_8390(dev);
+
+ ei_local->name = "AX88796";
+ ei_local->tx_start_page = start_page;
+ ei_local->stop_page = stop_page;
+ ei_local->word16 = (ax->plat->wordlength == 2);
+ ei_local->rx_start_page = start_page + TX_PAGES;
+
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_local->stop_page = ei_local->tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_local->reset_8390 = &ax_reset_8390;
+ ei_local->block_input = &ax_block_input;
+ ei_local->block_output = &ax_block_output;
+ ei_local->get_8390_hdr = &ax_get_8390_hdr;
+ ei_local->priv = 0;
+ ei_local->msg_enable = ax_msg_enable;
+
+ dev->netdev_ops = &ax_netdev_ops;
+ dev->ethtool_ops = &ax_ethtool_ops;
+
+ ret = ax_mii_init(dev);
+ if (ret)
+ goto out_irq;
+
+ ax_NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto out_irq;
+
+ netdev_info(dev, "%dbit, irq %d, %lx, MAC: %pM\n",
+ ei_local->word16 ? 16 : 8, dev->irq, dev->base_addr,
+ dev->dev_addr);
+
+ return 0;
+
+ out_irq:
+ /* cleanup irq */
+ free_irq(dev->irq, dev);
+ err_out:
+ return ret;
+}
+
+static int ax_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ struct resource *mem;
+
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+
+ iounmap(ei_local->mem);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+
+ if (ax->map2) {
+ iounmap(ax->map2);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(mem->start, resource_size(mem));
+ }
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+/*
+ * ax_probe
+ *
+ * This is the entry point when the platform device system uses to
+ * notify us of a new device to attach to. Allocate memory, find the
+ * resources and information passed, and map the necessary registers.
+ */
+static int ax_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ei_device *ei_local;
+ struct ax_device *ax;
+ struct resource *irq, *mem, *mem2;
+ unsigned long mem_size, mem2_size = 0;
+ int ret = 0;
+
+ dev = ax__alloc_ei_netdev(sizeof(struct ax_device));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ /* ok, let's setup our device */
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ ei_local = netdev_priv(dev);
+ ax = to_ax_dev(dev);
+
+ ax->plat = dev_get_platdata(&pdev->dev);
+ platform_set_drvdata(pdev, dev);
+
+ ei_local->rxcr_base = ax->plat->rcr_val;
+
+ /* find the platform resources */
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no IRQ specified\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ dev->irq = irq->start;
+ ax->irqflags = irq->flags & IRQF_TRIGGER_MASK;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no MEM specified\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ mem_size = resource_size(mem);
+
+ /*
+ * setup the register offsets from either the platform data or
+ * by using the size of the resource provided
+ */
+ if (ax->plat->reg_offsets)
+ ei_local->reg_offset = ax->plat->reg_offsets;
+ else {
+ ei_local->reg_offset = ax->reg_offsets;
+ for (ret = 0; ret < 0x18; ret++)
+ ax->reg_offsets[ret] = (mem_size / 0x18) * ret;
+ }
+
+ if (!request_mem_region(mem->start, mem_size, pdev->name)) {
+ dev_err(&pdev->dev, "cannot reserve registers\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ ei_local->mem = ioremap(mem->start, mem_size);
+ dev->base_addr = (unsigned long)ei_local->mem;
+
+ if (ei_local->mem == NULL) {
+ dev_err(&pdev->dev, "Cannot ioremap area %pR\n", mem);
+
+ ret = -ENXIO;
+ goto exit_req;
+ }
+
+ /* look for reset area */
+ mem2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem2) {
+ if (!ax->plat->reg_offsets) {
+ for (ret = 0; ret < 0x20; ret++)
+ ax->reg_offsets[ret] = (mem_size / 0x20) * ret;
+ }
+ } else {
+ mem2_size = resource_size(mem2);
+
+ if (!request_mem_region(mem2->start, mem2_size, pdev->name)) {
+ dev_err(&pdev->dev, "cannot reserve registers\n");
+ ret = -ENXIO;
+ goto exit_mem1;
+ }
+
+ ax->map2 = ioremap(mem2->start, mem2_size);
+ if (!ax->map2) {
+ dev_err(&pdev->dev, "cannot map reset register\n");
+ ret = -ENXIO;
+ goto exit_mem2;
+ }
+
+ ei_local->reg_offset[0x1f] = ax->map2 - ei_local->mem;
+ }
+
+ /* got resources, now initialise and register device */
+ ret = ax_init_dev(dev);
+ if (!ret)
+ return 0;
+
+ if (!ax->map2)
+ goto exit_mem1;
+
+ iounmap(ax->map2);
+
+ exit_mem2:
+ release_mem_region(mem2->start, mem2_size);
+
+ exit_mem1:
+ iounmap(ei_local->mem);
+
+ exit_req:
+ release_mem_region(mem->start, mem_size);
+
+ exit_mem:
+ free_netdev(dev);
+
+ return ret;
+}
+
+/* suspend and resume */
+
+#ifdef CONFIG_PM
+static int ax_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+ struct ax_device *ax = to_ax_dev(ndev);
+
+ ax->resume_open = ax->running;
+
+ netif_device_detach(ndev);
+ ax_close(ndev);
+
+ return 0;
+}
+
+static int ax_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ax_device *ax = to_ax_dev(ndev);
+
+ ax_initial_setup(ndev, netdev_priv(ndev));
+ ax_NS8390_init(ndev, ax->resume_open);
+ netif_device_attach(ndev);
+
+ if (ax->resume_open)
+ ax_open(ndev);
+
+ return 0;
+}
+
+#else
+#define ax_suspend NULL
+#define ax_resume NULL
+#endif
+
+static struct platform_driver axdrv = {
+ .driver = {
+ .name = "ax88796",
+ .owner = THIS_MODULE,
+ },
+ .probe = ax_probe,
+ .remove = ax_remove,
+ .suspend = ax_suspend,
+ .resume = ax_resume,
+};
+
+module_platform_driver(axdrv);
+
+MODULE_DESCRIPTION("AX88796 10/100 Ethernet platform driver");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ax88796");
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
new file mode 100644
index 00000000000..73c57a4a7b9
--- /dev/null
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -0,0 +1,1706 @@
+/*======================================================================
+
+ A PCMCIA ethernet driver for Asix AX88190-based cards
+
+ The Asix AX88190 is a NS8390-derived chipset with a few nasty
+ idiosyncracies that make it very inconvenient to support with a
+ standard 8390 driver. This driver is based on pcnet_cs, with the
+ tweaked 8390 code grafted on the end. Much of what I did was to
+ clean up and update a similar driver supplied by Asix, which was
+ adapted by William Lee, william@asix.com.tw.
+
+ Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
+
+ axnet_cs.c 1.28 2002/06/29 06:27:37
+
+ The network driver code is based on Donald Becker's NE2000 code:
+
+ Written 1992,1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+ Donald Becker may be reached at becker@scyld.com
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define AXNET_CMD 0x00
+#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define AXNET_MII_EEP 0x14 /* Offset of MII access port */
+#define AXNET_TEST 0x15 /* Offset of TEST Register port */
+#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */
+
+#define AXNET_START_PG 0x40 /* First page of TX buffer */
+#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
+
+#define IS_AX88190 0x0001
+#define IS_AX88790 0x0002
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+
+static int axnet_config(struct pcmcia_device *link);
+static void axnet_release(struct pcmcia_device *link);
+static int axnet_open(struct net_device *dev);
+static int axnet_close(struct net_device *dev);
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void axnet_tx_timeout(struct net_device *dev);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void axnet_reset_8390(struct net_device *dev);
+
+static int mdio_read(unsigned int addr, int phy_id, int loc);
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value);
+
+static void get_8390_hdr(struct net_device *,
+ struct e8390_pkt_hdr *, int);
+static void block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page);
+
+static void axnet_detach(struct pcmcia_device *p_dev);
+
+static void AX88190_init(struct net_device *dev, int startp);
+static int ax_open(struct net_device *dev);
+static int ax_close(struct net_device *dev);
+static irqreturn_t ax_interrupt(int irq, void *dev_id);
+static u32 axnet_msg_enable;
+
+/*====================================================================*/
+
+typedef struct axnet_dev_t {
+ struct pcmcia_device *p_dev;
+ caddr_t base;
+ struct timer_list watchdog;
+ int stale, fast_poll;
+ u_short link_status;
+ u_char duplex_flag;
+ int phy_id;
+ int flags;
+ int active_low;
+} axnet_dev_t;
+
+static inline axnet_dev_t *PRIV(struct net_device *dev)
+{
+ void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
+ return p;
+}
+
+static const struct net_device_ops axnet_netdev_ops = {
+ .ndo_open = axnet_open,
+ .ndo_stop = axnet_close,
+ .ndo_do_ioctl = axnet_ioctl,
+ .ndo_start_xmit = axnet_start_xmit,
+ .ndo_tx_timeout = axnet_tx_timeout,
+ .ndo_get_stats = get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int axnet_probe(struct pcmcia_device *link)
+{
+ axnet_dev_t *info;
+ struct net_device *dev;
+ struct ei_device *ei_local;
+
+ dev_dbg(&link->dev, "axnet_attach()\n");
+
+ dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(axnet_dev_t));
+ if (!dev)
+ return -ENOMEM;
+
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = axnet_msg_enable;
+ spin_lock_init(&ei_local->page_lock);
+
+ info = PRIV(dev);
+ info->p_dev = link;
+ link->priv = dev;
+ link->config_flags |= CONF_ENABLE_IRQ;
+
+ dev->netdev_ops = &axnet_netdev_ops;
+
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ return axnet_config(link);
+} /* axnet_attach */
+
+static void axnet_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
+
+ unregister_netdev(dev);
+
+ axnet_release(link);
+
+ free_netdev(dev);
+} /* axnet_detach */
+
+/*======================================================================
+
+ This probes for a card's hardware address by reading the PROM.
+
+======================================================================*/
+
+static int get_prom(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int i, j;
+
+ /* This is based on drivers/net/ethernet/8390/ne.c */
+ struct {
+ u_char value, offset;
+ } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x01, EN0_DCFG}, /* Set word-wide access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {0x10, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */
+ {0x04, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+
+ /* Not much of a test, but the alternatives are messy */
+ if (link->config_base != 0x03c0)
+ return 0;
+
+ axnet_reset_8390(dev);
+ mdelay(10);
+
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ for (i = 0; i < 6; i += 2) {
+ j = inw(ioaddr + AXNET_DATAPORT);
+ dev->dev_addr[i] = j & 0xff;
+ dev->dev_addr[i+1] = j >> 8;
+ }
+ return 1;
+} /* get_prom */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+ int j, ret;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ if (link->resource[0]->end == 32) {
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ /* for master/slave multifunction cards */
+ if (link->resource[1]->end > 0)
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ } else {
+ /* This should be two 16-port windows */
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ }
+ if (link->resource[0]->start == 0) {
+ for (j = 0; j < 0x400; j += 0x20) {
+ link->resource[0]->start = j ^ 0x300;
+ link->resource[1]->start = (j ^ 0x300) + 0x10;
+ link->io_lines = 16;
+ ret = pcmcia_request_io(link);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+ } else {
+ return pcmcia_request_io(link);
+ }
+}
+
+static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ p_dev->config_index = 0x05;
+ if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+ return -ENODEV;
+
+ return try_io_port(p_dev);
+}
+
+static int axnet_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ axnet_dev_t *info = PRIV(dev);
+ int i, j, j2, ret;
+
+ dev_dbg(&link->dev, "axnet_config(0x%p)\n", link);
+
+ /* don't trust the CIS on this; Linksys got it wrong */
+ link->config_regs = 0x63;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+ ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
+ if (ret != 0)
+ goto failed;
+
+ if (!link->irq)
+ goto failed;
+
+ if (resource_size(link->resource[1]) == 8)
+ link->config_flags |= CONF_ENABLE_SPKR;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+
+ if (!get_prom(link)) {
+ pr_notice("this is not an AX88190 card!\n");
+ pr_notice("use pcnet_cs instead.\n");
+ goto failed;
+ }
+
+ ei_status.name = "AX88190";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = AXNET_START_PG;
+ ei_status.rx_start_page = AXNET_START_PG + TX_PAGES;
+ ei_status.stop_page = AXNET_STOP_PG;
+ ei_status.reset_8390 = axnet_reset_8390;
+ ei_status.get_8390_hdr = get_8390_hdr;
+ ei_status.block_input = block_input;
+ ei_status.block_output = block_output;
+
+ if (inb(dev->base_addr + AXNET_TEST) != 0)
+ info->flags |= IS_AX88790;
+ else
+ info->flags |= IS_AX88190;
+
+ if (info->flags & IS_AX88790)
+ outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */
+
+ info->active_low = 0;
+
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+ j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+ if (j == j2) continue;
+ if ((j != 0) && (j != 0xffff)) break;
+ }
+
+ if (i == 32) {
+ /* Maybe PHY is in power down mode. (PPD_SET = 1)
+ Bit 2 of CCSR is active low. */
+ pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+ j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+ if (j == j2) continue;
+ if ((j != 0) && (j != 0xffff)) {
+ info->active_low = 1;
+ break;
+ }
+ }
+ }
+
+ info->phy_id = (i < 32) ? i : -1;
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ pr_notice("register_netdev() failed\n");
+ goto failed;
+ }
+
+ netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n",
+ ((info->flags & IS_AX88790) ? 7 : 1),
+ dev->base_addr, dev->irq, dev->dev_addr);
+ if (info->phy_id != -1) {
+ netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
+ info->phy_id, j);
+ } else {
+ netdev_notice(dev, " No MII transceivers found!\n");
+ }
+ return 0;
+
+failed:
+ axnet_release(link);
+ return -ENODEV;
+} /* axnet_config */
+
+static void axnet_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static int axnet_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int axnet_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ axnet_dev_t *info = PRIV(dev);
+
+ if (link->open) {
+ if (info->active_low == 1)
+ pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+
+ axnet_reset_8390(dev);
+ AX88190_init(dev, 1);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+
+/*======================================================================
+
+ MII interface support
+
+======================================================================*/
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DATA_WRITE0 0x00
+#define MDIO_DATA_WRITE1 0x08
+#define MDIO_DATA_READ 0x04
+#define MDIO_MASK 0x0f
+#define MDIO_ENB_IN 0x02
+
+static void mdio_sync(unsigned int addr)
+{
+ int bits;
+ for (bits = 0; bits < 32; bits++) {
+ outb_p(MDIO_DATA_WRITE1, addr);
+ outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+ u_int cmd = (0xf6<<10)|(phy_id<<5)|loc;
+ int i, retval = 0;
+
+ mdio_sync(addr);
+ for (i = 14; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb_p(dat, addr);
+ outb_p(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb_p(MDIO_ENB_IN, addr);
+ retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0);
+ outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb_p(dat, addr);
+ outb_p(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb_p(MDIO_ENB_IN, addr);
+ outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*====================================================================*/
+
+static int axnet_open(struct net_device *dev)
+{
+ int ret;
+ axnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
+
+ dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name);
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev);
+ if (ret)
+ return ret;
+
+ link->open++;
+
+ info->link_status = 0x00;
+ init_timer(&info->watchdog);
+ info->watchdog.function = ei_watchdog;
+ info->watchdog.data = (u_long)dev;
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+
+ return ax_open(dev);
+} /* axnet_open */
+
+/*====================================================================*/
+
+static int axnet_close(struct net_device *dev)
+{
+ axnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+
+ dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name);
+
+ ax_close(dev);
+ free_irq(dev->irq, dev);
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&info->watchdog);
+
+ return 0;
+} /* axnet_close */
+
+/*======================================================================
+
+ Hard reset the card. This used to pause for the same period that
+ a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void axnet_reset_8390(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ int i;
+
+ ei_status.txing = ei_status.dmaing = 0;
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+ outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET);
+
+ for (i = 0; i < 100; i++) {
+ if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+ break;
+ udelay(100);
+ }
+ outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+
+ if (i == 100)
+ netdev_err(dev, "axnet_reset_8390() did not complete\n");
+
+} /* axnet_reset_8390 */
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ PRIV(dev)->stale = 0;
+ return ax_interrupt(irq, dev_id);
+}
+
+static void ei_watchdog(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)(arg);
+ axnet_dev_t *info = PRIV(dev);
+ unsigned int nic_base = dev->base_addr;
+ unsigned int mii_addr = nic_base + AXNET_MII_EEP;
+ u_short link;
+
+ if (!netif_device_present(dev)) goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+ if (!info->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+ ei_irq_wrapper(dev->irq, dev);
+ info->fast_poll = HZ;
+ }
+ if (info->fast_poll) {
+ info->fast_poll--;
+ info->watchdog.expires = jiffies + 1;
+ add_timer(&info->watchdog);
+ return;
+ }
+
+ if (info->phy_id < 0)
+ goto reschedule;
+ link = mdio_read(mii_addr, info->phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ netdev_info(dev, "MII is missing!\n");
+ info->phy_id = -1;
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != info->link_status) {
+ u_short p = mdio_read(mii_addr, info->phy_id, 5);
+ netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+ if (link) {
+ info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00;
+ if (p)
+ netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n",
+ (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H');
+ else
+ netdev_info(dev, "link partner did not autonegotiate\n");
+ AX88190_init(dev, 1);
+ }
+ info->link_status = link;
+ }
+
+reschedule:
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ axnet_dev_t *info = PRIV(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP;
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = info->phy_id;
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+ return 0;
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+ insw(nic_base + AXNET_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr)>>1);
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+
+}
+
+/*====================================================================*/
+
+static void block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned int nic_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ if ((netif_msg_rx_status(ei_local)) && (count != 4))
+ netdev_dbg(dev, "[bi=%d]\n", count+4);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+ insw(nic_base + AXNET_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base + AXNET_DATAPORT), xfer_count++;
+
+}
+
+/*====================================================================*/
+
+static void block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ pr_debug("%s: [bo=%d]\n", dev->name, count);
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (count & 0x01)
+ count++;
+
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD);
+ outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
+}
+
+static const struct pcmcia_device_id axnet_ids[] = {
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081),
+ PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301),
+ PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106),
+ PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab),
+ PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202),
+ PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc),
+ PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef),
+ PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef),
+ PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1),
+ PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc),
+ PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061),
+ PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90),
+ PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058),
+ PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, axnet_ids);
+
+static struct pcmcia_driver axnet_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "axnet_cs",
+ .probe = axnet_probe,
+ .remove = axnet_detach,
+ .id_table = axnet_ids,
+ .suspend = axnet_suspend,
+ .resume = axnet_resume,
+};
+module_pcmcia_driver(axnet_cs_driver);
+
+/*====================================================================*/
+
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Seeing how at least eight drivers use this code, (not counting the
+ PCMCIA ones either) it is easy to break some card by what seems like
+ a simple innocent change. Please contact me or Donald if you think
+ you have found something that needs changing. -- PG
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+ Paul Gortmaker : exchange static int ei_pingpong for a #define,
+ also add better Tx error handling.
+ Paul Gortmaker : rewrite Rx overrun handling as per NS specs.
+ Alexey Kuznetsov : use the 8390's six bit hash multicast filter.
+ Paul Gortmaker : tweak ANK's above multicast changes a bit.
+ Paul Gortmaker : update packet statistics for v2.1.x
+ Alan Cox : support arbitrary stupid port mappings on the
+ 68K Macintosh. Support >16bit I/O spaces
+ Paul Gortmaker : add kmod support for auto-loading of the 8390
+ module by all drivers that require it.
+ Alan Cox : Spinlocking work, added 'BUG_83C690'
+ Paul Gortmaker : Separate out Tx timeout code from Tx path.
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+ */
+
+#include <linux/bitops.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct net_device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+
+/*
+ * SMP and the 8390 setup.
+ *
+ * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ * a page register that controls bank and packet buffer access. We guard
+ * this with ei_local->page_lock. Nobody should assume or set the page other
+ * than zero when the lock is not held. Lock holders must restore page 0
+ * before unlocking. Even pure readers must take the lock to protect in
+ * page 0.
+ *
+ * To make life difficult the chip can also be very slow. We therefore can't
+ * just use spinlocks. For the longer lockups we disable the irq the device
+ * sits on and hold the lock. We must hold the lock because there is a dual
+ * processor case other than interrupts (get stats/set multicast list in
+ * parallel with each other and transmit).
+ *
+ * Note: in theory we can just disable the irq on the card _but_ there is
+ * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ * enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ * Finally by special arrangement for the purpose of being generally
+ * annoying the transmit function is called bh atomic. That places
+ * restrictions on the user context callers as disable_irq won't save
+ * them.
+ */
+
+/**
+ * ax_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
+static int ax_open(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ /*
+ * Grab the page lock so we own the register set, then call
+ * the init function.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ AX88190_init(dev, 1);
+ /* Set the flag before we drop the lock, That way the IRQ arrives
+ after its set and we get no silly warnings */
+ netif_start_queue(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock)
+
+/**
+ * ax_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int ax_close(struct net_device *dev)
+{
+ unsigned long flags;
+
+ /*
+ * Hold the page lock during close
+ */
+
+ spin_lock_irqsave(&dev_lock(dev), flags);
+ AX88190_init(dev, 0);
+ spin_unlock_irqrestore(&dev_lock(dev), flags);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/**
+ * axnet_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void axnet_tx_timeout(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+ unsigned long flags;
+
+ dev->stats.tx_errors++;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ txsr = inb(e8390_base+EN0_TSR);
+ isr = inb(e8390_base+EN0_ISR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+ (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?",
+ txsr, isr, tickssofar);
+
+ if (!isr && !dev->stats.tx_packets)
+ {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Ugly but a reset can be slow, yet must be protected */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ AX88190_init(dev, 1);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ netif_wake_queue(dev);
+}
+
+/**
+ * axnet_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int length, send_length, output_page;
+ unsigned long flags;
+ u8 packet[ETH_ZLEN];
+
+ netif_stop_queue(dev);
+
+ length = skb->len;
+
+ /* Mask interrupts from the ethercard.
+ SMP: We have to grab the lock here otherwise the IRQ handler
+ on another CPU can flip window and race the IRQ mask set. We end
+ up trashing the mcast filter not disabling irqs if we don't lock */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /*
+ * Slow phase with lock held.
+ */
+
+ ei_local->irqlock = 1;
+
+ send_length = max(length, ETH_ZLEN);
+
+ /*
+ * We have two Tx slots available for use. Find the first free
+ * slot, and then perform some sanity checks. With two Tx bufs,
+ * you get very close to transmitting back-to-back packets. With
+ * only one Tx buf, the transmitter sits idle while you reload the
+ * card, leaving a substantial gap between each transmitted packet.
+ */
+
+ if (ei_local->tx1 == 0)
+ {
+ output_page = ei_local->tx_start_page;
+ ei_local->tx1 = send_length;
+ if ((netif_msg_tx_queued(ei_local)) &&
+ ei_local->tx2 > 0)
+ netdev_dbg(dev,
+ "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx2, ei_local->lasttx,
+ ei_local->txing);
+ }
+ else if (ei_local->tx2 == 0)
+ {
+ output_page = ei_local->tx_start_page + TX_PAGES/2;
+ ei_local->tx2 = send_length;
+ if ((netif_msg_tx_queued(ei_local)) &&
+ ei_local->tx1 > 0)
+ netdev_dbg(dev,
+ "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx1, ei_local->lasttx,
+ ei_local->txing);
+ }
+ else
+ { /* We should never get here. */
+ netif_dbg(ei_local, tx_err, dev,
+ "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+ ei_local->tx1, ei_local->tx2,
+ ei_local->lasttx);
+ ei_local->irqlock = 0;
+ netif_stop_queue(dev);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_BUSY;
+ }
+
+ /*
+ * Okay, now upload the packet and trigger a send if the transmitter
+ * isn't already sending. If it is busy, the interrupt handler will
+ * trigger the send later, upon receiving a Tx done interrupt.
+ */
+
+ if (length == skb->len)
+ ei_block_output(dev, length, skb->data, output_page);
+ else {
+ memset(packet, 0, ETH_ZLEN);
+ skb_copy_from_linear_data(skb, packet, skb->len);
+ ei_block_output(dev, length, packet, output_page);
+ }
+
+ if (! ei_local->txing)
+ {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, output_page);
+ dev->trans_start = jiffies;
+ if (output_page == ei_local->tx_start_page)
+ {
+ ei_local->tx1 = -1;
+ ei_local->lasttx = -1;
+ }
+ else
+ {
+ ei_local->tx2 = -1;
+ ei_local->lasttx = -2;
+ }
+ }
+ else ei_local->txqueue++;
+
+ if (ei_local->tx1 && ei_local->tx2)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ dev_kfree_skb (skb);
+ dev->stats.tx_bytes += send_length;
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ax_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t ax_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ long e8390_base;
+ int interrupts, nr_serviced = 0, i;
+ struct ei_device *ei_local;
+ int handled = 0;
+ unsigned long flags;
+
+ e8390_base = dev->base_addr;
+ ei_local = netdev_priv(dev);
+
+ /*
+ * Protect the irq test too.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+
+ if (ei_local->irqlock) {
+#if 1 /* This might just be an interrupt for a PCI device sharing this line */
+ const char *msg;
+ /* The "irqlock" check is only for testing. */
+ if (ei_local->irqlock)
+ msg = "Interrupted while interrupts are masked!";
+ else
+ msg = "Reentering the interrupt handler!";
+ netdev_info(dev, "%s, isr=%#2x imr=%#2x\n",
+ msg,
+ inb_p(e8390_base + EN0_ISR),
+ inb_p(e8390_base + EN0_IMR));
+#endif
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ return IRQ_NONE;
+ }
+
+ netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n",
+ inb_p(e8390_base + EN0_ISR));
+
+ outb_p(0x00, e8390_base + EN0_ISR);
+ ei_local->irqlock = 1;
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE)
+ {
+ if (!netif_running(dev) || (interrupts == 0xff)) {
+ netif_warn(ei_local, intr, dev,
+ "interrupt from stopped card\n");
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ interrupts = 0;
+ break;
+ }
+ handled = 1;
+
+ /* AX88190 bug fix. */
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ for (i = 0; i < 10; i++) {
+ if (!(inb(e8390_base + EN0_ISR) & interrupts))
+ break;
+ outb_p(0, e8390_base + EN0_ISR);
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ }
+ if (interrupts & ENISR_OVER)
+ ei_rx_overrun(dev);
+ else if (interrupts & (ENISR_RX+ENISR_RX_ERR))
+ {
+ /* Got a good (?) packet. */
+ ei_receive(dev);
+ }
+ /* Push the next to-transmit packet through. */
+ if (interrupts & ENISR_TX)
+ ei_tx_intr(dev);
+ else if (interrupts & ENISR_TX_ERR)
+ ei_tx_err(dev);
+
+ if (interrupts & ENISR_COUNTERS)
+ {
+ dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
+ dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+ }
+ }
+
+ if (interrupts && (netif_msg_intr(ei_local)))
+ {
+ handled = 1;
+ if (nr_serviced >= MAX_SERVICE)
+ {
+ /* 0xFF is valid for a card removal */
+ if (interrupts != 0xFF)
+ netdev_warn(dev,
+ "Too much work at interrupt, status %#2.2x\n",
+ interrupts);
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ netdev_warn(dev, "unknown interrupt %#2x\n",
+ interrupts);
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ return IRQ_RETVAL(handled);
+}
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+ unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+ netdev_dbg(dev, "transmitter error (%#2x):", txsr);
+ if (txsr & ENTSR_ABT)
+ pr_cont(" excess-collisions");
+ if (txsr & ENTSR_ND)
+ pr_cont(" non-deferral");
+ if (txsr & ENTSR_CRS)
+ pr_cont(" lost-carrier");
+ if (txsr & ENTSR_FU)
+ pr_cont(" FIFO-underrun");
+ if (txsr & ENTSR_CDH)
+ pr_cont(" lost-heartbeat");
+ pr_cont("\n");
+#endif
+
+ if (tx_was_aborted)
+ ei_tx_intr(dev);
+ else
+ {
+ dev->stats.tx_errors++;
+ if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++;
+ if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++;
+ if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++;
+ }
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int status = inb(e8390_base + EN0_TSR);
+
+ /*
+ * There are two Tx buffers, see which one finished, and trigger
+ * the send of another one if it exists.
+ */
+ ei_local->txqueue--;
+
+ if (ei_local->tx1 < 0)
+ {
+ if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+ netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n",
+ ei_local->name, ei_local->lasttx,
+ ei_local->tx1);
+ ei_local->tx1 = 0;
+ if (ei_local->tx2 > 0)
+ {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+ dev->trans_start = jiffies;
+ ei_local->tx2 = -1,
+ ei_local->lasttx = 2;
+ }
+ else ei_local->lasttx = 20, ei_local->txing = 0;
+ }
+ else if (ei_local->tx2 < 0)
+ {
+ if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
+ netdev_err(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n",
+ ei_local->name, ei_local->lasttx,
+ ei_local->tx2);
+ ei_local->tx2 = 0;
+ if (ei_local->tx1 > 0)
+ {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ ei_local->tx1 = -1;
+ ei_local->lasttx = 1;
+ }
+ else
+ ei_local->lasttx = 10, ei_local->txing = 0;
+ }
+// else
+// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+// ei_local->lasttx);
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL)
+ dev->stats.collisions++;
+ if (status & ENTSR_PTX)
+ dev->stats.tx_packets++;
+ else
+ {
+ dev->stats.tx_errors++;
+ if (status & ENTSR_ABT)
+ {
+ dev->stats.tx_aborted_errors++;
+ dev->stats.collisions += 16;
+ }
+ if (status & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (status & ENTSR_FU)
+ dev->stats.tx_fifo_errors++;
+ if (status & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers.
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char rxing_page, this_frame, next_frame;
+ unsigned short current_offset;
+ int rx_pkt_count = 0;
+ struct e8390_pkt_hdr rx_frame;
+
+ while (++rx_pkt_count < 10)
+ {
+ int pkt_len, pkt_stat;
+
+ /* Get the rx page (incoming packet pointer). */
+ rxing_page = inb_p(e8390_base + EN1_CURPAG -1);
+
+ /* Remove one frame from the ring. Boundary is always a page behind. */
+ this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ if (this_frame >= ei_local->stop_page)
+ this_frame = ei_local->rx_start_page;
+
+ /* Someday we'll omit the previous, iff we never get this message.
+ (There is at least one clone claimed to have a problem.)
+
+ Keep quiet if it looks like a card removal. One problem here
+ is that some clones crash in roughly the same way.
+ */
+ if ((netif_msg_rx_err(ei_local)) &&
+ this_frame != ei_local->current_page &&
+ (this_frame != 0x0 || rxing_page != 0xFF))
+ netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+ this_frame, ei_local->current_page);
+
+ if (this_frame == rxing_page) /* Read all the frames? */
+ break; /* Done for now */
+
+ current_offset = this_frame << 8;
+ ei_get_8390_hdr(dev, &rx_frame, this_frame);
+
+ pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+ pkt_stat = rx_frame.status;
+
+ next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+ if (pkt_len < 60 || pkt_len > 1518)
+ {
+ netif_err(ei_local, rx_err, dev,
+ "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ }
+ else if ((pkt_stat & 0x0F) == ENRSR_RXOK)
+ {
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (skb == NULL)
+ {
+ netif_err(ei_local, rx_err, dev,
+ "Couldn't allocate a sk_buff of size %d\n",
+ pkt_len);
+ dev->stats.rx_dropped++;
+ break;
+ }
+ else
+ {
+ skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
+ skb_put(skb, pkt_len); /* Make room */
+ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ if (pkt_stat & ENRSR_PHY)
+ dev->stats.multicast++;
+ }
+ }
+ else
+ {
+ netif_err(ei_local, rx_err, dev,
+ "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ dev->stats.rx_errors++;
+ /* NB: The NIC counts CRC, frame and missed errors. */
+ if (pkt_stat & ENRSR_FO)
+ dev->stats.rx_fifo_errors++;
+ }
+ next_frame = rx_frame.next;
+
+ /* This _should_ never happen: it's here for avoiding bad clones. */
+ if (next_frame >= ei_local->stop_page) {
+ netdev_info(dev, "next frame inconsistency, %#2x\n",
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network." Ugh.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so.
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+ axnet_dev_t *info = PRIV(dev);
+ long e8390_base = dev->base_addr;
+ unsigned char was_txing, must_resend = 0;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ /*
+ * Record whether a Tx was in progress and then issue the
+ * stop command.
+ */
+ was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n");
+ dev->stats.rx_over_errors++;
+
+ /*
+ * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+ * We wait at least 2ms.
+ */
+
+ mdelay(2);
+
+ /*
+ * Reset RBCR[01] back to zero as per magic incantation.
+ */
+ outb_p(0x00, e8390_base+EN0_RCNTLO);
+ outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+ /*
+ * See if any Tx was interrupted or not. According to NS, this
+ * step is vital, and skipping it will cause no end of havoc.
+ */
+
+ if (was_txing)
+ {
+ unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+ if (!tx_completed)
+ must_resend = 1;
+ }
+
+ /*
+ * Have to enter loopback mode and then restart the NIC before
+ * you are allowed to slurp packets up off the ring.
+ */
+ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+ /*
+ * Clear the Rx ring of all the debris, and ack the interrupt.
+ */
+ ei_receive(dev);
+
+ /*
+ * Leave loopback mode, and resend any packet that got stopped.
+ */
+ outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR);
+ if (must_resend)
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ * Collect the stats. This is called unlocked and from several contexts.
+ */
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /* If the card is stopped, just return the present stats. */
+ if (!netif_running(dev))
+ return &dev->stats;
+
+ spin_lock_irqsave(&ei_local->page_lock,flags);
+ /* Read the counter registers, assuming we are in page 0. */
+ dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
+ dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc(ETH_ALEN, ha->addr);
+ /*
+ * The 8390 uses the 6 most significant bits of the
+ * CRC to index the multicast table.
+ */
+ bits[crc>>29] |= (1<<((crc>>26)&7));
+ }
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ * Set or clear the multicast filter for this adaptor.
+ * Must be called with lock held.
+ */
+
+static void do_set_multicast_list(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ int i;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+ memset(ei_local->mcfilter, 0, 8);
+ if (!netdev_mc_empty(dev))
+ make_mc_bits(ei_local->mcfilter, dev);
+ } else {
+ /* set to accept-all */
+ memset(ei_local->mcfilter, 0xFF, 8);
+ }
+
+ outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+ for(i = 0; i < 8; i++)
+ {
+ outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+ }
+ outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+ if(dev->flags&IFF_PROMISC)
+ outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
+ else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+ outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
+ else
+ outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+}
+
+/*
+ * Called without lock held. This is invoked from user context and may
+ * be parallel to just about everything else. Its also fairly quick and
+ * not called too often. Must protect against both bh and irq users
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_lock(dev), flags);
+ do_set_multicast_list(dev);
+ spin_unlock_irqrestore(&dev_lock(dev), flags);
+}
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * AX88190_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean. non-zero value to initiate chip processing
+ *
+ * Must be called with lock held.
+ */
+
+static void AX88190_init(struct net_device *dev, int startp)
+{
+ axnet_dev_t *info = PRIV(dev);
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int i;
+ int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+
+ if(sizeof(struct e8390_pkt_hdr)!=4)
+ panic("8390.c: header struct mispacked\n");
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+ outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ outb_p(0x00, e8390_base + EN0_RCNTLO);
+ outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */
+ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
+ outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ outb_p(0xFF, e8390_base + EN0_ISR);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers. */
+
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+ for(i = 0; i < 6; i++)
+ {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
+ netdev_err(dev, "Hw. address read/write mismap %d\n", i);
+ }
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ netif_start_queue(dev);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+
+ if (info->flags & IS_AX88790) /* select Internal PHY */
+ outb(0x10, e8390_base + AXNET_GPIO);
+
+ if (startp)
+ {
+ outb_p(0xff, e8390_base + EN0_ISR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+ outb_p(E8390_TXCONFIG | info->duplex_flag,
+ e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
+ do_set_multicast_list(dev); /* (re)load the mcast table */
+ }
+}
+
+/* Trigger a transmit start, assuming the length is valid.
+ Always called with the page lock held */
+
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+
+ if (inb_p(e8390_base) & E8390_TRANS)
+ {
+ netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+ return;
+ }
+ outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ outb_p(start_page, e8390_base + EN0_TPSR);
+ outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c
new file mode 100644
index 00000000000..b36ee9e0d22
--- /dev/null
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -0,0 +1,874 @@
+/*
+ * linux/drivers/acorn/net/etherh.c
+ *
+ * Copyright (C) 2000-2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NS8390 I-cubed EtherH and ANT EtherM specific driver
+ * Thanks to I-Cubed for information on their cards.
+ * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
+ * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
+ * EtherM integration re-engineered by Russell King.
+ *
+ * Changelog:
+ * 08-12-1996 RMK 1.00 Created
+ * RMK 1.03 Added support for EtherLan500 cards
+ * 23-11-1997 RMK 1.04 Added media autodetection
+ * 16-04-1998 RMK 1.05 Improved media autodetection
+ * 10-02-2000 RMK 1.06 Updated for 2.3.43
+ * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8
+ * 12-10-1999 CK/TEW EtherM driver first release
+ * 21-12-2000 TTC EtherH/EtherM integration
+ * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver.
+ * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/system_info.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+
+#define ei_inb(_p) readb((void __iomem *)_p)
+#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p)
+#define ei_inb_p(_p) readb((void __iomem *)_p)
+#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p)
+
+#define DRV_NAME "etherh"
+#define DRV_VERSION "1.11"
+
+static char version[] =
+ "EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
+
+#include "lib8390.c"
+
+static u32 etherh_msg_enable;
+
+struct etherh_priv {
+ void __iomem *ioc_fast;
+ void __iomem *memc;
+ void __iomem *dma_base;
+ unsigned int id;
+ void __iomem *ctrl_port;
+ unsigned char ctrl;
+ u32 supported;
+};
+
+struct etherh_data {
+ unsigned long ns8390_offset;
+ unsigned long dataport_offset;
+ unsigned long ctrlport_offset;
+ int ctrl_ioc;
+ const char name[16];
+ u32 supported;
+ unsigned char tx_start_page;
+ unsigned char stop_page;
+};
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("EtherH/EtherM driver");
+MODULE_LICENSE("GPL");
+
+#define ETHERH500_DATAPORT 0x800 /* MEMC */
+#define ETHERH500_NS8390 0x000 /* MEMC */
+#define ETHERH500_CTRLPORT 0x800 /* IOC */
+
+#define ETHERH600_DATAPORT 0x040 /* MEMC */
+#define ETHERH600_NS8390 0x800 /* MEMC */
+#define ETHERH600_CTRLPORT 0x200 /* MEMC */
+
+#define ETHERH_CP_IE 1
+#define ETHERH_CP_IF 2
+#define ETHERH_CP_HEARTBEAT 2
+
+#define ETHERH_TX_START_PAGE 1
+#define ETHERH_STOP_PAGE 127
+
+/*
+ * These came from CK/TEW
+ */
+#define ETHERM_DATAPORT 0x200 /* MEMC */
+#define ETHERM_NS8390 0x800 /* MEMC */
+#define ETHERM_CTRLPORT 0x23c /* MEMC */
+
+#define ETHERM_TX_START_PAGE 64
+#define ETHERM_STOP_PAGE 127
+
+/* ------------------------------------------------------------------------ */
+
+#define etherh_priv(dev) \
+ ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
+
+static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+ unsigned char ctrl = eh->ctrl | mask;
+ eh->ctrl = ctrl;
+ writeb(ctrl, eh->ctrl_port);
+}
+
+static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+ unsigned char ctrl = eh->ctrl & ~mask;
+ eh->ctrl = ctrl;
+ writeb(ctrl, eh->ctrl_port);
+}
+
+static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
+{
+ return readb(eh->ctrl_port);
+}
+
+
+
+
+static void etherh_irq_enable(ecard_t *ec, int irqnr)
+{
+ struct etherh_priv *eh = ec->irq_data;
+
+ etherh_set_ctrl(eh, ETHERH_CP_IE);
+}
+
+static void etherh_irq_disable(ecard_t *ec, int irqnr)
+{
+ struct etherh_priv *eh = ec->irq_data;
+
+ etherh_clr_ctrl(eh, ETHERH_CP_IE);
+}
+
+static expansioncard_ops_t etherh_ops = {
+ .irqenable = etherh_irq_enable,
+ .irqdisable = etherh_irq_disable,
+};
+
+
+
+
+static void
+etherh_setif(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+ void __iomem *addr;
+
+ local_irq_save(flags);
+
+ /* set the interface type */
+ switch (etherh_priv(dev)->id) {
+ case PROD_I3_ETHERLAN600:
+ case PROD_I3_ETHERLAN600A:
+ addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ writeb((readb(addr) & 0xf8) | 1, addr);
+ break;
+ case IF_PORT_10BASET:
+ writeb((readb(addr) & 0xf8), addr);
+ break;
+ }
+ break;
+
+ case PROD_I3_ETHERLAN500:
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+ break;
+
+ case IF_PORT_10BASET:
+ etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ local_irq_restore(flags);
+}
+
+static int
+etherh_getifstat(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *addr;
+ int stat = 0;
+
+ switch (etherh_priv(dev)->id) {
+ case PROD_I3_ETHERLAN600:
+ case PROD_I3_ETHERLAN600A:
+ addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ stat = 1;
+ break;
+ case IF_PORT_10BASET:
+ stat = readb(addr) & 4;
+ break;
+ }
+ break;
+
+ case PROD_I3_ETHERLAN500:
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ stat = 1;
+ break;
+ case IF_PORT_10BASET:
+ stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
+ break;
+ }
+ break;
+
+ default:
+ stat = 0;
+ break;
+ }
+
+ return stat != 0;
+}
+
+/*
+ * Configure the interface. Note that we ignore the other
+ * parts of ifmap, since its mostly meaningless for this driver.
+ */
+static int etherh_set_config(struct net_device *dev, struct ifmap *map)
+{
+ switch (map->port) {
+ case IF_PORT_10BASE2:
+ case IF_PORT_10BASET:
+ /*
+ * If the user explicitly sets the interface
+ * media type, turn off automedia detection.
+ */
+ dev->flags &= ~IFF_AUTOMEDIA;
+ dev->if_port = map->port;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ etherh_setif(dev);
+
+ return 0;
+}
+
+/*
+ * Reset the 8390 (hard reset). Note that we can't actually do this.
+ */
+static void
+etherh_reset(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *addr = (void __iomem *)dev->base_addr;
+
+ writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
+
+ /*
+ * See if we need to change the interface type.
+ * Note that we use 'interface_num' as a flag
+ * to indicate that we need to change the media.
+ */
+ if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
+ ei_local->interface_num = 0;
+
+ if (dev->if_port == IF_PORT_10BASET)
+ dev->if_port = IF_PORT_10BASE2;
+ else
+ dev->if_port = IF_PORT_10BASET;
+
+ etherh_setif(dev);
+ }
+}
+
+/*
+ * Write a block of data out to the 8390
+ */
+static void
+etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long dma_start;
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in etherh_block_input: "
+ " DMAstat %d irqlock %d\n",
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ /*
+ * Make sure we have a round number of bytes if we're in word mode.
+ */
+ if (count & 1 && ei_local->word16)
+ count++;
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ count = (count + 1) & ~1;
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+
+ writeb (0x42, addr + EN0_RCNTLO);
+ writeb (0x00, addr + EN0_RCNTHI);
+ writeb (0x42, addr + EN0_RSARLO);
+ writeb (0x00, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ udelay (1);
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ writeb (count, addr + EN0_RCNTLO);
+ writeb (count >> 8, addr + EN0_RCNTHI);
+ writeb (0, addr + EN0_RSARLO);
+ writeb (start_page, addr + EN0_RSARHI);
+ writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16)
+ writesw (dma_base, buf, count >> 1);
+ else
+ writesb (dma_base, buf, count);
+
+ dma_start = jiffies;
+
+ while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for TX RDC\n");
+ etherh_reset (dev);
+ __NS8390_init (dev, 1);
+ break;
+ }
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * Read a block of data from the 8390
+ */
+static void
+etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char *buf;
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in etherh_block_input: "
+ " DMAstat %d irqlock %d\n",
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ buf = skb->data;
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+ writeb (count, addr + EN0_RCNTLO);
+ writeb (count >> 8, addr + EN0_RCNTHI);
+ writeb (ring_offset, addr + EN0_RSARLO);
+ writeb (ring_offset >> 8, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16) {
+ readsw (dma_base, buf, count >> 1);
+ if (count & 1)
+ buf[count - 1] = readb (dma_base);
+ } else
+ readsb (dma_base, buf, count);
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * Read a header from the 8390
+ */
+static void
+etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in etherh_get_header: "
+ " DMAstat %d irqlock %d\n",
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+ writeb (sizeof (*hdr), addr + EN0_RCNTLO);
+ writeb (0, addr + EN0_RCNTHI);
+ writeb (0, addr + EN0_RSARLO);
+ writeb (ring_page, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16)
+ readsw (dma_base, hdr, sizeof (*hdr) >> 1);
+ else
+ readsb (dma_base, hdr, sizeof (*hdr));
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * 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
+etherh_open(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev))
+ return -EAGAIN;
+
+ /*
+ * Make sure that we aren't going to change the
+ * media type on the next reset - we are about to
+ * do automedia manually now.
+ */
+ ei_local->interface_num = 0;
+
+ /*
+ * If we are doing automedia detection, do it now.
+ * This is more reliable than the 8390's detection.
+ */
+ if (dev->flags & IFF_AUTOMEDIA) {
+ dev->if_port = IF_PORT_10BASET;
+ etherh_setif(dev);
+ mdelay(1);
+ if (!etherh_getifstat(dev)) {
+ dev->if_port = IF_PORT_10BASE2;
+ etherh_setif(dev);
+ }
+ } else
+ etherh_setif(dev);
+
+ etherh_reset(dev);
+ __ei_open(dev);
+
+ return 0;
+}
+
+/*
+ * The inverse routine to etherh_open().
+ */
+static int
+etherh_close(struct net_device *dev)
+{
+ __ei_close (dev);
+ free_irq (dev->irq, dev);
+ return 0;
+}
+
+/*
+ * Initialisation
+ */
+
+static void __init etherh_banner(void)
+{
+ static int version_printed;
+
+ if ((etherh_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ pr_info("%s", version);
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string...
+ */
+static int etherh_addr(char *addr, struct expansion_card *ec)
+{
+ struct in_chunk_dir cd;
+ char *s;
+
+ if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
+ printk(KERN_ERR "%s: unable to read podule description string\n",
+ dev_name(&ec->dev));
+ goto no_addr;
+ }
+
+ s = strchr(cd.d.string, '(');
+ if (s) {
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ addr[i] = simple_strtoul(s + 1, &s, 0x10);
+ if (*s != (i == 5? ')' : ':'))
+ break;
+ }
+
+ if (i == 6)
+ return 0;
+ }
+
+ printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
+ dev_name(&ec->dev), cd.d.string);
+
+ no_addr:
+ return -ENODEV;
+}
+
+/*
+ * Create an ethernet address from the system serial number.
+ */
+static int __init etherm_addr(char *addr)
+{
+ unsigned int serial;
+
+ if (system_serial_low == 0 && system_serial_high == 0)
+ return -ENODEV;
+
+ serial = system_serial_low | system_serial_high;
+
+ addr[0] = 0;
+ addr[1] = 0;
+ addr[2] = 0xa4;
+ addr[3] = 0x10 + (serial >> 24);
+ addr[4] = serial >> 16;
+ addr[5] = serial >> 8;
+ return 0;
+}
+
+static void etherh_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 etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = etherh_priv(dev)->supported;
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+ cmd->duplex = DUPLEX_HALF;
+ cmd->port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
+ cmd->autoneg = (dev->flags & IFF_AUTOMEDIA ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE);
+ return 0;
+}
+
+static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ switch (cmd->autoneg) {
+ case AUTONEG_ENABLE:
+ dev->flags |= IFF_AUTOMEDIA;
+ break;
+
+ case AUTONEG_DISABLE:
+ switch (cmd->port) {
+ case PORT_TP:
+ dev->if_port = IF_PORT_10BASET;
+ break;
+
+ case PORT_BNC:
+ dev->if_port = IF_PORT_10BASE2;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ dev->flags &= ~IFF_AUTOMEDIA;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ etherh_setif(dev);
+
+ return 0;
+}
+
+static u32 etherh_get_msglevel(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ return ei_local->msg_enable;
+}
+
+static void etherh_set_msglevel(struct net_device *dev, u32 v)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ ei_local->msg_enable = v;
+}
+
+static const struct ethtool_ops etherh_ethtool_ops = {
+ .get_settings = etherh_get_settings,
+ .set_settings = etherh_set_settings,
+ .get_drvinfo = etherh_get_drvinfo,
+ .get_ts_info = ethtool_op_get_ts_info,
+ .get_msglevel = etherh_get_msglevel,
+ .set_msglevel = etherh_set_msglevel,
+};
+
+static const struct net_device_ops etherh_netdev_ops = {
+ .ndo_open = etherh_open,
+ .ndo_stop = etherh_close,
+ .ndo_set_config = etherh_set_config,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static u32 etherh_regoffsets[16];
+static u32 etherm_regoffsets[16];
+
+static int
+etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ const struct etherh_data *data = id->data;
+ struct ei_device *ei_local;
+ struct net_device *dev;
+ struct etherh_priv *eh;
+ int ret;
+
+ etherh_banner();
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ SET_NETDEV_DEV(dev, &ec->dev);
+
+ dev->netdev_ops = &etherh_netdev_ops;
+ dev->irq = ec->irq;
+ dev->ethtool_ops = &etherh_ethtool_ops;
+
+ if (data->supported & SUPPORTED_Autoneg)
+ dev->flags |= IFF_AUTOMEDIA;
+ if (data->supported & SUPPORTED_TP) {
+ dev->flags |= IFF_PORTSEL;
+ dev->if_port = IF_PORT_10BASET;
+ } else if (data->supported & SUPPORTED_BNC) {
+ dev->flags |= IFF_PORTSEL;
+ dev->if_port = IF_PORT_10BASE2;
+ } else
+ dev->if_port = IF_PORT_UNKNOWN;
+
+ eh = etherh_priv(dev);
+ eh->supported = data->supported;
+ eh->ctrl = 0;
+ eh->id = ec->cid.product;
+ eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE);
+ if (!eh->memc) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ eh->ctrl_port = eh->memc;
+ if (data->ctrl_ioc) {
+ eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE);
+ if (!eh->ioc_fast) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ eh->ctrl_port = eh->ioc_fast;
+ }
+
+ dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset;
+ eh->dma_base = eh->memc + data->dataport_offset;
+ eh->ctrl_port += data->ctrlport_offset;
+
+ /*
+ * IRQ and control port handling - only for non-NIC slot cards.
+ */
+ if (ec->slot_no != 8) {
+ ecard_setirq(ec, &etherh_ops, eh);
+ } else {
+ /*
+ * If we're in the NIC slot, make sure the IRQ is enabled
+ */
+ etherh_set_ctrl(eh, ETHERH_CP_IE);
+ }
+
+ ei_local = netdev_priv(dev);
+ spin_lock_init(&ei_local->page_lock);
+
+ if (ec->cid.product == PROD_ANT_ETHERM) {
+ etherm_addr(dev->dev_addr);
+ ei_local->reg_offset = etherm_regoffsets;
+ } else {
+ etherh_addr(dev->dev_addr, ec);
+ ei_local->reg_offset = etherh_regoffsets;
+ }
+
+ ei_local->name = dev->name;
+ ei_local->word16 = 1;
+ ei_local->tx_start_page = data->tx_start_page;
+ ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
+ ei_local->stop_page = data->stop_page;
+ ei_local->reset_8390 = etherh_reset;
+ ei_local->block_input = etherh_block_input;
+ ei_local->block_output = etherh_block_output;
+ ei_local->get_8390_hdr = etherh_get_header;
+ ei_local->interface_num = 0;
+ ei_local->msg_enable = etherh_msg_enable;
+
+ etherh_reset(dev);
+ __NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto free;
+
+ netdev_info(dev, "%s in slot %d, %pM\n",
+ data->name, ec->slot_no, dev->dev_addr);
+
+ ecard_set_drvdata(ec, dev);
+
+ return 0;
+
+ free:
+ free_netdev(dev);
+ release:
+ ecard_release_resources(ec);
+ out:
+ return ret;
+}
+
+static void etherh_remove(struct expansion_card *ec)
+{
+ struct net_device *dev = ecard_get_drvdata(ec);
+
+ ecard_set_drvdata(ec, NULL);
+
+ unregister_netdev(dev);
+
+ free_netdev(dev);
+
+ ecard_release_resources(ec);
+}
+
+static struct etherh_data etherm_data = {
+ .ns8390_offset = ETHERM_NS8390,
+ .dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT,
+ .ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT,
+ .name = "ANT EtherM",
+ .supported = SUPPORTED_10baseT_Half,
+ .tx_start_page = ETHERM_TX_START_PAGE,
+ .stop_page = ETHERM_STOP_PAGE,
+};
+
+static struct etherh_data etherlan500_data = {
+ .ns8390_offset = ETHERH500_NS8390,
+ .dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT,
+ .ctrlport_offset = ETHERH500_CTRLPORT,
+ .ctrl_ioc = 1,
+ .name = "i3 EtherH 500",
+ .supported = SUPPORTED_10baseT_Half,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600_data = {
+ .ns8390_offset = ETHERH600_NS8390,
+ .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
+ .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+ .name = "i3 EtherH 600",
+ .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600a_data = {
+ .ns8390_offset = ETHERH600_NS8390,
+ .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
+ .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+ .name = "i3 EtherH 600A",
+ .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static const struct ecard_id etherh_ids[] = {
+ { MANU_ANT, PROD_ANT_ETHERM, &etherm_data },
+ { MANU_I3, PROD_I3_ETHERLAN500, &etherlan500_data },
+ { MANU_I3, PROD_I3_ETHERLAN600, &etherlan600_data },
+ { MANU_I3, PROD_I3_ETHERLAN600A, &etherlan600a_data },
+ { 0xffff, 0xffff }
+};
+
+static struct ecard_driver etherh_driver = {
+ .probe = etherh_probe,
+ .remove = etherh_remove,
+ .id_table = etherh_ids,
+ .drv = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init etherh_init(void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ etherh_regoffsets[i] = i << 2;
+ etherm_regoffsets[i] = i << 5;
+ }
+
+ return ecard_register_driver(&etherh_driver);
+}
+
+static void __exit etherh_exit(void)
+{
+ ecard_remove_driver(&etherh_driver);
+}
+
+module_init(etherh_init);
+module_exit(etherh_exit);
diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c
new file mode 100644
index 00000000000..0fe19d609c2
--- /dev/null
+++ b/drivers/net/ethernet/8390/hydra.c
@@ -0,0 +1,278 @@
+/* New Hydra driver using generic 8390 core */
+/* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */
+
+/* This file is subject to the terms and conditions of the GNU General */
+/* Public License. See the file COPYING in the main directory of the */
+/* Linux distribution for more details. */
+
+/* Peter De Schrijver (p2@mind.be) */
+/* Oldenburg 2000 */
+
+/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a */
+/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM */
+/* and 10BASE-2 (thin coax) and AUI connectors. */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val,port) out_8(port,val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val,port) out_8(port,val)
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define NE_EN0_DCFG (0x0e*2)
+
+#define NESM_START_PG 0x0 /* First page of TX buffer */
+#define NESM_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define HYDRA_NIC_BASE 0xffe1
+#define HYDRA_ADDRPROM 0xffc0
+#define HYDRA_VERSION "v3.0alpha"
+
+#define WORDSWAP(a) ((((a)>>8)&0xff) | ((a)<<8))
+
+
+static int hydra_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent);
+static int hydra_init(struct zorro_dev *z);
+static int hydra_open(struct net_device *dev);
+static int hydra_close(struct net_device *dev);
+static void hydra_reset_8390(struct net_device *dev);
+static void hydra_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void hydra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hydra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void hydra_remove_one(struct zorro_dev *z);
+static u32 hydra_msg_enable;
+
+static struct zorro_device_id hydra_zorro_tbl[] = {
+ { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl);
+
+static struct zorro_driver hydra_driver = {
+ .name = "hydra",
+ .id_table = hydra_zorro_tbl,
+ .probe = hydra_init_one,
+ .remove = hydra_remove_one,
+};
+
+static int hydra_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ int err;
+
+ if (!request_mem_region(z->resource.start, 0x10000, "Hydra"))
+ return -EBUSY;
+ if ((err = hydra_init(z))) {
+ release_mem_region(z->resource.start, 0x10000);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static const struct net_device_ops hydra_netdev_ops = {
+ .ndo_open = hydra_open,
+ .ndo_stop = hydra_close,
+
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int hydra_init(struct zorro_dev *z)
+{
+ struct net_device *dev;
+ unsigned long board = (unsigned long)ZTWO_VADDR(z->resource.start);
+ unsigned long ioaddr = board+HYDRA_NIC_BASE;
+ const char name[] = "NE2000";
+ int start_page, stop_page;
+ int j;
+ int err;
+ struct ei_device *ei_local;
+
+ static u32 hydra_offsets[16] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ };
+
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return -ENOMEM;
+
+ for (j = 0; j < ETH_ALEN; j++)
+ dev->dev_addr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j));
+
+ /* We must set the 8390 for word mode. */
+ z_writeb(0x4b, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = hydra_msg_enable;
+ dev->base_addr = ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+
+ /* Install the Interrupt handler */
+ if (request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, IRQF_SHARED, "Hydra Ethernet",
+ dev)) {
+ free_netdev(dev);
+ return -EAGAIN;
+ }
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+ ei_status.bigendian = 1;
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = hydra_reset_8390;
+ ei_status.block_input = hydra_block_input;
+ ei_status.block_output = hydra_block_output;
+ ei_status.get_8390_hdr = hydra_get_8390_hdr;
+ ei_status.reg_offset = hydra_offsets;
+
+ dev->netdev_ops = &hydra_netdev_ops;
+ __NS8390_init(dev, 0);
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ free_netdev(dev);
+ return err;
+ }
+
+ zorro_set_drvdata(z, dev);
+
+ pr_info("%s: Hydra at %pR, address %pM (hydra.c " HYDRA_VERSION ")\n",
+ dev->name, &z->resource, dev->dev_addr);
+
+ return 0;
+}
+
+static int hydra_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int hydra_close(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
+ __ei_close(dev);
+ return 0;
+}
+
+static void hydra_reset_8390(struct net_device *dev)
+{
+ netdev_info(dev, "Hydra hw reset not there\n");
+}
+
+static void hydra_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ short *ptrs;
+ unsigned long hdr_start= (nic_base-HYDRA_NIC_BASE) +
+ ((ring_page - NESM_START_PG)<<8);
+ ptrs = (short *)hdr;
+
+ *(ptrs++) = z_readw(hdr_start);
+ *((short *)hdr) = WORDSWAP(*((short *)hdr));
+ hdr_start += 2;
+ *(ptrs++) = z_readw(hdr_start);
+ *((short *)hdr+1) = WORDSWAP(*((short *)hdr+1));
+}
+
+static void hydra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long nic_base = dev->base_addr;
+ unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+ unsigned long xfer_start = mem_base + ring_offset - (NESM_START_PG<<8);
+
+ if (count&1)
+ count++;
+
+ if (xfer_start+count > mem_base + (NESM_STOP_PG<<8)) {
+ int semi_count = (mem_base + (NESM_STOP_PG<<8)) - xfer_start;
+
+ z_memcpy_fromio(skb->data,xfer_start,semi_count);
+ count -= semi_count;
+ z_memcpy_fromio(skb->data+semi_count, mem_base, count);
+ } else
+ z_memcpy_fromio(skb->data, xfer_start,count);
+
+}
+
+static void hydra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ unsigned long nic_base = dev->base_addr;
+ unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+
+ if (count&1)
+ count++;
+
+ z_memcpy_toio(mem_base+((start_page - NESM_START_PG)<<8), buf, count);
+}
+
+static void hydra_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr)-HYDRA_NIC_BASE, 0x10000);
+ free_netdev(dev);
+}
+
+static int __init hydra_init_module(void)
+{
+ return zorro_register_driver(&hydra_driver);
+}
+
+static void __exit hydra_cleanup_module(void)
+{
+ zorro_unregister_driver(&hydra_driver);
+}
+
+module_init(hydra_init_module);
+module_exit(hydra_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
new file mode 100644
index 00000000000..599311f0e05
--- /dev/null
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -0,0 +1,1084 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Seeing how at least eight drivers use this code, (not counting the
+ PCMCIA ones either) it is easy to break some card by what seems like
+ a simple innocent change. Please contact me or Donald if you think
+ you have found something that needs changing. -- PG
+
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+ Paul Gortmaker : exchange static int ei_pingpong for a #define,
+ also add better Tx error handling.
+ Paul Gortmaker : rewrite Rx overrun handling as per NS specs.
+ Alexey Kuznetsov : use the 8390's six bit hash multicast filter.
+ Paul Gortmaker : tweak ANK's above multicast changes a bit.
+ Paul Gortmaker : update packet statistics for v2.1.x
+ Alan Cox : support arbitrary stupid port mappings on the
+ 68K Macintosh. Support >16bit I/O spaces
+ Paul Gortmaker : add kmod support for auto-loading of the 8390
+ module by all drivers that require it.
+ Alan Cox : Spinlocking work, added 'BUG_83C690'
+ Paul Gortmaker : Separate out Tx timeout code from Tx path.
+ Paul Gortmaker : Remove old unused single Tx buffer code.
+ Hayato Fujiwara : Add m32r support.
+ Paul Gortmaker : use skb_padto() instead of stack scratch area
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#define NS8390_CORE
+#include "8390.h"
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct net_device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+static void __NS8390_init(struct net_device *dev, int startp);
+
+static unsigned version_printed;
+static u32 msg_enable;
+module_param(msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+
+/*
+ * SMP and the 8390 setup.
+ *
+ * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ * a page register that controls bank and packet buffer access. We guard
+ * this with ei_local->page_lock. Nobody should assume or set the page other
+ * than zero when the lock is not held. Lock holders must restore page 0
+ * before unlocking. Even pure readers must take the lock to protect in
+ * page 0.
+ *
+ * To make life difficult the chip can also be very slow. We therefore can't
+ * just use spinlocks. For the longer lockups we disable the irq the device
+ * sits on and hold the lock. We must hold the lock because there is a dual
+ * processor case other than interrupts (get stats/set multicast list in
+ * parallel with each other and transmit).
+ *
+ * Note: in theory we can just disable the irq on the card _but_ there is
+ * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ * enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ * Finally by special arrangement for the purpose of being generally
+ * annoying the transmit function is called bh atomic. That places
+ * restrictions on the user context callers as disable_irq won't save
+ * them.
+ *
+ * Additional explanation of problems with locking by Alan Cox:
+ *
+ * "The author (me) didn't use spin_lock_irqsave because the slowness of the
+ * card means that approach caused horrible problems like losing serial data
+ * at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA
+ * chips with FPGA front ends.
+ *
+ * Ok the logic behind the 8390 is very simple:
+ *
+ * Things to know
+ * - IRQ delivery is asynchronous to the PCI bus
+ * - Blocking the local CPU IRQ via spin locks was too slow
+ * - The chip has register windows needing locking work
+ *
+ * So the path was once (I say once as people appear to have changed it
+ * in the mean time and it now looks rather bogus if the changes to use
+ * disable_irq_nosync_irqsave are disabling the local IRQ)
+ *
+ *
+ * Take the page lock
+ * Mask the IRQ on chip
+ * Disable the IRQ (but not mask locally- someone seems to have
+ * broken this with the lock validator stuff)
+ * [This must be _nosync as the page lock may otherwise
+ * deadlock us]
+ * Drop the page lock and turn IRQs back on
+ *
+ * At this point an existing IRQ may still be running but we can't
+ * get a new one
+ *
+ * Take the lock (so we know the IRQ has terminated) but don't mask
+ * the IRQs on the processor
+ * Set irqlock [for debug]
+ *
+ * Transmit (slow as ****)
+ *
+ * re-enable the IRQ
+ *
+ *
+ * We have to use disable_irq because otherwise you will get delayed
+ * interrupts on the APIC bus deadlocking the transmit path.
+ *
+ * Quite hairy but the chip simply wasn't designed for SMP and you can't
+ * even ACK an interrupt without risking corrupting other parallel
+ * activities on the chip." [lkml, 25 Jul 2007]
+ */
+
+
+
+/**
+ * ei_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
+static int __ei_open(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (dev->watchdog_timeo <= 0)
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ /*
+ * Grab the page lock so we own the register set, then call
+ * the init function.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ __NS8390_init(dev, 1);
+ /* Set the flag before we drop the lock, That way the IRQ arrives
+ after its set and we get no silly warnings */
+ netif_start_queue(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+/**
+ * ei_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int __ei_close(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /*
+ * Hold the page lock during close
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ __NS8390_init(dev, 0);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/**
+ * ei_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void __ei_tx_timeout(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+ unsigned long flags;
+
+ dev->stats.tx_errors++;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ txsr = ei_inb(e8390_base+EN0_TSR);
+ isr = ei_inb(e8390_base+EN0_ISR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d\n",
+ (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?",
+ txsr, isr, tickssofar);
+
+ if (!isr && !dev->stats.tx_packets) {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Ugly but a reset can be slow, yet must be protected */
+
+ disable_irq_nosync_lockdep(dev->irq);
+ spin_lock(&ei_local->page_lock);
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ __NS8390_init(dev, 1);
+
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep(dev->irq);
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+
+static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int send_length = skb->len, output_page;
+ unsigned long flags;
+ char buf[ETH_ZLEN];
+ char *data = skb->data;
+
+ if (skb->len < ETH_ZLEN) {
+ memset(buf, 0, ETH_ZLEN); /* more efficient than doing just the needed bits */
+ memcpy(buf, data, skb->len);
+ send_length = ETH_ZLEN;
+ data = buf;
+ }
+
+ /* Mask interrupts from the ethercard.
+ SMP: We have to grab the lock here otherwise the IRQ handler
+ on another CPU can flip window and race the IRQ mask set. We end
+ up trashing the mcast filter not disabling irqs if we don't lock */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ ei_outb_p(0x00, e8390_base + EN0_IMR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+
+ /*
+ * Slow phase with lock held.
+ */
+
+ disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
+
+ spin_lock(&ei_local->page_lock);
+
+ ei_local->irqlock = 1;
+
+ /*
+ * We have two Tx slots available for use. Find the first free
+ * slot, and then perform some sanity checks. With two Tx bufs,
+ * you get very close to transmitting back-to-back packets. With
+ * only one Tx buf, the transmitter sits idle while you reload the
+ * card, leaving a substantial gap between each transmitted packet.
+ */
+
+ if (ei_local->tx1 == 0) {
+ output_page = ei_local->tx_start_page;
+ ei_local->tx1 = send_length;
+ if ((netif_msg_tx_queued(ei_local)) &&
+ ei_local->tx2 > 0)
+ netdev_dbg(dev,
+ "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx2, ei_local->lasttx, ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
+ output_page = ei_local->tx_start_page + TX_PAGES/2;
+ ei_local->tx2 = send_length;
+ if ((netif_msg_tx_queued(ei_local)) &&
+ ei_local->tx1 > 0)
+ netdev_dbg(dev,
+ "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx1, ei_local->lasttx, ei_local->txing);
+ } else { /* We should never get here. */
+ netif_dbg(ei_local, tx_err, dev,
+ "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+ ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+ ei_local->irqlock = 0;
+ netif_stop_queue(dev);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_BUSY;
+ }
+
+ /*
+ * Okay, now upload the packet and trigger a send if the transmitter
+ * isn't already sending. If it is busy, the interrupt handler will
+ * trigger the send later, upon receiving a Tx done interrupt.
+ */
+
+ ei_block_output(dev, send_length, data, output_page);
+
+ if (!ei_local->txing) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, output_page);
+ if (output_page == ei_local->tx_start_page) {
+ ei_local->tx1 = -1;
+ ei_local->lasttx = -1;
+ } else {
+ ei_local->tx2 = -1;
+ ei_local->lasttx = -2;
+ }
+ } else
+ ei_local->txqueue++;
+
+ if (ei_local->tx1 && ei_local->tx2)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
+ skb_tx_timestamp(skb);
+ dev_consume_skb_any(skb);
+ dev->stats.tx_bytes += send_length;
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ei_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t __ei_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ unsigned long e8390_base = dev->base_addr;
+ int interrupts, nr_serviced = 0;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ /*
+ * Protect the irq test too.
+ */
+
+ spin_lock(&ei_local->page_lock);
+
+ if (ei_local->irqlock) {
+ /*
+ * This might just be an interrupt for a PCI device sharing
+ * this line
+ */
+ netdev_err(dev, "Interrupted while interrupts are masked! isr=%#2x imr=%#2x\n",
+ ei_inb_p(e8390_base + EN0_ISR),
+ ei_inb_p(e8390_base + EN0_IMR));
+ spin_unlock(&ei_local->page_lock);
+ return IRQ_NONE;
+ }
+
+ /* Change to page 0 and read the intr status reg. */
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n",
+ ei_inb_p(e8390_base + EN0_ISR));
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE) {
+ if (!netif_running(dev)) {
+ netdev_warn(dev, "interrupt from stopped card\n");
+ /* rmk - acknowledge the interrupts */
+ ei_outb_p(interrupts, e8390_base + EN0_ISR);
+ interrupts = 0;
+ break;
+ }
+ if (interrupts & ENISR_OVER)
+ ei_rx_overrun(dev);
+ else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ /* Got a good (?) packet. */
+ ei_receive(dev);
+ }
+ /* Push the next to-transmit packet through. */
+ if (interrupts & ENISR_TX)
+ ei_tx_intr(dev);
+ else if (interrupts & ENISR_TX_ERR)
+ ei_tx_err(dev);
+
+ if (interrupts & ENISR_COUNTERS) {
+ dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += ei_inb_p(e8390_base + EN0_COUNTER1);
+ dev->stats.rx_missed_errors += ei_inb_p(e8390_base + EN0_COUNTER2);
+ ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore any RDC interrupts that make it back to here. */
+ if (interrupts & ENISR_RDC)
+ ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ }
+
+ if (interrupts && (netif_msg_intr(ei_local))) {
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced >= MAX_SERVICE) {
+ /* 0xFF is valid for a card removal */
+ if (interrupts != 0xFF)
+ netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+ interrupts);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ netdev_warn(dev, "unknown interrupt %#2x\n", interrupts);
+ ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+ spin_unlock(&ei_local->page_lock);
+ return IRQ_RETVAL(nr_serviced > 0);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void __ei_poll(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ __ei_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ /* ei_local is used on some platforms via the EI_SHIFT macro */
+ struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+ unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR);
+ unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+ netdev_dbg(dev, "transmitter error (%#2x):", txsr);
+ if (txsr & ENTSR_ABT)
+ pr_cont(" excess-collisions ");
+ if (txsr & ENTSR_ND)
+ pr_cont(" non-deferral ");
+ if (txsr & ENTSR_CRS)
+ pr_cont(" lost-carrier ");
+ if (txsr & ENTSR_FU)
+ pr_cont(" FIFO-underrun ");
+ if (txsr & ENTSR_CDH)
+ pr_cont(" lost-heartbeat ");
+ pr_cont("\n");
+#endif
+
+ ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+
+ if (tx_was_aborted)
+ ei_tx_intr(dev);
+ else {
+ dev->stats.tx_errors++;
+ if (txsr & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (txsr & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (txsr & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int status = ei_inb(e8390_base + EN0_TSR);
+
+ ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+ /*
+ * There are two Tx buffers, see which one finished, and trigger
+ * the send of another one if it exists.
+ */
+ ei_local->txqueue--;
+
+ if (ei_local->tx1 < 0) {
+ if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+ pr_err("%s: bogus last_tx_buffer %d, tx1=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
+ ei_local->tx1 = 0;
+ if (ei_local->tx2 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+ dev->trans_start = jiffies;
+ ei_local->tx2 = -1,
+ ei_local->lasttx = 2;
+ } else
+ ei_local->lasttx = 20, ei_local->txing = 0;
+ } else if (ei_local->tx2 < 0) {
+ if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
+ pr_err("%s: bogus last_tx_buffer %d, tx2=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
+ ei_local->tx2 = 0;
+ if (ei_local->tx1 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ ei_local->tx1 = -1;
+ ei_local->lasttx = 1;
+ } else
+ ei_local->lasttx = 10, ei_local->txing = 0;
+ } /* else
+ netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+ ei_local->lasttx);
+*/
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL)
+ dev->stats.collisions++;
+ if (status & ENTSR_PTX)
+ dev->stats.tx_packets++;
+ else {
+ dev->stats.tx_errors++;
+ if (status & ENTSR_ABT) {
+ dev->stats.tx_aborted_errors++;
+ dev->stats.collisions += 16;
+ }
+ if (status & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (status & ENTSR_FU)
+ dev->stats.tx_fifo_errors++;
+ if (status & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers.
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char rxing_page, this_frame, next_frame;
+ unsigned short current_offset;
+ int rx_pkt_count = 0;
+ struct e8390_pkt_hdr rx_frame;
+ int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+ while (++rx_pkt_count < 10) {
+ int pkt_len, pkt_stat;
+
+ /* Get the rx page (incoming packet pointer). */
+ ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ rxing_page = ei_inb_p(e8390_base + EN1_CURPAG);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+ /* Remove one frame from the ring. Boundary is always a page behind. */
+ this_frame = ei_inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ if (this_frame >= ei_local->stop_page)
+ this_frame = ei_local->rx_start_page;
+
+ /* Someday we'll omit the previous, iff we never get this message.
+ (There is at least one clone claimed to have a problem.)
+
+ Keep quiet if it looks like a card removal. One problem here
+ is that some clones crash in roughly the same way.
+ */
+ if ((netif_msg_rx_status(ei_local)) &&
+ this_frame != ei_local->current_page &&
+ (this_frame != 0x0 || rxing_page != 0xFF))
+ netdev_err(dev,
+ "mismatched read page pointers %2x vs %2x\n",
+ this_frame, ei_local->current_page);
+
+ if (this_frame == rxing_page) /* Read all the frames? */
+ break; /* Done for now */
+
+ current_offset = this_frame << 8;
+ ei_get_8390_hdr(dev, &rx_frame, this_frame);
+
+ pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+ pkt_stat = rx_frame.status;
+
+ next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+ /* Check for bogosity warned by 3c503 book: the status byte is never
+ written. This happened a lot during testing! This code should be
+ cleaned up someday. */
+ if (rx_frame.next != next_frame &&
+ rx_frame.next != next_frame + 1 &&
+ rx_frame.next != next_frame - num_rx_pages &&
+ rx_frame.next != next_frame + 1 - num_rx_pages) {
+ ei_local->current_page = rxing_page;
+ ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ dev->stats.rx_errors++;
+ continue;
+ }
+
+ if (pkt_len < 60 || pkt_len > 1518) {
+ netif_dbg(ei_local, rx_status, dev,
+ "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ } else if ((pkt_stat & 0x0F) == ENRSR_RXOK) {
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (skb == NULL) {
+ netif_err(ei_local, rx_err, dev,
+ "Couldn't allocate a sk_buff of size %d\n",
+ pkt_len);
+ dev->stats.rx_dropped++;
+ break;
+ } else {
+ skb_reserve(skb, 2); /* IP headers on 16 byte boundaries */
+ skb_put(skb, pkt_len); /* Make room */
+ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+ skb->protocol = eth_type_trans(skb, dev);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ if (pkt_stat & ENRSR_PHY)
+ dev->stats.multicast++;
+ }
+ } else {
+ netif_err(ei_local, rx_err, dev,
+ "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ dev->stats.rx_errors++;
+ /* NB: The NIC counts CRC, frame and missed errors. */
+ if (pkt_stat & ENRSR_FO)
+ dev->stats.rx_fifo_errors++;
+ }
+ next_frame = rx_frame.next;
+
+ /* This _should_ never happen: it's here for avoiding bad clones. */
+ if (next_frame >= ei_local->stop_page) {
+ netdev_notice(dev, "next frame inconsistency, %#2x\n",
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ ei_outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+
+ /* We used to also ack ENISR_OVER here, but that would sometimes mask
+ a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+ ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network." Ugh.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so.
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ unsigned char was_txing, must_resend = 0;
+ /* ei_local is used on some platforms via the EI_SHIFT macro */
+ struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+
+ /*
+ * Record whether a Tx was in progress and then issue the
+ * stop command.
+ */
+ was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n");
+ dev->stats.rx_over_errors++;
+
+ /*
+ * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+ * Early datasheets said to poll the reset bit, but now they say that
+ * it "is not a reliable indicator and subsequently should be ignored."
+ * We wait at least 10ms.
+ */
+
+ mdelay(10);
+
+ /*
+ * Reset RBCR[01] back to zero as per magic incantation.
+ */
+ ei_outb_p(0x00, e8390_base+EN0_RCNTLO);
+ ei_outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+ /*
+ * See if any Tx was interrupted or not. According to NS, this
+ * step is vital, and skipping it will cause no end of havoc.
+ */
+
+ if (was_txing) {
+ unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+ if (!tx_completed)
+ must_resend = 1;
+ }
+
+ /*
+ * Have to enter loopback mode and then restart the NIC before
+ * you are allowed to slurp packets up off the ring.
+ */
+ ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+ ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+ /*
+ * Clear the Rx ring of all the debris, and ack the interrupt.
+ */
+ ei_receive(dev);
+ ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+
+ /*
+ * Leave loopback mode, and resend any packet that got stopped.
+ */
+ ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);
+ if (must_resend)
+ ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ * Collect the stats. This is called unlocked and from several contexts.
+ */
+
+static struct net_device_stats *__ei_get_stats(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /* If the card is stopped, just return the present stats. */
+ if (!netif_running(dev))
+ return &dev->stats;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ /* Read the counter registers, assuming we are in page 0. */
+ dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1);
+ dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ u32 crc = ether_crc(ETH_ALEN, ha->addr);
+ /*
+ * The 8390 uses the 6 most significant bits of the
+ * CRC to index the multicast table.
+ */
+ bits[crc>>29] |= (1<<((crc>>26)&7));
+ }
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ * Set or clear the multicast filter for this adaptor. May be called
+ * from a BH in 2.1.x. Must be called with lock held.
+ */
+
+static void do_set_multicast_list(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ int i;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+ memset(ei_local->mcfilter, 0, 8);
+ if (!netdev_mc_empty(dev))
+ make_mc_bits(ei_local->mcfilter, dev);
+ } else
+ memset(ei_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */
+
+ /*
+ * DP8390 manuals don't specify any magic sequence for altering
+ * the multicast regs on an already running card. To be safe, we
+ * ensure multicast mode is off prior to loading up the new hash
+ * table. If this proves to be not enough, we can always resort
+ * to stopping the NIC, loading the table and then restarting.
+ *
+ * Bug Alert! The MC regs on the SMC 83C690 (SMC Elite and SMC
+ * Elite16) appear to be write-only. The NS 8390 data sheet lists
+ * them as r/w so this is a bug. The SMC 83C790 (SMC Ultra and
+ * Ultra32 EISA) appears to have this bug fixed.
+ */
+
+ if (netif_running(dev))
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+ ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+ for (i = 0; i < 8; i++) {
+ ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+#ifndef BUG_83C690
+ if (ei_inb_p(e8390_base + EN1_MULT_SHIFT(i)) != ei_local->mcfilter[i])
+ netdev_err(dev, "Multicast filter read/write mismap %d\n",
+ i);
+#endif
+ }
+ ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+ if (dev->flags&IFF_PROMISC)
+ ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
+ else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+ ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
+ else
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+}
+
+/*
+ * Called without lock held. This is invoked from user context and may
+ * be parallel to just about everything else. Its also fairly quick and
+ * not called too often. Must protect against both bh and irq users
+ */
+
+static void __ei_set_multicast_list(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ do_set_multicast_list(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+}
+
+/**
+ * ethdev_setup - init rest of 8390 device struct
+ * @dev: network device structure to init
+ *
+ * Initialize the rest of the 8390 device structure. Do NOT __init
+ * this, as it is used by 8390 based modular drivers too.
+ */
+
+static void ethdev_setup(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if ((msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ pr_info("%s", version);
+
+ ether_setup(dev);
+
+ spin_lock_init(&ei_local->page_lock);
+}
+
+/**
+ * alloc_ei_netdev - alloc_etherdev counterpart for 8390
+ * @size: extra bytes to allocate
+ *
+ * Allocate 8390-specific net_device.
+ */
+static struct net_device *____alloc_ei_netdev(int size)
+{
+ return alloc_netdev(sizeof(struct ei_device) + size, "eth%d",
+ ethdev_setup);
+}
+
+
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * NS8390_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean. non-zero value to initiate chip processing
+ *
+ * Must be called with lock held.
+ */
+
+static void __NS8390_init(struct net_device *dev, int startp)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int i;
+ int endcfg = ei_local->word16
+ ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0))
+ : 0x48;
+
+ if (sizeof(struct e8390_pkt_hdr) != 4)
+ panic("8390.c: header struct mispacked\n");
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+ ei_outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ ei_outb_p(0x00, e8390_base + EN0_RCNTLO);
+ ei_outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+ ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ ei_outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
+ ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ ei_outb_p(0xFF, e8390_base + EN0_ISR);
+ ei_outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers. */
+
+ ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+ for (i = 0; i < 6; i++) {
+ ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ if ((netif_msg_probe(ei_local)) &&
+ ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i)) != dev->dev_addr[i])
+ netdev_err(dev,
+ "Hw. address read/write mismap %d\n", i);
+ }
+
+ ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+
+ if (startp) {
+ ei_outb_p(0xff, e8390_base + EN0_ISR);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+ ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
+ do_set_multicast_list(dev); /* (re)load the mcast table */
+ }
+}
+
+/* Trigger a transmit start, assuming the length is valid.
+ Always called with the page lock held */
+
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
+
+ if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) {
+ netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+ return;
+ }
+ ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ ei_outb_p(start_page, e8390_base + EN0_TPSR);
+ ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
new file mode 100644
index 00000000000..90e825e8abf
--- /dev/null
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -0,0 +1,882 @@
+/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
+ Ethernet cards on Linux */
+/* Based on the former daynaport.c driver, by Alan Cox. Some code
+ taken from or inspired by skeleton.c by Donald Becker, acenic.c by
+ Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference. */
+
+/* 2000-02-28: support added for Dayna and Kinetics cards by
+ A.G.deWijn@phys.uu.nl */
+/* 2000-04-04: support added for Dayna2 by bart@etpmod.phys.tue.nl */
+/* 2001-04-18: support for DaynaPort E/LC-M by rayk@knightsmanor.org */
+/* 2001-05-15: support for Cabletron ported from old daynaport driver
+ * and fixed access to Sonic Sys card which masquerades as a Farallon
+ * by rayk@knightsmanor.org */
+/* 2002-12-30: Try to support more cards, some clues from NetBSD driver */
+/* 2003-12-26: Make sure Asante cards always work. */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/nubus.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+#include <asm/hwtest.h>
+#include <asm/macints.h>
+
+static char version[] =
+ "v0.4 2001-05-15 David Huggins-Daines <dhd@debian.org> and others\n";
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val, port) out_8(port, val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val, port) out_8(port, val)
+
+#include "lib8390.c"
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */
+#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG
+ /* First page of TX buffer */
+
+/*
+ * Unfortunately it seems we have to hardcode these for the moment
+ * Shouldn't the card know about this?
+ * Does anyone know where to read it off the card?
+ * Do we trust the data provided by the card?
+ */
+
+#define DAYNA_8390_BASE 0x80000
+#define DAYNA_8390_MEM 0x00000
+
+#define CABLETRON_8390_BASE 0x90000
+#define CABLETRON_8390_MEM 0x00000
+
+#define INTERLAN_8390_BASE 0xE0000
+#define INTERLAN_8390_MEM 0xD0000
+
+enum mac8390_type {
+ MAC8390_NONE = -1,
+ MAC8390_APPLE,
+ MAC8390_ASANTE,
+ MAC8390_FARALLON,
+ MAC8390_CABLETRON,
+ MAC8390_DAYNA,
+ MAC8390_INTERLAN,
+ MAC8390_KINETICS,
+};
+
+static const char *cardname[] = {
+ "apple",
+ "asante",
+ "farallon",
+ "cabletron",
+ "dayna",
+ "interlan",
+ "kinetics",
+};
+
+static const int word16[] = {
+ 1, /* apple */
+ 1, /* asante */
+ 1, /* farallon */
+ 1, /* cabletron */
+ 0, /* dayna */
+ 1, /* interlan */
+ 0, /* kinetics */
+};
+
+/* on which cards do we use NuBus resources? */
+static const int useresources[] = {
+ 1, /* apple */
+ 1, /* asante */
+ 1, /* farallon */
+ 0, /* cabletron */
+ 0, /* dayna */
+ 0, /* interlan */
+ 0, /* kinetics */
+};
+
+enum mac8390_access {
+ ACCESS_UNKNOWN = 0,
+ ACCESS_32,
+ ACCESS_16,
+};
+
+extern int mac8390_memtest(struct net_device *dev);
+static int mac8390_initdev(struct net_device *dev, struct nubus_dev *ndev,
+ enum mac8390_type type);
+
+static int mac8390_open(struct net_device *dev);
+static int mac8390_close(struct net_device *dev);
+static void mac8390_no_reset(struct net_device *dev);
+static void interlan_reset(struct net_device *dev);
+
+/* Sane (32-bit chunk memory read/write) - Some Farallon and Apple do this*/
+static void sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+
+/* dayna_memcpy to and from card */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to,
+ int from, int count);
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+ const void *from, int count);
+
+/* Dayna - Dayna/Kinetics use this */
+static void dayna_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void dayna_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void dayna_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+
+#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c))
+#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c))
+
+#define memcmp_withio(a, b, c) memcmp((a), (void *)(b), (c))
+
+/* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void slow_sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void slow_sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count);
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count);
+static u32 mac8390_msg_enable;
+
+static enum mac8390_type __init mac8390_ident(struct nubus_dev *dev)
+{
+ switch (dev->dr_sw) {
+ case NUBUS_DRSW_3COM:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_APPLE_SONIC_NB:
+ case NUBUS_DRHW_APPLE_SONIC_LC:
+ case NUBUS_DRHW_SONNET:
+ return MAC8390_NONE;
+ break;
+ default:
+ return MAC8390_APPLE;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_APPLE:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_ASANTE_LC:
+ return MAC8390_NONE;
+ break;
+ case NUBUS_DRHW_CABLETRON:
+ return MAC8390_CABLETRON;
+ break;
+ default:
+ return MAC8390_APPLE;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_ASANTE:
+ return MAC8390_ASANTE;
+ break;
+
+ case NUBUS_DRSW_TECHWORKS:
+ case NUBUS_DRSW_DAYNA2:
+ case NUBUS_DRSW_DAYNA_LC:
+ if (dev->dr_hw == NUBUS_DRHW_CABLETRON)
+ return MAC8390_CABLETRON;
+ else
+ return MAC8390_APPLE;
+ break;
+
+ case NUBUS_DRSW_FARALLON:
+ return MAC8390_FARALLON;
+ break;
+
+ case NUBUS_DRSW_KINETICS:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_INTERLAN:
+ return MAC8390_INTERLAN;
+ break;
+ default:
+ return MAC8390_KINETICS;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_DAYNA:
+ /*
+ * These correspond to Dayna Sonic cards
+ * which use the macsonic driver
+ */
+ if (dev->dr_hw == NUBUS_DRHW_SMC9194 ||
+ dev->dr_hw == NUBUS_DRHW_INTERLAN)
+ return MAC8390_NONE;
+ else
+ return MAC8390_DAYNA;
+ break;
+ }
+ return MAC8390_NONE;
+}
+
+static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
+{
+ unsigned long outdata = 0xA5A0B5B0;
+ unsigned long indata = 0x00000000;
+ /* Try writing 32 bits */
+ memcpy_toio(membase, &outdata, 4);
+ /* Now compare them */
+ if (memcmp_withio(&outdata, membase, 4) == 0)
+ return ACCESS_32;
+ /* Write 16 bit output */
+ word_memcpy_tocard(membase, &outdata, 4);
+ /* Now read it back */
+ word_memcpy_fromcard(&indata, membase, 4);
+ if (outdata == indata)
+ return ACCESS_16;
+ return ACCESS_UNKNOWN;
+}
+
+static int __init mac8390_memsize(unsigned long membase)
+{
+ unsigned long flags;
+ int i, j;
+
+ local_irq_save(flags);
+ /* Check up to 32K in 4K increments */
+ for (i = 0; i < 8; i++) {
+ volatile unsigned short *m = (unsigned short *)(membase + (i * 0x1000));
+
+ /* Unwriteable - we have a fully decoded card and the
+ RAM end located */
+ if (hwreg_present(m) == 0)
+ break;
+
+ /* write a distinctive byte */
+ *m = 0xA5A0 | i;
+ /* check that we read back what we wrote */
+ if (*m != (0xA5A0 | i))
+ break;
+
+ /* check for partial decode and wrap */
+ for (j = 0; j < i; j++) {
+ volatile unsigned short *p = (unsigned short *)(membase + (j * 0x1000));
+ if (*p != (0xA5A0 | j))
+ break;
+ }
+ }
+ local_irq_restore(flags);
+ /*
+ * in any case, we stopped once we tried one block too many,
+ * or once we reached 32K
+ */
+ return i * 0x1000;
+}
+
+static bool __init mac8390_init(struct net_device *dev, struct nubus_dev *ndev,
+ enum mac8390_type cardtype)
+{
+ struct nubus_dir dir;
+ struct nubus_dirent ent;
+ int offset;
+ volatile unsigned short *i;
+
+ printk_once(KERN_INFO pr_fmt("%s"), version);
+
+ dev->irq = SLOT2IRQ(ndev->board->slot);
+ /* This is getting to be a habit */
+ dev->base_addr = (ndev->board->slot_addr |
+ ((ndev->board->slot & 0xf) << 20));
+
+ /*
+ * Get some Nubus info - we will trust the card's idea
+ * of where its memory and registers are.
+ */
+
+ if (nubus_get_func_dir(ndev, &dir) == -1) {
+ pr_err("%s: Unable to get Nubus functional directory for slot %X!\n",
+ dev->name, ndev->board->slot);
+ return false;
+ }
+
+ /* Get the MAC address */
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
+ pr_info("%s: Couldn't get MAC address!\n", dev->name);
+ return false;
+ }
+
+ nubus_get_rsrc_mem(dev->dev_addr, &ent, 6);
+
+ if (useresources[cardtype] == 1) {
+ nubus_rewinddir(&dir);
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
+ &ent) == -1) {
+ pr_err("%s: Memory offset resource for slot %X not found!\n",
+ dev->name, ndev->board->slot);
+ return false;
+ }
+ nubus_get_rsrc_mem(&offset, &ent, 4);
+ dev->mem_start = dev->base_addr + offset;
+ /* yes, this is how the Apple driver does it */
+ dev->base_addr = dev->mem_start + 0x10000;
+ nubus_rewinddir(&dir);
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH,
+ &ent) == -1) {
+ pr_info("%s: Memory length resource for slot %X not found, probing\n",
+ dev->name, ndev->board->slot);
+ offset = mac8390_memsize(dev->mem_start);
+ } else {
+ nubus_get_rsrc_mem(&offset, &ent, 4);
+ }
+ dev->mem_end = dev->mem_start + offset;
+ } else {
+ switch (cardtype) {
+ case MAC8390_KINETICS:
+ case MAC8390_DAYNA: /* it's the same */
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ DAYNA_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ DAYNA_8390_MEM);
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+ case MAC8390_INTERLAN:
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ INTERLAN_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ INTERLAN_8390_MEM);
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+ case MAC8390_CABLETRON:
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ CABLETRON_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ CABLETRON_8390_MEM);
+ /* The base address is unreadable if 0x00
+ * has been written to the command register
+ * Reset the chip by writing E8390_NODMA +
+ * E8390_PAGE0 + E8390_STOP just to be
+ * sure
+ */
+ i = (void *)dev->base_addr;
+ *i = 0x21;
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+
+ default:
+ pr_err("Card type %s is unsupported, sorry\n",
+ ndev->board->name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct net_device * __init mac8390_probe(int unit)
+{
+ struct net_device *dev;
+ struct nubus_dev *ndev = NULL;
+ int err = -ENODEV;
+ struct ei_device *ei_local;
+
+ static unsigned int slots;
+
+ enum mac8390_type cardtype;
+
+ /* probably should check for Nubus instead */
+
+ if (!MACH_IS_MAC)
+ return ERR_PTR(-ENODEV);
+
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0)
+ sprintf(dev->name, "eth%d", unit);
+
+ while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET,
+ ndev))) {
+ /* Have we seen it already? */
+ if (slots & (1 << ndev->board->slot))
+ continue;
+ slots |= 1 << ndev->board->slot;
+
+ cardtype = mac8390_ident(ndev);
+ if (cardtype == MAC8390_NONE)
+ continue;
+
+ if (!mac8390_init(dev, ndev, cardtype))
+ continue;
+
+ /* Do the nasty 8390 stuff */
+ if (!mac8390_initdev(dev, ndev, cardtype))
+ break;
+ }
+
+ if (!ndev)
+ goto out;
+
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = mac8390_msg_enable;
+
+ err = register_netdev(dev);
+ if (err)
+ goto out;
+ return dev;
+
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
+MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* overkill, of course */
+static struct net_device *dev_mac8390[15];
+int init_module(void)
+{
+ int i;
+ for (i = 0; i < 15; i++) {
+ struct net_device *dev = mac8390_probe(-1);
+ if (IS_ERR(dev))
+ break;
+ dev_mac890[i] = dev;
+ }
+ if (!i) {
+ pr_notice("No useable cards found, driver NOT installed.\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+ for (i = 0; i < 15; i++) {
+ struct net_device *dev = dev_mac890[i];
+ if (dev) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ }
+ }
+}
+
+#endif /* MODULE */
+
+static const struct net_device_ops mac8390_netdev_ops = {
+ .ndo_open = mac8390_open,
+ .ndo_stop = mac8390_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __init mac8390_initdev(struct net_device *dev,
+ struct nubus_dev *ndev,
+ enum mac8390_type type)
+{
+ static u32 fwrd4_offsets[16] = {
+ 0, 4, 8, 12,
+ 16, 20, 24, 28,
+ 32, 36, 40, 44,
+ 48, 52, 56, 60
+ };
+ static u32 back4_offsets[16] = {
+ 60, 56, 52, 48,
+ 44, 40, 36, 32,
+ 28, 24, 20, 16,
+ 12, 8, 4, 0
+ };
+ static u32 fwrd2_offsets[16] = {
+ 0, 2, 4, 6,
+ 8, 10, 12, 14,
+ 16, 18, 20, 22,
+ 24, 26, 28, 30
+ };
+
+ int access_bitmode = 0;
+
+ /* Now fill in our stuff */
+ dev->netdev_ops = &mac8390_netdev_ops;
+
+ /* GAR, ei_status is actually a macro even though it looks global */
+ ei_status.name = cardname[type];
+ ei_status.word16 = word16[type];
+
+ /* Cabletron's TX/RX buffers are backwards */
+ if (type == MAC8390_CABLETRON) {
+ ei_status.tx_start_page = CABLETRON_TX_START_PG;
+ ei_status.rx_start_page = CABLETRON_RX_START_PG;
+ ei_status.stop_page = CABLETRON_RX_STOP_PG;
+ ei_status.rmem_start = dev->mem_start;
+ ei_status.rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
+ } else {
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
+ ei_status.rmem_end = dev->mem_end;
+ }
+
+ /* Fill in model-specific information and functions */
+ switch (type) {
+ case MAC8390_FARALLON:
+ case MAC8390_APPLE:
+ switch (mac8390_testio(dev->mem_start)) {
+ case ACCESS_UNKNOWN:
+ pr_err("Don't know how to access card memory!\n");
+ return -ENODEV;
+ break;
+
+ case ACCESS_16:
+ /* 16 bit card, register map is reversed */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ break;
+
+ case ACCESS_32:
+ /* 32 bit card, register map is reversed */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = sane_block_input;
+ ei_status.block_output = sane_block_output;
+ ei_status.get_8390_hdr = sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ access_bitmode = 1;
+ break;
+ }
+ break;
+
+ case MAC8390_ASANTE:
+ /* Some Asante cards pass the 32 bit test
+ * but overwrite system memory when run at 32 bit.
+ * so we run them all at 16 bit.
+ */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ break;
+
+ case MAC8390_CABLETRON:
+ /* 16 bit card, register map is short forward */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = fwrd2_offsets;
+ break;
+
+ case MAC8390_DAYNA:
+ case MAC8390_KINETICS:
+ /* 16 bit memory, register map is forward */
+ /* dayna and similar */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = dayna_block_input;
+ ei_status.block_output = dayna_block_output;
+ ei_status.get_8390_hdr = dayna_get_8390_hdr;
+ ei_status.reg_offset = fwrd4_offsets;
+ break;
+
+ case MAC8390_INTERLAN:
+ /* 16 bit memory, register map is forward */
+ ei_status.reset_8390 = interlan_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = fwrd4_offsets;
+ break;
+
+ default:
+ pr_err("Card type %s is unsupported, sorry\n",
+ ndev->board->name);
+ return -ENODEV;
+ }
+
+ __NS8390_init(dev, 0);
+
+ /* Good, done, now spit out some messages */
+ pr_info("%s: %s in slot %X (type %s)\n",
+ dev->name, ndev->board->name, ndev->board->slot,
+ cardname[type]);
+ pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
+ dev->dev_addr, dev->irq,
+ (unsigned int)(dev->mem_end - dev->mem_start) >> 10,
+ dev->mem_start, access_bitmode ? 32 : 16);
+ return 0;
+}
+
+static int mac8390_open(struct net_device *dev)
+{
+ int err;
+
+ __ei_open(dev);
+ err = request_irq(dev->irq, __ei_interrupt, 0, "8390 Ethernet", dev);
+ if (err)
+ pr_err("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return err;
+}
+
+static int mac8390_close(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ __ei_close(dev);
+ return 0;
+}
+
+static void mac8390_no_reset(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ ei_status.txing = 0;
+ netif_info(ei_local, hw, dev, "reset not supported\n");
+}
+
+static void interlan_reset(struct net_device *dev)
+{
+ unsigned char *target = nubus_slot_addr(IRQ2SLOT(dev->irq));
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_info(ei_local, hw, dev, "Need to reset the NS8390 t=%lu...",
+ jiffies);
+ ei_status.txing = 0;
+ target[0xC0000] = 0;
+ if (netif_msg_hw(ei_local))
+ pr_cont("reset complete\n");
+}
+
+/* dayna_memcpy_fromio/dayna_memcpy_toio */
+/* directly from daynaport.c by Alan Cox */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from,
+ int count)
+{
+ volatile unsigned char *ptr;
+ unsigned char *target = to;
+ from <<= 1; /* word, skip overhead */
+ ptr = (unsigned char *)(dev->mem_start+from);
+ /* Leading byte? */
+ if (from & 2) {
+ *target++ = ptr[-1];
+ ptr += 2;
+ count--;
+ }
+ while (count >= 2) {
+ *(unsigned short *)target = *(unsigned short volatile *)ptr;
+ ptr += 4; /* skip cruft */
+ target += 2;
+ count -= 2;
+ }
+ /* Trailing byte? */
+ if (count)
+ *target = *ptr;
+}
+
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+ const void *from, int count)
+{
+ volatile unsigned short *ptr;
+ const unsigned char *src = from;
+ to <<= 1; /* word, skip overhead */
+ ptr = (unsigned short *)(dev->mem_start+to);
+ /* Leading byte? */
+ if (to & 2) { /* avoid a byte write (stomps on other data) */
+ ptr[-1] = (ptr[-1]&0xFF00)|*src++;
+ ptr++;
+ count--;
+ }
+ while (count >= 2) {
+ *ptr++ = *(unsigned short *)src; /* Copy and */
+ ptr++; /* skip cruft */
+ src += 2;
+ count -= 2;
+ }
+ /* Trailing byte? */
+ if (count) {
+ /* card doesn't like byte writes */
+ *ptr = (*ptr & 0x00FF) | (*src << 8);
+ }
+}
+
+/* sane block input/output */
+static void sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+ memcpy_fromio(hdr, dev->mem_start + hdr_start, 4);
+ /* Fix endianness */
+ hdr->count = swab16(hdr->count);
+}
+
+static void sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base + dev->mem_start;
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ memcpy_fromio(skb->data, dev->mem_start + xfer_base,
+ semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.rmem_start,
+ count);
+ } else {
+ memcpy_fromio(skb->data, dev->mem_start + xfer_base, count);
+ }
+}
+
+static void sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ memcpy_toio(dev->mem_start + shmem, buf, count);
+}
+
+/* dayna block input/output */
+static void dayna_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+
+ dayna_memcpy_fromcard(dev, hdr, hdr_start, 4);
+ /* Fix endianness */
+ hdr->count = (hdr->count & 0xFF) << 8 | (hdr->count >> 8);
+}
+
+static void dayna_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base+dev->mem_start;
+
+ /* Note the offset math is done in card memory space which is word
+ per long onto our space. */
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
+ count -= semi_count;
+ dayna_memcpy_fromcard(dev, skb->data + semi_count,
+ ei_status.rmem_start - dev->mem_start,
+ count);
+ } else {
+ dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
+ }
+}
+
+static void dayna_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ dayna_memcpy_tocard(dev, shmem, buf, count);
+}
+
+/* Cabletron block I/O */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+ word_memcpy_fromcard(hdr, dev->mem_start + hdr_start, 4);
+ /* Register endianism - fix here rather than 8390.c */
+ hdr->count = (hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void slow_sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base+dev->mem_start;
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+ semi_count);
+ count -= semi_count;
+ word_memcpy_fromcard(skb->data + semi_count,
+ ei_status.rmem_start, count);
+ } else {
+ word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+ count);
+ }
+}
+
+static void slow_sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ word_memcpy_tocard(dev->mem_start + shmem, buf, count);
+}
+
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count)
+{
+ volatile unsigned short *to = (void *)tp;
+ const unsigned short *from = fp;
+
+ count++;
+ count /= 2;
+
+ while (count--)
+ *to++ = *from++;
+}
+
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count)
+{
+ unsigned short *to = tp;
+ const volatile unsigned short *from = (const void *)fp;
+
+ count++;
+ count /= 2;
+
+ while (count--)
+ *to++ = *from++;
+}
+
+
diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
new file mode 100644
index 00000000000..38fcdcf7c4c
--- /dev/null
+++ b/drivers/net/ethernet/8390/mcf8390.c
@@ -0,0 +1,481 @@
+/*
+ * Support for ColdFire CPU based boards using a NS8390 Ethernet device.
+ *
+ * Derived from the many other 8390 drivers.
+ *
+ * (C) Copyright 2012, Greg Ungerer <gerg@uclinux.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <asm/mcf8390.h>
+
+static const char version[] =
+ "mcf8390.c: (15-06-2012) Greg Ungerer <gerg@uclinux.org>";
+
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset */
+#define NE_RESET 0x1f /* Issue a read to reset ,a write to clear */
+#define NE_EN0_ISR 0x07
+#define NE_EN0_DCFG 0x0e
+#define NE_EN0_RSARLO 0x08
+#define NE_EN0_RSARHI 0x09
+#define NE_EN0_RCNTLO 0x0a
+#define NE_EN0_RXCR 0x0c
+#define NE_EN0_TXCR 0x0d
+#define NE_EN0_RCNTHI 0x0b
+#define NE_EN0_IMR 0x0f
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+static u32 mcf8390_msg_enable;
+
+#ifdef NE2000_ODDOFFSET
+/*
+ * A lot of the ColdFire boards use a separate address region for odd offset
+ * register addresses. The following functions convert and map as required.
+ * Note that the data port accesses are treated a little differently, and
+ * always accessed via the insX/outsX functions.
+ */
+static inline u32 NE_PTR(u32 addr)
+{
+ if (addr & 1)
+ return addr - 1 + NE2000_ODDOFFSET;
+ return addr;
+}
+
+static inline u32 NE_DATA_PTR(u32 addr)
+{
+ return addr;
+}
+
+void ei_outb(u32 val, u32 addr)
+{
+ NE2000_BYTE *rp;
+
+ rp = (NE2000_BYTE *) NE_PTR(addr);
+ *rp = RSWAP(val);
+}
+
+#define ei_inb ei_inb
+u8 ei_inb(u32 addr)
+{
+ NE2000_BYTE *rp, val;
+
+ rp = (NE2000_BYTE *) NE_PTR(addr);
+ val = *rp;
+ return (u8) (RSWAP(val) & 0xff);
+}
+
+void ei_insb(u32 addr, void *vbuf, int len)
+{
+ NE2000_BYTE *rp, val;
+ u8 *buf;
+
+ buf = (u8 *) vbuf;
+ rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *rp;
+ *buf++ = RSWAP(val);
+ }
+}
+
+void ei_insw(u32 addr, void *vbuf, int len)
+{
+ volatile u16 *rp;
+ u16 w, *buf;
+
+ buf = (u16 *) vbuf;
+ rp = (volatile u16 *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *rp;
+ *buf++ = BSWAP(w);
+ }
+}
+
+void ei_outsb(u32 addr, const void *vbuf, int len)
+{
+ NE2000_BYTE *rp, val;
+ u8 *buf;
+
+ buf = (u8 *) vbuf;
+ rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *buf++;
+ *rp = RSWAP(val);
+ }
+}
+
+void ei_outsw(u32 addr, const void *vbuf, int len)
+{
+ volatile u16 *rp;
+ u16 w, *buf;
+
+ buf = (u16 *) vbuf;
+ rp = (volatile u16 *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *buf++;
+ *rp = BSWAP(w);
+ }
+}
+
+#else /* !NE2000_ODDOFFSET */
+
+#define ei_inb inb
+#define ei_outb outb
+#define ei_insb insb
+#define ei_insw insw
+#define ei_outsb outsb
+#define ei_outsw outsw
+
+#endif /* !NE2000_ODDOFFSET */
+
+#define ei_inb_p ei_inb
+#define ei_outb_p ei_outb
+
+#include "lib8390.c"
+
+/*
+ * Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void mcf8390_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ u32 addr = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
+
+ ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RESET) == 0) {
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete\n", __func__);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RESET, addr + NE_EN0_ISR);
+}
+
+/*
+ * This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+static void mcf8390_dmaing_err(const char *func, struct net_device *dev,
+ struct ei_device *ei_local)
+{
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ func, ei_local->dmaing, ei_local->irqlock);
+}
+
+/*
+ * Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+static void mcf8390_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+
+ if (ei_local->dmaing) {
+ mcf8390_dmaing_err(__func__, dev, ei_local);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+ ei_outb(sizeof(struct e8390_pkt_hdr), addr + NE_EN0_RCNTLO);
+ ei_outb(0, addr + NE_EN0_RCNTHI);
+ ei_outb(0, addr + NE_EN0_RSARLO); /* On page boundary */
+ ei_outb(ring_page, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
+
+ ei_insw(addr + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr) >> 1);
+
+ outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+
+ hdr->count = cpu_to_le16(hdr->count);
+}
+
+/*
+ * Block input and output, similar to the Crynwr packet driver.
+ * If you are porting to a new ethercard, look at the packet driver source
+ * for hints. The NEx000 doesn't share the on-board packet memory --
+ * you have to put the packet out through the "remote DMA" dataport
+ * using z_writeb.
+ */
+static void mcf8390_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+ char *buf = skb->data;
+
+ if (ei_local->dmaing) {
+ mcf8390_dmaing_err(__func__, dev, ei_local);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+ ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
+ ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
+ ei_outb(ring_offset & 0xff, addr + NE_EN0_RSARLO);
+ ei_outb(ring_offset >> 8, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
+
+ ei_insw(addr + NE_DATAPORT, buf, count >> 1);
+ if (count & 1)
+ buf[count - 1] = ei_inb(addr + NE_DATAPORT);
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+}
+
+static void mcf8390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+ unsigned long dma_start;
+
+ /* Make sure we transfer all bytes if 16bit IO writes */
+ if (count & 0x1)
+ count++;
+
+ if (ei_local->dmaing) {
+ mcf8390_dmaing_err(__func__, dev, ei_local);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, addr + NE_CMD);
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
+ ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
+ ei_outb(0x00, addr + NE_EN0_RSARLO);
+ ei_outb(start_page, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RWRITE + E8390_START, addr + NE_CMD);
+
+ ei_outsw(addr + NE_DATAPORT, buf, count >> 1);
+
+ dma_start = jiffies;
+ while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RDC) == 0) {
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC\n");
+ mcf8390_reset_8390(dev);
+ __NS8390_init(dev, 1);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+}
+
+static const struct net_device_ops mcf8390_netdev_ops = {
+ .ndo_open = __ei_open,
+ .ndo_stop = __ei_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int mcf8390_init(struct net_device *dev)
+{
+ static u32 offsets[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char SA_prom[32];
+ u32 addr = dev->base_addr;
+ int start_page, stop_page;
+ int i, ret;
+
+ mcf8390_reset_8390(dev);
+
+ /*
+ * Read the 16 bytes of station address PROM.
+ * We must first initialize registers,
+ * similar to NS8390_init(eifdev, 0).
+ * We can't reliably read the SAPROM address without this.
+ * (I learned the hard way!).
+ */
+ {
+ static const struct {
+ u32 value;
+ u32 offset;
+ } program_seq[] = {
+ {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+ /* Select page 0 */
+ {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
+ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_IMR}, /* Mask completion irq */
+ {0xFF, NE_EN0_ISR},
+ {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
+ {32, NE_EN0_RCNTLO},
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
+ {0x00, NE_EN0_RSARHI},
+ {E8390_RREAD + E8390_START, NE_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
+ ei_outb(program_seq[i].value,
+ addr + program_seq[i].offset);
+ }
+ }
+
+ for (i = 0; i < 16; i++) {
+ SA_prom[i] = ei_inb(addr + NE_DATAPORT);
+ ei_inb(addr + NE_DATAPORT);
+ }
+
+ /* We must set the 8390 for word mode. */
+ ei_outb(0x49, addr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ /* Install the Interrupt handler */
+ ret = request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ netdev_dbg(dev, "Found ethernet address: %pM\n", dev->dev_addr);
+
+ ei_local->name = "mcf8390";
+ ei_local->tx_start_page = start_page;
+ ei_local->stop_page = stop_page;
+ ei_local->word16 = 1;
+ ei_local->rx_start_page = start_page + TX_PAGES;
+ ei_local->reset_8390 = mcf8390_reset_8390;
+ ei_local->block_input = mcf8390_block_input;
+ ei_local->block_output = mcf8390_block_output;
+ ei_local->get_8390_hdr = mcf8390_get_8390_hdr;
+ ei_local->reg_offset = offsets;
+
+ dev->netdev_ops = &mcf8390_netdev_ops;
+ __NS8390_init(dev, 0);
+ ret = register_netdev(dev);
+ if (ret) {
+ free_irq(dev->irq, dev);
+ return ret;
+ }
+
+ netdev_info(dev, "addr=0x%08x irq=%d, Ethernet Address %pM\n",
+ addr, dev->irq, dev->dev_addr);
+ return 0;
+}
+
+static int mcf8390_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ei_device *ei_local;
+ struct resource *mem, *irq;
+ resource_size_t msize;
+ int ret;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (irq == NULL) {
+ dev_err(&pdev->dev, "no IRQ specified?\n");
+ return -ENXIO;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem == NULL) {
+ dev_err(&pdev->dev, "no memory address specified?\n");
+ return -ENXIO;
+ }
+ msize = resource_size(mem);
+ if (!request_mem_region(mem->start, msize, pdev->name))
+ return -EBUSY;
+
+ dev = ____alloc_ei_netdev(0);
+ if (dev == NULL) {
+ release_mem_region(mem->start, msize);
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = mcf8390_msg_enable;
+
+ dev->irq = irq->start;
+ dev->base_addr = mem->start;
+
+ ret = mcf8390_init(dev);
+ if (ret) {
+ release_mem_region(mem->start, msize);
+ free_netdev(dev);
+ return ret;
+ }
+ return 0;
+}
+
+static int mcf8390_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ unregister_netdev(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem)
+ release_mem_region(mem->start, resource_size(mem));
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver mcf8390_drv = {
+ .driver = {
+ .name = "mcf8390",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcf8390_probe,
+ .remove = mcf8390_remove,
+};
+
+module_platform_driver(mcf8390_drv);
+
+MODULE_DESCRIPTION("MCF8390 ColdFire NS8390 driver");
+MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mcf8390");
diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c
new file mode 100644
index 00000000000..58eaa8f3494
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne.c
@@ -0,0 +1,1017 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ This driver should work with many programmed-I/O 8390-based ethernet
+ boards. Currently it supports the NE1000, NE2000, many clones,
+ and some Cabletron products.
+
+ Changelog:
+
+ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made
+ sanity checks and bad clone support optional.
+ Paul Gortmaker : new reset code, reset card after probe at boot.
+ Paul Gortmaker : multiple card support for module users.
+ Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c
+ Paul Gortmaker : Allow users with bad cards to avoid full probe.
+ Paul Gortmaker : PCI probe changes, more PCI cards supported.
+ rjohnson@analogic.com : Changed init order so an interrupt will only
+ occur after memory is allocated for dev->priv. Deallocated memory
+ last in cleanup_modue()
+ Richard Guenther : Added support for ISAPnP cards
+ Paul Gortmaker : Discontinued PCI support - use ne2k-pci.c instead.
+ Hayato Fujiwara : Add m32r support.
+
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static const char version1[] =
+"ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)\n";
+static const char version2[] =
+"Last modified Nov 1, 2000 by Paul Gortmaker\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define SUPPORT_NE_BAD_CLONES
+/* 0xbad = bad sig or no reset ack */
+#define BAD 0xbad
+
+#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
+static struct platform_device *pdev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS];
+static u32 ne_msg_enable;
+
+#ifdef MODULE
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+module_param_named(msg_enable, ne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(io, "I/O base address(es),required");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+#endif /* MODULE */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+#if !defined(MODULE) && (defined(CONFIG_ISA) || defined(CONFIG_M32R))
+/* Do we need a portlist for the ISA auto-probe ? */
+#define NEEDS_PORTLIST
+#endif
+
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+#ifdef NEEDS_PORTLIST
+static unsigned int netcard_portlist[] __initdata = {
+ 0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0
+};
+#endif
+
+static struct isapnp_device_id isapnp_clone_list[] __initdata = {
+ { ISAPNP_CARD_ID('A','X','E',0x2011),
+ ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011),
+ (long) "NetGear EA201" },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216),
+ (long) "NN NE2000" },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6),
+ (long) "Generic PNP" },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list);
+
+#ifdef SUPPORT_NE_BAD_CLONES
+/* A list of bad clones that we none-the-less recognize. */
+static struct { const char *name8, *name16; unsigned char SAprefix[4];}
+bad_clone_list[] __initdata = {
+ {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
+ {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
+ {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
+ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
+ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+ {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
+ {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
+ {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
+ {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
+ {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */
+#ifdef CONFIG_MACH_TX49XX
+ {"RBHMA4X00-RTL8019", "RBHMA4X00-RTL8019", {0x00, 0x60, 0x0a}}, /* Toshiba built-in */
+#endif
+ {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */
+ {NULL,}
+};
+#endif
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#if defined(CONFIG_PLAT_MAPPI)
+# define DCR_VAL 0x4b
+#elif defined(CONFIG_PLAT_OAKS32R) || \
+ defined(CONFIG_MACH_TX49XX)
+# define DCR_VAL 0x48 /* 8-bit mode */
+#else
+# define DCR_VAL 0x49
+#endif
+
+static int ne_probe1(struct net_device *dev, unsigned long ioaddr);
+static int ne_probe_isapnp(struct net_device *dev);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+static int __init do_ne_probe(struct net_device *dev)
+{
+ unsigned long base_addr = dev->base_addr;
+#ifdef NEEDS_PORTLIST
+ int orig_irq = dev->irq;
+#endif
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0x1ff) { /* Check a single specified location. */
+ int ret = ne_probe1(dev, base_addr);
+ if (ret)
+ netdev_warn(dev, "ne.c: No NE*000 card found at "
+ "i/o = %#lx\n", base_addr);
+ return ret;
+ }
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ /* Then look for any installed ISAPnP clones */
+ if (isapnp_present() && (ne_probe_isapnp(dev) == 0))
+ return 0;
+
+#ifdef NEEDS_PORTLIST
+ /* Last resort. The semi-risky ISA auto-probe. */
+ for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) {
+ int ioaddr = netcard_portlist[base_addr];
+ dev->irq = orig_irq;
+ if (ne_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+#endif
+
+ return -ENODEV;
+}
+
+static int __init ne_probe_isapnp(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; isapnp_clone_list[i].vendor != 0; i++) {
+ struct pnp_dev *idev = NULL;
+
+ while ((idev = pnp_find_dev(NULL,
+ isapnp_clone_list[i].vendor,
+ isapnp_clone_list[i].function,
+ idev))) {
+ /* Avoid already found cards from previous calls */
+ if (pnp_device_attach(idev) < 0)
+ continue;
+ if (pnp_activate_dev(idev) < 0) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* if no io and irq, search for next */
+ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* found it */
+ dev->base_addr = pnp_port_start(idev, 0);
+ dev->irq = pnp_irq(idev, 0);
+ netdev_info(dev,
+ "ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+ (char *) isapnp_clone_list[i].driver_data,
+ dev->base_addr, dev->irq);
+ if (ne_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
+ netdev_err(dev,
+ "ne.c: Probe of ISAPnP card at %#lx failed.\n",
+ dev->base_addr);
+ pnp_device_detach(idev);
+ return -ENXIO;
+ }
+ ei_status.priv = (unsigned long)idev;
+ break;
+ }
+ if (!idev)
+ continue;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+ int neX000, ctron, copam, bad_card;
+ int reg0, ret;
+ static unsigned version_printed;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ reg0 = inb_p(ioaddr);
+ if (reg0 == 0xFF) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* Do a preliminary verification that we have a 8390. */
+ {
+ int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + 0x0d);
+ outb_p(0xff, ioaddr + 0x0d);
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr);
+ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ if ((ne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ netdev_info(dev, "%s%s", version1, version2);
+
+ netdev_info(dev, "NE*000 ethercard probe at %#3lx:", ioaddr);
+
+ /* A user with a poor card that fails to ack the reset, or that
+ does not have a valid 0x57,0x57 signature can still use this
+ without having to recompile. Specifying an i/o address along
+ with an otherwise unused dev->mem_end value of "0xBAD" will
+ cause the driver to skip these parts of the probe. */
+
+ bad_card = ((dev->base_addr != 0) && (dev->mem_end == BAD));
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+
+ {
+ unsigned long reset_start_time = jiffies;
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ if (bad_card) {
+ pr_cont(" (warning: no reset ack)");
+ break;
+ } else {
+ pr_cont(" not found (no reset ack).\n");
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390p_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned char value, offset; } program_seq[] =
+ {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+ if (SA_prom[i] != SA_prom[i+1])
+ wordlength = 1;
+ }
+
+ if (wordlength == 2)
+ {
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+ /* We must set the 8390 for word mode. */
+ outb_p(DCR_VAL, ioaddr + EN0_DCFG);
+ start_page = NESM_START_PG;
+
+ /*
+ * Realtek RTL8019AS datasheet says that the PSTOP register
+ * shouldn't exceed 0x60 in 8-bit mode.
+ * This chip can be identified by reading the signature from
+ * the remote byte count registers (otherwise write-only)...
+ */
+ if ((DCR_VAL & 0x01) == 0 && /* 8-bit mode */
+ inb(ioaddr + EN0_RCNTLO) == 0x50 &&
+ inb(ioaddr + EN0_RCNTHI) == 0x70)
+ stop_page = 0x60;
+ else
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+#if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R)
+ neX000 = ((SA_prom[14] == 0x57 && SA_prom[15] == 0x57)
+ || (SA_prom[14] == 0x42 && SA_prom[15] == 0x42));
+#else
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+#endif
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+ copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
+
+ /* Set up the rest of the parameters. */
+ if (neX000 || bad_card || copam) {
+ name = (wordlength == 2) ? "NE2000" : "NE1000";
+ }
+ else if (ctron)
+ {
+ name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+ start_page = 0x01;
+ stop_page = (wordlength == 2) ? 0x40 : 0x20;
+ }
+ else
+ {
+#ifdef SUPPORT_NE_BAD_CLONES
+ /* Ack! Well, there might be a *bad* NE*000 clone there.
+ Check for total bogus addresses. */
+ for (i = 0; bad_clone_list[i].name8; i++)
+ {
+ if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
+ SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
+ SA_prom[2] == bad_clone_list[i].SAprefix[2])
+ {
+ if (wordlength == 2)
+ {
+ name = bad_clone_list[i].name16;
+ } else {
+ name = bad_clone_list[i].name8;
+ }
+ break;
+ }
+ }
+ if (bad_clone_list[i].name8 == NULL)
+ {
+ pr_cont(" not found (invalid signature %2.2x %2.2x).\n",
+ SA_prom[14], SA_prom[15]);
+ ret = -ENXIO;
+ goto err_out;
+ }
+#else
+ pr_cont(" not found.\n");
+ ret = -ENXIO;
+ goto err_out;
+#endif
+ }
+
+ if (dev->irq < 2)
+ {
+ unsigned long cookie = probe_irq_on();
+ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, ioaddr + EN0_RCNTLO);
+ outb_p(0x00, ioaddr + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ mdelay(10); /* wait 10ms for interrupt to propagate */
+ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
+ dev->irq = probe_irq_off(cookie);
+ if (netif_msg_probe(ei_local))
+ pr_cont(" autoirq is %d", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ if (! dev->irq) {
+ pr_cont(" failed to detect IRQ line.\n");
+ ret = -EAGAIN;
+ goto err_out;
+ }
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ ret = request_irq(dev->irq, eip_interrupt, 0, name, dev);
+ if (ret) {
+ pr_cont(" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
+ goto err_out;
+ }
+
+ dev->base_addr = ioaddr;
+
+#ifdef CONFIG_PLAT_MAPPI
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+ ioaddr + E8390_CMD); /* 0x61 */
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = SA_prom[i]
+ = inb_p(ioaddr + EN1_PHYS_SHIFT(i));
+ }
+#else
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = SA_prom[i];
+ }
+#endif
+
+ pr_cont("%pM\n", dev->dev_addr);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+
+ /* Use 16-bit mode only if this wasn't overridden by DCR_VAL */
+ ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01));
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+ ei_status.priv = 0;
+
+ dev->netdev_ops = &eip_netdev_ops;
+ NS8390p_init(dev, 0);
+
+ ei_local->msg_enable = ne_msg_enable;
+ ret = register_netdev(dev);
+ if (ret)
+ goto out_irq;
+ netdev_info(dev, "%s found at %#lx, using IRQ %d.\n",
+ name, ioaddr, dev->irq);
+ return 0;
+
+out_irq:
+ free_irq(dev->irq, dev);
+err_out:
+ release_region(ioaddr, NE_IO_EXTENT);
+ return ret;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+
+static void ne_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ netdev_err(dev, "ne_reset_8390() did not complete.\n");
+ break;
+ }
+ outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+
+ if (ei_status.dmaing)
+ {
+ netdev_err(dev, "DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d].\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.word16)
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ are porting to a new ethercard, look at the packet driver source for hints.
+ The NEx000 doesn't share the on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+ struct ei_device *ei_local = netdev_priv(dev);
+#endif
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing)
+ {
+ netdev_err(dev, "DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d].\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16)
+ {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+
+ if (netif_msg_rx_status(ei_local))
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ netdev_warn(dev, "RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+ struct ei_device *ei_local = netdev_priv(dev);
+#endif
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing)
+ {
+ netdev_err(dev, "DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ udelay(6);
+#endif
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+
+ if (netif_msg_tx_queued(ei_local))
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+
+ if (tries <= 0)
+ {
+ netdev_warn(dev, "Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ ne_reset_8390(dev);
+ NS8390p_init(dev, 1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static int __init ne_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ int err, this_dev = pdev->id;
+ struct resource *res;
+
+ dev = alloc_eip_netdev();
+ if (!dev)
+ return -ENOMEM;
+
+ /* ne.c doesn't populate resources in platform_device, but
+ * rbtx4927_ne_init and rbtx4938_ne_init do register devices
+ * with resources.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res) {
+ dev->base_addr = res->start;
+ dev->irq = platform_get_irq(pdev, 0);
+ } else {
+ if (this_dev < 0 || this_dev >= MAX_NE_CARDS) {
+ free_netdev(dev);
+ return -EINVAL;
+ }
+ dev->base_addr = io[this_dev];
+ dev->irq = irq[this_dev];
+ dev->mem_end = bad[this_dev];
+ }
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ err = do_ne_probe(dev);
+ if (err) {
+ free_netdev(dev);
+ return err;
+ }
+ platform_set_drvdata(pdev, dev);
+
+ /* Update with any values found by probing, don't update if
+ * resources were specified.
+ */
+ if (!res) {
+ io[this_dev] = dev->base_addr;
+ irq[this_dev] = dev->irq;
+ }
+ return 0;
+}
+
+static int ne_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (dev) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ netif_device_detach(dev);
+ unregister_netdev(dev);
+ if (idev)
+ pnp_device_detach(idev);
+ /* Careful ne_drv_remove can be called twice, once from
+ * the platform_driver.remove and again when the
+ * platform_device is being removed.
+ */
+ ei_status.priv = 0;
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ free_netdev(dev);
+ }
+ return 0;
+}
+
+/* Remove unused devices or all if true. */
+static void ne_loop_rm_unreg(int all)
+{
+ int this_dev;
+ struct platform_device *pdev;
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ pdev = pdev_ne[this_dev];
+ /* No network device == unused */
+ if (pdev && (!platform_get_drvdata(pdev) || all)) {
+ ne_drv_remove(pdev);
+ platform_device_unregister(pdev);
+ pdev_ne[this_dev] = NULL;
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static int ne_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ netif_device_detach(dev);
+ if (idev)
+ pnp_stop_dev(idev);
+ }
+ return 0;
+}
+
+static int ne_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ if (idev)
+ pnp_start_dev(idev);
+ ne_reset_8390(dev);
+ NS8390p_init(dev, 1);
+ netif_device_attach(dev);
+ }
+ return 0;
+}
+#else
+#define ne_drv_suspend NULL
+#define ne_drv_resume NULL
+#endif
+
+static struct platform_driver ne_driver = {
+ .remove = ne_drv_remove,
+ .suspend = ne_drv_suspend,
+ .resume = ne_drv_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __init ne_add_devices(void)
+{
+ int this_dev;
+ struct platform_device *pdev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ if (pdev_ne[this_dev])
+ continue;
+ pdev = platform_device_register_simple(
+ DRV_NAME, this_dev, NULL, 0);
+ if (IS_ERR(pdev))
+ continue;
+ pdev_ne[this_dev] = pdev;
+ }
+}
+
+#ifdef MODULE
+int __init init_module(void)
+{
+ int retval;
+ ne_add_devices();
+ retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+ if (retval) {
+ if (io[0] == 0)
+ pr_notice("ne.c: You must supply \"io=0xNNN\""
+ " value(s) for ISA cards.\n");
+ ne_loop_rm_unreg(1);
+ return retval;
+ }
+
+ /* Unregister unused platform_devices. */
+ ne_loop_rm_unreg(0);
+ return retval;
+}
+#else /* MODULE */
+static int __init ne_init(void)
+{
+ int retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+
+ /* Unregister unused platform_devices. */
+ ne_loop_rm_unreg(0);
+ return retval;
+}
+module_init(ne_init);
+
+struct net_device * __init ne_probe(int unit)
+{
+ int this_dev;
+ struct net_device *dev;
+
+ /* Find an empty slot, that is no net_device and zero io port. */
+ this_dev = 0;
+ while ((pdev_ne[this_dev] && platform_get_drvdata(pdev_ne[this_dev])) ||
+ io[this_dev]) {
+ if (++this_dev == MAX_NE_CARDS)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Get irq, io from kernel command line */
+ dev = alloc_eip_netdev();
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ io[this_dev] = dev->base_addr;
+ irq[this_dev] = dev->irq;
+ bad[this_dev] = dev->mem_end;
+
+ free_netdev(dev);
+
+ ne_add_devices();
+
+ /* return the first device found */
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ if (pdev_ne[this_dev]) {
+ dev = platform_get_drvdata(pdev_ne[this_dev]);
+ if (dev)
+ return dev;
+ }
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+#endif /* MODULE */
+
+static void __exit ne_exit(void)
+{
+ platform_driver_unregister(&ne_driver);
+ ne_loop_rm_unreg(1);
+}
+module_exit(ne_exit);
diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
new file mode 100644
index 00000000000..f395c967262
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -0,0 +1,741 @@
+/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
+/*
+ A Linux device driver for PCI NE2000 clones.
+
+ Authors and other copyright holders:
+ 1992-2000 by Donald Becker, NE2000 core and various modifications.
+ 1995-1998 by Paul Gortmaker, core modifications and PCI support.
+ Copyright 1993 assigned to the United States Government as represented
+ by the Director, National Security Agency.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Issues remaining:
+ People are making PCI ne2000 clones! Oh the horror, the horror...
+ Limited full-duplex support.
+*/
+
+#define DRV_NAME "ne2k-pci"
+#define DRV_VERSION "1.03"
+#define DRV_RELDATE "9/22/2003"
+
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[MAX_UNITS];
+static int options[MAX_UNITS];
+
+/* Force a non std. amount of memory. Units are 256 byte pages. */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "8390.h"
+
+static u32 ne2k_msg_enable;
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] =
+ KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE
+ " D. Becker/P. Gortmaker\n";
+
+#if defined(__powerpc__)
+#define inl_le(addr) le32_to_cpu(inl(addr))
+#define inw_le(addr) le16_to_cpu(inw(addr))
+#endif
+
+#define PFX DRV_NAME ": "
+
+MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
+MODULE_DESCRIPTION("PCI NE2000 clone driver");
+MODULE_LICENSE("GPL");
+
+module_param_named(msg_enable, ne2k_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+MODULE_PARM_DESC(options, "Bit 5: full duplex");
+MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
+
+/* Some defines that people can play with if so inclined. */
+
+/* Use 32 bit data-movement operations instead of 16 bit. */
+#define USE_LONGIO
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Flags. We rename an existing ei_status field to store flags! */
+/* Thus only the low 8 bits are usable for non-init-time flags. */
+#define ne2k_flags reg0
+enum {
+ ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */
+ FORCE_FDX=0x20, /* User override. */
+ REALTEK_FDX=0x40, HOLTEK_FDX=0x80,
+ STOP_PG_0x60=0x100,
+};
+
+enum ne2k_pci_chipsets {
+ CH_RealTek_RTL_8029 = 0,
+ CH_Winbond_89C940,
+ CH_Compex_RL2000,
+ CH_KTI_ET32P2,
+ CH_NetVin_NV5000SC,
+ CH_Via_86C926,
+ CH_SureCom_NE34,
+ CH_Winbond_W89C940F,
+ CH_Holtek_HT80232,
+ CH_Holtek_HT80229,
+ CH_Winbond_89C940_8c4a,
+};
+
+
+static struct {
+ char *name;
+ int flags;
+} pci_clone_list[] = {
+ {"RealTek RTL-8029", REALTEK_FDX},
+ {"Winbond 89C940", 0},
+ {"Compex RL2000", 0},
+ {"KTI ET32P2", 0},
+ {"NetVin NV5000SC", 0},
+ {"Via 86C926", ONLY_16BIT_IO},
+ {"SureCom NE34", 0},
+ {"Winbond W89C940F", 0},
+ {"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
+ {"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
+ {"Winbond W89C940(misprogrammed)", 0},
+ {NULL,}
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(ne2k_pci_tbl) = {
+ { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
+ { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
+ { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
+ { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
+ { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
+ { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
+ { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
+ { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
+ { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
+ { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
+ { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl);
+
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+
+static int ne2k_pci_open(struct net_device *dev);
+static int ne2k_pci_close(struct net_device *dev);
+
+static void ne2k_pci_reset_8390(struct net_device *dev);
+static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne2k_pci_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne2k_pci_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static const struct ethtool_ops ne2k_pci_ethtool_ops;
+
+
+
+/* There is no room in the standard 8390 structure for extra info we need,
+ so we build a meta/outer-wrapper structure.. */
+struct ne2k_pci_card {
+ struct net_device *dev;
+ struct pci_dev *pci_dev;
+};
+
+
+
+/*
+ NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
+ buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes
+ 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
+ detected by their SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+*/
+
+static const struct net_device_ops ne2k_netdev_ops = {
+ .ndo_open = ne2k_pci_open,
+ .ndo_stop = ne2k_pci_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int ne2k_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ int i;
+ unsigned char SA_prom[32];
+ int start_page, stop_page;
+ int irq, reg0, chip_idx = ent->driver_data;
+ static unsigned int fnd_cnt;
+ long ioaddr;
+ int flags = pci_clone_list[chip_idx].flags;
+ struct ei_device *ei_local;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
+ fnd_cnt++;
+
+ i = pci_enable_device (pdev);
+ if (i)
+ return i;
+
+ ioaddr = pci_resource_start (pdev, 0);
+ irq = pdev->irq;
+
+ if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
+ dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n");
+ return -ENODEV;
+ }
+
+ if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
+ dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
+ NE_IO_EXTENT, ioaddr);
+ return -EBUSY;
+ }
+
+ reg0 = inb(ioaddr);
+ if (reg0 == 0xFF)
+ goto err_out_free_res;
+
+ /* Do a preliminary verification that we have a 8390. */
+ {
+ int regd;
+ outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb(ioaddr + 0x0d);
+ outb(0xff, ioaddr + 0x0d);
+ outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb(ioaddr + EN0_COUNTER0) != 0) {
+ outb(reg0, ioaddr);
+ outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ goto err_out_free_res;
+ }
+ }
+
+ /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */
+ dev = alloc_ei_netdev();
+ if (!dev) {
+ dev_err(&pdev->dev, "cannot allocate ethernet device\n");
+ goto err_out_free_res;
+ }
+ dev->netdev_ops = &ne2k_netdev_ops;
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = ne2k_msg_enable;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ {
+ unsigned long reset_start_time = jiffies;
+
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ /* This looks like a horrible timing loop, but it should never take
+ more than a few cycles.
+ */
+ while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ /* Limit wait: '2' avoids jiffy roll-over. */
+ if (jiffies - reset_start_time > 2) {
+ dev_err(&pdev->dev,
+ "Card failure (no reset ack).\n");
+ goto err_out_free_netdev;
+ }
+
+ outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned char value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x49, EN0_DCFG}, /* Set word-wide access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+
+ /* Note: all PCI cards have at least 16 bit access, so we don't have
+ to check for 8 bit cards. Most cards permit 32 bit access. */
+ if (flags & ONLY_32BIT_IO) {
+ for (i = 0; i < 4 ; i++)
+ ((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT));
+ } else
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+
+ /* We always set the 8390 registers for word mode. */
+ outb(0x49, ioaddr + EN0_DCFG);
+ start_page = NESM_START_PG;
+
+ stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG;
+
+ /* Set up the rest of the parameters. */
+ dev->irq = irq;
+ dev->base_addr = ioaddr;
+ pci_set_drvdata(pdev, dev);
+
+ ei_status.name = pci_clone_list[chip_idx].name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+ ei_status.ne2k_flags = flags;
+ if (fnd_cnt < MAX_UNITS) {
+ if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX))
+ ei_status.ne2k_flags |= FORCE_FDX;
+ }
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne2k_pci_reset_8390;
+ ei_status.block_input = &ne2k_pci_block_input;
+ ei_status.block_output = &ne2k_pci_block_output;
+ ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
+ ei_status.priv = (unsigned long) pdev;
+
+ dev->ethtool_ops = &ne2k_pci_ethtool_ops;
+ NS8390_init(dev, 0);
+
+ memcpy(dev->dev_addr, SA_prom, dev->addr_len);
+
+ i = register_netdev(dev);
+ if (i)
+ goto err_out_free_netdev;
+
+ netdev_info(dev, "%s found at %#lx, IRQ %d, %pM.\n",
+ pci_clone_list[chip_idx].name, ioaddr, dev->irq,
+ dev->dev_addr);
+
+ return 0;
+
+err_out_free_netdev:
+ free_netdev (dev);
+err_out_free_res:
+ release_region (ioaddr, NE_IO_EXTENT);
+ return -ENODEV;
+}
+
+/*
+ * Magic incantation sequence for full duplex on the supported cards.
+ */
+static inline int set_realtek_fdx(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */
+ outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */
+ outb(0x40, ioaddr + 0x06); /* Enable full duplex */
+ outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */
+ outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */
+ return 0;
+}
+
+static inline int set_holtek_fdx(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
+ return 0;
+}
+
+static int ne2k_pci_set_fdx(struct net_device *dev)
+{
+ if (ei_status.ne2k_flags & REALTEK_FDX)
+ return set_realtek_fdx(dev);
+ else if (ei_status.ne2k_flags & HOLTEK_FDX)
+ return set_holtek_fdx(dev);
+
+ return -EOPNOTSUPP;
+}
+
+static int ne2k_pci_open(struct net_device *dev)
+{
+ int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev);
+ if (ret)
+ return ret;
+
+ if (ei_status.ne2k_flags & FORCE_FDX)
+ ne2k_pci_set_fdx(dev);
+
+ ei_open(dev);
+ return 0;
+}
+
+static int ne2k_pci_close(struct net_device *dev)
+{
+ ei_close(dev);
+ free_irq(dev->irq, dev);
+ return 0;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+static void ne2k_pci_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n",
+ jiffies);
+
+ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2) {
+ netdev_err(dev, "ne2k_pci_reset_8390() did not complete.\n");
+ break;
+ }
+ outb(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ long nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne2k_pci_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d].\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb(0, nic_base + EN0_RCNTHI);
+ outb(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb(ring_page, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ } else {
+ *(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT));
+ le16_to_cpus(&hdr->count);
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ are porting to a new ethercard, look at the packet driver source for hints.
+ The NEx000 doesn't share the on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static void ne2k_pci_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ long nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne2k_pci_block_input "
+ "[DMAstat:%d][irqlock:%d].\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+ count = (count + 3) & 0xFFFC;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(count & 0xff, nic_base + EN0_RCNTLO);
+ outb(count >> 8, nic_base + EN0_RCNTHI);
+ outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+ }
+ } else {
+ insl(NE_BASE + NE_DATAPORT, buf, count>>2);
+ if (count & 3) {
+ buf += count & ~3;
+ if (count & 2) {
+ __le16 *b = (__le16 *)buf;
+
+ *b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT));
+ buf = (char *)b;
+ }
+ if (count & 1)
+ *buf = inb(NE_BASE + NE_DATAPORT);
+ }
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ long nic_base = NE_BASE;
+ unsigned long dma_start;
+
+ /* On little-endian it's always safe to round the count up for
+ word writes. */
+ if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+ count = (count + 3) & 0xFFFC;
+ else
+ if (count & 0x01)
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in ne2k_pci_block_output."
+ "[DMAstat:%d][irqlock:%d]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb(0x42, nic_base + EN0_RCNTLO);
+ outb(0x00, nic_base + EN0_RCNTHI);
+ outb(0x42, nic_base + EN0_RSARLO);
+ outb(0x00, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+#endif
+ outb(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb(count & 0xff, nic_base + EN0_RCNTLO);
+ outb(count >> 8, nic_base + EN0_RCNTHI);
+ outb(0x00, nic_base + EN0_RSARLO);
+ outb(start_page, nic_base + EN0_RSARHI);
+ outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
+ if (count & 3) {
+ buf += count & ~3;
+ if (count & 2) {
+ __le16 *b = (__le16 *)buf;
+
+ outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT);
+ buf = (char *)b;
+ }
+ }
+ }
+
+ dma_start = jiffies;
+
+ while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ ne2k_pci_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ei_device *ei = netdev_priv(dev);
+ struct pci_dev *pci_dev = (struct pci_dev *) ei->priv;
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info));
+}
+
+static u32 ne2k_pci_get_msglevel(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ return ei_local->msg_enable;
+}
+
+static void ne2k_pci_set_msglevel(struct net_device *dev, u32 v)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ ei_local->msg_enable = v;
+}
+
+static const struct ethtool_ops ne2k_pci_ethtool_ops = {
+ .get_drvinfo = ne2k_pci_get_drvinfo,
+ .get_msglevel = ne2k_pci_get_msglevel,
+ .set_msglevel = ne2k_pci_set_msglevel,
+};
+
+static void ne2k_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ BUG_ON(!dev);
+ unregister_netdev(dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM
+static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ netif_device_detach(dev);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int ne2k_pci_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ int rc;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ NS8390_init(dev, 1);
+ netif_device_attach(dev);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver ne2k_driver = {
+ .name = DRV_NAME,
+ .probe = ne2k_pci_init_one,
+ .remove = ne2k_pci_remove_one,
+ .id_table = ne2k_pci_tbl,
+#ifdef CONFIG_PM
+ .suspend = ne2k_pci_suspend,
+ .resume = ne2k_pci_resume,
+#endif /* CONFIG_PM */
+
+};
+
+
+static int __init ne2k_pci_init(void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
+ return pci_register_driver(&ne2k_driver);
+}
+
+
+static void __exit ne2k_pci_cleanup(void)
+{
+ pci_unregister_driver (&ne2k_driver);
+}
+
+module_init(ne2k_pci_init);
+module_exit(ne2k_pci_cleanup);
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
new file mode 100644
index 00000000000..ca3c2b921cf
--- /dev/null
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -0,0 +1,1702 @@
+/*======================================================================
+
+ A PCMCIA ethernet driver for NS8390-based cards
+
+ This driver supports the D-Link DE-650 and Linksys EthernetCard
+ cards, the newer D-Link and Linksys combo cards, Accton EN2212
+ cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
+ mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
+ Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
+ mode. It will also handle the Socket EA card in either mode.
+
+ Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+ pcnet_cs.c 1.153 2003/11/09 18:53:09
+
+ The network driver code is based on Donald Becker's NE2000 code:
+
+ Written 1992,1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+ Donald Becker may be reached at becker@scyld.com
+
+ Based also on Keith Moore's changes to Don Becker's code, for IBM
+ CCAE support. Drivers merged back together, and shared-memory
+ Socket EA support added, by Ken Raeburn, September 1995.
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/log2.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define PCNET_CMD 0x00
+#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */
+
+#define PCNET_START_PG 0x40 /* First page of TX buffer */
+#define PCNET_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* Socket EA cards have a larger packet buffer */
+#define SOCKET_START_PG 0x01
+#define SOCKET_STOP_PG 0xff
+
+#define PCNET_RDC_TIMEOUT (2*HZ/100) /* Max wait in jiffies for Tx RDC */
+
+static const char *if_names[] = { "auto", "10baseT", "10base2"};
+static u32 pcnet_msg_enable;
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(if_port, 1); /* Transceiver type */
+INT_MODULE_PARM(use_big_buf, 1); /* use 64K packet buffer? */
+INT_MODULE_PARM(mem_speed, 0); /* shared mem speed, in ns */
+INT_MODULE_PARM(delay_output, 0); /* pause after xmit? */
+INT_MODULE_PARM(delay_time, 4); /* in usec */
+INT_MODULE_PARM(use_shmem, -1); /* use shared memory? */
+INT_MODULE_PARM(full_duplex, 0); /* full duplex? */
+
+/* Ugh! Let the user hardwire the hardware address for queer cards */
+static int hw_addr[6] = { 0, /* ... */ };
+module_param_array(hw_addr, int, NULL, 0);
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev);
+static int pcnet_config(struct pcmcia_device *link);
+static void pcnet_release(struct pcmcia_device *link);
+static int pcnet_open(struct net_device *dev);
+static int pcnet_close(struct net_device *dev);
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void pcnet_reset_8390(struct net_device *dev);
+static int set_config(struct net_device *dev, struct ifmap *map);
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+ int stop_pg, int cm_offset);
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+ int stop_pg);
+
+static void pcnet_detach(struct pcmcia_device *p_dev);
+
+/*====================================================================*/
+
+typedef struct hw_info_t {
+ u_int offset;
+ u_char a0, a1, a2;
+ u_int flags;
+} hw_info_t;
+
+#define DELAY_OUTPUT 0x01
+#define HAS_MISC_REG 0x02
+#define USE_BIG_BUF 0x04
+#define HAS_IBM_MISC 0x08
+#define IS_DL10019 0x10
+#define IS_DL10022 0x20
+#define HAS_MII 0x40
+#define USE_SHMEM 0x80 /* autodetected */
+
+#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */
+#define MII_PHYID_REV_MASK 0xfffffff0
+#define MII_PHYID_REG1 0x02
+#define MII_PHYID_REG2 0x03
+
+static hw_info_t hw_info[] = {
+ { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
+ { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
+ { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
+ { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
+ DELAY_OUTPUT | HAS_IBM_MISC },
+ { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
+ { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
+ { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
+ { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
+ { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
+ { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
+ { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
+ { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
+ { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
+ { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
+ { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
+ { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
+ { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
+ { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
+ { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
+ { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
+ { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
+ { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
+ DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+ { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
+ { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
+ { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
+ { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
+ { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
+};
+
+#define NR_INFO ARRAY_SIZE(hw_info)
+
+static hw_info_t default_info = { 0, 0, 0, 0, 0 };
+static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
+static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
+
+typedef struct pcnet_dev_t {
+ struct pcmcia_device *p_dev;
+ u_int flags;
+ void __iomem *base;
+ struct timer_list watchdog;
+ int stale, fast_poll;
+ u_char phy_id;
+ u_char eth_phy, pna_phy;
+ u_short link_status;
+ u_long mii_reset;
+} pcnet_dev_t;
+
+static inline pcnet_dev_t *PRIV(struct net_device *dev)
+{
+ char *p = netdev_priv(dev);
+ return (pcnet_dev_t *)(p + sizeof(struct ei_device));
+}
+
+static const struct net_device_ops pcnet_netdev_ops = {
+ .ndo_open = pcnet_open,
+ .ndo_stop = pcnet_close,
+ .ndo_set_config = set_config,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_do_ioctl = ei_ioctl,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int pcnet_probe(struct pcmcia_device *link)
+{
+ pcnet_dev_t *info;
+ struct net_device *dev;
+
+ dev_dbg(&link->dev, "pcnet_attach()\n");
+
+ /* Create new ethernet device */
+ dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
+ if (!dev) return -ENOMEM;
+ info = PRIV(dev);
+ info->p_dev = link;
+ link->priv = dev;
+
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ dev->netdev_ops = &pcnet_netdev_ops;
+
+ return pcnet_config(link);
+} /* pcnet_attach */
+
+static void pcnet_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "pcnet_detach\n");
+
+ unregister_netdev(dev);
+
+ pcnet_release(link);
+
+ free_netdev(dev);
+} /* pcnet_detach */
+
+/*======================================================================
+
+ This probes for a card's hardware address, for card types that
+ encode this information in their CIS.
+
+======================================================================*/
+
+static hw_info_t *get_hwinfo(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ u_char __iomem *base, *virt;
+ int i, j;
+
+ /* Allocate a small memory window */
+ link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ link->resource[2]->start = 0; link->resource[2]->end = 0;
+ i = pcmcia_request_window(link, link->resource[2], 0);
+ if (i != 0)
+ return NULL;
+
+ virt = ioremap(link->resource[2]->start,
+ resource_size(link->resource[2]));
+ for (i = 0; i < NR_INFO; i++) {
+ pcmcia_map_mem_page(link, link->resource[2],
+ hw_info[i].offset & ~(resource_size(link->resource[2])-1));
+ base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)];
+ if ((readb(base+0) == hw_info[i].a0) &&
+ (readb(base+2) == hw_info[i].a1) &&
+ (readb(base+4) == hw_info[i].a2)) {
+ for (j = 0; j < 6; j++)
+ dev->dev_addr[j] = readb(base + (j<<1));
+ break;
+ }
+ }
+
+ iounmap(virt);
+ j = pcmcia_release_window(link, link->resource[2]);
+ return (i < NR_INFO) ? hw_info+i : NULL;
+} /* get_hwinfo */
+
+/*======================================================================
+
+ This probes for a card's hardware address by reading the PROM.
+ It checks the address against a list of known types, then falls
+ back to a simple NE2000 clone signature check.
+
+======================================================================*/
+
+static hw_info_t *get_prom(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ u_char prom[32];
+ int i, j;
+
+ /* This is lifted straight from drivers/net/ethernet/8390/ne.c */
+ struct {
+ u_char value, offset;
+ } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+
+ pcnet_reset_8390(dev);
+ mdelay(10);
+
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ for (i = 0; i < 32; i++)
+ prom[i] = inb(ioaddr + PCNET_DATAPORT);
+ for (i = 0; i < NR_INFO; i++) {
+ if ((prom[0] == hw_info[i].a0) &&
+ (prom[2] == hw_info[i].a1) &&
+ (prom[4] == hw_info[i].a2))
+ break;
+ }
+ if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
+ for (j = 0; j < 6; j++)
+ dev->dev_addr[j] = prom[j<<1];
+ return (i < NR_INFO) ? hw_info+i : &default_info;
+ }
+ return NULL;
+} /* get_prom */
+
+/*======================================================================
+
+ For DL10019 based cards, like the Linksys EtherFast
+
+======================================================================*/
+
+static hw_info_t *get_dl10019(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+ u_char sum;
+
+ for (sum = 0, i = 0x14; i < 0x1c; i++)
+ sum += inb_p(dev->base_addr + i);
+ if (sum != 0xff)
+ return NULL;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
+ i = inb(dev->base_addr + 0x1f);
+ return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
+}
+
+/*======================================================================
+
+ For Asix AX88190 based cards
+
+======================================================================*/
+
+static hw_info_t *get_ax88190(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int i, j;
+
+ /* Not much of a test, but the alternatives are messy */
+ if (link->config_base != 0x03c0)
+ return NULL;
+
+ outb_p(0x01, ioaddr + EN0_DCFG); /* Set word-wide access. */
+ outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0400. */
+ outb_p(0x04, ioaddr + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
+
+ for (i = 0; i < 6; i += 2) {
+ j = inw(ioaddr + PCNET_DATAPORT);
+ dev->dev_addr[i] = j & 0xff;
+ dev->dev_addr[i+1] = j >> 8;
+ }
+ return NULL;
+}
+
+/*======================================================================
+
+ This should be totally unnecessary... but when we can't figure
+ out the hardware address any other way, we'll let the user hard
+ wire it when the module is initialized.
+
+======================================================================*/
+
+static hw_info_t *get_hwired(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+
+ for (i = 0; i < 6; i++)
+ if (hw_addr[i] != 0) break;
+ if (i == 6)
+ return NULL;
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = hw_addr[i];
+
+ return &default_info;
+} /* get_hwired */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+ int j, ret;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ if (link->resource[0]->end == 32) {
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ if (link->resource[1]->end > 0) {
+ /* for master/slave multifunction cards */
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ }
+ } else {
+ /* This should be two 16-port windows */
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ }
+ if (link->resource[0]->start == 0) {
+ for (j = 0; j < 0x400; j += 0x20) {
+ link->resource[0]->start = j ^ 0x300;
+ link->resource[1]->start = (j ^ 0x300) + 0x10;
+ link->io_lines = 16;
+ ret = pcmcia_request_io(link);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+ } else {
+ return pcmcia_request_io(link);
+ }
+}
+
+static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int *priv = priv_data;
+ int try = (*priv & 0x1);
+
+ *priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10;
+
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+ return -EINVAL;
+
+ if (try)
+ p_dev->io_lines = 16;
+ return try_io_port(p_dev);
+}
+
+static hw_info_t *pcnet_try_config(struct pcmcia_device *link,
+ int *has_shmem, int try)
+{
+ struct net_device *dev = link->priv;
+ hw_info_t *local_hw_info;
+ pcnet_dev_t *info = PRIV(dev);
+ int priv = try;
+ int ret;
+
+ ret = pcmcia_loop_config(link, pcnet_confcheck, &priv);
+ if (ret) {
+ dev_warn(&link->dev, "no useable port range found\n");
+ return NULL;
+ }
+ *has_shmem = (priv & 0x10);
+
+ if (!link->irq)
+ return NULL;
+
+ if (resource_size(link->resource[1]) == 8)
+ link->config_flags |= CONF_ENABLE_SPKR;
+
+ if ((link->manf_id == MANFID_IBM) &&
+ (link->card_id == PRODID_IBM_HOME_AND_AWAY))
+ link->config_index |= 0x10;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ return NULL;
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+
+ if (info->flags & HAS_MISC_REG) {
+ if ((if_port == 1) || (if_port == 2))
+ dev->if_port = if_port;
+ else
+ dev_notice(&link->dev, "invalid if_port requested\n");
+ } else
+ dev->if_port = 0;
+
+ if ((link->config_base == 0x03c0) &&
+ (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
+ dev_info(&link->dev,
+ "this is an AX88190 card - use axnet_cs instead.\n");
+ return NULL;
+ }
+
+ local_hw_info = get_hwinfo(link);
+ if (!local_hw_info)
+ local_hw_info = get_prom(link);
+ if (!local_hw_info)
+ local_hw_info = get_dl10019(link);
+ if (!local_hw_info)
+ local_hw_info = get_ax88190(link);
+ if (!local_hw_info)
+ local_hw_info = get_hwired(link);
+
+ return local_hw_info;
+}
+
+static int pcnet_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ pcnet_dev_t *info = PRIV(dev);
+ int start_pg, stop_pg, cm_offset;
+ int has_shmem = 0;
+ hw_info_t *local_hw_info;
+ struct ei_device *ei_local;
+
+ dev_dbg(&link->dev, "pcnet_config\n");
+
+ local_hw_info = pcnet_try_config(link, &has_shmem, 0);
+ if (!local_hw_info) {
+ /* check whether forcing io_lines to 16 helps... */
+ pcmcia_disable_device(link);
+ local_hw_info = pcnet_try_config(link, &has_shmem, 1);
+ if (local_hw_info == NULL) {
+ dev_notice(&link->dev, "unable to read hardware net"
+ " address for io base %#3lx\n", dev->base_addr);
+ goto failed;
+ }
+ }
+
+ info->flags = local_hw_info->flags;
+ /* Check for user overrides */
+ info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
+ if ((link->manf_id == MANFID_SOCKET) &&
+ ((link->card_id == PRODID_SOCKET_LPE) ||
+ (link->card_id == PRODID_SOCKET_LPE_CF) ||
+ (link->card_id == PRODID_SOCKET_EIO)))
+ info->flags &= ~USE_BIG_BUF;
+ if (!use_big_buf)
+ info->flags &= ~USE_BIG_BUF;
+
+ if (info->flags & USE_BIG_BUF) {
+ start_pg = SOCKET_START_PG;
+ stop_pg = SOCKET_STOP_PG;
+ cm_offset = 0x10000;
+ } else {
+ start_pg = PCNET_START_PG;
+ stop_pg = PCNET_STOP_PG;
+ cm_offset = 0;
+ }
+
+ /* has_shmem is ignored if use_shmem != -1 */
+ if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
+ (setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
+ setup_dma_config(link, start_pg, stop_pg);
+
+ ei_status.name = "NE2000";
+ ei_status.word16 = 1;
+ ei_status.reset_8390 = pcnet_reset_8390;
+
+ if (info->flags & (IS_DL10019|IS_DL10022))
+ mii_phy_probe(dev);
+
+ SET_NETDEV_DEV(dev, &link->dev);
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = pcnet_msg_enable;
+
+ if (register_netdev(dev) != 0) {
+ pr_notice("register_netdev() failed\n");
+ goto failed;
+ }
+
+ if (info->flags & (IS_DL10019|IS_DL10022)) {
+ u_char id = inb(dev->base_addr + 0x1a);
+ netdev_info(dev, "NE2000 (DL100%d rev %02x): ",
+ (info->flags & IS_DL10022) ? 22 : 19, id);
+ if (info->pna_phy)
+ pr_cont("PNA, ");
+ } else {
+ netdev_info(dev, "NE2000 Compatible: ");
+ }
+ pr_cont("io %#3lx, irq %d,", dev->base_addr, dev->irq);
+ if (info->flags & USE_SHMEM)
+ pr_cont(" mem %#5lx,", dev->mem_start);
+ if (info->flags & HAS_MISC_REG)
+ pr_cont(" %s xcvr,", if_names[dev->if_port]);
+ pr_cont(" hw_addr %pM\n", dev->dev_addr);
+ return 0;
+
+failed:
+ pcnet_release(link);
+ return -ENODEV;
+} /* pcnet_config */
+
+static void pcnet_release(struct pcmcia_device *link)
+{
+ pcnet_dev_t *info = PRIV(link->priv);
+
+ dev_dbg(&link->dev, "pcnet_release\n");
+
+ if (info->flags & USE_SHMEM)
+ iounmap(info->base);
+
+ pcmcia_disable_device(link);
+}
+
+static int pcnet_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int pcnet_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open) {
+ pcnet_reset_8390(dev);
+ NS8390_init(dev, 1);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+
+/*======================================================================
+
+ MII interface support for DL10019 and DL10022 based cards
+
+ On the DL10019, the MII IO direction bit is 0x10; on the DL10022
+ it is 0x20. Setting both bits seems to work on both card types.
+
+======================================================================*/
+
+#define DLINK_GPIO 0x1c
+#define DLINK_DIAG 0x1d
+#define DLINK_EEPROM 0x1e
+
+#define MDIO_SHIFT_CLK 0x80
+#define MDIO_DATA_OUT 0x40
+#define MDIO_DIR_WRITE 0x30
+#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ 0x10
+#define MDIO_MASK 0x0f
+
+static void mdio_sync(unsigned int addr)
+{
+ int bits, mask = inb(addr) & MDIO_MASK;
+ for (bits = 0; bits < 32; bits++) {
+ outb(mask | MDIO_DATA_WRITE1, addr);
+ outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+ u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+ int i, retval = 0, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 13; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb(mask, addr);
+ retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb(mask, addr);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*======================================================================
+
+ EEPROM access routines for DL10019 and DL10022 based cards
+
+======================================================================*/
+
+#define EE_EEP 0x40
+#define EE_ASIC 0x10
+#define EE_CS 0x08
+#define EE_CK 0x04
+#define EE_DO 0x02
+#define EE_DI 0x01
+#define EE_ADOT 0x01 /* DataOut for ASIC */
+#define EE_READ_CMD 0x06
+
+#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */
+
+static int read_eeprom(unsigned int ioaddr, int location)
+{
+ int i, retval = 0;
+ unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+ int read_cmd = location | (EE_READ_CMD << 8);
+
+ outb(0, ee_addr);
+ outb(EE_EEP|EE_CS, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+ outb_p(EE_EEP|EE_CS|dataval, ee_addr);
+ outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
+ }
+ outb(EE_EEP|EE_CS, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
+ retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
+ outb_p(EE_EEP|EE_CS, ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ outb(0, ee_addr);
+ return retval;
+}
+
+/*
+ The internal ASIC registers can be changed by EEPROM READ access
+ with EE_ASIC bit set.
+ In ASIC mode, EE_ADOT is used to output the data to the ASIC.
+*/
+
+static void write_asic(unsigned int ioaddr, int location, short asic_data)
+{
+ int i;
+ unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+ short dataval;
+ int read_cmd = location | (EE_READ_CMD << 8);
+
+ asic_data |= read_eeprom(ioaddr, location);
+
+ outb(0, ee_addr);
+ outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
+
+ read_cmd = read_cmd >> 1;
+
+ /* Shift the read command bits out. */
+ for (i = 9; i >= 0; i--) {
+ dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+ }
+ // sync
+ outb(EE_ASIC|EE_CS, ee_addr);
+ outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
+ outb(EE_ASIC|EE_CS, ee_addr);
+
+ for (i = 15; i >= 0; i--) {
+ dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
+ outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+ outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
+ outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+ }
+
+ /* Terminate the ASIC access. */
+ outb(EE_ASIC|EE_DI, ee_addr);
+ outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
+ outb(EE_ASIC|EE_DI, ee_addr);
+
+ outb(0, ee_addr);
+}
+
+/*====================================================================*/
+
+static void set_misc_reg(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ pcnet_dev_t *info = PRIV(dev);
+ u_char tmp;
+
+ if (info->flags & HAS_MISC_REG) {
+ tmp = inb_p(nic_base + PCNET_MISC) & ~3;
+ if (dev->if_port == 2)
+ tmp |= 1;
+ if (info->flags & USE_BIG_BUF)
+ tmp |= 2;
+ if (info->flags & HAS_IBM_MISC)
+ tmp |= 8;
+ outb_p(tmp, nic_base + PCNET_MISC);
+ }
+ if (info->flags & IS_DL10022) {
+ if (info->flags & HAS_MII) {
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+ /* Restart MII autonegotiation */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+ info->mii_reset = jiffies;
+ } else {
+ outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+ }
+ } else if (info->flags & IS_DL10019) {
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+ /* Restart MII autonegotiation */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+ }
+}
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+ int i;
+ u_int tmp, phyid;
+
+ for (i = 31; i >= 0; i--) {
+ tmp = mdio_read(mii_addr, i, 1);
+ if ((tmp == 0) || (tmp == 0xffff))
+ continue;
+ tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+ phyid = tmp << 16;
+ phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+ phyid &= MII_PHYID_REV_MASK;
+ netdev_dbg(dev, "MII at %d is 0x%08x\n", i, phyid);
+ if (phyid == AM79C9XX_HOME_PHY) {
+ info->pna_phy = i;
+ } else if (phyid != AM79C9XX_ETH_PHY) {
+ info->eth_phy = i;
+ }
+ }
+}
+
+static int pcnet_open(struct net_device *dev)
+{
+ int ret;
+ pcnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
+
+ dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ set_misc_reg(dev);
+
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev->name, dev);
+ if (ret)
+ return ret;
+
+ link->open++;
+
+ info->phy_id = info->eth_phy;
+ info->link_status = 0x00;
+ init_timer(&info->watchdog);
+ info->watchdog.function = ei_watchdog;
+ info->watchdog.data = (u_long)dev;
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+
+ return ei_open(dev);
+} /* pcnet_open */
+
+/*====================================================================*/
+
+static int pcnet_close(struct net_device *dev)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+
+ dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
+
+ ei_close(dev);
+ free_irq(dev->irq, dev);
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&info->watchdog);
+
+ return 0;
+} /* pcnet_close */
+
+/*======================================================================
+
+ Hard reset the card. This used to pause for the same period that
+ a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void pcnet_reset_8390(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ int i;
+
+ ei_status.txing = ei_status.dmaing = 0;
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+ outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
+
+ for (i = 0; i < 100; i++) {
+ if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+ break;
+ udelay(100);
+ }
+ outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+
+ if (i == 100)
+ netdev_err(dev, "pcnet_reset_8390() did not complete.\n");
+
+ set_misc_reg(dev);
+
+} /* pcnet_reset_8390 */
+
+/*====================================================================*/
+
+static int set_config(struct net_device *dev, struct ifmap *map)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (!(info->flags & HAS_MISC_REG))
+ return -EOPNOTSUPP;
+ else if ((map->port < 1) || (map->port > 2))
+ return -EINVAL;
+ dev->if_port = map->port;
+ netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+ NS8390_init(dev, 1);
+ }
+ return 0;
+}
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ pcnet_dev_t *info;
+ irqreturn_t ret = ei_interrupt(irq, dev_id);
+
+ if (ret == IRQ_HANDLED) {
+ info = PRIV(dev);
+ info->stale = 0;
+ }
+ return ret;
+}
+
+static void ei_watchdog(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)arg;
+ pcnet_dev_t *info = PRIV(dev);
+ unsigned int nic_base = dev->base_addr;
+ unsigned int mii_addr = nic_base + DLINK_GPIO;
+ u_short link;
+
+ if (!netif_device_present(dev)) goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+ if (!info->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+ ei_irq_wrapper(dev->irq, dev);
+ info->fast_poll = HZ;
+ }
+ if (info->fast_poll) {
+ info->fast_poll--;
+ info->watchdog.expires = jiffies + 1;
+ add_timer(&info->watchdog);
+ return;
+ }
+
+ if (!(info->flags & HAS_MII))
+ goto reschedule;
+
+ mdio_read(mii_addr, info->phy_id, 1);
+ link = mdio_read(mii_addr, info->phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ if (info->eth_phy) {
+ info->phy_id = info->eth_phy = 0;
+ } else {
+ netdev_info(dev, "MII is missing!\n");
+ info->flags &= ~HAS_MII;
+ }
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != info->link_status) {
+ u_short p = mdio_read(mii_addr, info->phy_id, 5);
+ netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+ if (link && (info->flags & IS_DL10022)) {
+ /* Disable collision detection on full duplex links */
+ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
+ } else if (link && (info->flags & IS_DL10019)) {
+ /* Disable collision detection on full duplex links */
+ write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
+ }
+ if (link) {
+ if (info->phy_id == info->eth_phy) {
+ if (p)
+ netdev_info(dev, "autonegotiation complete: "
+ "%sbaseT-%cD selected\n",
+ ((p & 0x0180) ? "100" : "10"),
+ ((p & 0x0140) ? 'F' : 'H'));
+ else
+ netdev_info(dev, "link partner did not autonegotiate\n");
+ }
+ NS8390_init(dev, 1);
+ }
+ info->link_status = link;
+ }
+ if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
+ link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+ if (((info->phy_id == info->pna_phy) && link) ||
+ ((info->phy_id != info->pna_phy) && !link)) {
+ /* isolate this MII and try flipping to the other one */
+ mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+ info->phy_id ^= info->pna_phy ^ info->eth_phy;
+ netdev_info(dev, "switched to %s transceiver\n",
+ (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+ mdio_write(mii_addr, info->phy_id, 0,
+ (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+ info->link_status = 0;
+ info->mii_reset = jiffies;
+ }
+ }
+
+reschedule:
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+
+ if (!(info->flags & (IS_DL10019|IS_DL10022)))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = info->phy_id;
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+ return 0;
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void dma_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in dma_block_input."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+ insw(nic_base + PCNET_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr)>>1);
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static void dma_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned int nic_base = dev->base_addr;
+ int xfer_count = count;
+ char *buf = skb->data;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if ((netif_msg_rx_status(ei_local)) && (count != 4))
+ netdev_dbg(dev, "[bi=%d]\n", count+4);
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in dma_block_input."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+ insw(nic_base + PCNET_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++;
+
+ /* This was for the ALPHA version only, but enough people have been
+ encountering problems that it is still here. */
+#ifdef PCMCIA_DEBUG
+ /* DMA termination address check... */
+ if (netif_msg_rx_status(ei_local)) {
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ netdev_notice(dev, "RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+} /* dma_block_input */
+
+/*====================================================================*/
+
+static void dma_block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ unsigned int nic_base = dev->base_addr;
+ pcnet_dev_t *info = PRIV(dev);
+#ifdef PCMCIA_DEBUG
+ int retries = 0;
+ struct ei_device *ei_local = netdev_priv(dev);
+#endif
+ u_long dma_start;
+
+#ifdef PCMCIA_DEBUG
+ netif_dbg(ei_local, tx_queued, dev, "[bo=%d]\n", count);
+#endif
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (count & 0x01)
+ count++;
+ if (ei_status.dmaing) {
+ netdev_err(dev, "DMAing conflict in dma_block_output."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);
+
+#ifdef PCMCIA_DEBUG
+ retry:
+#endif
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD);
+ outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
+
+ dma_start = jiffies;
+
+#ifdef PCMCIA_DEBUG
+ /* This was for the ALPHA version only, but enough people have been
+ encountering problems that it is still here. */
+ /* DMA termination address check... */
+ if (netif_msg_tx_queued(ei_local)) {
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ netdev_notice(dev, "Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ pcnet_reset_8390(dev);
+ NS8390_init(dev, 1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ if (info->flags & DELAY_OUTPUT)
+ udelay((long)delay_time);
+ ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+ int stop_pg)
+{
+ struct net_device *dev = link->priv;
+
+ ei_status.tx_start_page = start_pg;
+ ei_status.rx_start_page = start_pg + TX_PAGES;
+ ei_status.stop_page = stop_pg;
+
+ /* set up block i/o functions */
+ ei_status.get_8390_hdr = dma_get_8390_hdr;
+ ei_status.block_input = dma_block_input;
+ ei_status.block_output = dma_block_output;
+
+ return 0;
+}
+
+/*====================================================================*/
+
+static void copyin(void *dest, void __iomem *src, int c)
+{
+ u_short *d = dest;
+ u_short __iomem *s = src;
+ int odd;
+
+ if (c <= 0)
+ return;
+ odd = (c & 1); c >>= 1;
+
+ if (c) {
+ do { *d++ = __raw_readw(s++); } while (--c);
+ }
+ /* get last byte by fetching a word and masking */
+ if (odd)
+ *((u_char *)d) = readw(s) & 0xff;
+}
+
+static void copyout(void __iomem *dest, const void *src, int c)
+{
+ u_short __iomem *d = dest;
+ const u_short *s = src;
+ int odd;
+
+ if (c <= 0)
+ return;
+ odd = (c & 1); c >>= 1;
+
+ if (c) {
+ do { __raw_writew(*s++, d++); } while (--c);
+ }
+ /* copy last byte doing a read-modify-write */
+ if (odd)
+ writew((readw(d) & 0xff00) | *(u_char *)s, d);
+}
+
+/*====================================================================*/
+
+static void shmem_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
+ + (ring_page << 8)
+ - (ei_status.rx_start_page << 8);
+
+ copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ void __iomem *base = ei_status.mem;
+ unsigned long offset = (TX_PAGES<<8) + ring_offset
+ - (ei_status.rx_start_page << 8);
+ char *buf = skb->data;
+
+ if (offset + count > ei_status.priv) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.priv - offset;
+ copyin(buf, base + offset, semi_count);
+ buf += semi_count;
+ offset = TX_PAGES<<8;
+ count -= semi_count;
+ }
+ copyin(buf, base + offset, count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ void __iomem *shmem = ei_status.mem + (start_page << 8);
+ shmem -= ei_status.tx_start_page << 8;
+ copyout(shmem, buf, count);
+}
+
+/*====================================================================*/
+
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+ int stop_pg, int cm_offset)
+{
+ struct net_device *dev = link->priv;
+ pcnet_dev_t *info = PRIV(dev);
+ int i, window_size, offset, ret;
+
+ window_size = (stop_pg - start_pg) << 8;
+ if (window_size > 32 * 1024)
+ window_size = 32 * 1024;
+
+ /* Make sure it's a power of two. */
+ window_size = roundup_pow_of_two(window_size);
+
+ /* Allocate a memory window */
+ link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ link->resource[3]->flags |= WIN_USE_WAIT;
+ link->resource[3]->start = 0; link->resource[3]->end = window_size;
+ ret = pcmcia_request_window(link, link->resource[3], mem_speed);
+ if (ret)
+ goto failed;
+
+ offset = (start_pg << 8) + cm_offset;
+ offset -= offset % window_size;
+ ret = pcmcia_map_mem_page(link, link->resource[3], offset);
+ if (ret)
+ goto failed;
+
+ /* Try scribbling on the buffer */
+ info->base = ioremap(link->resource[3]->start,
+ resource_size(link->resource[3]));
+ for (i = 0; i < (TX_PAGES<<8); i += 2)
+ __raw_writew((i>>1), info->base+offset+i);
+ udelay(100);
+ for (i = 0; i < (TX_PAGES<<8); i += 2)
+ if (__raw_readw(info->base+offset+i) != (i>>1)) break;
+ pcnet_reset_8390(dev);
+ if (i != (TX_PAGES<<8)) {
+ iounmap(info->base);
+ pcmcia_release_window(link, link->resource[3]);
+ info->base = NULL;
+ goto failed;
+ }
+
+ ei_status.mem = info->base + offset;
+ ei_status.priv = resource_size(link->resource[3]);
+ dev->mem_start = (u_long)ei_status.mem;
+ dev->mem_end = dev->mem_start + resource_size(link->resource[3]);
+
+ ei_status.tx_start_page = start_pg;
+ ei_status.rx_start_page = start_pg + TX_PAGES;
+ ei_status.stop_page = start_pg + (
+ (resource_size(link->resource[3]) - offset) >> 8);
+
+ /* set up block i/o functions */
+ ei_status.get_8390_hdr = shmem_get_8390_hdr;
+ ei_status.block_input = shmem_block_input;
+ ei_status.block_output = shmem_block_output;
+
+ info->flags |= USE_SHMEM;
+ return 0;
+
+failed:
+ return 1;
+}
+
+/*====================================================================*/
+
+static const struct pcmcia_device_id pcnet_ids[] = {
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
+ PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
+ PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
+ PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab),
+ PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110),
+ PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041),
+ PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121),
+ PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID1234("Socket", "CF 10/100 Ethernet Card", "Revision B", "05/11/06", 0xb38bcc2e, 0x4de88352, 0xeaca6c8d, 0x7e57c22e),
+ PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("CNet ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID123("EFA ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0),
+ PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82),
+ PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8),
+ PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab),
+ PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11),
+ PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96),
+ PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1),
+ PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd),
+ PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190),
+ PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504),
+ PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a),
+ PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79),
+ PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7),
+ PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a),
+ PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9),
+ PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722),
+ PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2),
+ PCMCIA_DEVICE_PROD_ID12("corega", "Ether CF-TD", 0x0a21501a, 0x6589340a),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether CF-TD LAN Card", 0x5261440f, 0x8797663b),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d),
+ PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-TD", 0x5261440f, 0x47d5ca83),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9),
+ PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2),
+ PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "(CG-LAPCCTXD)", 0x5261440f, 0x73ec0d88),
+ PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04),
+ PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d),
+ PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814),
+ PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0),
+ PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf),
+ PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995),
+ PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233),
+ PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660+", 0x1a424a1c, 0x50dcd0ec),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9),
+ PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84),
+ PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9),
+ PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1),
+ PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1),
+ PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb),
+ PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11),
+ PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6),
+ PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517),
+ PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb),
+ PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327),
+ PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947),
+ PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6),
+ PCMCIA_DEVICE_PROD_ID12("IC-CARD+", "IC-CARD+", 0x93693494, 0x93693494),
+ PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b),
+ PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0),
+ PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956),
+ PCMCIA_DEVICE_PROD_ID12("KENTRONICS", "KEP-230", 0xaf8144c9, 0x868f6616),
+ PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64),
+ PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5),
+ PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3),
+ PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab),
+ PCMCIA_DEVICE_PROD_ID12("LEMEL", "LM-N89TX PRO", 0xbbefb52f, 0xd2897a97),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78),
+ PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline + 10/100 Network PC Card (PCM100H1)", 0x733cc81, 0x7a3e5c3a),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TE", 0x88fcdeda, 0x0e714bee),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN10TE", 0x88fcdeda, 0xc1e2521c),
+ PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0),
+ PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578),
+ PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307),
+ PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4),
+ PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c),
+ PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6),
+ PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472),
+ PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7),
+ PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9),
+ PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8),
+ PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e),
+ PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f),
+ PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", " Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1),
+ PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80),
+ PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50),
+ PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110),
+ PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("RIOS Systems Co.", "PC CARD3 ETHERNET", 0x7dd33481, 0x10b41826),
+ PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df),
+ PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0),
+ PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd),
+ PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388),
+ PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265),
+ PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e),
+ PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8),
+ PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa),
+ PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f),
+ PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a),
+ PCMCIA_DEVICE_PROD_ID13("Hypertec", "EP401", 0x8787bec7, 0xf6e4a31e),
+ PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e),
+ PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89),
+ PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360),
+ PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de),
+ PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f),
+ PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a),
+ PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078),
+ /* too generic! */
+ /* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
+ PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
+ PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
+ PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("Allied Telesis,K.K", "Ethernet LAN Card", 0x2ad62f3c, 0x9fd2f0a2, "cis/LA-PCM.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "cis/PE520.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b),
+ PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0",
+ 0xb4be14e3, 0x43ac239b, 0x0877b627),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
+MODULE_FIRMWARE("cis/PCMLM28.cis");
+MODULE_FIRMWARE("cis/DP83903.cis");
+MODULE_FIRMWARE("cis/LA-PCM.cis");
+MODULE_FIRMWARE("cis/PE520.cis");
+MODULE_FIRMWARE("cis/NE2K.cis");
+MODULE_FIRMWARE("cis/PE-200.cis");
+MODULE_FIRMWARE("cis/tamarack.cis");
+
+static struct pcmcia_driver pcnet_driver = {
+ .name = "pcnet_cs",
+ .probe = pcnet_probe,
+ .remove = pcnet_detach,
+ .owner = THIS_MODULE,
+ .id_table = pcnet_ids,
+ .suspend = pcnet_suspend,
+ .resume = pcnet_resume,
+};
+module_pcmcia_driver(pcnet_driver);
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
new file mode 100644
index 00000000000..139385dcdaa
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -0,0 +1,631 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+ This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
+
+ Written 1993-1998 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This driver uses the cards in the 8390-compatible mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c. The code in this file is responsible for
+
+ ultra_probe() Detecting and initializing the card.
+ ultra_probe1()
+ ultra_probe_isapnp()
+
+ ultra_open() The card-specific details of starting, stopping
+ ultra_reset_8390() and resetting the 8390 NIC core.
+ ultra_close()
+
+ ultra_block_input() Routines for reading and writing blocks of
+ ultra_block_output() packet buffer memory.
+ ultra_pio_input()
+ ultra_pio_output()
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver now supports the programmed-I/O (PIO) data transfer mode of
+ the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
+ That support (if available) is in smc-ez.c.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users.
+ Donald Becker : 4/17/96 PIO support, minor potential problems avoided.
+ Donald Becker : 6/6/96 correctly set auto-wrap bit.
+ Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
+
+ Note about the ISA PnP support:
+
+ This driver can not autoprobe for more than one SMC EtherEZ PnP card.
+ You have to configure the second card manually through the /proc/isapnp
+ interface and then load the module with an explicit io=0x___ option.
+*/
+
+static const char version[] =
+ "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-ultra"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ultra_portlist[] __initdata =
+{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
+
+static int ultra_probe1(struct net_device *dev, int ioaddr);
+
+#ifdef __ISAPNP__
+static int ultra_probe_isapnp(struct net_device *dev);
+#endif
+
+static int ultra_open(struct net_device *dev);
+static void ultra_reset_8390(struct net_device *dev);
+static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_pio_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_pio_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+static int ultra_close_card(struct net_device *dev);
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id ultra_device_ids[] __initdata = {
+ { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+ ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+ (long) "SMC EtherEZ (8416)" },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
+#endif
+
+static u32 ultra_msg_enable;
+
+#define START_PG 0x00 /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
+#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */
+#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT 0x08 /* Early receive warning count. */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ultra_poll(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ ei_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+/* Probe for the Ultra. This looks like a 8013 with the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following.
+*/
+
+static int __init do_ultra_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ultra_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+#ifdef __ISAPNP__
+ /* Look for any installed ISAPnP cards */
+ if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
+ return 0;
+#endif
+
+ for (i = 0; ultra_portlist[i]; i++) {
+ dev->irq = irq;
+ if (ultra_probe1(dev, ultra_portlist[i]) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ultra_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_ultra_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ultra_netdev_ops = {
+ .ndo_open = ultra_open,
+ .ndo_stop = ultra_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ultra_poll,
+#endif
+};
+
+static int __init ultra_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, retval;
+ int checksum = 0;
+ const char *model_name;
+ unsigned char eeprom_irq = 0;
+ static unsigned version_printed;
+ /* Values from various config regs. */
+ unsigned char num_pages, irqreg, addr, piomode;
+ unsigned char idreg = inb(ioaddr + 7);
+ unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ /* Check the ID nibble. */
+ if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
+ && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Select the station address register set. */
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xFF) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ netdev_info(dev, version);
+
+ model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ netdev_info(dev, "%s at %#3x, %pM", model_name,
+ ioaddr, dev->dev_addr);
+
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+ piomode = inb(ioaddr + 0x8);
+ addr = inb(ioaddr + 0xb);
+ irqreg = inb(ioaddr + 0xd);
+
+ /* Switch back to the station address register set so that the MS-DOS driver
+ can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
+
+ if (dev->irq < 2) {
+ unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+ int irq;
+
+ /* The IRQ bits are split. */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+
+ if (irq == 0) {
+ pr_cont(", failed to detect IRQ line.\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ dev->irq = irq;
+ eeprom_irq = 1;
+ }
+
+ /* The 8390 isn't at the base address, so fake the offset */
+ dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+ {
+ static const int addr_tbl[4] = {
+ 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
+ };
+ static const short num_pages_tbl[4] = {
+ 0x20, 0x40, 0x80, 0xff
+ };
+
+ dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ }
+
+ ei_status.name = model_name;
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = num_pages;
+
+ ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
+ if (!ei_status.mem) {
+ pr_cont(", failed to ioremap.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+ if (piomode) {
+ pr_cont(", %s IRQ %d programmed-I/O mode.\n",
+ eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
+ ei_status.block_input = &ultra_pio_input;
+ ei_status.block_output = &ultra_pio_output;
+ ei_status.get_8390_hdr = &ultra_pio_get_hdr;
+ } else {
+ pr_cont(", %s IRQ %d memory %#lx-%#lx.\n",
+ eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start,
+ dev->mem_end-1);
+ ei_status.block_input = &ultra_block_input;
+ ei_status.block_output = &ultra_block_output;
+ ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+ }
+ ei_status.reset_8390 = &ultra_reset_8390;
+
+ dev->netdev_ops = &ultra_netdev_ops;
+ NS8390_init(dev, 0);
+ ei_local->msg_enable = ultra_msg_enable;
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out;
+ return 0;
+out:
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+ return retval;
+}
+
+#ifdef __ISAPNP__
+static int __init ultra_probe_isapnp(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
+ struct pnp_dev *idev = NULL;
+
+ while ((idev = pnp_find_dev(NULL,
+ ultra_device_ids[i].vendor,
+ ultra_device_ids[i].function,
+ idev))) {
+ /* Avoid already found cards from previous calls */
+ if (pnp_device_attach(idev) < 0)
+ continue;
+ if (pnp_activate_dev(idev) < 0) {
+ __again:
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* if no io and irq, search for next */
+ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
+ goto __again;
+ /* found it */
+ dev->base_addr = pnp_port_start(idev, 0);
+ dev->irq = pnp_irq(idev, 0);
+ netdev_info(dev,
+ "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+ (char *) ultra_device_ids[i].driver_data,
+ dev->base_addr, dev->irq);
+ if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
+ netdev_err(dev,
+ "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n",
+ dev->base_addr);
+ pnp_device_detach(idev);
+ return -ENXIO;
+ }
+ ei_status.priv = (unsigned long)idev;
+ break;
+ }
+ if (!idev)
+ continue;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+#endif
+
+static int
+ultra_open(struct net_device *dev)
+{
+ int retval;
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
+ 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
+
+ retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
+ if (retval)
+ return retval;
+
+ outb(0x00, ioaddr); /* Disable shared memory for safety. */
+ outb(0x80, ioaddr + 5);
+ /* Set the IRQ line. */
+ outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
+ outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
+ outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
+
+ if (ei_status.block_input == &ultra_pio_input) {
+ outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */
+ outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */
+ } else
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+ /* Set the early receive warning level in window 0 high enough not
+ to receive ERW interrupts. */
+ outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+ outb(0xff, dev->base_addr + EN0_ERWCNT);
+ ei_open(dev);
+ return 0;
+}
+
+static void
+ultra_reset_8390(struct net_device *dev)
+{
+ int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ outb(ULTRA_RESET, cmd_port);
+ netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies);
+ ei_status.txing = 0;
+
+ outb(0x00, cmd_port); /* Disable shared memory for safety. */
+ outb(0x80, cmd_port + 5);
+ if (ei_status.block_input == &ultra_pio_input)
+ outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */
+ else
+ outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */
+
+ netif_dbg(ei_local, hw, dev, "reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
+
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
+#ifdef __BIG_ENDIAN
+ /* Officially this is what we are doing, but the readl() is faster */
+ /* unfortunately it isn't endian aware of the struct */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void
+ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ if (ring_offset + count > ei_status.stop_page*256) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.stop_page*256 - ring_offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ } else {
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static void
+ultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ memcpy_toio(shmem, buf, count);
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+/* The identical operations for programmed I/O cards.
+ The PIO model is trivial to use: the 16 bit start address is written
+ byte-sequentially to IOPA, with no intervening I/O operations, and the
+ data is read or written to the IOPD data port.
+ The only potential complication is that the address register is shared
+ and must be always be rewritten between each read/write direction change.
+ This is no problem for us, as the 8390 code ensures that we are single
+ threaded. */
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(ring_page, ioaddr + IOPA);
+ insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+static void ultra_pio_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ char *buf = skb->data;
+
+ /* For now set the address again, although it should already be correct. */
+ outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(ring_offset >> 8, ioaddr + IOPA);
+ /* We know skbuffs are padded to at least word alignment. */
+ insw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static void ultra_pio_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(start_page, ioaddr + IOPA);
+ /* An extra odd byte is OK here as well. */
+ outsw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static int
+ultra_close_card(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
+
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq, dev);
+
+ NS8390_init(dev, 0);
+
+ /* We should someday disable shared memory and change to 8-bit mode
+ "just in case"... */
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
+static struct net_device *dev_ultra[MAX_ULTRA_CARDS];
+static int io[MAX_ULTRA_CARDS];
+static int irq[MAX_ULTRA_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_named(msg_enable, ultra_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (do_ultra_probe(dev) == 0) {
+ dev_ultra[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* NB: ultra_close_card() does free_irq */
+#ifdef __ISAPNP__
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ if (idev)
+ pnp_device_detach(idev);
+#endif
+ release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct net_device *dev = dev_ultra[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c
new file mode 100644
index 00000000000..aca957d4e12
--- /dev/null
+++ b/drivers/net/ethernet/8390/stnic.c
@@ -0,0 +1,303 @@
+/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999 kaz Kojima
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <mach-se/mach/se.h>
+#include <asm/machvec.h>
+#ifdef CONFIG_SH_STANDARD_BIOS
+#include <asm/sh_bios.h>
+#endif
+
+#include "8390.h"
+
+#define DRV_NAME "stnic"
+
+#define byte unsigned char
+#define half unsigned short
+#define word unsigned int
+#define vbyte volatile unsigned char
+#define vhalf volatile unsigned short
+#define vword volatile unsigned int
+
+#define STNIC_RUN 0x01 /* 1 == Run, 0 == reset. */
+
+#define START_PG 0 /* First page of TX buffer */
+#define STOP_PG 128 /* Last page +1 of RX ring */
+
+/* Alias */
+#define STNIC_CR E8390_CMD
+#define PG0_RSAR0 EN0_RSARLO
+#define PG0_RSAR1 EN0_RSARHI
+#define PG0_RBCR0 EN0_RCNTLO
+#define PG0_RBCR1 EN0_RCNTHI
+
+#define CR_RRD E8390_RREAD
+#define CR_RWR E8390_RWRITE
+#define CR_PG0 E8390_PAGE0
+#define CR_STA E8390_START
+#define CR_RDMA E8390_NODMA
+
+/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS. */
+static byte stnic_eadr[6] =
+{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07};
+
+static struct net_device *stnic_dev;
+
+static void stnic_reset (struct net_device *dev);
+static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void stnic_block_input (struct net_device *dev, int count,
+ struct sk_buff *skb , int ring_offset);
+static void stnic_block_output (struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+
+static void stnic_init (struct net_device *dev);
+
+static u32 stnic_msg_enable;
+
+module_param_named(msg_enable, stnic_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+
+/* SH7750 specific read/write io. */
+static inline void
+STNIC_DELAY (void)
+{
+ vword trash;
+ trash = *(vword *) 0xa0000000;
+ trash = *(vword *) 0xa0000000;
+ trash = *(vword *) 0xa0000000;
+}
+
+static inline byte
+STNIC_READ (int reg)
+{
+ byte val;
+
+ val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff;
+ STNIC_DELAY ();
+ return val;
+}
+
+static inline void
+STNIC_WRITE (int reg, byte val)
+{
+ *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8);
+ STNIC_DELAY ();
+}
+
+static int __init stnic_probe(void)
+{
+ struct net_device *dev;
+ int i, err;
+ struct ei_device *ei_local;
+
+ /* If we are not running on a SolutionEngine, give up now */
+ if (! MACH_SE)
+ return -ENODEV;
+
+ /* New style probing API */
+ dev = alloc_ei_netdev();
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+ sh_bios_get_node_addr (stnic_eadr);
+#endif
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = stnic_eadr[i];
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = 0x1000;
+ dev->irq = IRQ_STNIC;
+ dev->netdev_ops = &ei_netdev_ops;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (err) {
+ netdev_emerg(dev, " unable to get IRQ %d.\n", dev->irq);
+ free_netdev(dev);
+ return err;
+ }
+
+ ei_status.name = dev->name;
+ ei_status.word16 = 1;
+#ifdef __LITTLE_ENDIAN__
+ ei_status.bigendian = 0;
+#else
+ ei_status.bigendian = 1;
+#endif
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = STOP_PG;
+
+ ei_status.reset_8390 = &stnic_reset;
+ ei_status.get_8390_hdr = &stnic_get_hdr;
+ ei_status.block_input = &stnic_block_input;
+ ei_status.block_output = &stnic_block_output;
+
+ stnic_init (dev);
+ ei_local = netdev_priv(dev);
+ ei_local->msg_enable = stnic_msg_enable;
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(dev->irq, dev);
+ free_netdev(dev);
+ return err;
+ }
+ stnic_dev = dev;
+
+ netdev_info(dev, "NS ST-NIC 83902A\n");
+
+ return 0;
+}
+
+static void
+stnic_reset (struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ *(vhalf *) PA_83902_RST = 0;
+ udelay (5);
+ netif_warn(ei_local, hw, dev, "8390 reset done (%ld).\n", jiffies);
+ *(vhalf *) PA_83902_RST = ~0;
+ udelay (5);
+}
+
+static void
+stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ half buf[2];
+
+ STNIC_WRITE (PG0_RSAR0, 0);
+ STNIC_WRITE (PG0_RSAR1, ring_page);
+ STNIC_WRITE (PG0_RBCR0, 4);
+ STNIC_WRITE (PG0_RBCR1, 0);
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+ buf[0] = *(vhalf *) PA_83902_IF;
+ STNIC_DELAY ();
+ buf[1] = *(vhalf *) PA_83902_IF;
+ STNIC_DELAY ();
+ hdr->next = buf[0] >> 8;
+ hdr->status = buf[0] & 0xff;
+#ifdef __LITTLE_ENDIAN__
+ hdr->count = buf[1];
+#else
+ hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8);
+#endif
+
+ netif_dbg(ei_local, probe, dev, "ring %x status %02x next %02x count %04x.\n",
+ ring_page, hdr->status, hdr->next, hdr->count);
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you are
+ porting to a new ethercard look at the packet driver source for hints.
+ The HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static void
+stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb,
+ int offset)
+{
+ char *buf = skb->data;
+ half val;
+
+ STNIC_WRITE (PG0_RSAR0, offset & 0xff);
+ STNIC_WRITE (PG0_RSAR1, offset >> 8);
+ STNIC_WRITE (PG0_RBCR0, length & 0xff);
+ STNIC_WRITE (PG0_RBCR1, length >> 8);
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+ if (length & 1)
+ length++;
+
+ while (length > 0)
+ {
+ val = *(vhalf *) PA_83902_IF;
+#ifdef __LITTLE_ENDIAN__
+ *buf++ = val & 0xff;
+ *buf++ = val >> 8;
+#else
+ *buf++ = val >> 8;
+ *buf++ = val & 0xff;
+#endif
+ STNIC_DELAY ();
+ length -= sizeof (half);
+ }
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+static void
+stnic_block_output (struct net_device *dev, int length,
+ const unsigned char *buf, int output_page)
+{
+ STNIC_WRITE (PG0_RBCR0, 1); /* Write non-zero value */
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+ STNIC_DELAY ();
+
+ STNIC_WRITE (PG0_RBCR0, length & 0xff);
+ STNIC_WRITE (PG0_RBCR1, length >> 8);
+ STNIC_WRITE (PG0_RSAR0, 0);
+ STNIC_WRITE (PG0_RSAR1, output_page);
+ STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);
+
+ if (length & 1)
+ length++;
+
+ while (length > 0)
+ {
+#ifdef __LITTLE_ENDIAN__
+ *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0];
+#else
+ *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1];
+#endif
+ STNIC_DELAY ();
+ buf += sizeof (half);
+ length -= sizeof (half);
+ }
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* This function resets the STNIC if something screws up. */
+static void
+stnic_init (struct net_device *dev)
+{
+ stnic_reset (dev);
+ NS8390_init (dev, 0);
+}
+
+static void __exit stnic_cleanup(void)
+{
+ unregister_netdev(stnic_dev);
+ free_irq(stnic_dev->irq, stnic_dev);
+ free_netdev(stnic_dev);
+}
+
+module_init(stnic_probe);
+module_exit(stnic_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
new file mode 100644
index 00000000000..dd7d816bde5
--- /dev/null
+++ b/drivers/net/ethernet/8390/wd.c
@@ -0,0 +1,574 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is a driver for WD8003 and WD8013 "compatible" ethercards.
+
+ Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users, support
+ for non-standard memory sizes.
+
+
+*/
+
+static const char version[] =
+ "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "wd"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] __initdata =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+static int wd_probe1(struct net_device *dev, int ioaddr);
+
+static int wd_open(struct net_device *dev);
+static void wd_reset_8390(struct net_device *dev);
+static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void wd_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void wd_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static int wd_close(struct net_device *dev);
+
+static u32 wd_msg_enable;
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* Offset to ASIC command register. */
+#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
+#define WD_MEMENB 0x40 /* Enable the shared memory. */
+#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
+#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
+#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
+#define WD_IO_EXTENT 32
+
+
+/* Probe for the WD8003 and WD8013. These cards have the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following. A Soundblaster can have the same checksum as an WDethercard,
+ so we have an extra exclusionary check for it.
+
+ The wd_probe1() routine initializes the card and fills the
+ station address field. */
+
+static int __init do_wd_probe(struct net_device *dev)
+{
+ int i;
+ struct resource *r;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+ int mem_start = dev->mem_start;
+ int mem_end = dev->mem_end;
+
+ if (base_addr > 0x1ff) { /* Check a user specified location. */
+ r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
+ if ( r == NULL)
+ return -EBUSY;
+ i = wd_probe1(dev, base_addr);
+ if (i != 0)
+ release_region(base_addr, WD_IO_EXTENT);
+ else
+ r->name = dev->name;
+ return i;
+ }
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; wd_portlist[i]; i++) {
+ int ioaddr = wd_portlist[i];
+ r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
+ if (r == NULL)
+ continue;
+ if (wd_probe1(dev, ioaddr) == 0) {
+ r->name = dev->name;
+ return 0;
+ }
+ release_region(ioaddr, WD_IO_EXTENT);
+ dev->irq = irq;
+ dev->mem_start = mem_start;
+ dev->mem_end = mem_end;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init wd_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_wd_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops wd_netdev_ops = {
+ .ndo_open = wd_open,
+ .ndo_stop = wd_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init wd_probe1(struct net_device *dev, int ioaddr)
+{
+ int i;
+ int err;
+ int checksum = 0;
+ int ancient = 0; /* An old card without config registers. */
+ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
+ const char *model_name;
+ static unsigned version_printed;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
+ || inb(ioaddr + 9) == 0xff
+ || (checksum & 0xff) != 0xFF)
+ return -ENODEV;
+
+ /* Check for semi-valid mem_start/end values if supplied. */
+ if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+ netdev_warn(dev,
+ "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+ dev->mem_start = 0;
+ dev->mem_end = 0;
+ }
+
+ if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+ netdev_info(dev, version);
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr);
+
+ /* The following PureData probe code was contributed by
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+ configuration differently from others so we have to check for them.
+ This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+ */
+ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+ unsigned char reg5 = inb(ioaddr+5);
+
+ switch (inb(ioaddr+2)) {
+ case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
+ case 0x05: word16 = 0; model_name = "PDUC8023"; break;
+ case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+ /* Either 0x01 (dumb) or they've released a new version. */
+ default: word16 = 0; model_name = "PDI8023"; break;
+ }
+ dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else { /* End of PureData probe */
+ /* This method of checking for a 16-bit board is borrowed from the
+ we.c driver. A simpler method is just to look in ASIC reg. 0x03.
+ I'm comparing the two method in alpha test to make certain they
+ return the same result. */
+ /* Check for the old 8 bit board - it has register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ model_name = "WD8003-old";
+ word16 = 0;
+ } else {
+ int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
+ int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ /* Magic to set ASIC to word-wide mode. */
+ outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ model_name = "WD8013";
+ word16 = 1; /* We have a 16bit board here! */
+ } else {
+ model_name = "WD8003";
+ word16 = 0;
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
+ }
+#ifndef final_version
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+ pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ word16 ? 16 : 8,
+ (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+ }
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+ /* Allow a compile-time override. */
+ dev->mem_start = WD_SHMEM;
+#else
+ if (dev->mem_start == 0) {
+ /* Sanity and old 8003 check */
+ int reg0 = inb(ioaddr);
+ if (reg0 == 0xff || reg0 == 0) {
+ /* Future plan: this could check a few likely locations first. */
+ dev->mem_start = 0xd0000;
+ pr_cont(" assigning address %#lx", dev->mem_start);
+ } else {
+ int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ /* Some boards don't have the register 5 -- it returns 0xff. */
+ if (high_addr_bits == 0x1f || word16 == 0)
+ high_addr_bits = 0x01;
+ dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ }
+ }
+#endif
+
+ /* The 8390 isn't at the base address -- the ASIC regs are there! */
+ dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+ if (dev->irq < 2) {
+ static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
+ int reg1 = inb(ioaddr+1);
+ int reg4 = inb(ioaddr+4);
+ if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
+ short nic_addr = ioaddr+WD_NIC_OFFSET;
+ unsigned long irq_mask;
+
+ /* We have an old-style ethercard that doesn't report its IRQ
+ line. Do autoirq to find the IRQ line. Note that this IS NOT
+ a reliable way to trigger an interrupt. */
+ outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+ outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
+
+ irq_mask = probe_irq_on();
+ outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
+ outb_p(0x00, nic_addr + EN0_RCNTLO);
+ outb_p(0x00, nic_addr + EN0_RCNTHI);
+ outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+ mdelay(20);
+ dev->irq = probe_irq_off(irq_mask);
+
+ outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
+
+ if (netif_msg_drv(ei_local))
+ pr_cont(" autoirq is %d", dev->irq);
+ if (dev->irq < 2)
+ dev->irq = word16 ? 10 : 5;
+ } else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (i) {
+ pr_cont(" unable to get IRQ %d.\n", dev->irq);
+ return i;
+ }
+
+ /* OK, were are certain this is going to work. Setup the device. */
+ ei_status.name = model_name;
+ ei_status.word16 = word16;
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+ /* Don't map in the shared memory until the board is actually opened. */
+
+ /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+ if (dev->mem_end != 0) {
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ ei_status.priv = dev->mem_end - dev->mem_start;
+ } else {
+ ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+ ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
+ }
+
+ ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
+ if (!ei_status.mem) {
+ free_irq(dev->irq, dev);
+ return -ENOMEM;
+ }
+
+ pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+ model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = wd_reset_8390;
+ ei_status.block_input = wd_block_input;
+ ei_status.block_output = wd_block_output;
+ ei_status.get_8390_hdr = wd_get_8390_hdr;
+
+ dev->netdev_ops = &wd_netdev_ops;
+ NS8390_init(dev, 0);
+ ei_local->msg_enable = wd_msg_enable;
+
+#if 1
+ /* Enable interrupt generation on softconfig cards -- M.U */
+ /* .. but possibly potentially unsafe - Donald */
+ if (inb(ioaddr+14) & 0x20)
+ outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(dev->irq, dev);
+ iounmap(ei_status.mem);
+ }
+ return err;
+}
+
+static int
+wd_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+ ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ if (ei_status.word16)
+ outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+ outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+
+ return ei_open(dev);
+}
+
+static void
+wd_reset_8390(struct net_device *dev)
+{
+ int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ outb(WD_RESET, wd_cmd_port);
+ netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n",
+ jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+ if (ei_status.word16)
+ outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+
+ netif_dbg(ei_local, hw, dev, "reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
+
+ /* We'll always get a 4 byte header read followed by a packet read, so
+ we enable 16 bit mode before the header, and disable after the body. */
+ if (ei_status.word16)
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
+#ifdef __BIG_ENDIAN
+ /* Officially this is what we are doing, but the readl() is faster */
+ /* unfortunately it isn't endian aware of the struct */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+ on the Western digital card where there is no choice of how to do it.
+ The only complications are that the ring buffer wraps, and need to map
+ switch between 8- and 16-bit modes. */
+
+static void
+wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long offset = ring_offset - (WD_START_PG<<8);
+ void __iomem *xfer_start = ei_status.mem + offset;
+
+ if (offset + count > ei_status.priv) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.priv - offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+
+ /* Turn off 16 bit access so that reboot works. ISA brain-damage */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+}
+
+static void
+wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
+
+
+ if (ei_status.word16) {
+ /* Turn on and off 16 bit access so that reboot works. */
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ memcpy_toio(shmem, buf, count);
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ } else
+ memcpy_toio(shmem, buf, count);
+}
+
+
+static int
+wd_close(struct net_device *dev)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
+ ei_close(dev);
+
+ /* Change from 16-bit to 8-bit shared memory so reboot works. */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+ /* And disable the shared memory. */
+ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
+static struct net_device *dev_wd[MAX_WD_CARDS];
+static int io[MAX_WD_CARDS];
+static int irq[MAX_WD_CARDS];
+static int mem[MAX_WD_CARDS];
+static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param_array(mem_end, int, NULL, 0);
+module_param_named(msg_enable, wd_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
+MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
+MODULE_PARM_DESC(mem_end, "memory end address(es)");
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = mem_end[this_dev];
+ if (do_wd_probe(dev) == 0) {
+ dev_wd[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct net_device *dev = dev_wd[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
new file mode 100644
index 00000000000..8308728fad0
--- /dev/null
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -0,0 +1,458 @@
+/*
+ * Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver
+ *
+ * (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM)
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on all the other NE2000 drivers for Linux
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS
+ * Ethernet Controllers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/zorro.h>
+#include <linux/jiffies.h>
+
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val, port) out_8(port, val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val, port) out_8(port, val)
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+static u32 zorro8390_msg_enable;
+
+#include "lib8390.c"
+
+#define DRV_NAME "zorro8390"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD (0x00 * 2)
+#define NE_DATAPORT (0x10 * 2) /* NatSemi-defined port window offset */
+#define NE_RESET (0x1f * 2) /* Issue a read to reset,
+ * a write to clear. */
+#define NE_IO_EXTENT (0x20 * 2)
+
+#define NE_EN0_ISR (0x07 * 2)
+#define NE_EN0_DCFG (0x0e * 2)
+
+#define NE_EN0_RSARLO (0x08 * 2)
+#define NE_EN0_RSARHI (0x09 * 2)
+#define NE_EN0_RCNTLO (0x0a * 2)
+#define NE_EN0_RXCR (0x0c * 2)
+#define NE_EN0_TXCR (0x0d * 2)
+#define NE_EN0_RCNTHI (0x0b * 2)
+#define NE_EN0_IMR (0x0f * 2)
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define WORDSWAP(a) ((((a) >> 8) & 0xff) | ((a) << 8))
+
+static struct card_info {
+ zorro_id id;
+ const char *name;
+ unsigned int offset;
+} cards[] = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 },
+};
+
+/* Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void zorro8390_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, hw, dev, "resetting - t=%ld...\n", jiffies);
+
+ z_writeb(z_readb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete\n", __func__);
+ break;
+ }
+ z_writeb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+static void zorro8390_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int cnt;
+ short *ptrs;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_warn(dev,
+ "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+ z_writeb(0, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
+ z_writeb(ring_page, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ ptrs = (short *)hdr;
+ for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+
+ hdr->count = WORDSWAP(hdr->count);
+
+ ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.
+ * If you are porting to a new ethercard, look at the packet driver source
+ * for hints. The NEx000 doesn't share the on-board packet memory --
+ * you have to put the packet out through the "remote DMA" dataport
+ * using z_writeb.
+ */
+static void zorro8390_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+ short *ptrs;
+ int cnt;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+ z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+ if (count & 0x01)
+ buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void zorro8390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+ short *ptrs;
+ int cnt;
+
+ /* Round the count up for word writes. Do we need to do this?
+ * What effect will an odd byte count have on the 8390?
+ * I should check someday.
+ */
+ if (count & 0x01)
+ count++;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0x00, nic_base + NE_EN0_RSARLO);
+ z_writeb(start_page, nic_base + NE_EN0_RSARHI);
+
+ z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ z_writew(*ptrs++, NE_BASE + NE_DATAPORT);
+
+ dma_start = jiffies;
+
+ while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) {
+ /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC\n");
+ zorro8390_reset_8390(dev);
+ __NS8390_init(dev, 1);
+ break;
+ }
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static int zorro8390_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int zorro8390_close(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard\n");
+ __ei_close(dev);
+ return 0;
+}
+
+static void zorro8390_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2);
+ free_netdev(dev);
+}
+
+static struct zorro_device_id zorro8390_zorro_tbl[] = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl);
+
+static const struct net_device_ops zorro8390_netdev_ops = {
+ .ndo_open = zorro8390_open,
+ .ndo_stop = zorro8390_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int zorro8390_init(struct net_device *dev, unsigned long board,
+ const char *name, void __iomem *ioaddr)
+{
+ int i;
+ int err;
+ unsigned char SA_prom[32];
+ int start_page, stop_page;
+ struct ei_device *ei_local = netdev_priv(dev);
+ static u32 zorro8390_offsets[16] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ };
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ {
+ unsigned long reset_start_time = jiffies;
+
+ z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies,
+ reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "not found (no reset ack)\n");
+ return -ENODEV;
+ }
+
+ z_writeb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ * We must first initialize registers,
+ * similar to NS8390_init(eifdev, 0).
+ * We can't reliably read the SAPROM address without this.
+ * (I learned the hard way!).
+ */
+ {
+ static const struct {
+ u32 value;
+ u32 offset;
+ } program_seq[] = {
+ {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+ /* Select page 0 */
+ {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
+ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_IMR}, /* Mask completion irq */
+ {0xFF, NE_EN0_ISR},
+ {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
+ {32, NE_EN0_RCNTLO},
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
+ {0x00, NE_EN0_RSARHI},
+ {E8390_RREAD + E8390_START, NE_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ z_writeb(program_seq[i].value,
+ ioaddr + program_seq[i].offset);
+ }
+ for (i = 0; i < 16; i++) {
+ SA_prom[i] = z_readb(ioaddr + NE_DATAPORT);
+ (void)z_readb(ioaddr + NE_DATAPORT);
+ }
+
+ /* We must set the 8390 for word mode. */
+ z_writeb(0x49, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+
+ /* Install the Interrupt handler */
+ i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt,
+ IRQF_SHARED, DRV_NAME, dev);
+ if (i)
+ return i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = zorro8390_reset_8390;
+ ei_status.block_input = zorro8390_block_input;
+ ei_status.block_output = zorro8390_block_output;
+ ei_status.get_8390_hdr = zorro8390_get_8390_hdr;
+ ei_status.reg_offset = zorro8390_offsets;
+
+ dev->netdev_ops = &zorro8390_netdev_ops;
+ __NS8390_init(dev, 0);
+
+ ei_local->msg_enable = zorro8390_msg_enable;
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ return err;
+ }
+
+ netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n",
+ name, board, dev->dev_addr);
+
+ return 0;
+}
+
+static int zorro8390_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ struct net_device *dev;
+ unsigned long board, ioaddr;
+ int err, i;
+
+ for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--)
+ if (z->id == cards[i].id)
+ break;
+ if (i < 0)
+ return -ENODEV;
+
+ board = z->resource.start;
+ ioaddr = board + cards[i].offset;
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return -ENOMEM;
+ if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) {
+ free_netdev(dev);
+ return -EBUSY;
+ }
+ err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr));
+ if (err) {
+ release_mem_region(ioaddr, NE_IO_EXTENT * 2);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+ return 0;
+}
+
+static struct zorro_driver zorro8390_driver = {
+ .name = "zorro8390",
+ .id_table = zorro8390_zorro_tbl,
+ .probe = zorro8390_init_one,
+ .remove = zorro8390_remove_one,
+};
+
+static int __init zorro8390_init_module(void)
+{
+ return zorro_register_driver(&zorro8390_driver);
+}
+
+static void __exit zorro8390_cleanup_module(void)
+{
+ zorro_unregister_driver(&zorro8390_driver);
+}
+
+module_init(zorro8390_init_module);
+module_exit(zorro8390_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
new file mode 100644
index 00000000000..edb71866185
--- /dev/null
+++ b/drivers/net/ethernet/Kconfig
@@ -0,0 +1,186 @@
+#
+# Ethernet LAN device configuration
+#
+
+menuconfig ETHERNET
+ bool "Ethernet driver support"
+ depends on NET
+ default y
+ ---help---
+ This section contains all the Ethernet device drivers.
+
+if ETHERNET
+
+config MDIO
+ tristate
+
+config SUNGEM_PHY
+ tristate
+
+source "drivers/net/ethernet/3com/Kconfig"
+source "drivers/net/ethernet/adaptec/Kconfig"
+source "drivers/net/ethernet/aeroflex/Kconfig"
+source "drivers/net/ethernet/allwinner/Kconfig"
+source "drivers/net/ethernet/alteon/Kconfig"
+source "drivers/net/ethernet/altera/Kconfig"
+source "drivers/net/ethernet/amd/Kconfig"
+source "drivers/net/ethernet/apple/Kconfig"
+source "drivers/net/ethernet/arc/Kconfig"
+source "drivers/net/ethernet/atheros/Kconfig"
+source "drivers/net/ethernet/cadence/Kconfig"
+source "drivers/net/ethernet/adi/Kconfig"
+source "drivers/net/ethernet/broadcom/Kconfig"
+source "drivers/net/ethernet/brocade/Kconfig"
+source "drivers/net/ethernet/calxeda/Kconfig"
+source "drivers/net/ethernet/chelsio/Kconfig"
+source "drivers/net/ethernet/cirrus/Kconfig"
+source "drivers/net/ethernet/cisco/Kconfig"
+
+config CX_ECAT
+ tristate "Beckhoff CX5020 EtherCAT master support"
+ depends on PCI
+ depends on X86 || COMPILE_TEST
+ ---help---
+ Driver for EtherCAT master module located on CCAT FPGA
+ that can be found on Beckhoff CX5020, and possibly other of CX
+ Beckhoff CX series industrial PCs.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ec_bhf.
+
+source "drivers/net/ethernet/davicom/Kconfig"
+
+config DNET
+ tristate "Dave ethernet support (DNET)"
+ depends on HAS_IOMEM
+ select PHYLIB
+ ---help---
+ The Dave ethernet interface (DNET) is found on Qong Board FPGA.
+ Say Y to include support for the DNET chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called dnet.
+
+source "drivers/net/ethernet/dec/Kconfig"
+source "drivers/net/ethernet/dlink/Kconfig"
+source "drivers/net/ethernet/emulex/Kconfig"
+source "drivers/net/ethernet/neterion/Kconfig"
+source "drivers/net/ethernet/faraday/Kconfig"
+source "drivers/net/ethernet/freescale/Kconfig"
+source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
+source "drivers/net/ethernet/hp/Kconfig"
+source "drivers/net/ethernet/ibm/Kconfig"
+source "drivers/net/ethernet/intel/Kconfig"
+source "drivers/net/ethernet/i825xx/Kconfig"
+source "drivers/net/ethernet/xscale/Kconfig"
+source "drivers/net/ethernet/icplus/Kconfig"
+
+config JME
+ tristate "JMicron(R) PCI-Express Gigabit Ethernet support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ This driver supports the PCI-Express gigabit ethernet adapters
+ based on JMicron JMC250 chipset.
+
+ To compile this driver as a module, choose M here. The module
+ will be called jme.
+
+config KORINA
+ tristate "Korina (IDT RC32434) Ethernet support"
+ depends on MIKROTIK_RB532
+ ---help---
+ If you have a Mikrotik RouterBoard 500 or IDT RC32434
+ based system say Y. Otherwise say N.
+
+config LANTIQ_ETOP
+ tristate "Lantiq SoC ETOP driver"
+ depends on SOC_TYPE_XWAY
+ ---help---
+ Support for the MII0 inside the Lantiq SoC
+
+source "drivers/net/ethernet/marvell/Kconfig"
+source "drivers/net/ethernet/mellanox/Kconfig"
+source "drivers/net/ethernet/micrel/Kconfig"
+source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
+source "drivers/net/ethernet/myricom/Kconfig"
+
+config FEALNX
+ tristate "Myson MTD-8xx PCI Ethernet support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
+ cards. <http://www.myson.com.tw/>
+
+source "drivers/net/ethernet/natsemi/Kconfig"
+source "drivers/net/ethernet/8390/Kconfig"
+
+config NET_NETX
+ tristate "NetX Ethernet support"
+ select MII
+ depends on ARCH_NETX
+ ---help---
+ This is support for the Hilscher netX builtin Ethernet ports
+
+ To compile this driver as a module, choose M here. The module
+ will be called netx-eth.
+
+source "drivers/net/ethernet/nuvoton/Kconfig"
+source "drivers/net/ethernet/nvidia/Kconfig"
+source "drivers/net/ethernet/nxp/Kconfig"
+source "drivers/net/ethernet/octeon/Kconfig"
+source "drivers/net/ethernet/oki-semi/Kconfig"
+
+config ETHOC
+ tristate "OpenCores 10/100 Mbps Ethernet MAC support"
+ depends on HAS_IOMEM && HAS_DMA
+ select MII
+ select PHYLIB
+ select CRC32
+ select BITREVERSE
+ ---help---
+ Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
+
+source "drivers/net/ethernet/packetengines/Kconfig"
+source "drivers/net/ethernet/pasemi/Kconfig"
+source "drivers/net/ethernet/qlogic/Kconfig"
+source "drivers/net/ethernet/realtek/Kconfig"
+source "drivers/net/ethernet/renesas/Kconfig"
+source "drivers/net/ethernet/rdc/Kconfig"
+
+config S6GMAC
+ tristate "S6105 GMAC ethernet support"
+ depends on XTENSA_VARIANT_S6000
+ select PHYLIB
+ ---help---
+ This driver supports the on chip ethernet device on the
+ S6105 xtensa processor.
+
+ To compile this driver as a module, choose M here. The module
+ will be called s6gmac.
+
+source "drivers/net/ethernet/samsung/Kconfig"
+source "drivers/net/ethernet/seeq/Kconfig"
+source "drivers/net/ethernet/silan/Kconfig"
+source "drivers/net/ethernet/sis/Kconfig"
+source "drivers/net/ethernet/sfc/Kconfig"
+source "drivers/net/ethernet/sgi/Kconfig"
+source "drivers/net/ethernet/smsc/Kconfig"
+source "drivers/net/ethernet/stmicro/Kconfig"
+source "drivers/net/ethernet/sun/Kconfig"
+source "drivers/net/ethernet/tehuti/Kconfig"
+source "drivers/net/ethernet/ti/Kconfig"
+source "drivers/net/ethernet/tile/Kconfig"
+source "drivers/net/ethernet/toshiba/Kconfig"
+source "drivers/net/ethernet/tundra/Kconfig"
+source "drivers/net/ethernet/via/Kconfig"
+source "drivers/net/ethernet/wiznet/Kconfig"
+source "drivers/net/ethernet/xilinx/Kconfig"
+source "drivers/net/ethernet/xircom/Kconfig"
+
+endif # ETHERNET
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
new file mode 100644
index 00000000000..58de3339ab3
--- /dev/null
+++ b/drivers/net/ethernet/Makefile
@@ -0,0 +1,83 @@
+#
+# Makefile for the Linux network Ethernet device drivers.
+#
+
+obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
+obj-$(CONFIG_NET_VENDOR_8390) += 8390/
+obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
+obj-$(CONFIG_GRETH) += aeroflex/
+obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
+obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
+obj-$(CONFIG_ALTERA_TSE) += altera/
+obj-$(CONFIG_NET_VENDOR_AMD) += amd/
+obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
+obj-$(CONFIG_NET_VENDOR_ARC) += arc/
+obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
+obj-$(CONFIG_NET_CADENCE) += cadence/
+obj-$(CONFIG_NET_BFIN) += adi/
+obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
+obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/
+obj-$(CONFIG_NET_CALXEDA_XGMAC) += calxeda/
+obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
+obj-$(CONFIG_NET_VENDOR_CIRRUS) += cirrus/
+obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_CX_ECAT) += ec_bhf.o
+obj-$(CONFIG_DM9000) += davicom/
+obj-$(CONFIG_DNET) += dnet.o
+obj-$(CONFIG_NET_VENDOR_DEC) += dec/
+obj-$(CONFIG_NET_VENDOR_DLINK) += dlink/
+obj-$(CONFIG_NET_VENDOR_EMULEX) += emulex/
+obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
+obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
+obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
+obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
+obj-$(CONFIG_NET_VENDOR_HP) += hp/
+obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
+obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
+obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
+obj-$(CONFIG_NET_VENDOR_XSCALE) += xscale/
+obj-$(CONFIG_IP1000) += icplus/
+obj-$(CONFIG_JME) += jme.o
+obj-$(CONFIG_KORINA) += korina.o
+obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
+obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
+obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
+obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
+obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
+obj-$(CONFIG_FEALNX) += fealnx.o
+obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
+obj-$(CONFIG_NET_NETX) += netx-eth.o
+obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
+obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
+obj-$(CONFIG_LPC_ENET) += nxp/
+obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
+obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
+obj-$(CONFIG_ETHOC) += ethoc.o
+obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/
+obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
+obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
+obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
+obj-$(CONFIG_SH_ETH) += renesas/
+obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
+obj-$(CONFIG_S6GMAC) += s6gmac.o
+obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
+obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
+obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
+obj-$(CONFIG_NET_VENDOR_SIS) += sis/
+obj-$(CONFIG_SFC) += sfc/
+obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
+obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
+obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
+obj-$(CONFIG_NET_VENDOR_SUN) += sun/
+obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
+obj-$(CONFIG_NET_VENDOR_TI) += ti/
+obj-$(CONFIG_TILE_NET) += tile/
+obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
+obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
+obj-$(CONFIG_NET_VENDOR_VIA) += via/
+obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
+obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
+obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
diff --git a/drivers/net/ethernet/adaptec/Kconfig b/drivers/net/ethernet/adaptec/Kconfig
new file mode 100644
index 00000000000..5c804bbe3da
--- /dev/null
+++ b/drivers/net/ethernet/adaptec/Kconfig
@@ -0,0 +1,35 @@
+#
+# Adaptec network device configuration
+#
+
+config NET_VENDOR_ADAPTEC
+ bool "Adaptec devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Adaptec cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_ADAPTEC
+
+config ADAPTEC_STARFIRE
+ tristate "Adaptec Starfire/DuraLAN support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network
+ adapter. The DuraLAN chip is used on the 64 bit PCI boards from
+ Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip
+ driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called starfire. This is recommended.
+
+endif # NET_VENDOR_ADAPTEC
diff --git a/drivers/net/ethernet/adaptec/Makefile b/drivers/net/ethernet/adaptec/Makefile
new file mode 100644
index 00000000000..6c07b758ac0
--- /dev/null
+++ b/drivers/net/ethernet/adaptec/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Adaptec network device drivers.
+#
+
+obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c
new file mode 100644
index 00000000000..40dbbf74033
--- /dev/null
+++ b/drivers/net/ethernet/adaptec/starfire.c
@@ -0,0 +1,2060 @@
+/* starfire.c: Linux device driver for the Adaptec Starfire network adapter. */
+/*
+ Written 1998-2000 by Donald Becker.
+
+ Current maintainer is Ion Badulescu <ionut ta badula tod org>. Please
+ send all bug reports to me, and not to Donald Becker, as this code
+ has been heavily modified from Donald's original version.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The information below comes from Donald Becker's original driver:
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/starfire.html
+ [link no longer provides useful info -jgarzik]
+
+*/
+
+#define DRV_NAME "starfire"
+#define DRV_VERSION "2.1"
+#define DRV_RELDATE "July 6, 2008"
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/mm.h>
+#include <linux/firmware.h>
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+/*
+ * The current frame processor firmware fails to checksum a fragment
+ * of length 1. If and when this is fixed, the #define below can be removed.
+ */
+#define HAS_BROKEN_FIRMWARE
+
+/*
+ * If using the broken firmware, data must be padded to the next 32-bit boundary.
+ */
+#ifdef HAS_BROKEN_FIRMWARE
+#define PADDING_MASK 3
+#endif
+
+/*
+ * Define this if using the driver with the zero-copy patch
+ */
+#define ZEROCOPY
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define VLAN_SUPPORT
+#endif
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+/* Used for tuning interrupt latency vs. overhead. */
+static int intr_latency;
+static int small_frames;
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+static int max_interrupt_work = 20;
+static int mtu;
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ The Starfire has a 512 element hash table based on the Ethernet CRC. */
+static const int multicast_filter_limit = 512;
+/* Whether to do TCP/UDP checksums in hardware */
+static int enable_hw_cksum = 1;
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+/*
+ * Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ * Setting to > 1518 effectively disables this feature.
+ *
+ * NOTE:
+ * The ia64 doesn't allow for unaligned loads even of integers being
+ * misaligned on a 2 byte boundary. Thus always force copying of
+ * packets as the starfire doesn't allow for misaligned DMAs ;-(
+ * 23/10/2000 - Jes
+ *
+ * The Alpha and the Sparc don't like unaligned loads, either. On Sparc64,
+ * at least, having unaligned frames leads to a rather serious performance
+ * penalty. -Ion
+ */
+#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__)
+static int rx_copybreak = PKT_BUF_SZ;
+#else
+static int rx_copybreak /* = 0 */;
+#endif
+
+/* PCI DMA burst size -- on sparc64 we want to force it to 64 bytes, on the others the default of 128 is fine. */
+#ifdef __sparc__
+#define DMA_BURST_SIZE 64
+#else
+#define DMA_BURST_SIZE 128
+#endif
+
+/* Operational parameters that are set at compile time. */
+
+/* The "native" ring sizes are either 256 or 2048.
+ However in some modes a descriptor may be marked to wrap the ring earlier.
+*/
+#define RX_RING_SIZE 256
+#define TX_RING_SIZE 32
+/* The completion queues are fixed at 1024 entries i.e. 4K or 8KB. */
+#define DONE_Q_SIZE 1024
+/* All queues must be aligned on a 256-byte boundary */
+#define QUEUE_ALIGN 256
+
+#if RX_RING_SIZE > 256
+#define RX_Q_ENTRIES Rx2048QEntries
+#else
+#define RX_Q_ENTRIES Rx256QEntries
+#endif
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2 * HZ)
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+/* 64-bit dma_addr_t */
+#define ADDR_64BITS /* This chip uses 64 bit addresses. */
+#define netdrv_addr_t __le64
+#define cpu_to_dma(x) cpu_to_le64(x)
+#define dma_to_cpu(x) le64_to_cpu(x)
+#define RX_DESC_Q_ADDR_SIZE RxDescQAddr64bit
+#define TX_DESC_Q_ADDR_SIZE TxDescQAddr64bit
+#define RX_COMPL_Q_ADDR_SIZE RxComplQAddr64bit
+#define TX_COMPL_Q_ADDR_SIZE TxComplQAddr64bit
+#define RX_DESC_ADDR_SIZE RxDescAddr64bit
+#else /* 32-bit dma_addr_t */
+#define netdrv_addr_t __le32
+#define cpu_to_dma(x) cpu_to_le32(x)
+#define dma_to_cpu(x) le32_to_cpu(x)
+#define RX_DESC_Q_ADDR_SIZE RxDescQAddr32bit
+#define TX_DESC_Q_ADDR_SIZE TxDescQAddr32bit
+#define RX_COMPL_Q_ADDR_SIZE RxComplQAddr32bit
+#define TX_COMPL_Q_ADDR_SIZE TxComplQAddr32bit
+#define RX_DESC_ADDR_SIZE RxDescAddr32bit
+#endif
+
+#define skb_first_frag_len(skb) skb_headlen(skb)
+#define skb_num_frags(skb) (skb_shinfo(skb)->nr_frags + 1)
+
+/* Firmware names */
+#define FIRMWARE_RX "adaptec/starfire_rx.bin"
+#define FIRMWARE_TX "adaptec/starfire_tx.bin"
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] =
+KERN_INFO "starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n"
+" (unofficial 2.2/2.4 kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n";
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_FIRMWARE(FIRMWARE_RX);
+MODULE_FIRMWARE(FIRMWARE_TX);
+
+module_param(max_interrupt_work, int, 0);
+module_param(mtu, int, 0);
+module_param(debug, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param(intr_latency, int, 0);
+module_param(small_frames, int, 0);
+module_param(enable_hw_cksum, int, 0);
+MODULE_PARM_DESC(max_interrupt_work, "Maximum events handled per interrupt");
+MODULE_PARM_DESC(mtu, "MTU (all boards)");
+MODULE_PARM_DESC(debug, "Debug level (0-6)");
+MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(intr_latency, "Maximum interrupt latency, in microseconds");
+MODULE_PARM_DESC(small_frames, "Maximum size of receive frames that bypass interrupt latency (0,64,128,256,512)");
+MODULE_PARM_DESC(enable_hw_cksum, "Enable/disable hardware cksum support (0/1)");
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is for the Adaptec 6915 "Starfire" 64 bit PCI Ethernet adapter.
+
+II. Board-specific settings
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+The Starfire hardware uses multiple fixed-size descriptor queues/rings. The
+ring sizes are set fixed by the hardware, but may optionally be wrapped
+earlier by the END bit in the descriptor.
+This driver uses that hardware queue size for the Rx ring, where a large
+number of entries has no ill effect beyond increases the potential backlog.
+The Tx ring is wrapped with the END bit, since a large hardware Tx queue
+disables the queue layer priority ordering and we have no mechanism to
+utilize the hardware two-level priority queue. When modifying the
+RX/TX_RING_SIZE pay close attention to page sizes and the ring-empty warning
+levels.
+
+IIIb/c. Transmit/Receive Structure
+
+See the Adaptec manual for the many possible structures, and options for
+each structure. There are far too many to document all of them here.
+
+For transmit this driver uses type 0/1 transmit descriptors (depending
+on the 32/64 bitness of the architecture), and relies on automatic
+minimum-length padding. It does not use the completion queue
+consumer index, but instead checks for non-zero status entries.
+
+For receive this driver uses type 2/3 receive descriptors. The driver
+allocates full frame size skbuffs for the Rx ring buffers, so all frames
+should fit in a single descriptor. The driver does not use the completion
+queue consumer index, but instead checks for non-zero status entries.
+
+When an incoming frame is less than RX_COPYBREAK bytes long, a fresh skbuff
+is allocated and the frame is copied to the new skbuff. When the incoming
+frame is larger, the skbuff is passed directly up the protocol stack.
+Buffers consumed this way are replaced by newly allocated skbuffs in a later
+phase of receive.
+
+A notable aspect of operation is that unaligned buffers are not permitted by
+the Starfire hardware. Thus the IP header at offset 14 in an ethernet frame
+isn't longword aligned, which may cause problems on some machine
+e.g. Alphas and IA64. For these architectures, the driver is forced to copy
+the frame into a new skbuff unconditionally. Copied frames are put into the
+skbuff at an offset of "+2", thus 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and the netif_queue
+status. If the number of free Tx slots in the ring falls below a certain number
+(currently hardcoded to 4), it signals the upper layer to stop the queue.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the netif_queue is stopped and the
+number of free Tx slow is above the threshold, it signals the upper layer to
+restart the queue.
+
+IV. Notes
+
+IVb. References
+
+The Adaptec Starfire manuals, available only from Adaptec.
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
+
+IVc. Errata
+
+- StopOnPerr is broken, don't enable
+- Hardware ethernet padding exposes random data, perform software padding
+ instead (unverified -- works correctly for all the hardware I have)
+
+*/
+
+
+
+enum chip_capability_flags {CanHaveMII=1, };
+
+enum chipset {
+ CH_6915 = 0,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(starfire_pci_tbl) = {
+ { PCI_VDEVICE(ADAPTEC, 0x6915), CH_6915 },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, starfire_pci_tbl);
+
+/* A chip capabilities table, matching the CH_xxx entries in xxx_pci_tbl[] above. */
+static const struct chip_info {
+ const char *name;
+ int drv_flags;
+} netdrv_tbl[] = {
+ { "Adaptec Starfire 6915", CanHaveMII },
+};
+
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+ In general, only the important configuration values or bits changed
+ multiple times should be defined symbolically.
+*/
+enum register_offsets {
+ PCIDeviceConfig=0x50040, GenCtrl=0x50070, IntrTimerCtrl=0x50074,
+ IntrClear=0x50080, IntrStatus=0x50084, IntrEnable=0x50088,
+ MIICtrl=0x52000, TxStationAddr=0x50120, EEPROMCtrl=0x51000,
+ GPIOCtrl=0x5008C, TxDescCtrl=0x50090,
+ TxRingPtr=0x50098, HiPriTxRingPtr=0x50094, /* Low and High priority. */
+ TxRingHiAddr=0x5009C, /* 64 bit address extension. */
+ TxProducerIdx=0x500A0, TxConsumerIdx=0x500A4,
+ TxThreshold=0x500B0,
+ CompletionHiAddr=0x500B4, TxCompletionAddr=0x500B8,
+ RxCompletionAddr=0x500BC, RxCompletionQ2Addr=0x500C0,
+ CompletionQConsumerIdx=0x500C4, RxDMACtrl=0x500D0,
+ RxDescQCtrl=0x500D4, RxDescQHiAddr=0x500DC, RxDescQAddr=0x500E0,
+ RxDescQIdx=0x500E8, RxDMAStatus=0x500F0, RxFilterMode=0x500F4,
+ TxMode=0x55000, VlanType=0x55064,
+ PerfFilterTable=0x56000, HashTable=0x56100,
+ TxGfpMem=0x58000, RxGfpMem=0x5a000,
+};
+
+/*
+ * Bits in the interrupt status/mask registers.
+ * Warning: setting Intr[Ab]NormalSummary in the IntrEnable register
+ * enables all the interrupt sources that are or'ed into those status bits.
+ */
+enum intr_status_bits {
+ IntrLinkChange=0xf0000000, IntrStatsMax=0x08000000,
+ IntrAbnormalSummary=0x02000000, IntrGeneralTimer=0x01000000,
+ IntrSoftware=0x800000, IntrRxComplQ1Low=0x400000,
+ IntrTxComplQLow=0x200000, IntrPCI=0x100000,
+ IntrDMAErr=0x080000, IntrTxDataLow=0x040000,
+ IntrRxComplQ2Low=0x020000, IntrRxDescQ1Low=0x010000,
+ IntrNormalSummary=0x8000, IntrTxDone=0x4000,
+ IntrTxDMADone=0x2000, IntrTxEmpty=0x1000,
+ IntrEarlyRxQ2=0x0800, IntrEarlyRxQ1=0x0400,
+ IntrRxQ2Done=0x0200, IntrRxQ1Done=0x0100,
+ IntrRxGFPDead=0x80, IntrRxDescQ2Low=0x40,
+ IntrNoTxCsum=0x20, IntrTxBadID=0x10,
+ IntrHiPriTxBadID=0x08, IntrRxGfp=0x04,
+ IntrTxGfp=0x02, IntrPCIPad=0x01,
+ /* not quite bits */
+ IntrRxDone=IntrRxQ2Done | IntrRxQ1Done,
+ IntrRxEmpty=IntrRxDescQ1Low | IntrRxDescQ2Low,
+ IntrNormalMask=0xff00, IntrAbnormalMask=0x3ff00fe,
+};
+
+/* Bits in the RxFilterMode register. */
+enum rx_mode_bits {
+ AcceptBroadcast=0x04, AcceptAllMulticast=0x02, AcceptAll=0x01,
+ AcceptMulticast=0x10, PerfectFilter=0x40, HashFilter=0x30,
+ PerfectFilterVlan=0x80, MinVLANPrio=0xE000, VlanMode=0x0200,
+ WakeupOnGFP=0x0800,
+};
+
+/* Bits in the TxMode register */
+enum tx_mode_bits {
+ MiiSoftReset=0x8000, MIILoopback=0x4000,
+ TxFlowEnable=0x0800, RxFlowEnable=0x0400,
+ PadEnable=0x04, FullDuplex=0x02, HugeFrame=0x01,
+};
+
+/* Bits in the TxDescCtrl register. */
+enum tx_ctrl_bits {
+ TxDescSpaceUnlim=0x00, TxDescSpace32=0x10, TxDescSpace64=0x20,
+ TxDescSpace128=0x30, TxDescSpace256=0x40,
+ TxDescType0=0x00, TxDescType1=0x01, TxDescType2=0x02,
+ TxDescType3=0x03, TxDescType4=0x04,
+ TxNoDMACompletion=0x08,
+ TxDescQAddr64bit=0x80, TxDescQAddr32bit=0,
+ TxHiPriFIFOThreshShift=24, TxPadLenShift=16,
+ TxDMABurstSizeShift=8,
+};
+
+/* Bits in the RxDescQCtrl register. */
+enum rx_ctrl_bits {
+ RxBufferLenShift=16, RxMinDescrThreshShift=0,
+ RxPrefetchMode=0x8000, RxVariableQ=0x2000,
+ Rx2048QEntries=0x4000, Rx256QEntries=0,
+ RxDescAddr64bit=0x1000, RxDescAddr32bit=0,
+ RxDescQAddr64bit=0x0100, RxDescQAddr32bit=0,
+ RxDescSpace4=0x000, RxDescSpace8=0x100,
+ RxDescSpace16=0x200, RxDescSpace32=0x300,
+ RxDescSpace64=0x400, RxDescSpace128=0x500,
+ RxConsumerWrEn=0x80,
+};
+
+/* Bits in the RxDMACtrl register. */
+enum rx_dmactrl_bits {
+ RxReportBadFrames=0x80000000, RxDMAShortFrames=0x40000000,
+ RxDMABadFrames=0x20000000, RxDMACrcErrorFrames=0x10000000,
+ RxDMAControlFrame=0x08000000, RxDMAPauseFrame=0x04000000,
+ RxChecksumIgnore=0, RxChecksumRejectTCPUDP=0x02000000,
+ RxChecksumRejectTCPOnly=0x01000000,
+ RxCompletionQ2Enable=0x800000,
+ RxDMAQ2Disable=0, RxDMAQ2FPOnly=0x100000,
+ RxDMAQ2SmallPkt=0x200000, RxDMAQ2HighPrio=0x300000,
+ RxDMAQ2NonIP=0x400000,
+ RxUseBackupQueue=0x080000, RxDMACRC=0x040000,
+ RxEarlyIntThreshShift=12, RxHighPrioThreshShift=8,
+ RxBurstSizeShift=0,
+};
+
+/* Bits in the RxCompletionAddr register */
+enum rx_compl_bits {
+ RxComplQAddr64bit=0x80, RxComplQAddr32bit=0,
+ RxComplProducerWrEn=0x40,
+ RxComplType0=0x00, RxComplType1=0x10,
+ RxComplType2=0x20, RxComplType3=0x30,
+ RxComplThreshShift=0,
+};
+
+/* Bits in the TxCompletionAddr register */
+enum tx_compl_bits {
+ TxComplQAddr64bit=0x80, TxComplQAddr32bit=0,
+ TxComplProducerWrEn=0x40,
+ TxComplIntrStatus=0x20,
+ CommonQueueMode=0x10,
+ TxComplThreshShift=0,
+};
+
+/* Bits in the GenCtrl register */
+enum gen_ctrl_bits {
+ RxEnable=0x05, TxEnable=0x0a,
+ RxGFPEnable=0x10, TxGFPEnable=0x20,
+};
+
+/* Bits in the IntrTimerCtrl register */
+enum intr_ctrl_bits {
+ Timer10X=0x800, EnableIntrMasking=0x60, SmallFrameBypass=0x100,
+ SmallFrame64=0, SmallFrame128=0x200, SmallFrame256=0x400, SmallFrame512=0x600,
+ IntrLatencyMask=0x1f,
+};
+
+/* The Rx and Tx buffer descriptors. */
+struct starfire_rx_desc {
+ netdrv_addr_t rxaddr;
+};
+enum rx_desc_bits {
+ RxDescValid=1, RxDescEndRing=2,
+};
+
+/* Completion queue entry. */
+struct short_rx_done_desc {
+ __le32 status; /* Low 16 bits is length. */
+};
+struct basic_rx_done_desc {
+ __le32 status; /* Low 16 bits is length. */
+ __le16 vlanid;
+ __le16 status2;
+};
+struct csum_rx_done_desc {
+ __le32 status; /* Low 16 bits is length. */
+ __le16 csum; /* Partial checksum */
+ __le16 status2;
+};
+struct full_rx_done_desc {
+ __le32 status; /* Low 16 bits is length. */
+ __le16 status3;
+ __le16 status2;
+ __le16 vlanid;
+ __le16 csum; /* partial checksum */
+ __le32 timestamp;
+};
+/* XXX: this is ugly and I'm not sure it's worth the trouble -Ion */
+#ifdef VLAN_SUPPORT
+typedef struct full_rx_done_desc rx_done_desc;
+#define RxComplType RxComplType3
+#else /* not VLAN_SUPPORT */
+typedef struct csum_rx_done_desc rx_done_desc;
+#define RxComplType RxComplType2
+#endif /* not VLAN_SUPPORT */
+
+enum rx_done_bits {
+ RxOK=0x20000000, RxFIFOErr=0x10000000, RxBufQ2=0x08000000,
+};
+
+/* Type 1 Tx descriptor. */
+struct starfire_tx_desc_1 {
+ __le32 status; /* Upper bits are status, lower 16 length. */
+ __le32 addr;
+};
+
+/* Type 2 Tx descriptor. */
+struct starfire_tx_desc_2 {
+ __le32 status; /* Upper bits are status, lower 16 length. */
+ __le32 reserved;
+ __le64 addr;
+};
+
+#ifdef ADDR_64BITS
+typedef struct starfire_tx_desc_2 starfire_tx_desc;
+#define TX_DESC_TYPE TxDescType2
+#else /* not ADDR_64BITS */
+typedef struct starfire_tx_desc_1 starfire_tx_desc;
+#define TX_DESC_TYPE TxDescType1
+#endif /* not ADDR_64BITS */
+#define TX_DESC_SPACING TxDescSpaceUnlim
+
+enum tx_desc_bits {
+ TxDescID=0xB0000000,
+ TxCRCEn=0x01000000, TxDescIntr=0x08000000,
+ TxRingWrap=0x04000000, TxCalTCP=0x02000000,
+};
+struct tx_done_desc {
+ __le32 status; /* timestamp, index. */
+#if 0
+ __le32 intrstatus; /* interrupt status */
+#endif
+};
+
+struct rx_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
+struct tx_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ unsigned int used_slots;
+};
+
+#define PHY_CNT 2
+struct netdev_private {
+ /* Descriptor rings first for alignment. */
+ struct starfire_rx_desc *rx_ring;
+ starfire_tx_desc *tx_ring;
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_ring_dma;
+ /* The addresses of rx/tx-in-place skbuffs. */
+ struct rx_ring_info rx_info[RX_RING_SIZE];
+ struct tx_ring_info tx_info[TX_RING_SIZE];
+ /* Pointers to completion queues (full pages). */
+ rx_done_desc *rx_done_q;
+ dma_addr_t rx_done_q_dma;
+ unsigned int rx_done;
+ struct tx_done_desc *tx_done_q;
+ dma_addr_t tx_done_q_dma;
+ unsigned int tx_done;
+ struct napi_struct napi;
+ struct net_device *dev;
+ struct pci_dev *pci_dev;
+#ifdef VLAN_SUPPORT
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+#endif
+ void *queue_mem;
+ dma_addr_t queue_mem_dma;
+ size_t queue_mem_size;
+
+ /* Frequently used values: keep some adjacent for cache effect. */
+ spinlock_t lock;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int cur_tx, dirty_tx, reap_tx;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ /* These values keep track of the transceiver/media in use. */
+ int speed100; /* Set if speed == 100MBit. */
+ u32 tx_mode;
+ u32 intr_timer_ctrl;
+ u8 tx_threshold;
+ /* MII transceiver section. */
+ struct mii_if_info mii_if; /* MII lib hooks/info */
+ int phy_cnt; /* MII device addresses. */
+ unsigned char phys[PHY_CNT]; /* MII device addresses. */
+ void __iomem *base;
+};
+
+
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int netdev_open(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+static void tx_timeout(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t intr_handler(int irq, void *dev_instance);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int __netdev_rx(struct net_device *dev, int *quota);
+static int netdev_poll(struct napi_struct *napi, int budget);
+static void refill_rx_ring(struct net_device *dev);
+static void netdev_error(struct net_device *dev, int intr_status);
+static void set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_close(struct net_device *dev);
+static void netdev_media_change(struct net_device *dev);
+static const struct ethtool_ops ethtool_ops;
+
+
+#ifdef VLAN_SUPPORT
+static int netdev_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct netdev_private *np = netdev_priv(dev);
+
+ spin_lock(&np->lock);
+ if (debug > 1)
+ printk("%s: Adding vlanid %d to vlan filter\n", dev->name, vid);
+ set_bit(vid, np->active_vlans);
+ set_rx_mode(dev);
+ spin_unlock(&np->lock);
+
+ return 0;
+}
+
+static int netdev_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct netdev_private *np = netdev_priv(dev);
+
+ spin_lock(&np->lock);
+ if (debug > 1)
+ printk("%s: removing vlanid %d from vlan filter\n", dev->name, vid);
+ clear_bit(vid, np->active_vlans);
+ set_rx_mode(dev);
+ spin_unlock(&np->lock);
+
+ return 0;
+}
+#endif /* VLAN_SUPPORT */
+
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = netdev_open,
+ .ndo_stop = netdev_close,
+ .ndo_start_xmit = start_tx,
+ .ndo_tx_timeout = tx_timeout,
+ .ndo_get_stats = get_stats,
+ .ndo_set_rx_mode = set_rx_mode,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef VLAN_SUPPORT
+ .ndo_vlan_rx_add_vid = netdev_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = netdev_vlan_rx_kill_vid,
+#endif
+};
+
+static int starfire_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct device *d = &pdev->dev;
+ struct netdev_private *np;
+ int i, irq, chip_idx = ent->driver_data;
+ struct net_device *dev;
+ long ioaddr;
+ void __iomem *base;
+ int drv_flags, io_size;
+ int boguscnt;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
+ if (pci_enable_device (pdev))
+ return -EIO;
+
+ ioaddr = pci_resource_start(pdev, 0);
+ io_size = pci_resource_len(pdev, 0);
+ if (!ioaddr || ((pci_resource_flags(pdev, 0) & IORESOURCE_MEM) == 0)) {
+ dev_err(d, "no PCI MEM resources, aborting\n");
+ return -ENODEV;
+ }
+
+ dev = alloc_etherdev(sizeof(*np));
+ if (!dev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ irq = pdev->irq;
+
+ if (pci_request_regions (pdev, DRV_NAME)) {
+ dev_err(d, "cannot reserve PCI resources, aborting\n");
+ goto err_out_free_netdev;
+ }
+
+ base = ioremap(ioaddr, io_size);
+ if (!base) {
+ dev_err(d, "cannot remap %#x @ %#lx, aborting\n",
+ io_size, ioaddr);
+ goto err_out_free_res;
+ }
+
+ pci_set_master(pdev);
+
+ /* enable MWI -- it vastly improves Rx performance on sparc64 */
+ pci_try_set_mwi(pdev);
+
+#ifdef ZEROCOPY
+ /* Starfire can do TCP/UDP checksumming */
+ if (enable_hw_cksum)
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
+#endif /* ZEROCOPY */
+
+#ifdef VLAN_SUPPORT
+ dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
+#endif /* VLAN_RX_KILL_VID */
+#ifdef ADDR_64BITS
+ dev->features |= NETIF_F_HIGHDMA;
+#endif /* ADDR_64BITS */
+
+ /* Serial EEPROM reads are hidden by the hardware. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = readb(base + EEPROMCtrl + 20 - i);
+
+#if ! defined(final_version) /* Dump the EEPROM contents during development. */
+ if (debug > 4)
+ for (i = 0; i < 0x20; i++)
+ printk("%2.2x%s",
+ (unsigned int)readb(base + EEPROMCtrl + i),
+ i % 16 != 15 ? " " : "\n");
+#endif
+
+ /* Issue soft reset */
+ writel(MiiSoftReset, base + TxMode);
+ udelay(1000);
+ writel(0, base + TxMode);
+
+ /* Reset the chip to erase previous misconfiguration. */
+ writel(1, base + PCIDeviceConfig);
+ boguscnt = 1000;
+ while (--boguscnt > 0) {
+ udelay(10);
+ if ((readl(base + PCIDeviceConfig) & 1) == 0)
+ break;
+ }
+ if (boguscnt == 0)
+ printk("%s: chipset reset never completed!\n", dev->name);
+ /* wait a little longer */
+ udelay(1000);
+
+ np = netdev_priv(dev);
+ np->dev = dev;
+ np->base = base;
+ spin_lock_init(&np->lock);
+ pci_set_drvdata(pdev, dev);
+
+ np->pci_dev = pdev;
+
+ np->mii_if.dev = dev;
+ np->mii_if.mdio_read = mdio_read;
+ np->mii_if.mdio_write = mdio_write;
+ np->mii_if.phy_id_mask = 0x1f;
+ np->mii_if.reg_num_mask = 0x1f;
+
+ drv_flags = netdrv_tbl[chip_idx].drv_flags;
+
+ np->speed100 = 1;
+
+ /* timer resolution is 128 * 0.8us */
+ np->intr_timer_ctrl = (((intr_latency * 10) / 1024) & IntrLatencyMask) |
+ Timer10X | EnableIntrMasking;
+
+ if (small_frames > 0) {
+ np->intr_timer_ctrl |= SmallFrameBypass;
+ switch (small_frames) {
+ case 1 ... 64:
+ np->intr_timer_ctrl |= SmallFrame64;
+ break;
+ case 65 ... 128:
+ np->intr_timer_ctrl |= SmallFrame128;
+ break;
+ case 129 ... 256:
+ np->intr_timer_ctrl |= SmallFrame256;
+ break;
+ default:
+ np->intr_timer_ctrl |= SmallFrame512;
+ if (small_frames > 512)
+ printk("Adjusting small_frames down to 512\n");
+ break;
+ }
+ }
+
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->ethtool_ops = &ethtool_ops;
+
+ netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work);
+
+ if (mtu)
+ dev->mtu = mtu;
+
+ if (register_netdev(dev))
+ goto err_out_cleardev;
+
+ printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
+ dev->name, netdrv_tbl[chip_idx].name, base,
+ dev->dev_addr, irq);
+
+ if (drv_flags & CanHaveMII) {
+ int phy, phy_idx = 0;
+ int mii_status;
+ for (phy = 0; phy < 32 && phy_idx < PHY_CNT; phy++) {
+ mdio_write(dev, phy, MII_BMCR, BMCR_RESET);
+ mdelay(100);
+ boguscnt = 1000;
+ while (--boguscnt > 0)
+ if ((mdio_read(dev, phy, MII_BMCR) & BMCR_RESET) == 0)
+ break;
+ if (boguscnt == 0) {
+ printk("%s: PHY#%d reset never completed!\n", dev->name, phy);
+ continue;
+ }
+ mii_status = mdio_read(dev, phy, MII_BMSR);
+ if (mii_status != 0) {
+ np->phys[phy_idx++] = phy;
+ np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE);
+ printk(KERN_INFO "%s: MII PHY found at address %d, status "
+ "%#4.4x advertising %#4.4x.\n",
+ dev->name, phy, mii_status, np->mii_if.advertising);
+ /* there can be only one PHY on-board */
+ break;
+ }
+ }
+ np->phy_cnt = phy_idx;
+ if (np->phy_cnt > 0)
+ np->mii_if.phy_id = np->phys[0];
+ else
+ memset(&np->mii_if, 0, sizeof(np->mii_if));
+ }
+
+ printk(KERN_INFO "%s: scatter-gather and hardware TCP cksumming %s.\n",
+ dev->name, enable_hw_cksum ? "enabled" : "disabled");
+ return 0;
+
+err_out_cleardev:
+ iounmap(base);
+err_out_free_res:
+ pci_release_regions (pdev);
+err_out_free_netdev:
+ free_netdev(dev);
+ return -ENODEV;
+}
+
+
+/* Read the MII Management Data I/O (MDIO) interfaces. */
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *mdio_addr = np->base + MIICtrl + (phy_id<<7) + (location<<2);
+ int result, boguscnt=1000;
+ /* ??? Should we add a busy-wait here? */
+ do {
+ result = readl(mdio_addr);
+ } while ((result & 0xC0000000) != 0x80000000 && --boguscnt > 0);
+ if (boguscnt == 0)
+ return 0;
+ if ((result & 0xffff) == 0xffff)
+ return 0;
+ return result & 0xffff;
+}
+
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *mdio_addr = np->base + MIICtrl + (phy_id<<7) + (location<<2);
+ writel(value, mdio_addr);
+ /* The busy-wait will occur before a read. */
+}
+
+
+static int netdev_open(struct net_device *dev)
+{
+ const struct firmware *fw_rx, *fw_tx;
+ const __be32 *fw_rx_data, *fw_tx_data;
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ const int irq = np->pci_dev->irq;
+ int i, retval;
+ size_t tx_size, rx_size;
+ size_t tx_done_q_size, rx_done_q_size, tx_ring_size, rx_ring_size;
+
+ /* Do we ever need to reset the chip??? */
+
+ retval = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
+ if (retval)
+ return retval;
+
+ /* Disable the Rx and Tx, and reset the chip. */
+ writel(0, ioaddr + GenCtrl);
+ writel(1, ioaddr + PCIDeviceConfig);
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+ dev->name, irq);
+
+ /* Allocate the various queues. */
+ if (!np->queue_mem) {
+ tx_done_q_size = ((sizeof(struct tx_done_desc) * DONE_Q_SIZE + QUEUE_ALIGN - 1) / QUEUE_ALIGN) * QUEUE_ALIGN;
+ rx_done_q_size = ((sizeof(rx_done_desc) * DONE_Q_SIZE + QUEUE_ALIGN - 1) / QUEUE_ALIGN) * QUEUE_ALIGN;
+ tx_ring_size = ((sizeof(starfire_tx_desc) * TX_RING_SIZE + QUEUE_ALIGN - 1) / QUEUE_ALIGN) * QUEUE_ALIGN;
+ rx_ring_size = sizeof(struct starfire_rx_desc) * RX_RING_SIZE;
+ np->queue_mem_size = tx_done_q_size + rx_done_q_size + tx_ring_size + rx_ring_size;
+ np->queue_mem = pci_alloc_consistent(np->pci_dev, np->queue_mem_size, &np->queue_mem_dma);
+ if (np->queue_mem == NULL) {
+ free_irq(irq, dev);
+ return -ENOMEM;
+ }
+
+ np->tx_done_q = np->queue_mem;
+ np->tx_done_q_dma = np->queue_mem_dma;
+ np->rx_done_q = (void *) np->tx_done_q + tx_done_q_size;
+ np->rx_done_q_dma = np->tx_done_q_dma + tx_done_q_size;
+ np->tx_ring = (void *) np->rx_done_q + rx_done_q_size;
+ np->tx_ring_dma = np->rx_done_q_dma + rx_done_q_size;
+ np->rx_ring = (void *) np->tx_ring + tx_ring_size;
+ np->rx_ring_dma = np->tx_ring_dma + tx_ring_size;
+ }
+
+ /* Start with no carrier, it gets adjusted later */
+ netif_carrier_off(dev);
+ init_ring(dev);
+ /* Set the size of the Rx buffers. */
+ writel((np->rx_buf_sz << RxBufferLenShift) |
+ (0 << RxMinDescrThreshShift) |
+ RxPrefetchMode | RxVariableQ |
+ RX_Q_ENTRIES |
+ RX_DESC_Q_ADDR_SIZE | RX_DESC_ADDR_SIZE |
+ RxDescSpace4,
+ ioaddr + RxDescQCtrl);
+
+ /* Set up the Rx DMA controller. */
+ writel(RxChecksumIgnore |
+ (0 << RxEarlyIntThreshShift) |
+ (6 << RxHighPrioThreshShift) |
+ ((DMA_BURST_SIZE / 32) << RxBurstSizeShift),
+ ioaddr + RxDMACtrl);
+
+ /* Set Tx descriptor */
+ writel((2 << TxHiPriFIFOThreshShift) |
+ (0 << TxPadLenShift) |
+ ((DMA_BURST_SIZE / 32) << TxDMABurstSizeShift) |
+ TX_DESC_Q_ADDR_SIZE |
+ TX_DESC_SPACING | TX_DESC_TYPE,
+ ioaddr + TxDescCtrl);
+
+ writel( (np->queue_mem_dma >> 16) >> 16, ioaddr + RxDescQHiAddr);
+ writel( (np->queue_mem_dma >> 16) >> 16, ioaddr + TxRingHiAddr);
+ writel( (np->queue_mem_dma >> 16) >> 16, ioaddr + CompletionHiAddr);
+ writel(np->rx_ring_dma, ioaddr + RxDescQAddr);
+ writel(np->tx_ring_dma, ioaddr + TxRingPtr);
+
+ writel(np->tx_done_q_dma, ioaddr + TxCompletionAddr);
+ writel(np->rx_done_q_dma |
+ RxComplType |
+ (0 << RxComplThreshShift),
+ ioaddr + RxCompletionAddr);
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Filling in the station address.\n", dev->name);
+
+ /* Fill both the Tx SA register and the Rx perfect filter. */
+ for (i = 0; i < 6; i++)
+ writeb(dev->dev_addr[i], ioaddr + TxStationAddr + 5 - i);
+ /* The first entry is special because it bypasses the VLAN filter.
+ Don't use it. */
+ writew(0, ioaddr + PerfFilterTable);
+ writew(0, ioaddr + PerfFilterTable + 4);
+ writew(0, ioaddr + PerfFilterTable + 8);
+ for (i = 1; i < 16; i++) {
+ __be16 *eaddrs = (__be16 *)dev->dev_addr;
+ void __iomem *setup_frm = ioaddr + PerfFilterTable + i * 16;
+ writew(be16_to_cpu(eaddrs[2]), setup_frm); setup_frm += 4;
+ writew(be16_to_cpu(eaddrs[1]), setup_frm); setup_frm += 4;
+ writew(be16_to_cpu(eaddrs[0]), setup_frm); setup_frm += 8;
+ }
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ np->tx_mode = TxFlowEnable|RxFlowEnable|PadEnable; /* modified when link is up. */
+ writel(MiiSoftReset | np->tx_mode, ioaddr + TxMode);
+ udelay(1000);
+ writel(np->tx_mode, ioaddr + TxMode);
+ np->tx_threshold = 4;
+ writel(np->tx_threshold, ioaddr + TxThreshold);
+
+ writel(np->intr_timer_ctrl, ioaddr + IntrTimerCtrl);
+
+ napi_enable(&np->napi);
+
+ netif_start_queue(dev);
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Setting the Rx and Tx modes.\n", dev->name);
+ set_rx_mode(dev);
+
+ np->mii_if.advertising = mdio_read(dev, np->phys[0], MII_ADVERTISE);
+ check_duplex(dev);
+
+ /* Enable GPIO interrupts on link change */
+ writel(0x0f00ff00, ioaddr + GPIOCtrl);
+
+ /* Set the interrupt mask */
+ writel(IntrRxDone | IntrRxEmpty | IntrDMAErr |
+ IntrTxDMADone | IntrStatsMax | IntrLinkChange |
+ IntrRxGFPDead | IntrNoTxCsum | IntrTxBadID,
+ ioaddr + IntrEnable);
+ /* Enable PCI interrupts. */
+ writel(0x00800000 | readl(ioaddr + PCIDeviceConfig),
+ ioaddr + PCIDeviceConfig);
+
+#ifdef VLAN_SUPPORT
+ /* Set VLAN type to 802.1q */
+ writel(ETH_P_8021Q, ioaddr + VlanType);
+#endif /* VLAN_SUPPORT */
+
+ retval = request_firmware(&fw_rx, FIRMWARE_RX, &np->pci_dev->dev);
+ if (retval) {
+ printk(KERN_ERR "starfire: Failed to load firmware \"%s\"\n",
+ FIRMWARE_RX);
+ goto out_init;
+ }
+ if (fw_rx->size % 4) {
+ printk(KERN_ERR "starfire: bogus length %zu in \"%s\"\n",
+ fw_rx->size, FIRMWARE_RX);
+ retval = -EINVAL;
+ goto out_rx;
+ }
+ retval = request_firmware(&fw_tx, FIRMWARE_TX, &np->pci_dev->dev);
+ if (retval) {
+ printk(KERN_ERR "starfire: Failed to load firmware \"%s\"\n",
+ FIRMWARE_TX);
+ goto out_rx;
+ }
+ if (fw_tx->size % 4) {
+ printk(KERN_ERR "starfire: bogus length %zu in \"%s\"\n",
+ fw_tx->size, FIRMWARE_TX);
+ retval = -EINVAL;
+ goto out_tx;
+ }
+ fw_rx_data = (const __be32 *)&fw_rx->data[0];
+ fw_tx_data = (const __be32 *)&fw_tx->data[0];
+ rx_size = fw_rx->size / 4;
+ tx_size = fw_tx->size / 4;
+
+ /* Load Rx/Tx firmware into the frame processors */
+ for (i = 0; i < rx_size; i++)
+ writel(be32_to_cpup(&fw_rx_data[i]), ioaddr + RxGfpMem + i * 4);
+ for (i = 0; i < tx_size; i++)
+ writel(be32_to_cpup(&fw_tx_data[i]), ioaddr + TxGfpMem + i * 4);
+ if (enable_hw_cksum)
+ /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */
+ writel(TxEnable|TxGFPEnable|RxEnable|RxGFPEnable, ioaddr + GenCtrl);
+ else
+ /* Enable the Rx and Tx units only. */
+ writel(TxEnable|RxEnable, ioaddr + GenCtrl);
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Done netdev_open().\n",
+ dev->name);
+
+out_tx:
+ release_firmware(fw_tx);
+out_rx:
+ release_firmware(fw_rx);
+out_init:
+ if (retval)
+ netdev_close(dev);
+ return retval;
+}
+
+
+static void check_duplex(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ u16 reg0;
+ int silly_count = 1000;
+
+ mdio_write(dev, np->phys[0], MII_ADVERTISE, np->mii_if.advertising);
+ mdio_write(dev, np->phys[0], MII_BMCR, BMCR_RESET);
+ udelay(500);
+ while (--silly_count && mdio_read(dev, np->phys[0], MII_BMCR) & BMCR_RESET)
+ /* do nothing */;
+ if (!silly_count) {
+ printk("%s: MII reset failed!\n", dev->name);
+ return;
+ }
+
+ reg0 = mdio_read(dev, np->phys[0], MII_BMCR);
+
+ if (!np->mii_if.force_media) {
+ reg0 |= BMCR_ANENABLE | BMCR_ANRESTART;
+ } else {
+ reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART);
+ if (np->speed100)
+ reg0 |= BMCR_SPEED100;
+ if (np->mii_if.full_duplex)
+ reg0 |= BMCR_FULLDPLX;
+ printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n",
+ dev->name,
+ np->speed100 ? "100" : "10",
+ np->mii_if.full_duplex ? "full" : "half");
+ }
+ mdio_write(dev, np->phys[0], MII_BMCR, reg0);
+}
+
+
+static void tx_timeout(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ int old_debug;
+
+ printk(KERN_WARNING "%s: Transmit timed out, status %#8.8x, "
+ "resetting...\n", dev->name, (int) readl(ioaddr + IntrStatus));
+
+ /* Perhaps we should reinitialize the hardware here. */
+
+ /*
+ * Stop and restart the interface.
+ * Cheat and increase the debug level temporarily.
+ */
+ old_debug = debug;
+ debug = 2;
+ netdev_close(dev);
+ netdev_open(dev);
+ debug = old_debug;
+
+ /* Trigger an immediate transmit demand. */
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ dev->stats.tx_errors++;
+ netif_wake_queue(dev);
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ int i;
+
+ np->cur_rx = np->cur_tx = np->reap_tx = 0;
+ np->dirty_rx = np->dirty_tx = np->rx_done = np->tx_done = 0;
+
+ np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz);
+ np->rx_info[i].skb = skb;
+ if (skb == NULL)
+ break;
+ np->rx_info[i].mapping = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ /* Grrr, we cannot offset to correctly align the IP header. */
+ np->rx_ring[i].rxaddr = cpu_to_dma(np->rx_info[i].mapping | RxDescValid);
+ }
+ writew(i - 1, np->base + RxDescQIdx);
+ np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ /* Clear the remainder of the Rx buffer ring. */
+ for ( ; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].rxaddr = 0;
+ np->rx_info[i].skb = NULL;
+ np->rx_info[i].mapping = 0;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ np->rx_ring[RX_RING_SIZE - 1].rxaddr |= cpu_to_dma(RxDescEndRing);
+
+ /* Clear the completion rings. */
+ for (i = 0; i < DONE_Q_SIZE; i++) {
+ np->rx_done_q[i].status = 0;
+ np->tx_done_q[i].status = 0;
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ memset(&np->tx_info[i], 0, sizeof(np->tx_info[i]));
+}
+
+
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ unsigned int entry;
+ u32 status;
+ int i;
+
+ /*
+ * be cautious here, wrapping the queue has weird semantics
+ * and we may not have enough slots even when it seems we do.
+ */
+ if ((np->cur_tx - np->dirty_tx) + skb_num_frags(skb) * 2 > TX_RING_SIZE) {
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+
+#if defined(ZEROCOPY) && defined(HAS_BROKEN_FIRMWARE)
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (skb_padto(skb, (skb->len + PADDING_MASK) & ~PADDING_MASK))
+ return NETDEV_TX_OK;
+ }
+#endif /* ZEROCOPY && HAS_BROKEN_FIRMWARE */
+
+ entry = np->cur_tx % TX_RING_SIZE;
+ for (i = 0; i < skb_num_frags(skb); i++) {
+ int wrap_ring = 0;
+ status = TxDescID;
+
+ if (i == 0) {
+ np->tx_info[entry].skb = skb;
+ status |= TxCRCEn;
+ if (entry >= TX_RING_SIZE - skb_num_frags(skb)) {
+ status |= TxRingWrap;
+ wrap_ring = 1;
+ }
+ if (np->reap_tx) {
+ status |= TxDescIntr;
+ np->reap_tx = 0;
+ }
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ status |= TxCalTCP;
+ dev->stats.tx_compressed++;
+ }
+ status |= skb_first_frag_len(skb) | (skb_num_frags(skb) << 16);
+
+ np->tx_info[entry].mapping =
+ pci_map_single(np->pci_dev, skb->data, skb_first_frag_len(skb), PCI_DMA_TODEVICE);
+ } else {
+ const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[i - 1];
+ status |= skb_frag_size(this_frag);
+ np->tx_info[entry].mapping =
+ pci_map_single(np->pci_dev,
+ skb_frag_address(this_frag),
+ skb_frag_size(this_frag),
+ PCI_DMA_TODEVICE);
+ }
+
+ np->tx_ring[entry].addr = cpu_to_dma(np->tx_info[entry].mapping);
+ np->tx_ring[entry].status = cpu_to_le32(status);
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: Tx #%d/#%d slot %d status %#8.8x.\n",
+ dev->name, np->cur_tx, np->dirty_tx,
+ entry, status);
+ if (wrap_ring) {
+ np->tx_info[entry].used_slots = TX_RING_SIZE - entry;
+ np->cur_tx += np->tx_info[entry].used_slots;
+ entry = 0;
+ } else {
+ np->tx_info[entry].used_slots = 1;
+ np->cur_tx += np->tx_info[entry].used_slots;
+ entry++;
+ }
+ /* scavenge the tx descriptors twice per TX_RING_SIZE */
+ if (np->cur_tx % (TX_RING_SIZE / 2) == 0)
+ np->reap_tx = 1;
+ }
+
+ /* Non-x86: explicitly flush descriptor cache lines here. */
+ /* Ensure all descriptors are written back before the transmit is
+ initiated. - Jes */
+ wmb();
+
+ /* Update the producer index. */
+ writel(entry * (sizeof(starfire_tx_desc) / 8), np->base + TxProducerIdx);
+
+ /* 4 is arbitrary, but should be ok */
+ if ((np->cur_tx - np->dirty_tx) + 4 > TX_RING_SIZE)
+ netif_stop_queue(dev);
+
+ return NETDEV_TX_OK;
+}
+
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static irqreturn_t intr_handler(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ int boguscnt = max_interrupt_work;
+ int consumer;
+ int tx_status;
+ int handled = 0;
+
+ do {
+ u32 intr_status = readl(ioaddr + IntrClear);
+
+ if (debug > 4)
+ printk(KERN_DEBUG "%s: Interrupt status %#8.8x.\n",
+ dev->name, intr_status);
+
+ if (intr_status == 0 || intr_status == (u32) -1)
+ break;
+
+ handled = 1;
+
+ if (intr_status & (IntrRxDone | IntrRxEmpty)) {
+ u32 enable;
+
+ if (likely(napi_schedule_prep(&np->napi))) {
+ __napi_schedule(&np->napi);
+ enable = readl(ioaddr + IntrEnable);
+ enable &= ~(IntrRxDone | IntrRxEmpty);
+ writel(enable, ioaddr + IntrEnable);
+ /* flush PCI posting buffers */
+ readl(ioaddr + IntrEnable);
+ } else {
+ /* Paranoia check */
+ enable = readl(ioaddr + IntrEnable);
+ if (enable & (IntrRxDone | IntrRxEmpty)) {
+ printk(KERN_INFO
+ "%s: interrupt while in poll!\n",
+ dev->name);
+ enable &= ~(IntrRxDone | IntrRxEmpty);
+ writel(enable, ioaddr + IntrEnable);
+ }
+ }
+ }
+
+ /* Scavenge the skbuff list based on the Tx-done queue.
+ There are redundant checks here that may be cleaned up
+ after the driver has proven to be reliable. */
+ consumer = readl(ioaddr + TxConsumerIdx);
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: Tx Consumer index is %d.\n",
+ dev->name, consumer);
+
+ while ((tx_status = le32_to_cpu(np->tx_done_q[np->tx_done].status)) != 0) {
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: Tx completion #%d entry %d is %#8.8x.\n",
+ dev->name, np->dirty_tx, np->tx_done, tx_status);
+ if ((tx_status & 0xe0000000) == 0xa0000000) {
+ dev->stats.tx_packets++;
+ } else if ((tx_status & 0xe0000000) == 0x80000000) {
+ u16 entry = (tx_status & 0x7fff) / sizeof(starfire_tx_desc);
+ struct sk_buff *skb = np->tx_info[entry].skb;
+ np->tx_info[entry].skb = NULL;
+ pci_unmap_single(np->pci_dev,
+ np->tx_info[entry].mapping,
+ skb_first_frag_len(skb),
+ PCI_DMA_TODEVICE);
+ np->tx_info[entry].mapping = 0;
+ np->dirty_tx += np->tx_info[entry].used_slots;
+ entry = (entry + np->tx_info[entry].used_slots) % TX_RING_SIZE;
+ {
+ int i;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ pci_unmap_single(np->pci_dev,
+ np->tx_info[entry].mapping,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
+ PCI_DMA_TODEVICE);
+ np->dirty_tx++;
+ entry++;
+ }
+ }
+
+ dev_kfree_skb_irq(skb);
+ }
+ np->tx_done_q[np->tx_done].status = 0;
+ np->tx_done = (np->tx_done + 1) % DONE_Q_SIZE;
+ }
+ writew(np->tx_done, ioaddr + CompletionQConsumerIdx + 2);
+
+ if (netif_queue_stopped(dev) &&
+ (np->cur_tx - np->dirty_tx + 4 < TX_RING_SIZE)) {
+ /* The ring is no longer full, wake the queue. */
+ netif_wake_queue(dev);
+ }
+
+ /* Stats overflow */
+ if (intr_status & IntrStatsMax)
+ get_stats(dev);
+
+ /* Media change interrupt. */
+ if (intr_status & IntrLinkChange)
+ netdev_media_change(dev);
+
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status & IntrAbnormalSummary)
+ netdev_error(dev, intr_status);
+
+ if (--boguscnt < 0) {
+ if (debug > 1)
+ printk(KERN_WARNING "%s: Too much work at interrupt, "
+ "status=%#8.8x.\n",
+ dev->name, intr_status);
+ break;
+ }
+ } while (1);
+
+ if (debug > 4)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#8.8x.\n",
+ dev->name, (int) readl(ioaddr + IntrStatus));
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * This routine is logically part of the interrupt/poll handler, but separated
+ * for clarity and better register allocation.
+ */
+static int __netdev_rx(struct net_device *dev, int *quota)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ u32 desc_status;
+ int retcode = 0;
+
+ /* If EOP is set on the next entry, it's a new packet. Send it up. */
+ while ((desc_status = le32_to_cpu(np->rx_done_q[np->rx_done].status)) != 0) {
+ struct sk_buff *skb;
+ u16 pkt_len;
+ int entry;
+ rx_done_desc *desc = &np->rx_done_q[np->rx_done];
+
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() status of %d was %#8.8x.\n", np->rx_done, desc_status);
+ if (!(desc_status & RxOK)) {
+ /* There was an error. */
+ if (debug > 2)
+ printk(KERN_DEBUG " netdev_rx() Rx error was %#8.8x.\n", desc_status);
+ dev->stats.rx_errors++;
+ if (desc_status & RxFIFOErr)
+ dev->stats.rx_fifo_errors++;
+ goto next_rx;
+ }
+
+ if (*quota <= 0) { /* out of rx quota */
+ retcode = 1;
+ goto out;
+ }
+ (*quota)--;
+
+ pkt_len = desc_status; /* Implicitly Truncate */
+ entry = (desc_status >> 16) & 0x7ff;
+
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d, quota %d.\n", pkt_len, *quota);
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+ if (pkt_len < rx_copybreak &&
+ (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ pci_dma_sync_single_for_cpu(np->pci_dev,
+ np->rx_info[entry].mapping,
+ pkt_len, PCI_DMA_FROMDEVICE);
+ skb_copy_to_linear_data(skb, np->rx_info[entry].skb->data, pkt_len);
+ pci_dma_sync_single_for_device(np->pci_dev,
+ np->rx_info[entry].mapping,
+ pkt_len, PCI_DMA_FROMDEVICE);
+ skb_put(skb, pkt_len);
+ } else {
+ pci_unmap_single(np->pci_dev, np->rx_info[entry].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ skb = np->rx_info[entry].skb;
+ skb_put(skb, pkt_len);
+ np->rx_info[entry].skb = NULL;
+ np->rx_info[entry].mapping = 0;
+ }
+#ifndef final_version /* Remove after testing. */
+ /* You will want this info for the initial debug. */
+ if (debug > 5) {
+ printk(KERN_DEBUG " Rx data %pM %pM %2.2x%2.2x.\n",
+ skb->data, skb->data + 6,
+ skb->data[12], skb->data[13]);
+ }
+#endif
+
+ skb->protocol = eth_type_trans(skb, dev);
+#ifdef VLAN_SUPPORT
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() status2 of %d was %#4.4x.\n", np->rx_done, le16_to_cpu(desc->status2));
+#endif
+ if (le16_to_cpu(desc->status2) & 0x0100) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ dev->stats.rx_compressed++;
+ }
+ /*
+ * This feature doesn't seem to be working, at least
+ * with the two firmware versions I have. If the GFP sees
+ * an IP fragment, it either ignores it completely, or reports
+ * "bad checksum" on it.
+ *
+ * Maybe I missed something -- corrections are welcome.
+ * Until then, the printk stays. :-) -Ion
+ */
+ else if (le16_to_cpu(desc->status2) & 0x0040) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = le16_to_cpu(desc->csum);
+ printk(KERN_DEBUG "%s: checksum_hw, status2 = %#x\n", dev->name, le16_to_cpu(desc->status2));
+ }
+#ifdef VLAN_SUPPORT
+ if (le16_to_cpu(desc->status2) & 0x0200) {
+ u16 vlid = le16_to_cpu(desc->vlanid);
+
+ if (debug > 4) {
+ printk(KERN_DEBUG " netdev_rx() vlanid = %d\n",
+ vlid);
+ }
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlid);
+ }
+#endif /* VLAN_SUPPORT */
+ netif_receive_skb(skb);
+ dev->stats.rx_packets++;
+
+ next_rx:
+ np->cur_rx++;
+ desc->status = 0;
+ np->rx_done = (np->rx_done + 1) % DONE_Q_SIZE;
+ }
+
+ if (*quota == 0) { /* out of rx quota */
+ retcode = 1;
+ goto out;
+ }
+ writew(np->rx_done, np->base + CompletionQConsumerIdx);
+
+ out:
+ refill_rx_ring(dev);
+ if (debug > 5)
+ printk(KERN_DEBUG " exiting netdev_rx(): %d, status of %d was %#8.8x.\n",
+ retcode, np->rx_done, desc_status);
+ return retcode;
+}
+
+static int netdev_poll(struct napi_struct *napi, int budget)
+{
+ struct netdev_private *np = container_of(napi, struct netdev_private, napi);
+ struct net_device *dev = np->dev;
+ u32 intr_status;
+ void __iomem *ioaddr = np->base;
+ int quota = budget;
+
+ do {
+ writel(IntrRxDone | IntrRxEmpty, ioaddr + IntrClear);
+
+ if (__netdev_rx(dev, &quota))
+ goto out;
+
+ intr_status = readl(ioaddr + IntrStatus);
+ } while (intr_status & (IntrRxDone | IntrRxEmpty));
+
+ napi_complete(napi);
+ intr_status = readl(ioaddr + IntrEnable);
+ intr_status |= IntrRxDone | IntrRxEmpty;
+ writel(intr_status, ioaddr + IntrEnable);
+
+ out:
+ if (debug > 5)
+ printk(KERN_DEBUG " exiting netdev_poll(): %d.\n",
+ budget - quota);
+
+ /* Restart Rx engine if stopped. */
+ return budget - quota;
+}
+
+static void refill_rx_ring(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ struct sk_buff *skb;
+ int entry = -1;
+
+ /* Refill the Rx ring buffers. */
+ for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+ entry = np->dirty_rx % RX_RING_SIZE;
+ if (np->rx_info[entry].skb == NULL) {
+ skb = netdev_alloc_skb(dev, np->rx_buf_sz);
+ np->rx_info[entry].skb = skb;
+ if (skb == NULL)
+ break; /* Better luck next round. */
+ np->rx_info[entry].mapping =
+ pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ np->rx_ring[entry].rxaddr =
+ cpu_to_dma(np->rx_info[entry].mapping | RxDescValid);
+ }
+ if (entry == RX_RING_SIZE - 1)
+ np->rx_ring[entry].rxaddr |= cpu_to_dma(RxDescEndRing);
+ }
+ if (entry >= 0)
+ writew(entry, np->base + RxDescQIdx);
+}
+
+
+static void netdev_media_change(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ u16 reg0, reg1, reg4, reg5;
+ u32 new_tx_mode;
+ u32 new_intr_timer_ctrl;
+
+ /* reset status first */
+ mdio_read(dev, np->phys[0], MII_BMCR);
+ mdio_read(dev, np->phys[0], MII_BMSR);
+
+ reg0 = mdio_read(dev, np->phys[0], MII_BMCR);
+ reg1 = mdio_read(dev, np->phys[0], MII_BMSR);
+
+ if (reg1 & BMSR_LSTATUS) {
+ /* link is up */
+ if (reg0 & BMCR_ANENABLE) {
+ /* autonegotiation is enabled */
+ reg4 = mdio_read(dev, np->phys[0], MII_ADVERTISE);
+ reg5 = mdio_read(dev, np->phys[0], MII_LPA);
+ if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) {
+ np->speed100 = 1;
+ np->mii_if.full_duplex = 1;
+ } else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) {
+ np->speed100 = 1;
+ np->mii_if.full_duplex = 0;
+ } else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) {
+ np->speed100 = 0;
+ np->mii_if.full_duplex = 1;
+ } else {
+ np->speed100 = 0;
+ np->mii_if.full_duplex = 0;
+ }
+ } else {
+ /* autonegotiation is disabled */
+ if (reg0 & BMCR_SPEED100)
+ np->speed100 = 1;
+ else
+ np->speed100 = 0;
+ if (reg0 & BMCR_FULLDPLX)
+ np->mii_if.full_duplex = 1;
+ else
+ np->mii_if.full_duplex = 0;
+ }
+ netif_carrier_on(dev);
+ printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n",
+ dev->name,
+ np->speed100 ? "100" : "10",
+ np->mii_if.full_duplex ? "full" : "half");
+
+ new_tx_mode = np->tx_mode & ~FullDuplex; /* duplex setting */
+ if (np->mii_if.full_duplex)
+ new_tx_mode |= FullDuplex;
+ if (np->tx_mode != new_tx_mode) {
+ np->tx_mode = new_tx_mode;
+ writel(np->tx_mode | MiiSoftReset, ioaddr + TxMode);
+ udelay(1000);
+ writel(np->tx_mode, ioaddr + TxMode);
+ }
+
+ new_intr_timer_ctrl = np->intr_timer_ctrl & ~Timer10X;
+ if (np->speed100)
+ new_intr_timer_ctrl |= Timer10X;
+ if (np->intr_timer_ctrl != new_intr_timer_ctrl) {
+ np->intr_timer_ctrl = new_intr_timer_ctrl;
+ writel(new_intr_timer_ctrl, ioaddr + IntrTimerCtrl);
+ }
+ } else {
+ netif_carrier_off(dev);
+ printk(KERN_DEBUG "%s: Link is down\n", dev->name);
+ }
+}
+
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+ struct netdev_private *np = netdev_priv(dev);
+
+ /* Came close to underrunning the Tx FIFO, increase threshold. */
+ if (intr_status & IntrTxDataLow) {
+ if (np->tx_threshold <= PKT_BUF_SZ / 16) {
+ writel(++np->tx_threshold, np->base + TxThreshold);
+ printk(KERN_NOTICE "%s: PCI bus congestion, increasing Tx FIFO threshold to %d bytes\n",
+ dev->name, np->tx_threshold * 16);
+ } else
+ printk(KERN_WARNING "%s: PCI Tx underflow -- adapter is probably malfunctioning\n", dev->name);
+ }
+ if (intr_status & IntrRxGFPDead) {
+ dev->stats.rx_fifo_errors++;
+ dev->stats.rx_errors++;
+ }
+ if (intr_status & (IntrNoTxCsum | IntrDMAErr)) {
+ dev->stats.tx_fifo_errors++;
+ dev->stats.tx_errors++;
+ }
+ if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrRxGFPDead | IntrNoTxCsum | IntrPCIPad)) && debug)
+ printk(KERN_ERR "%s: Something Wicked happened! %#8.8x.\n",
+ dev->name, intr_status);
+}
+
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+
+ /* This adapter architecture needs no SMP locks. */
+ dev->stats.tx_bytes = readl(ioaddr + 0x57010);
+ dev->stats.rx_bytes = readl(ioaddr + 0x57044);
+ dev->stats.tx_packets = readl(ioaddr + 0x57000);
+ dev->stats.tx_aborted_errors =
+ readl(ioaddr + 0x57024) + readl(ioaddr + 0x57028);
+ dev->stats.tx_window_errors = readl(ioaddr + 0x57018);
+ dev->stats.collisions =
+ readl(ioaddr + 0x57004) + readl(ioaddr + 0x57008);
+
+ /* The chip only need report frame silently dropped. */
+ dev->stats.rx_dropped += readw(ioaddr + RxDMAStatus);
+ writew(0, ioaddr + RxDMAStatus);
+ dev->stats.rx_crc_errors = readl(ioaddr + 0x5703C);
+ dev->stats.rx_frame_errors = readl(ioaddr + 0x57040);
+ dev->stats.rx_length_errors = readl(ioaddr + 0x57058);
+ dev->stats.rx_missed_errors = readl(ioaddr + 0x5707C);
+
+ return &dev->stats;
+}
+
+#ifdef VLAN_SUPPORT
+static u32 set_vlan_mode(struct netdev_private *np)
+{
+ u32 ret = VlanMode;
+ u16 vid;
+ void __iomem *filter_addr = np->base + HashTable + 8;
+ int vlan_count = 0;
+
+ for_each_set_bit(vid, np->active_vlans, VLAN_N_VID) {
+ if (vlan_count == 32)
+ break;
+ writew(vid, filter_addr);
+ filter_addr += 16;
+ vlan_count++;
+ }
+ if (vlan_count == 32) {
+ ret |= PerfectFilterVlan;
+ while (vlan_count < 32) {
+ writew(0, filter_addr);
+ filter_addr += 16;
+ vlan_count++;
+ }
+ }
+ return ret;
+}
+#endif /* VLAN_SUPPORT */
+
+static void set_rx_mode(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ u32 rx_mode = MinVLANPrio;
+ struct netdev_hw_addr *ha;
+ int i;
+
+#ifdef VLAN_SUPPORT
+ rx_mode |= set_vlan_mode(np);
+#endif /* VLAN_SUPPORT */
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ rx_mode |= AcceptAll;
+ } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to match, or accept all multicasts. */
+ rx_mode |= AcceptBroadcast|AcceptAllMulticast|PerfectFilter;
+ } else if (netdev_mc_count(dev) <= 14) {
+ /* Use the 16 element perfect filter, skip first two entries. */
+ void __iomem *filter_addr = ioaddr + PerfFilterTable + 2 * 16;
+ __be16 *eaddrs;
+ netdev_for_each_mc_addr(ha, dev) {
+ eaddrs = (__be16 *) ha->addr;
+ writew(be16_to_cpu(eaddrs[2]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[1]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[0]), filter_addr); filter_addr += 8;
+ }
+ eaddrs = (__be16 *)dev->dev_addr;
+ i = netdev_mc_count(dev) + 2;
+ while (i++ < 16) {
+ writew(be16_to_cpu(eaddrs[0]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[1]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[2]), filter_addr); filter_addr += 8;
+ }
+ rx_mode |= AcceptBroadcast|PerfectFilter;
+ } else {
+ /* Must use a multicast hash table. */
+ void __iomem *filter_addr;
+ __be16 *eaddrs;
+ __le16 mc_filter[32] __attribute__ ((aligned(sizeof(long)))); /* Multicast hash filter */
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ netdev_for_each_mc_addr(ha, dev) {
+ /* The chip uses the upper 9 CRC bits
+ as index into the hash table */
+ int bit_nr = ether_crc_le(ETH_ALEN, ha->addr) >> 23;
+ __le32 *fptr = (__le32 *) &mc_filter[(bit_nr >> 4) & ~1];
+
+ *fptr |= cpu_to_le32(1 << (bit_nr & 31));
+ }
+ /* Clear the perfect filter list, skip first two entries. */
+ filter_addr = ioaddr + PerfFilterTable + 2 * 16;
+ eaddrs = (__be16 *)dev->dev_addr;
+ for (i = 2; i < 16; i++) {
+ writew(be16_to_cpu(eaddrs[0]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[1]), filter_addr); filter_addr += 4;
+ writew(be16_to_cpu(eaddrs[2]), filter_addr); filter_addr += 8;
+ }
+ for (filter_addr = ioaddr + HashTable, i = 0; i < 32; filter_addr+= 16, i++)
+ writew(mc_filter[i], filter_addr);
+ rx_mode |= AcceptBroadcast|PerfectFilter|HashFilter;
+ }
+ writel(rx_mode, ioaddr + RxFilterMode);
+}
+
+static int check_if_running(struct net_device *dev)
+{
+ if (!netif_running(dev))
+ return -EINVAL;
+ return 0;
+}
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
+}
+
+static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ spin_lock_irq(&np->lock);
+ mii_ethtool_gset(&np->mii_if, ecmd);
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ int res;
+ spin_lock_irq(&np->lock);
+ res = mii_ethtool_sset(&np->mii_if, ecmd);
+ spin_unlock_irq(&np->lock);
+ check_duplex(dev);
+ return res;
+}
+
+static int nway_reset(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii_if);
+}
+
+static u32 get_link(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii_if);
+}
+
+static u32 get_msglevel(struct net_device *dev)
+{
+ return debug;
+}
+
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+ debug = val;
+}
+
+static const struct ethtool_ops ethtool_ops = {
+ .begin = check_if_running,
+ .get_drvinfo = get_drvinfo,
+ .get_settings = get_settings,
+ .set_settings = set_settings,
+ .nway_reset = nway_reset,
+ .get_link = get_link,
+ .get_msglevel = get_msglevel,
+ .set_msglevel = set_msglevel,
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ int rc;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ if ((cmd == SIOCSMIIREG) && (data->phy_id == np->phys[0]))
+ check_duplex(dev);
+
+ return rc;
+}
+
+static int netdev_close(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->base;
+ int i;
+
+ netif_stop_queue(dev);
+
+ napi_disable(&np->napi);
+
+ if (debug > 1) {
+ printk(KERN_DEBUG "%s: Shutting down ethercard, Intr status %#8.8x.\n",
+ dev->name, (int) readl(ioaddr + IntrStatus));
+ printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n",
+ dev->name, np->cur_tx, np->dirty_tx,
+ np->cur_rx, np->dirty_rx);
+ }
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ writel(0, ioaddr + IntrEnable);
+
+ /* Stop the chip's Tx and Rx processes. */
+ writel(0, ioaddr + GenCtrl);
+ readl(ioaddr + GenCtrl);
+
+ if (debug > 5) {
+ printk(KERN_DEBUG" Tx ring at %#llx:\n",
+ (long long) np->tx_ring_dma);
+ for (i = 0; i < 8 /* TX_RING_SIZE is huge! */; i++)
+ printk(KERN_DEBUG " #%d desc. %#8.8x %#llx -> %#8.8x.\n",
+ i, le32_to_cpu(np->tx_ring[i].status),
+ (long long) dma_to_cpu(np->tx_ring[i].addr),
+ le32_to_cpu(np->tx_done_q[i].status));
+ printk(KERN_DEBUG " Rx ring at %#llx -> %p:\n",
+ (long long) np->rx_ring_dma, np->rx_done_q);
+ if (np->rx_done_q)
+ for (i = 0; i < 8 /* RX_RING_SIZE */; i++) {
+ printk(KERN_DEBUG " #%d desc. %#llx -> %#8.8x\n",
+ i, (long long) dma_to_cpu(np->rx_ring[i].rxaddr), le32_to_cpu(np->rx_done_q[i].status));
+ }
+ }
+
+ free_irq(np->pci_dev->irq, dev);
+
+ /* Free all the skbuffs in the Rx queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].rxaddr = cpu_to_dma(0xBADF00D0); /* An invalid address. */
+ if (np->rx_info[i].skb != NULL) {
+ pci_unmap_single(np->pci_dev, np->rx_info[i].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(np->rx_info[i].skb);
+ }
+ np->rx_info[i].skb = NULL;
+ np->rx_info[i].mapping = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ struct sk_buff *skb = np->tx_info[i].skb;
+ if (skb == NULL)
+ continue;
+ pci_unmap_single(np->pci_dev,
+ np->tx_info[i].mapping,
+ skb_first_frag_len(skb), PCI_DMA_TODEVICE);
+ np->tx_info[i].mapping = 0;
+ dev_kfree_skb(skb);
+ np->tx_info[i].skb = NULL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int starfire_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ netif_device_detach(dev);
+ netdev_close(dev);
+ }
+
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev,state));
+
+ return 0;
+}
+
+static int starfire_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ if (netif_running(dev)) {
+ netdev_open(dev);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+
+static void starfire_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct netdev_private *np = netdev_priv(dev);
+
+ BUG_ON(!dev);
+
+ unregister_netdev(dev);
+
+ if (np->queue_mem)
+ pci_free_consistent(pdev, np->queue_mem_size, np->queue_mem, np->queue_mem_dma);
+
+
+ /* XXX: add wakeup code -- requires firmware for MagicPacket */
+ pci_set_power_state(pdev, PCI_D3hot); /* go to sleep in D3 mode */
+ pci_disable_device(pdev);
+
+ iounmap(np->base);
+ pci_release_regions(pdev);
+
+ free_netdev(dev); /* Will also free np!! */
+}
+
+
+static struct pci_driver starfire_driver = {
+ .name = DRV_NAME,
+ .probe = starfire_init_one,
+ .remove = starfire_remove_one,
+#ifdef CONFIG_PM
+ .suspend = starfire_suspend,
+ .resume = starfire_resume,
+#endif /* CONFIG_PM */
+ .id_table = starfire_pci_tbl,
+};
+
+
+static int __init starfire_init (void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+
+ printk(KERN_INFO DRV_NAME ": polling (NAPI) enabled\n");
+#endif
+
+ BUILD_BUG_ON(sizeof(dma_addr_t) != sizeof(netdrv_addr_t));
+
+ return pci_register_driver(&starfire_driver);
+}
+
+
+static void __exit starfire_cleanup (void)
+{
+ pci_unregister_driver (&starfire_driver);
+}
+
+
+module_init(starfire_init);
+module_exit(starfire_cleanup);
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig
new file mode 100644
index 00000000000..f952fff6a9a
--- /dev/null
+++ b/drivers/net/ethernet/adi/Kconfig
@@ -0,0 +1,69 @@
+#
+# Blackfin device configuration
+#
+
+config NET_BFIN
+ bool "Blackfin devices"
+ depends on BF516 || BF518 || BF526 || BF527 || BF536 || BF537
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y.
+ Make sure you know the name of your card. Read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ If unsure, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the remaining Blackfin card questions. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_BFIN
+
+config BFIN_MAC
+ tristate "Blackfin on-chip MAC support"
+ depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
+ select CRC32
+ select MII
+ select PHYLIB
+ select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
+ ---help---
+ This is the driver for Blackfin on-chip mac device. Say Y if you want
+ it compiled into the kernel. This driver is also available as a
+ module ( = code which can be inserted in and removed from the running
+ kernel whenever you want). The module will be called bfin_mac.
+
+config BFIN_MAC_USE_L1
+ bool "Use L1 memory for rx/tx packets"
+ depends on BFIN_MAC && (BF527 || BF537)
+ default y
+ ---help---
+ To get maximum network performance, you should use L1 memory as rx/tx
+ buffers. Say N here if you want to reserve L1 memory for other uses.
+
+config BFIN_TX_DESC_NUM
+ int "Number of transmit buffer packets"
+ depends on BFIN_MAC
+ range 6 10 if BFIN_MAC_USE_L1
+ range 10 100
+ default "10"
+ ---help---
+ Set the number of buffer packets used in driver.
+
+config BFIN_RX_DESC_NUM
+ int "Number of receive buffer packets"
+ depends on BFIN_MAC
+ range 20 100 if BFIN_MAC_USE_L1
+ range 20 800
+ default "20"
+ ---help---
+ Set the number of buffer packets used in driver.
+
+config BFIN_MAC_USE_HWSTAMP
+ bool "Use IEEE 1588 hwstamp"
+ depends on BFIN_MAC && BF518
+ select PTP_1588_CLOCK
+ default y
+ ---help---
+ To support the IEEE 1588 Precision Time Protocol (PTP), select y here
+
+endif # NET_BFIN
diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile
new file mode 100644
index 00000000000..b1fbe195d0e
--- /dev/null
+++ b/drivers/net/ethernet/adi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Blackfin device drivers.
+#
+
+obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
new file mode 100644
index 00000000000..7ae74d450e8
--- /dev/null
+++ b/drivers/net/ethernet/adi/bfin_mac.c
@@ -0,0 +1,1921 @@
+/*
+ * Blackfin On-Chip MAC Driver
+ *
+ * Copyright 2004-2010 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#define DRV_VERSION "1.1"
+#define DRV_DESC "Blackfin on-chip Ethernet MAC driver"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/div64.h>
+#include <asm/dpmc.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/portmux.h>
+#include <mach/pll.h>
+
+#include "bfin_mac.h"
+
+MODULE_AUTHOR("Bryan Wu, Luke Yang");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_ALIAS("platform:bfin_mac");
+
+#if defined(CONFIG_BFIN_MAC_USE_L1)
+# define bfin_mac_alloc(dma_handle, size, num) l1_data_sram_zalloc(size*num)
+# define bfin_mac_free(dma_handle, ptr, num) l1_data_sram_free(ptr)
+#else
+# define bfin_mac_alloc(dma_handle, size, num) \
+ dma_alloc_coherent(NULL, size*num, dma_handle, GFP_KERNEL)
+# define bfin_mac_free(dma_handle, ptr, num) \
+ dma_free_coherent(NULL, sizeof(*ptr)*num, ptr, dma_handle)
+#endif
+
+#define PKT_BUF_SZ 1580
+
+#define MAX_TIMEOUT_CNT 500
+
+/* pointers to maintain transmit list */
+static struct net_dma_desc_tx *tx_list_head;
+static struct net_dma_desc_tx *tx_list_tail;
+static struct net_dma_desc_rx *rx_list_head;
+static struct net_dma_desc_rx *rx_list_tail;
+static struct net_dma_desc_rx *current_rx_ptr;
+static struct net_dma_desc_tx *current_tx_ptr;
+static struct net_dma_desc_tx *tx_desc;
+static struct net_dma_desc_rx *rx_desc;
+
+static void desc_list_free(void)
+{
+ struct net_dma_desc_rx *r;
+ struct net_dma_desc_tx *t;
+ int i;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+ dma_addr_t dma_handle = 0;
+#endif
+
+ if (tx_desc) {
+ t = tx_list_head;
+ for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+ if (t) {
+ if (t->skb) {
+ dev_kfree_skb(t->skb);
+ t->skb = NULL;
+ }
+ t = t->next;
+ }
+ }
+ bfin_mac_free(dma_handle, tx_desc, CONFIG_BFIN_TX_DESC_NUM);
+ }
+
+ if (rx_desc) {
+ r = rx_list_head;
+ for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+ if (r) {
+ if (r->skb) {
+ dev_kfree_skb(r->skb);
+ r->skb = NULL;
+ }
+ r = r->next;
+ }
+ }
+ bfin_mac_free(dma_handle, rx_desc, CONFIG_BFIN_RX_DESC_NUM);
+ }
+}
+
+static int desc_list_init(struct net_device *dev)
+{
+ int i;
+ struct sk_buff *new_skb;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+ /*
+ * This dma_handle is useless in Blackfin dma_alloc_coherent().
+ * The real dma handler is the return value of dma_alloc_coherent().
+ */
+ dma_addr_t dma_handle;
+#endif
+
+ tx_desc = bfin_mac_alloc(&dma_handle,
+ sizeof(struct net_dma_desc_tx),
+ CONFIG_BFIN_TX_DESC_NUM);
+ if (tx_desc == NULL)
+ goto init_error;
+
+ rx_desc = bfin_mac_alloc(&dma_handle,
+ sizeof(struct net_dma_desc_rx),
+ CONFIG_BFIN_RX_DESC_NUM);
+ if (rx_desc == NULL)
+ goto init_error;
+
+ /* init tx_list */
+ tx_list_head = tx_list_tail = tx_desc;
+
+ for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+ struct net_dma_desc_tx *t = tx_desc + i;
+ struct dma_descriptor *a = &(t->desc_a);
+ struct dma_descriptor *b = &(t->desc_b);
+
+ /*
+ * disable DMA
+ * read from memory WNR = 0
+ * wordsize is 32 bits
+ * 6 half words is desc size
+ * large desc flow
+ */
+ a->config = WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
+ a->start_addr = (unsigned long)t->packet;
+ a->x_count = 0;
+ a->next_dma_desc = b;
+
+ /*
+ * enabled DMA
+ * write to memory WNR = 1
+ * wordsize is 32 bits
+ * disable interrupt
+ * 6 half words is desc size
+ * large desc flow
+ */
+ b->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
+ b->start_addr = (unsigned long)(&(t->status));
+ b->x_count = 0;
+
+ t->skb = NULL;
+ tx_list_tail->desc_b.next_dma_desc = a;
+ tx_list_tail->next = t;
+ tx_list_tail = t;
+ }
+ tx_list_tail->next = tx_list_head; /* tx_list is a circle */
+ tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
+ current_tx_ptr = tx_list_head;
+
+ /* init rx_list */
+ rx_list_head = rx_list_tail = rx_desc;
+
+ for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+ struct net_dma_desc_rx *r = rx_desc + i;
+ struct dma_descriptor *a = &(r->desc_a);
+ struct dma_descriptor *b = &(r->desc_b);
+
+ /* allocate a new skb for next time receive */
+ new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
+ if (!new_skb)
+ goto init_error;
+
+ skb_reserve(new_skb, NET_IP_ALIGN);
+ /* Invidate the data cache of skb->data range when it is write back
+ * cache. It will prevent overwritting the new data from DMA
+ */
+ blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
+ (unsigned long)new_skb->end);
+ r->skb = new_skb;
+
+ /*
+ * enabled DMA
+ * write to memory WNR = 1
+ * wordsize is 32 bits
+ * disable interrupt
+ * 6 half words is desc size
+ * large desc flow
+ */
+ a->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
+ /* since RXDWA is enabled */
+ a->start_addr = (unsigned long)new_skb->data - 2;
+ a->x_count = 0;
+ a->next_dma_desc = b;
+
+ /*
+ * enabled DMA
+ * write to memory WNR = 1
+ * wordsize is 32 bits
+ * enable interrupt
+ * 6 half words is desc size
+ * large desc flow
+ */
+ b->config = DMAEN | WNR | WDSIZE_32 | DI_EN |
+ NDSIZE_6 | DMAFLOW_LARGE;
+ b->start_addr = (unsigned long)(&(r->status));
+ b->x_count = 0;
+
+ rx_list_tail->desc_b.next_dma_desc = a;
+ rx_list_tail->next = r;
+ rx_list_tail = r;
+ }
+ rx_list_tail->next = rx_list_head; /* rx_list is a circle */
+ rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
+ current_rx_ptr = rx_list_head;
+
+ return 0;
+
+init_error:
+ desc_list_free();
+ pr_err("kmalloc failed\n");
+ return -ENOMEM;
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+/*
+ * MII operations
+ */
+/* Wait until the previous MDC/MDIO transaction has completed */
+static int bfin_mdio_poll(void)
+{
+ int timeout_cnt = MAX_TIMEOUT_CNT;
+
+ /* poll the STABUSY bit */
+ while ((bfin_read_EMAC_STAADD()) & STABUSY) {
+ udelay(1);
+ if (timeout_cnt-- < 0) {
+ pr_err("wait MDC/MDIO transaction to complete timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/* Read an off-chip register in a PHY through the MDC/MDIO port */
+static int bfin_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ int ret;
+
+ ret = bfin_mdio_poll();
+ if (ret)
+ return ret;
+
+ /* read mode */
+ bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |
+ SET_REGAD((u16) regnum) |
+ STABUSY);
+
+ ret = bfin_mdio_poll();
+ if (ret)
+ return ret;
+
+ return (int) bfin_read_EMAC_STADAT();
+}
+
+/* Write an off-chip register in a PHY through the MDC/MDIO port */
+static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 value)
+{
+ int ret;
+
+ ret = bfin_mdio_poll();
+ if (ret)
+ return ret;
+
+ bfin_write_EMAC_STADAT((u32) value);
+
+ /* write mode */
+ bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |
+ SET_REGAD((u16) regnum) |
+ STAOP |
+ STABUSY);
+
+ return bfin_mdio_poll();
+}
+
+static void bfin_mac_adjust_link(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ struct phy_device *phydev = lp->phydev;
+ unsigned long flags;
+ int new_state = 0;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ if (phydev->link) {
+ /* Now we make sure that we can be in full duplex mode.
+ * If not, we operate in half-duplex mode. */
+ if (phydev->duplex != lp->old_duplex) {
+ u32 opmode = bfin_read_EMAC_OPMODE();
+ new_state = 1;
+
+ if (phydev->duplex)
+ opmode |= FDMODE;
+ else
+ opmode &= ~(FDMODE);
+
+ bfin_write_EMAC_OPMODE(opmode);
+ lp->old_duplex = phydev->duplex;
+ }
+
+ if (phydev->speed != lp->old_speed) {
+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
+ u32 opmode = bfin_read_EMAC_OPMODE();
+ switch (phydev->speed) {
+ case 10:
+ opmode |= RMII_10;
+ break;
+ case 100:
+ opmode &= ~RMII_10;
+ break;
+ default:
+ netdev_warn(dev,
+ "Ack! Speed (%d) is not 10/100!\n",
+ phydev->speed);
+ break;
+ }
+ bfin_write_EMAC_OPMODE(opmode);
+ }
+
+ new_state = 1;
+ lp->old_speed = phydev->speed;
+ }
+
+ if (!lp->old_link) {
+ new_state = 1;
+ lp->old_link = 1;
+ }
+ } else if (lp->old_link) {
+ new_state = 1;
+ lp->old_link = 0;
+ lp->old_speed = 0;
+ lp->old_duplex = -1;
+ }
+
+ if (new_state) {
+ u32 opmode = bfin_read_EMAC_OPMODE();
+ phy_print_status(phydev);
+ pr_debug("EMAC_OPMODE = 0x%08x\n", opmode);
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/* MDC = 2.5 MHz */
+#define MDC_CLK 2500000
+
+static int mii_probe(struct net_device *dev, int phy_mode)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ unsigned short sysctl;
+ int i;
+ u32 sclk, mdc_div;
+
+ /* Enable PHY output early */
+ if (!(bfin_read_VR_CTL() & CLKBUFOE))
+ bfin_write_VR_CTL(bfin_read_VR_CTL() | CLKBUFOE);
+
+ sclk = get_sclk();
+ mdc_div = ((sclk / MDC_CLK) / 2) - 1;
+
+ sysctl = bfin_read_EMAC_SYSCTL();
+ sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
+ bfin_write_EMAC_SYSCTL(sysctl);
+
+ /* search for connected PHY device */
+ for (i = 0; i < PHY_MAX_ADDR; ++i) {
+ struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i];
+
+ if (!tmp_phydev)
+ continue; /* no PHY here... */
+
+ phydev = tmp_phydev;
+ break; /* found it */
+ }
+
+ /* now we are supposed to have a proper phydev, to attach to... */
+ if (!phydev) {
+ netdev_err(dev, "no phy device found\n");
+ return -ENODEV;
+ }
+
+ if (phy_mode != PHY_INTERFACE_MODE_RMII &&
+ phy_mode != PHY_INTERFACE_MODE_MII) {
+ netdev_err(dev, "invalid phy interface mode\n");
+ return -EINVAL;
+ }
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev),
+ &bfin_mac_adjust_link, phy_mode);
+
+ if (IS_ERR(phydev)) {
+ netdev_err(dev, "could not attach PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ /* mask with MAC supported features */
+ phydev->supported &= (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg
+ | SUPPORTED_Pause | SUPPORTED_Asym_Pause
+ | SUPPORTED_MII
+ | SUPPORTED_TP);
+
+ phydev->advertising = phydev->supported;
+
+ lp->old_link = 0;
+ lp->old_speed = 0;
+ lp->old_duplex = -1;
+ lp->phydev = phydev;
+
+ pr_info("attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s, irq=%d, mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
+ phydev->drv->name, dev_name(&phydev->dev), phydev->irq,
+ MDC_CLK, mdc_div, sclk/1000000);
+
+ return 0;
+}
+
+/*
+ * Ethtool support
+ */
+
+/*
+ * interrupt routine for magic packet wakeup
+ */
+static irqreturn_t bfin_mac_wake_interrupt(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+static int
+bfin_mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ if (lp->phydev)
+ return phy_ethtool_gset(lp->phydev, cmd);
+
+ return -EINVAL;
+}
+
+static int
+bfin_mac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (lp->phydev)
+ return phy_ethtool_sset(lp->phydev, cmd);
+
+ return -EINVAL;
+}
+
+static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+ strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info));
+}
+
+static void bfin_mac_ethtool_getwol(struct net_device *dev,
+ struct ethtool_wolinfo *wolinfo)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ wolinfo->supported = WAKE_MAGIC;
+ wolinfo->wolopts = lp->wol;
+}
+
+static int bfin_mac_ethtool_setwol(struct net_device *dev,
+ struct ethtool_wolinfo *wolinfo)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ int rc;
+
+ if (wolinfo->wolopts & (WAKE_MAGICSECURE |
+ WAKE_UCAST |
+ WAKE_MCAST |
+ WAKE_BCAST |
+ WAKE_ARP))
+ return -EOPNOTSUPP;
+
+ lp->wol = wolinfo->wolopts;
+
+ if (lp->wol && !lp->irq_wake_requested) {
+ /* register wake irq handler */
+ rc = request_irq(IRQ_MAC_WAKEDET, bfin_mac_wake_interrupt,
+ 0, "EMAC_WAKE", dev);
+ if (rc)
+ return rc;
+ lp->irq_wake_requested = true;
+ }
+
+ if (!lp->wol && lp->irq_wake_requested) {
+ free_irq(IRQ_MAC_WAKEDET, dev);
+ lp->irq_wake_requested = false;
+ }
+
+ /* Make sure the PHY driver doesn't suspend */
+ device_init_wakeup(&dev->dev, lp->wol);
+
+ return 0;
+}
+
+#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
+static int bfin_mac_ethtool_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ info->so_timestamping =
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ info->phc_index = lp->phc_index;
+ info->tx_types =
+ (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+ info->rx_filters =
+ (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
+ return 0;
+}
+#endif
+
+static const struct ethtool_ops bfin_mac_ethtool_ops = {
+ .get_settings = bfin_mac_ethtool_getsettings,
+ .set_settings = bfin_mac_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = bfin_mac_ethtool_getdrvinfo,
+ .get_wol = bfin_mac_ethtool_getwol,
+ .set_wol = bfin_mac_ethtool_setwol,
+#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
+ .get_ts_info = bfin_mac_ethtool_get_ts_info,
+#endif
+};
+
+/**************************************************************************/
+static void setup_system_regs(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ int i;
+ unsigned short sysctl;
+
+ /*
+ * Odd word alignment for Receive Frame DMA word
+ * Configure checksum support and rcve frame word alignment
+ */
+ sysctl = bfin_read_EMAC_SYSCTL();
+ /*
+ * check if interrupt is requested for any PHY,
+ * enable PHY interrupt only if needed
+ */
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ if (lp->mii_bus->irq[i] != PHY_POLL)
+ break;
+ if (i < PHY_MAX_ADDR)
+ sysctl |= PHYIE;
+ sysctl |= RXDWA;
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+ sysctl |= RXCKS;
+#else
+ sysctl &= ~RXCKS;
+#endif
+ bfin_write_EMAC_SYSCTL(sysctl);
+
+ bfin_write_EMAC_MMC_CTL(RSTC | CROLL);
+
+ /* Set vlan regs to let 1522 bytes long packets pass through */
+ bfin_write_EMAC_VLAN1(lp->vlan1_mask);
+ bfin_write_EMAC_VLAN2(lp->vlan2_mask);
+
+ /* Initialize the TX DMA channel registers */
+ bfin_write_DMA2_X_COUNT(0);
+ bfin_write_DMA2_X_MODIFY(4);
+ bfin_write_DMA2_Y_COUNT(0);
+ bfin_write_DMA2_Y_MODIFY(0);
+
+ /* Initialize the RX DMA channel registers */
+ bfin_write_DMA1_X_COUNT(0);
+ bfin_write_DMA1_X_MODIFY(4);
+ bfin_write_DMA1_Y_COUNT(0);
+ bfin_write_DMA1_Y_MODIFY(0);
+}
+
+static void setup_mac_addr(u8 *mac_addr)
+{
+ u32 addr_low = le32_to_cpu(*(__le32 *) & mac_addr[0]);
+ u16 addr_hi = le16_to_cpu(*(__le16 *) & mac_addr[4]);
+
+ /* this depends on a little-endian machine */
+ bfin_write_EMAC_ADDRLO(addr_low);
+ bfin_write_EMAC_ADDRHI(addr_hi);
+}
+
+static int bfin_mac_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ if (netif_running(dev))
+ return -EBUSY;
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ setup_mac_addr(dev->dev_addr);
+ return 0;
+}
+
+#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
+#define bfin_mac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE)
+
+static u32 bfin_select_phc_clock(u32 input_clk, unsigned int *shift_result)
+{
+ u32 ipn = 1000000000UL / input_clk;
+ u32 ppn = 1;
+ unsigned int shift = 0;
+
+ while (ppn <= ipn) {
+ ppn <<= 1;
+ shift++;
+ }
+ *shift_result = shift;
+ return 1000000000UL / ppn;
+}
+
+static int bfin_mac_hwtstamp_set(struct net_device *netdev,
+ struct ifreq *ifr)
+{
+ struct hwtstamp_config config;
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u16 ptpctl;
+ u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
+ __func__, config.flags, config.tx_type, config.rx_filter);
+
+ /* reserved for future extensions */
+ if (config.flags)
+ return -EINVAL;
+
+ if ((config.tx_type != HWTSTAMP_TX_OFF) &&
+ (config.tx_type != HWTSTAMP_TX_ON))
+ return -ERANGE;
+
+ ptpctl = bfin_read_EMAC_PTP_CTL();
+
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ /*
+ * Dont allow any timestamping
+ */
+ ptpfv3 = 0xFFFFFFFF;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ /*
+ * Clear the five comparison mask bits (bits[12:8]) in EMAC_PTP_CTL)
+ * to enable all the field matches.
+ */
+ ptpctl &= ~0x1F00;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of the EMAC_PTP_FOFF register.
+ */
+ ptpfoff = 0x4A24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
+ * registers.
+ */
+ ptpfv1 = 0x11040800;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * The default value (0xFFFC) allows the timestamping of both
+ * received Sync messages and Delay_Req messages.
+ */
+ ptpfv3 = 0xFFFFFFFC;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ /* Clear all five comparison mask bits (bits[12:8]) in the
+ * EMAC_PTP_CTL register to enable all the field matches.
+ */
+ ptpctl &= ~0x1F00;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of the EMAC_PTP_FOFF register, except set
+ * the PTPCOF field to 0x2A.
+ */
+ ptpfoff = 0x2A24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
+ * registers.
+ */
+ ptpfv1 = 0x11040800;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * To allow the timestamping of Pdelay_Req and Pdelay_Resp, set
+ * the value to 0xFFF0.
+ */
+ ptpfv3 = 0xFFFFFFF0;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ /*
+ * Clear bits 8 and 12 of the EMAC_PTP_CTL register to enable only the
+ * EFTM and PTPCM field comparison.
+ */
+ ptpctl &= ~0x1100;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of all the fields of the EMAC_PTP_FOFF
+ * register, except set the PTPCOF field to 0x0E.
+ */
+ ptpfoff = 0x0E24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Program bits [15:0] of the EMAC_PTP_FV1 register to 0x88F7, which
+ * corresponds to PTP messages on the MAC layer.
+ */
+ ptpfv1 = 0x110488F7;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * To allow the timestamping of Pdelay_Req and Pdelay_Resp
+ * messages, set the value to 0xFFF0.
+ */
+ ptpfv3 = 0xFFFFFFF0;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ if (config.tx_type == HWTSTAMP_TX_OFF &&
+ bfin_mac_hwtstamp_is_none(config.rx_filter)) {
+ ptpctl &= ~PTP_EN;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+
+ SSYNC();
+ } else {
+ ptpctl |= PTP_EN;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+
+ /*
+ * clear any existing timestamp
+ */
+ bfin_read_EMAC_PTP_RXSNAPLO();
+ bfin_read_EMAC_PTP_RXSNAPHI();
+
+ bfin_read_EMAC_PTP_TXSNAPLO();
+ bfin_read_EMAC_PTP_TXSNAPHI();
+
+ SSYNC();
+ }
+
+ lp->stamp_cfg = config;
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
+
+static int bfin_mac_hwtstamp_get(struct net_device *netdev,
+ struct ifreq *ifr)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+
+ return copy_to_user(ifr->ifr_data, &lp->stamp_cfg,
+ sizeof(lp->stamp_cfg)) ?
+ -EFAULT : 0;
+}
+
+static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+
+ if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
+ int timeout_cnt = MAX_TIMEOUT_CNT;
+
+ /* When doing time stamping, keep the connection to the socket
+ * a while longer
+ */
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+ /*
+ * The timestamping is done at the EMAC module's MII/RMII interface
+ * when the module sees the Start of Frame of an event message packet. This
+ * interface is the closest possible place to the physical Ethernet transmission
+ * medium, providing the best timing accuracy.
+ */
+ while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt))
+ udelay(1);
+ if (timeout_cnt == 0)
+ netdev_err(netdev, "timestamp the TX packet failed\n");
+ else {
+ struct skb_shared_hwtstamps shhwtstamps;
+ u64 ns;
+ u64 regval;
+
+ regval = bfin_read_EMAC_PTP_TXSNAPLO();
+ regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32;
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ ns = regval << lp->shift;
+ shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ skb_tstamp_tx(skb, &shhwtstamps);
+ }
+ }
+}
+
+static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u32 valid;
+ u64 regval, ns;
+ struct skb_shared_hwtstamps *shhwtstamps;
+
+ if (bfin_mac_hwtstamp_is_none(lp->stamp_cfg.rx_filter))
+ return;
+
+ valid = bfin_read_EMAC_PTP_ISTAT() & RXEL;
+ if (!valid)
+ return;
+
+ shhwtstamps = skb_hwtstamps(skb);
+
+ regval = bfin_read_EMAC_PTP_RXSNAPLO();
+ regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32;
+ ns = regval << lp->shift;
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+}
+
+static void bfin_mac_hwtstamp_init(struct net_device *netdev)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u64 addend, ppb;
+ u32 input_clk, phc_clk;
+
+ /* Initialize hardware timer */
+ input_clk = get_sclk();
+ phc_clk = bfin_select_phc_clock(input_clk, &lp->shift);
+ addend = phc_clk * (1ULL << 32);
+ do_div(addend, input_clk);
+ bfin_write_EMAC_PTP_ADDEND((u32)addend);
+
+ lp->addend = addend;
+ ppb = 1000000000ULL * input_clk;
+ do_div(ppb, phc_clk);
+ lp->max_ppb = ppb - 1000000000ULL - 1ULL;
+
+ /* Initialize hwstamp config */
+ lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE;
+ lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF;
+}
+
+static u64 bfin_ptp_time_read(struct bfin_mac_local *lp)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ lo = bfin_read_EMAC_PTP_TIMELO();
+ hi = bfin_read_EMAC_PTP_TIMEHI();
+
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ ns <<= lp->shift;
+
+ return ns;
+}
+
+static void bfin_ptp_time_write(struct bfin_mac_local *lp, u64 ns)
+{
+ u32 hi, lo;
+
+ ns >>= lp->shift;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+
+ bfin_write_EMAC_PTP_TIMELO(lo);
+ bfin_write_EMAC_PTP_TIMEHI(hi);
+}
+
+/* PTP Hardware Clock operations */
+
+static int bfin_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 adj;
+ u32 diff, addend;
+ int neg_adj = 0;
+ struct bfin_mac_local *lp =
+ container_of(ptp, struct bfin_mac_local, caps);
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ addend = lp->addend;
+ adj = addend;
+ adj *= ppb;
+ diff = div_u64(adj, 1000000000ULL);
+
+ addend = neg_adj ? addend - diff : addend + diff;
+
+ bfin_write_EMAC_PTP_ADDEND(addend);
+
+ return 0;
+}
+
+static int bfin_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct bfin_mac_local *lp =
+ container_of(ptp, struct bfin_mac_local, caps);
+
+ spin_lock_irqsave(&lp->phc_lock, flags);
+
+ now = bfin_ptp_time_read(lp);
+ now += delta;
+ bfin_ptp_time_write(lp, now);
+
+ spin_unlock_irqrestore(&lp->phc_lock, flags);
+
+ return 0;
+}
+
+static int bfin_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ unsigned long flags;
+ struct bfin_mac_local *lp =
+ container_of(ptp, struct bfin_mac_local, caps);
+
+ spin_lock_irqsave(&lp->phc_lock, flags);
+
+ ns = bfin_ptp_time_read(lp);
+
+ spin_unlock_irqrestore(&lp->phc_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int bfin_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ u64 ns;
+ unsigned long flags;
+ struct bfin_mac_local *lp =
+ container_of(ptp, struct bfin_mac_local, caps);
+
+ ns = ts->tv_sec * 1000000000ULL;
+ ns += ts->tv_nsec;
+
+ spin_lock_irqsave(&lp->phc_lock, flags);
+
+ bfin_ptp_time_write(lp, ns);
+
+ spin_unlock_irqrestore(&lp->phc_lock, flags);
+
+ return 0;
+}
+
+static int bfin_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info bfin_ptp_caps = {
+ .owner = THIS_MODULE,
+ .name = "BF518 clock",
+ .max_adj = 0,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .n_pins = 0,
+ .pps = 0,
+ .adjfreq = bfin_ptp_adjfreq,
+ .adjtime = bfin_ptp_adjtime,
+ .gettime = bfin_ptp_gettime,
+ .settime = bfin_ptp_settime,
+ .enable = bfin_ptp_enable,
+};
+
+static int bfin_phc_init(struct net_device *netdev, struct device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+
+ lp->caps = bfin_ptp_caps;
+ lp->caps.max_adj = lp->max_ppb;
+ lp->clock = ptp_clock_register(&lp->caps, dev);
+ if (IS_ERR(lp->clock))
+ return PTR_ERR(lp->clock);
+
+ lp->phc_index = ptp_clock_index(lp->clock);
+ spin_lock_init(&lp->phc_lock);
+
+ return 0;
+}
+
+static void bfin_phc_release(struct bfin_mac_local *lp)
+{
+ ptp_clock_unregister(lp->clock);
+}
+
+#else
+# define bfin_mac_hwtstamp_is_none(cfg) 0
+# define bfin_mac_hwtstamp_init(dev)
+# define bfin_mac_hwtstamp_set(dev, ifr) (-EOPNOTSUPP)
+# define bfin_mac_hwtstamp_get(dev, ifr) (-EOPNOTSUPP)
+# define bfin_rx_hwtstamp(dev, skb)
+# define bfin_tx_hwtstamp(dev, skb)
+# define bfin_phc_init(netdev, dev) 0
+# define bfin_phc_release(lp)
+#endif
+
+static inline void _tx_reclaim_skb(void)
+{
+ do {
+ tx_list_head->desc_a.config &= ~DMAEN;
+ tx_list_head->status.status_word = 0;
+ if (tx_list_head->skb) {
+ dev_consume_skb_any(tx_list_head->skb);
+ tx_list_head->skb = NULL;
+ }
+ tx_list_head = tx_list_head->next;
+
+ } while (tx_list_head->status.status_word != 0);
+}
+
+static void tx_reclaim_skb(struct bfin_mac_local *lp)
+{
+ int timeout_cnt = MAX_TIMEOUT_CNT;
+
+ if (tx_list_head->status.status_word != 0)
+ _tx_reclaim_skb();
+
+ if (current_tx_ptr->next == tx_list_head) {
+ while (tx_list_head->status.status_word == 0) {
+ /* slow down polling to avoid too many queue stop. */
+ udelay(10);
+ /* reclaim skb if DMA is not running. */
+ if (!(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN))
+ break;
+ if (timeout_cnt-- < 0)
+ break;
+ }
+
+ if (timeout_cnt >= 0)
+ _tx_reclaim_skb();
+ else
+ netif_stop_queue(lp->ndev);
+ }
+
+ if (current_tx_ptr->next != tx_list_head &&
+ netif_queue_stopped(lp->ndev))
+ netif_wake_queue(lp->ndev);
+
+ if (tx_list_head != current_tx_ptr) {
+ /* shorten the timer interval if tx queue is stopped */
+ if (netif_queue_stopped(lp->ndev))
+ lp->tx_reclaim_timer.expires =
+ jiffies + (TX_RECLAIM_JIFFIES >> 4);
+ else
+ lp->tx_reclaim_timer.expires =
+ jiffies + TX_RECLAIM_JIFFIES;
+
+ mod_timer(&lp->tx_reclaim_timer,
+ lp->tx_reclaim_timer.expires);
+ }
+
+ return;
+}
+
+static void tx_reclaim_skb_timeout(unsigned long lp)
+{
+ tx_reclaim_skb((struct bfin_mac_local *)lp);
+}
+
+static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ u16 *data;
+ u32 data_align = (unsigned long)(skb->data) & 0x3;
+
+ current_tx_ptr->skb = skb;
+
+ if (data_align == 0x2) {
+ /* move skb->data to current_tx_ptr payload */
+ data = (u16 *)(skb->data) - 1;
+ *data = (u16)(skb->len);
+ /*
+ * When transmitting an Ethernet packet, the PTP_TSYNC module requires
+ * a DMA_Length_Word field associated with the packet. The lower 12 bits
+ * of this field are the length of the packet payload in bytes and the higher
+ * 4 bits are the timestamping enable field.
+ */
+ if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
+ *data |= 0x1000;
+
+ current_tx_ptr->desc_a.start_addr = (u32)data;
+ /* this is important! */
+ blackfin_dcache_flush_range((u32)data,
+ (u32)((u8 *)data + skb->len + 4));
+ } else {
+ *((u16 *)(current_tx_ptr->packet)) = (u16)(skb->len);
+ /* enable timestamping for the sent packet */
+ if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
+ *((u16 *)(current_tx_ptr->packet)) |= 0x1000;
+ memcpy((u8 *)(current_tx_ptr->packet + 2), skb->data,
+ skb->len);
+ current_tx_ptr->desc_a.start_addr =
+ (u32)current_tx_ptr->packet;
+ blackfin_dcache_flush_range(
+ (u32)current_tx_ptr->packet,
+ (u32)(current_tx_ptr->packet + skb->len + 2));
+ }
+
+ /* make sure the internal data buffers in the core are drained
+ * so that the DMA descriptors are completely written when the
+ * DMA engine goes to fetch them below
+ */
+ SSYNC();
+
+ /* always clear status buffer before start tx dma */
+ current_tx_ptr->status.status_word = 0;
+
+ /* enable this packet's dma */
+ current_tx_ptr->desc_a.config |= DMAEN;
+
+ /* tx dma is running, just return */
+ if (bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)
+ goto out;
+
+ /* tx dma is not running */
+ bfin_write_DMA2_NEXT_DESC_PTR(&(current_tx_ptr->desc_a));
+ /* dma enabled, read from memory, size is 6 */
+ bfin_write_DMA2_CONFIG(current_tx_ptr->desc_a.config);
+ /* Turn on the EMAC tx */
+ bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
+
+out:
+ bfin_tx_hwtstamp(dev, skb);
+
+ current_tx_ptr = current_tx_ptr->next;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += (skb->len);
+
+ tx_reclaim_skb(lp);
+
+ return NETDEV_TX_OK;
+}
+
+#define IP_HEADER_OFF 0
+#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
+ RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
+
+static void bfin_mac_rx(struct net_device *dev)
+{
+ struct sk_buff *skb, *new_skb;
+ unsigned short len;
+ struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+ unsigned int i;
+ unsigned char fcs[ETH_FCS_LEN + 1];
+#endif
+
+ /* check if frame status word reports an error condition
+ * we which case we simply drop the packet
+ */
+ if (current_rx_ptr->status.status_word & RX_ERROR_MASK) {
+ netdev_notice(dev, "rx: receive error - packet dropped\n");
+ dev->stats.rx_dropped++;
+ goto out;
+ }
+
+ /* allocate a new skb for next time receive */
+ skb = current_rx_ptr->skb;
+
+ new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
+ if (!new_skb) {
+ dev->stats.rx_dropped++;
+ goto out;
+ }
+ /* reserve 2 bytes for RXDWA padding */
+ skb_reserve(new_skb, NET_IP_ALIGN);
+ /* Invidate the data cache of skb->data range when it is write back
+ * cache. It will prevent overwritting the new data from DMA
+ */
+ blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
+ (unsigned long)new_skb->end);
+
+ current_rx_ptr->skb = new_skb;
+ current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
+
+ len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
+ /* Deduce Ethernet FCS length from Ethernet payload length */
+ len -= ETH_FCS_LEN;
+ skb_put(skb, len);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ bfin_rx_hwtstamp(dev, skb);
+
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+ /* Checksum offloading only works for IPv4 packets with the standard IP header
+ * length of 20 bytes, because the blackfin MAC checksum calculation is
+ * based on that assumption. We must NOT use the calculated checksum if our
+ * IP version or header break that assumption.
+ */
+ if (skb->data[IP_HEADER_OFF] == 0x45) {
+ skb->csum = current_rx_ptr->status.ip_payload_csum;
+ /*
+ * Deduce Ethernet FCS from hardware generated IP payload checksum.
+ * IP checksum is based on 16-bit one's complement algorithm.
+ * To deduce a value from checksum is equal to add its inversion.
+ * If the IP payload len is odd, the inversed FCS should also
+ * begin from odd address and leave first byte zero.
+ */
+ if (skb->len % 2) {
+ fcs[0] = 0;
+ for (i = 0; i < ETH_FCS_LEN; i++)
+ fcs[i + 1] = ~skb->data[skb->len + i];
+ skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum);
+ } else {
+ for (i = 0; i < ETH_FCS_LEN; i++)
+ fcs[i] = ~skb->data[skb->len + i];
+ skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum);
+ }
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+#endif
+
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+out:
+ current_rx_ptr->status.status_word = 0x00000000;
+ current_rx_ptr = current_rx_ptr->next;
+}
+
+/* interrupt routine to handle rx and error signal */
+static irqreturn_t bfin_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ int number = 0;
+
+get_one_packet:
+ if (current_rx_ptr->status.status_word == 0) {
+ /* no more new packet received */
+ if (number == 0) {
+ if (current_rx_ptr->next->status.status_word != 0) {
+ current_rx_ptr = current_rx_ptr->next;
+ goto real_rx;
+ }
+ }
+ bfin_write_DMA1_IRQ_STATUS(bfin_read_DMA1_IRQ_STATUS() |
+ DMA_DONE | DMA_ERR);
+ return IRQ_HANDLED;
+ }
+
+real_rx:
+ bfin_mac_rx(dev);
+ number++;
+ goto get_one_packet;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bfin_mac_poll(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ disable_irq(IRQ_MAC_RX);
+ bfin_mac_interrupt(IRQ_MAC_RX, dev);
+ tx_reclaim_skb(lp);
+ enable_irq(IRQ_MAC_RX);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static void bfin_mac_disable(void)
+{
+ unsigned int opmode;
+
+ opmode = bfin_read_EMAC_OPMODE();
+ opmode &= (~RE);
+ opmode &= (~TE);
+ /* Turn off the EMAC */
+ bfin_write_EMAC_OPMODE(opmode);
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static int bfin_mac_enable(struct phy_device *phydev)
+{
+ int ret;
+ u32 opmode;
+
+ pr_debug("%s\n", __func__);
+
+ /* Set RX DMA */
+ bfin_write_DMA1_NEXT_DESC_PTR(&(rx_list_head->desc_a));
+ bfin_write_DMA1_CONFIG(rx_list_head->desc_a.config);
+
+ /* Wait MII done */
+ ret = bfin_mdio_poll();
+ if (ret)
+ return ret;
+
+ /* We enable only RX here */
+ /* ASTP : Enable Automatic Pad Stripping
+ PR : Promiscuous Mode for test
+ PSF : Receive frames with total length less than 64 bytes.
+ FDMODE : Full Duplex Mode
+ LB : Internal Loopback for test
+ RE : Receiver Enable */
+ opmode = bfin_read_EMAC_OPMODE();
+ if (opmode & FDMODE)
+ opmode |= PSF;
+ else
+ opmode |= DRO | DC | PSF;
+ opmode |= RE;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
+ opmode |= RMII; /* For Now only 100MBit are supported */
+#if defined(CONFIG_BF537) || defined(CONFIG_BF536)
+ if (__SILICON_REVISION__ < 3) {
+ /*
+ * This isn't publicly documented (fun times!), but in
+ * silicon <=0.2, the RX and TX pins are clocked together.
+ * So in order to recv, we must enable the transmit side
+ * as well. This will cause a spurious TX interrupt too,
+ * but we can easily consume that.
+ */
+ opmode |= TE;
+ }
+#endif
+ }
+
+ /* Turn on the EMAC rx */
+ bfin_write_EMAC_OPMODE(opmode);
+
+ return 0;
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void bfin_mac_timeout(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ pr_debug("%s: %s\n", dev->name, __func__);
+
+ bfin_mac_disable();
+
+ del_timer(&lp->tx_reclaim_timer);
+
+ /* reset tx queue and free skb */
+ while (tx_list_head != current_tx_ptr) {
+ tx_list_head->desc_a.config &= ~DMAEN;
+ tx_list_head->status.status_word = 0;
+ if (tx_list_head->skb) {
+ dev_kfree_skb(tx_list_head->skb);
+ tx_list_head->skb = NULL;
+ }
+ tx_list_head = tx_list_head->next;
+ }
+
+ if (netif_queue_stopped(lp->ndev))
+ netif_wake_queue(lp->ndev);
+
+ bfin_mac_enable(lp->phydev);
+
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+static void bfin_mac_multicast_hash(struct net_device *dev)
+{
+ u32 emac_hashhi, emac_hashlo;
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ emac_hashhi = emac_hashlo = 0;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc(ETH_ALEN, ha->addr);
+ crc >>= 26;
+
+ if (crc & 0x20)
+ emac_hashhi |= 1 << (crc & 0x1f);
+ else
+ emac_hashlo |= 1 << (crc & 0x1f);
+ }
+
+ bfin_write_EMAC_HASHHI(emac_hashhi);
+ bfin_write_EMAC_HASHLO(emac_hashlo);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void bfin_mac_set_multicast_list(struct net_device *dev)
+{
+ u32 sysctl;
+
+ if (dev->flags & IFF_PROMISC) {
+ netdev_info(dev, "set promisc mode\n");
+ sysctl = bfin_read_EMAC_OPMODE();
+ sysctl |= PR;
+ bfin_write_EMAC_OPMODE(sysctl);
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* accept all multicast */
+ sysctl = bfin_read_EMAC_OPMODE();
+ sysctl |= PAM;
+ bfin_write_EMAC_OPMODE(sysctl);
+ } else if (!netdev_mc_empty(dev)) {
+ /* set up multicast hash table */
+ sysctl = bfin_read_EMAC_OPMODE();
+ sysctl |= HM;
+ bfin_write_EMAC_OPMODE(sysctl);
+ bfin_mac_multicast_hash(dev);
+ } else {
+ /* clear promisc or multicast mode */
+ sysctl = bfin_read_EMAC_OPMODE();
+ sysctl &= ~(RAF | PAM);
+ bfin_write_EMAC_OPMODE(sysctl);
+ }
+}
+
+static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCSHWTSTAMP:
+ return bfin_mac_hwtstamp_set(netdev, ifr);
+ case SIOCGHWTSTAMP:
+ return bfin_mac_hwtstamp_get(netdev, ifr);
+ default:
+ if (lp->phydev)
+ return phy_mii_ioctl(lp->phydev, ifr, cmd);
+ else
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void bfin_mac_shutdown(struct net_device *dev)
+{
+ /* Turn off the EMAC */
+ bfin_write_EMAC_OPMODE(0x00000000);
+ /* Turn off the EMAC RX DMA */
+ bfin_write_DMA1_CONFIG(0x0000);
+ bfin_write_DMA2_CONFIG(0x0000);
+}
+
+/*
+ * Open and Initialize the interface
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int bfin_mac_open(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ int ret;
+ pr_debug("%s: %s\n", dev->name, __func__);
+
+ /*
+ * Check that the address is valid. If its not, refuse
+ * to bring the device up. The user must specify an
+ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ netdev_warn(dev, "no valid ethernet hw addr\n");
+ return -EINVAL;
+ }
+
+ /* initial rx and tx list */
+ ret = desc_list_init(dev);
+ if (ret)
+ return ret;
+
+ phy_start(lp->phydev);
+ setup_system_regs(dev);
+ setup_mac_addr(dev->dev_addr);
+
+ bfin_mac_disable();
+ ret = bfin_mac_enable(lp->phydev);
+ if (ret)
+ return ret;
+ pr_debug("hardware init finished\n");
+
+ netif_start_queue(dev);
+ netif_carrier_on(dev);
+
+ return 0;
+}
+
+/*
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world. Caused by
+ * an 'ifconfig ethX down'
+ */
+static int bfin_mac_close(struct net_device *dev)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ pr_debug("%s: %s\n", dev->name, __func__);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ phy_stop(lp->phydev);
+ phy_write(lp->phydev, MII_BMCR, BMCR_PDOWN);
+
+ /* clear everything */
+ bfin_mac_shutdown(dev);
+
+ /* free the rx/tx buffers */
+ desc_list_free();
+
+ return 0;
+}
+
+static const struct net_device_ops bfin_mac_netdev_ops = {
+ .ndo_open = bfin_mac_open,
+ .ndo_stop = bfin_mac_close,
+ .ndo_start_xmit = bfin_mac_hard_start_xmit,
+ .ndo_set_mac_address = bfin_mac_set_mac_address,
+ .ndo_tx_timeout = bfin_mac_timeout,
+ .ndo_set_rx_mode = bfin_mac_set_multicast_list,
+ .ndo_do_ioctl = bfin_mac_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = bfin_mac_poll,
+#endif
+};
+
+static int bfin_mac_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct bfin_mac_local *lp;
+ struct platform_device *pd;
+ struct bfin_mii_bus_platform_data *mii_bus_data;
+ int rc;
+
+ ndev = alloc_etherdev(sizeof(struct bfin_mac_local));
+ if (!ndev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
+ lp = netdev_priv(ndev);
+ lp->ndev = ndev;
+
+ /* Grab the MAC address in the MAC */
+ *(__le32 *) (&(ndev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO());
+ *(__le16 *) (&(ndev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI());
+
+ /* probe mac */
+ /*todo: how to proble? which is revision_register */
+ bfin_write_EMAC_ADDRLO(0x12345678);
+ if (bfin_read_EMAC_ADDRLO() != 0x12345678) {
+ dev_err(&pdev->dev, "Cannot detect Blackfin on-chip ethernet MAC controller!\n");
+ rc = -ENODEV;
+ goto out_err_probe_mac;
+ }
+
+
+ /*
+ * Is it valid? (Did bootloader initialize it?)
+ * Grab the MAC from the board somehow
+ * this is done in the arch/blackfin/mach-bfxxx/boards/eth_mac.c
+ */
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ if (bfin_get_ether_addr(ndev->dev_addr) ||
+ !is_valid_ether_addr(ndev->dev_addr)) {
+ /* Still not valid, get a random one */
+ netdev_warn(ndev, "Setting Ethernet MAC to a random one\n");
+ eth_hw_addr_random(ndev);
+ }
+ }
+
+ setup_mac_addr(ndev->dev_addr);
+
+ if (!dev_get_platdata(&pdev->dev)) {
+ dev_err(&pdev->dev, "Cannot get platform device bfin_mii_bus!\n");
+ rc = -ENODEV;
+ goto out_err_probe_mac;
+ }
+ pd = dev_get_platdata(&pdev->dev);
+ lp->mii_bus = platform_get_drvdata(pd);
+ if (!lp->mii_bus) {
+ dev_err(&pdev->dev, "Cannot get mii_bus!\n");
+ rc = -ENODEV;
+ goto out_err_probe_mac;
+ }
+ lp->mii_bus->priv = ndev;
+ mii_bus_data = dev_get_platdata(&pd->dev);
+
+ rc = mii_probe(ndev, mii_bus_data->phy_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "MII Probe failed!\n");
+ goto out_err_mii_probe;
+ }
+
+ lp->vlan1_mask = ETH_P_8021Q | mii_bus_data->vlan1_mask;
+ lp->vlan2_mask = ETH_P_8021Q | mii_bus_data->vlan2_mask;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(ndev);
+
+ ndev->netdev_ops = &bfin_mac_netdev_ops;
+ ndev->ethtool_ops = &bfin_mac_ethtool_ops;
+
+ init_timer(&lp->tx_reclaim_timer);
+ lp->tx_reclaim_timer.data = (unsigned long)lp;
+ lp->tx_reclaim_timer.function = tx_reclaim_skb_timeout;
+
+ spin_lock_init(&lp->lock);
+
+ /* now, enable interrupts */
+ /* register irq handler */
+ rc = request_irq(IRQ_MAC_RX, bfin_mac_interrupt,
+ 0, "EMAC_RX", ndev);
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot request Blackfin MAC RX IRQ!\n");
+ rc = -EBUSY;
+ goto out_err_request_irq;
+ }
+
+ rc = register_netdev(ndev);
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot register net device!\n");
+ goto out_err_reg_ndev;
+ }
+
+ bfin_mac_hwtstamp_init(ndev);
+ rc = bfin_phc_init(ndev, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot register PHC device!\n");
+ goto out_err_phc;
+ }
+
+ /* now, print out the card info, in a short format.. */
+ netdev_info(ndev, "%s, Version %s\n", DRV_DESC, DRV_VERSION);
+
+ return 0;
+
+out_err_phc:
+out_err_reg_ndev:
+ free_irq(IRQ_MAC_RX, ndev);
+out_err_request_irq:
+out_err_mii_probe:
+ mdiobus_unregister(lp->mii_bus);
+ mdiobus_free(lp->mii_bus);
+out_err_probe_mac:
+ free_netdev(ndev);
+
+ return rc;
+}
+
+static int bfin_mac_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct bfin_mac_local *lp = netdev_priv(ndev);
+
+ bfin_phc_release(lp);
+
+ lp->mii_bus->priv = NULL;
+
+ unregister_netdev(ndev);
+
+ free_irq(IRQ_MAC_RX, ndev);
+
+ free_netdev(ndev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct net_device *net_dev = platform_get_drvdata(pdev);
+ struct bfin_mac_local *lp = netdev_priv(net_dev);
+
+ if (lp->wol) {
+ bfin_write_EMAC_OPMODE((bfin_read_EMAC_OPMODE() & ~TE) | RE);
+ bfin_write_EMAC_WKUP_CTL(MPKE);
+ enable_irq_wake(IRQ_MAC_WAKEDET);
+ } else {
+ if (netif_running(net_dev))
+ bfin_mac_close(net_dev);
+ }
+
+ return 0;
+}
+
+static int bfin_mac_resume(struct platform_device *pdev)
+{
+ struct net_device *net_dev = platform_get_drvdata(pdev);
+ struct bfin_mac_local *lp = netdev_priv(net_dev);
+
+ if (lp->wol) {
+ bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
+ bfin_write_EMAC_WKUP_CTL(0);
+ disable_irq_wake(IRQ_MAC_WAKEDET);
+ } else {
+ if (netif_running(net_dev))
+ bfin_mac_open(net_dev);
+ }
+
+ return 0;
+}
+#else
+#define bfin_mac_suspend NULL
+#define bfin_mac_resume NULL
+#endif /* CONFIG_PM */
+
+static int bfin_mii_bus_probe(struct platform_device *pdev)
+{
+ struct mii_bus *miibus;
+ struct bfin_mii_bus_platform_data *mii_bus_pd;
+ const unsigned short *pin_req;
+ int rc, i;
+
+ mii_bus_pd = dev_get_platdata(&pdev->dev);
+ if (!mii_bus_pd) {
+ dev_err(&pdev->dev, "No peripherals in platform data!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We are setting up a network card,
+ * so set the GPIO pins to Ethernet mode
+ */
+ pin_req = mii_bus_pd->mac_peripherals;
+ rc = peripheral_request_list(pin_req, KBUILD_MODNAME);
+ if (rc) {
+ dev_err(&pdev->dev, "Requesting peripherals failed!\n");
+ return rc;
+ }
+
+ rc = -ENOMEM;
+ miibus = mdiobus_alloc();
+ if (miibus == NULL)
+ goto out_err_alloc;
+ miibus->read = bfin_mdiobus_read;
+ miibus->write = bfin_mdiobus_write;
+
+ miibus->parent = &pdev->dev;
+ miibus->name = "bfin_mii_bus";
+ miibus->phy_mask = mii_bus_pd->phy_mask;
+
+ snprintf(miibus->id, MII_BUS_ID_SIZE, "%s-%x",
+ pdev->name, pdev->id);
+ miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+ if (!miibus->irq)
+ goto out_err_irq_alloc;
+
+ for (i = rc; i < PHY_MAX_ADDR; ++i)
+ miibus->irq[i] = PHY_POLL;
+
+ rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR);
+ if (rc != mii_bus_pd->phydev_number)
+ dev_err(&pdev->dev, "Invalid number (%i) of phydevs\n",
+ mii_bus_pd->phydev_number);
+ for (i = 0; i < rc; ++i) {
+ unsigned short phyaddr = mii_bus_pd->phydev_data[i].addr;
+ if (phyaddr < PHY_MAX_ADDR)
+ miibus->irq[phyaddr] = mii_bus_pd->phydev_data[i].irq;
+ else
+ dev_err(&pdev->dev,
+ "Invalid PHY address %i for phydev %i\n",
+ phyaddr, i);
+ }
+
+ rc = mdiobus_register(miibus);
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
+ goto out_err_mdiobus_register;
+ }
+
+ platform_set_drvdata(pdev, miibus);
+ return 0;
+
+out_err_mdiobus_register:
+ kfree(miibus->irq);
+out_err_irq_alloc:
+ mdiobus_free(miibus);
+out_err_alloc:
+ peripheral_free_list(pin_req);
+
+ return rc;
+}
+
+static int bfin_mii_bus_remove(struct platform_device *pdev)
+{
+ struct mii_bus *miibus = platform_get_drvdata(pdev);
+ struct bfin_mii_bus_platform_data *mii_bus_pd =
+ dev_get_platdata(&pdev->dev);
+
+ mdiobus_unregister(miibus);
+ kfree(miibus->irq);
+ mdiobus_free(miibus);
+ peripheral_free_list(mii_bus_pd->mac_peripherals);
+
+ return 0;
+}
+
+static struct platform_driver bfin_mii_bus_driver = {
+ .probe = bfin_mii_bus_probe,
+ .remove = bfin_mii_bus_remove,
+ .driver = {
+ .name = "bfin_mii_bus",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver bfin_mac_driver = {
+ .probe = bfin_mac_probe,
+ .remove = bfin_mac_remove,
+ .resume = bfin_mac_resume,
+ .suspend = bfin_mac_suspend,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bfin_mac_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&bfin_mii_bus_driver);
+ if (!ret)
+ return platform_driver_register(&bfin_mac_driver);
+ return -ENODEV;
+}
+
+module_init(bfin_mac_init);
+
+static void __exit bfin_mac_cleanup(void)
+{
+ platform_driver_unregister(&bfin_mac_driver);
+ platform_driver_unregister(&bfin_mii_bus_driver);
+}
+
+module_exit(bfin_mac_cleanup);
+
diff --git a/drivers/net/ethernet/adi/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h
new file mode 100644
index 00000000000..6dec86ac97c
--- /dev/null
+++ b/drivers/net/ethernet/adi/bfin_mac.h
@@ -0,0 +1,109 @@
+/*
+ * Blackfin On-Chip MAC Driver
+ *
+ * Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#ifndef _BFIN_MAC_H_
+#define _BFIN_MAC_H_
+
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timer.h>
+#include <linux/etherdevice.h>
+#include <linux/bfin_mac.h>
+
+/*
+ * Disable hardware checksum for bug #5600 if writeback cache is
+ * enabled. Otherwize, corrupted RX packet will be sent up stack
+ * without error mark.
+ */
+#ifndef CONFIG_BFIN_EXTMEM_WRITEBACK
+#define BFIN_MAC_CSUM_OFFLOAD
+#endif
+
+#define TX_RECLAIM_JIFFIES (HZ / 5)
+
+struct dma_descriptor {
+ struct dma_descriptor *next_dma_desc;
+ unsigned long start_addr;
+ unsigned short config;
+ unsigned short x_count;
+};
+
+struct status_area_rx {
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+ unsigned short ip_hdr_csum; /* ip header checksum */
+ /* ip payload(udp or tcp or others) checksum */
+ unsigned short ip_payload_csum;
+#endif
+ unsigned long status_word; /* the frame status word */
+};
+
+struct status_area_tx {
+ unsigned long status_word; /* the frame status word */
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_rx {
+ struct net_dma_desc_rx *next;
+ struct sk_buff *skb;
+ struct dma_descriptor desc_a;
+ struct dma_descriptor desc_b;
+ struct status_area_rx status;
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_tx {
+ struct net_dma_desc_tx *next;
+ struct sk_buff *skb;
+ struct dma_descriptor desc_a;
+ struct dma_descriptor desc_b;
+ unsigned char packet[1560];
+ struct status_area_tx status;
+};
+
+struct bfin_mac_local {
+ /*
+ * these are things that the kernel wants me to keep, so users
+ * can find out semi-useless statistics of how well the card is
+ * performing
+ */
+ struct net_device_stats stats;
+
+ spinlock_t lock;
+
+ int wol; /* Wake On Lan */
+ int irq_wake_requested;
+ struct timer_list tx_reclaim_timer;
+ struct net_device *ndev;
+
+ /* Data for EMAC_VLAN1 regs */
+ u16 vlan1_mask, vlan2_mask;
+
+ /* MII and PHY stuffs */
+ int old_link; /* used by bf537_adjust_link */
+ int old_speed;
+ int old_duplex;
+
+ struct phy_device *phydev;
+ struct mii_bus *mii_bus;
+
+#if defined(CONFIG_BFIN_MAC_USE_HWSTAMP)
+ u32 addend;
+ unsigned int shift;
+ s32 max_ppb;
+ struct hwtstamp_config stamp_cfg;
+ struct ptp_clock_info caps;
+ struct ptp_clock *clock;
+ int phc_index;
+ spinlock_t phc_lock; /* protects time lo/hi registers */
+#endif
+};
+
+int bfin_get_ether_addr(char *addr);
+
+#endif
diff --git a/drivers/net/ethernet/aeroflex/Kconfig b/drivers/net/ethernet/aeroflex/Kconfig
new file mode 100644
index 00000000000..4f4a8d78fd5
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/Kconfig
@@ -0,0 +1,11 @@
+#
+# Aeroflex Gaisler network device configuration
+#
+
+config GRETH
+ tristate "Aeroflex Gaisler GRETH Ethernet MAC support"
+ depends on SPARC
+ select PHYLIB
+ select CRC32
+ ---help---
+ Say Y here if you want to use the Aeroflex Gaisler GRETH Ethernet MAC.
diff --git a/drivers/net/ethernet/aeroflex/Makefile b/drivers/net/ethernet/aeroflex/Makefile
new file mode 100644
index 00000000000..6e62a679282
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Aeroflex Gaisler network device drivers.
+#
+
+obj-$(CONFIG_GRETH) += greth.o
diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c
new file mode 100644
index 00000000000..23578dfee24
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/greth.c
@@ -0,0 +1,1607 @@
+/*
+ * Aeroflex Gaisler GRETH 10/100/1G Ethernet MAC.
+ *
+ * 2005-2010 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports GRETH 10/100 and GRETH 10/100/1G Ethernet MACs
+ * available in the GRLIB VHDL IP core library.
+ *
+ * Full documentation of both cores can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * The Gigabit version supports scatter/gather DMA, any alignment of
+ * buffers and checksum offloading.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Contributors: Kristoffer Glembo
+ * Daniel Hellstrom
+ * Marko Isomaki
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/io.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+#include <asm/byteorder.h>
+
+#ifdef CONFIG_SPARC
+#include <asm/idprom.h>
+#endif
+
+#include "greth.h"
+
+#define GRETH_DEF_MSG_ENABLE \
+ (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+static int greth_debug = -1; /* -1 == use GRETH_DEF_MSG_ENABLE as value */
+module_param(greth_debug, int, 0);
+MODULE_PARM_DESC(greth_debug, "GRETH bitmapped debugging message enable value");
+
+/* Accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
+static int macaddr[6];
+module_param_array(macaddr, int, NULL, 0);
+MODULE_PARM_DESC(macaddr, "GRETH Ethernet MAC address");
+
+static int greth_edcl = 1;
+module_param(greth_edcl, int, 0);
+MODULE_PARM_DESC(greth_edcl, "GRETH EDCL usage indicator. Set to 1 if EDCL is used.");
+
+static int greth_open(struct net_device *dev);
+static netdev_tx_t greth_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static netdev_tx_t greth_start_xmit_gbit(struct sk_buff *skb,
+ struct net_device *dev);
+static int greth_rx(struct net_device *dev, int limit);
+static int greth_rx_gbit(struct net_device *dev, int limit);
+static void greth_clean_tx(struct net_device *dev);
+static void greth_clean_tx_gbit(struct net_device *dev);
+static irqreturn_t greth_interrupt(int irq, void *dev_id);
+static int greth_close(struct net_device *dev);
+static int greth_set_mac_add(struct net_device *dev, void *p);
+static void greth_set_multicast_list(struct net_device *dev);
+
+#define GRETH_REGLOAD(a) (be32_to_cpu(__raw_readl(&(a))))
+#define GRETH_REGSAVE(a, v) (__raw_writel(cpu_to_be32(v), &(a)))
+#define GRETH_REGORIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) | (v))))
+#define GRETH_REGANDIN(a, v) (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) & (v))))
+
+#define NEXT_TX(N) (((N) + 1) & GRETH_TXBD_NUM_MASK)
+#define SKIP_TX(N, C) (((N) + C) & GRETH_TXBD_NUM_MASK)
+#define NEXT_RX(N) (((N) + 1) & GRETH_RXBD_NUM_MASK)
+
+static void greth_print_rx_packet(void *addr, int len)
+{
+ print_hex_dump(KERN_DEBUG, "RX: ", DUMP_PREFIX_OFFSET, 16, 1,
+ addr, len, true);
+}
+
+static void greth_print_tx_packet(struct sk_buff *skb)
+{
+ int i;
+ int length;
+
+ if (skb_shinfo(skb)->nr_frags == 0)
+ length = skb->len;
+ else
+ length = skb_headlen(skb);
+
+ print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, length, true);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+
+ print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb_frag_address(&skb_shinfo(skb)->frags[i]),
+ skb_shinfo(skb)->frags[i].size, true);
+ }
+}
+
+static inline void greth_enable_tx(struct greth_private *greth)
+{
+ wmb();
+ GRETH_REGORIN(greth->regs->control, GRETH_TXEN);
+}
+
+static inline void greth_disable_tx(struct greth_private *greth)
+{
+ GRETH_REGANDIN(greth->regs->control, ~GRETH_TXEN);
+}
+
+static inline void greth_enable_rx(struct greth_private *greth)
+{
+ wmb();
+ GRETH_REGORIN(greth->regs->control, GRETH_RXEN);
+}
+
+static inline void greth_disable_rx(struct greth_private *greth)
+{
+ GRETH_REGANDIN(greth->regs->control, ~GRETH_RXEN);
+}
+
+static inline void greth_enable_irqs(struct greth_private *greth)
+{
+ GRETH_REGORIN(greth->regs->control, GRETH_RXI | GRETH_TXI);
+}
+
+static inline void greth_disable_irqs(struct greth_private *greth)
+{
+ GRETH_REGANDIN(greth->regs->control, ~(GRETH_RXI|GRETH_TXI));
+}
+
+static inline void greth_write_bd(u32 *bd, u32 val)
+{
+ __raw_writel(cpu_to_be32(val), bd);
+}
+
+static inline u32 greth_read_bd(u32 *bd)
+{
+ return be32_to_cpu(__raw_readl(bd));
+}
+
+static void greth_clean_rings(struct greth_private *greth)
+{
+ int i;
+ struct greth_bd *rx_bdp = greth->rx_bd_base;
+ struct greth_bd *tx_bdp = greth->tx_bd_base;
+
+ if (greth->gbit_mac) {
+
+ /* Free and unmap RX buffers */
+ for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) {
+ if (greth->rx_skbuff[i] != NULL) {
+ dev_kfree_skb(greth->rx_skbuff[i]);
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&rx_bdp->addr),
+ MAX_FRAME_SIZE+NET_IP_ALIGN,
+ DMA_FROM_DEVICE);
+ }
+ }
+
+ /* TX buffers */
+ while (greth->tx_free < GRETH_TXBD_NUM) {
+
+ struct sk_buff *skb = greth->tx_skbuff[greth->tx_last];
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ tx_bdp = greth->tx_bd_base + greth->tx_last;
+ greth->tx_last = NEXT_TX(greth->tx_last);
+
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&tx_bdp->addr),
+ skb_headlen(skb),
+ DMA_TO_DEVICE);
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ tx_bdp = greth->tx_bd_base + greth->tx_last;
+
+ dma_unmap_page(greth->dev,
+ greth_read_bd(&tx_bdp->addr),
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+
+ greth->tx_last = NEXT_TX(greth->tx_last);
+ }
+ greth->tx_free += nr_frags+1;
+ dev_kfree_skb(skb);
+ }
+
+
+ } else { /* 10/100 Mbps MAC */
+
+ for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) {
+ kfree(greth->rx_bufs[i]);
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&rx_bdp->addr),
+ MAX_FRAME_SIZE,
+ DMA_FROM_DEVICE);
+ }
+ for (i = 0; i < GRETH_TXBD_NUM; i++, tx_bdp++) {
+ kfree(greth->tx_bufs[i]);
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&tx_bdp->addr),
+ MAX_FRAME_SIZE,
+ DMA_TO_DEVICE);
+ }
+ }
+}
+
+static int greth_init_rings(struct greth_private *greth)
+{
+ struct sk_buff *skb;
+ struct greth_bd *rx_bd, *tx_bd;
+ u32 dma_addr;
+ int i;
+
+ rx_bd = greth->rx_bd_base;
+ tx_bd = greth->tx_bd_base;
+
+ /* Initialize descriptor rings and buffers */
+ if (greth->gbit_mac) {
+
+ for (i = 0; i < GRETH_RXBD_NUM; i++) {
+ skb = netdev_alloc_skb(greth->netdev, MAX_FRAME_SIZE+NET_IP_ALIGN);
+ if (skb == NULL) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Error allocating DMA ring.\n");
+ goto cleanup;
+ }
+ skb_reserve(skb, NET_IP_ALIGN);
+ dma_addr = dma_map_single(greth->dev,
+ skb->data,
+ MAX_FRAME_SIZE+NET_IP_ALIGN,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(greth->dev, dma_addr)) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Could not create initial DMA mapping\n");
+ goto cleanup;
+ }
+ greth->rx_skbuff[i] = skb;
+ greth_write_bd(&rx_bd[i].addr, dma_addr);
+ greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE);
+ }
+
+ } else {
+
+ /* 10/100 MAC uses a fixed set of buffers and copy to/from SKBs */
+ for (i = 0; i < GRETH_RXBD_NUM; i++) {
+
+ greth->rx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL);
+
+ if (greth->rx_bufs[i] == NULL) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Error allocating DMA ring.\n");
+ goto cleanup;
+ }
+
+ dma_addr = dma_map_single(greth->dev,
+ greth->rx_bufs[i],
+ MAX_FRAME_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(greth->dev, dma_addr)) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Could not create initial DMA mapping\n");
+ goto cleanup;
+ }
+ greth_write_bd(&rx_bd[i].addr, dma_addr);
+ greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE);
+ }
+ for (i = 0; i < GRETH_TXBD_NUM; i++) {
+
+ greth->tx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL);
+
+ if (greth->tx_bufs[i] == NULL) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Error allocating DMA ring.\n");
+ goto cleanup;
+ }
+
+ dma_addr = dma_map_single(greth->dev,
+ greth->tx_bufs[i],
+ MAX_FRAME_SIZE,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(greth->dev, dma_addr)) {
+ if (netif_msg_ifup(greth))
+ dev_err(greth->dev, "Could not create initial DMA mapping\n");
+ goto cleanup;
+ }
+ greth_write_bd(&tx_bd[i].addr, dma_addr);
+ greth_write_bd(&tx_bd[i].stat, 0);
+ }
+ }
+ greth_write_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat,
+ greth_read_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat) | GRETH_BD_WR);
+
+ /* Initialize pointers. */
+ greth->rx_cur = 0;
+ greth->tx_next = 0;
+ greth->tx_last = 0;
+ greth->tx_free = GRETH_TXBD_NUM;
+
+ /* Initialize descriptor base address */
+ GRETH_REGSAVE(greth->regs->tx_desc_p, greth->tx_bd_base_phys);
+ GRETH_REGSAVE(greth->regs->rx_desc_p, greth->rx_bd_base_phys);
+
+ return 0;
+
+cleanup:
+ greth_clean_rings(greth);
+ return -ENOMEM;
+}
+
+static int greth_open(struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ int err;
+
+ err = greth_init_rings(greth);
+ if (err) {
+ if (netif_msg_ifup(greth))
+ dev_err(&dev->dev, "Could not allocate memory for DMA rings\n");
+ return err;
+ }
+
+ err = request_irq(greth->irq, greth_interrupt, 0, "eth", (void *) dev);
+ if (err) {
+ if (netif_msg_ifup(greth))
+ dev_err(&dev->dev, "Could not allocate interrupt %d\n", dev->irq);
+ greth_clean_rings(greth);
+ return err;
+ }
+
+ if (netif_msg_ifup(greth))
+ dev_dbg(&dev->dev, " starting queue\n");
+ netif_start_queue(dev);
+
+ GRETH_REGSAVE(greth->regs->status, 0xFF);
+
+ napi_enable(&greth->napi);
+
+ greth_enable_irqs(greth);
+ greth_enable_tx(greth);
+ greth_enable_rx(greth);
+ return 0;
+
+}
+
+static int greth_close(struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+
+ napi_disable(&greth->napi);
+
+ greth_disable_irqs(greth);
+ greth_disable_tx(greth);
+ greth_disable_rx(greth);
+
+ netif_stop_queue(dev);
+
+ free_irq(greth->irq, (void *) dev);
+
+ greth_clean_rings(greth);
+
+ return 0;
+}
+
+static netdev_tx_t
+greth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct greth_bd *bdp;
+ int err = NETDEV_TX_OK;
+ u32 status, dma_addr, ctrl;
+ unsigned long flags;
+
+ /* Clean TX Ring */
+ greth_clean_tx(greth->netdev);
+
+ if (unlikely(greth->tx_free <= 0)) {
+ spin_lock_irqsave(&greth->devlock, flags);/*save from poll/irq*/
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ /* Enable TX IRQ only if not already in poll() routine */
+ if (ctrl & GRETH_RXI)
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_TXI);
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (netif_msg_pktdata(greth))
+ greth_print_tx_packet(skb);
+
+
+ if (unlikely(skb->len > MAX_FRAME_SIZE)) {
+ dev->stats.tx_errors++;
+ goto out;
+ }
+
+ bdp = greth->tx_bd_base + greth->tx_next;
+ dma_addr = greth_read_bd(&bdp->addr);
+
+ memcpy((unsigned char *) phys_to_virt(dma_addr), skb->data, skb->len);
+
+ dma_sync_single_for_device(greth->dev, dma_addr, skb->len, DMA_TO_DEVICE);
+
+ status = GRETH_BD_EN | GRETH_BD_IE | (skb->len & GRETH_BD_LEN);
+ greth->tx_bufs_length[greth->tx_next] = skb->len & GRETH_BD_LEN;
+
+ /* Wrap around descriptor ring */
+ if (greth->tx_next == GRETH_TXBD_NUM_MASK) {
+ status |= GRETH_BD_WR;
+ }
+
+ greth->tx_next = NEXT_TX(greth->tx_next);
+ greth->tx_free--;
+
+ /* Write descriptor control word and enable transmission */
+ greth_write_bd(&bdp->stat, status);
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
+ greth_enable_tx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+
+out:
+ dev_kfree_skb(skb);
+ return err;
+}
+
+
+static netdev_tx_t
+greth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct greth_bd *bdp;
+ u32 status = 0, dma_addr, ctrl;
+ int curr_tx, nr_frags, i, err = NETDEV_TX_OK;
+ unsigned long flags;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ /* Clean TX Ring */
+ greth_clean_tx_gbit(dev);
+
+ if (greth->tx_free < nr_frags + 1) {
+ spin_lock_irqsave(&greth->devlock, flags);/*save from poll/irq*/
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ /* Enable TX IRQ only if not already in poll() routine */
+ if (ctrl & GRETH_RXI)
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_TXI);
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ err = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ if (netif_msg_pktdata(greth))
+ greth_print_tx_packet(skb);
+
+ if (unlikely(skb->len > MAX_FRAME_SIZE)) {
+ dev->stats.tx_errors++;
+ goto out;
+ }
+
+ /* Save skb pointer. */
+ greth->tx_skbuff[greth->tx_next] = skb;
+
+ /* Linear buf */
+ if (nr_frags != 0)
+ status = GRETH_TXBD_MORE;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ status |= GRETH_TXBD_CSALL;
+ status |= skb_headlen(skb) & GRETH_BD_LEN;
+ if (greth->tx_next == GRETH_TXBD_NUM_MASK)
+ status |= GRETH_BD_WR;
+
+
+ bdp = greth->tx_bd_base + greth->tx_next;
+ greth_write_bd(&bdp->stat, status);
+ dma_addr = dma_map_single(greth->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(greth->dev, dma_addr)))
+ goto map_error;
+
+ greth_write_bd(&bdp->addr, dma_addr);
+
+ curr_tx = NEXT_TX(greth->tx_next);
+
+ /* Frags */
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ greth->tx_skbuff[curr_tx] = NULL;
+ bdp = greth->tx_bd_base + curr_tx;
+
+ status = GRETH_BD_EN;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ status |= GRETH_TXBD_CSALL;
+ status |= skb_frag_size(frag) & GRETH_BD_LEN;
+
+ /* Wrap around descriptor ring */
+ if (curr_tx == GRETH_TXBD_NUM_MASK)
+ status |= GRETH_BD_WR;
+
+ /* More fragments left */
+ if (i < nr_frags - 1)
+ status |= GRETH_TXBD_MORE;
+ else
+ status |= GRETH_BD_IE; /* enable IRQ on last fragment */
+
+ greth_write_bd(&bdp->stat, status);
+
+ dma_addr = skb_frag_dma_map(greth->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(greth->dev, dma_addr)))
+ goto frag_map_error;
+
+ greth_write_bd(&bdp->addr, dma_addr);
+
+ curr_tx = NEXT_TX(curr_tx);
+ }
+
+ wmb();
+
+ /* Enable the descriptor chain by enabling the first descriptor */
+ bdp = greth->tx_bd_base + greth->tx_next;
+ greth_write_bd(&bdp->stat, greth_read_bd(&bdp->stat) | GRETH_BD_EN);
+ greth->tx_next = curr_tx;
+ greth->tx_free -= nr_frags + 1;
+
+ wmb();
+
+ spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
+ greth_enable_tx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+
+ return NETDEV_TX_OK;
+
+frag_map_error:
+ /* Unmap SKB mappings that succeeded and disable descriptor */
+ for (i = 0; greth->tx_next + i != curr_tx; i++) {
+ bdp = greth->tx_bd_base + greth->tx_next + i;
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&bdp->addr),
+ greth_read_bd(&bdp->stat) & GRETH_BD_LEN,
+ DMA_TO_DEVICE);
+ greth_write_bd(&bdp->stat, 0);
+ }
+map_error:
+ if (net_ratelimit())
+ dev_warn(greth->dev, "Could not create TX DMA mapping\n");
+ dev_kfree_skb(skb);
+out:
+ return err;
+}
+
+static irqreturn_t greth_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct greth_private *greth;
+ u32 status, ctrl;
+ irqreturn_t retval = IRQ_NONE;
+
+ greth = netdev_priv(dev);
+
+ spin_lock(&greth->devlock);
+
+ /* Get the interrupt events that caused us to be here. */
+ status = GRETH_REGLOAD(greth->regs->status);
+
+ /* Must see if interrupts are enabled also, INT_TX|INT_RX flags may be
+ * set regardless of whether IRQ is enabled or not. Especially
+ * important when shared IRQ.
+ */
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+
+ /* Handle rx and tx interrupts through poll */
+ if (((status & (GRETH_INT_RE | GRETH_INT_RX)) && (ctrl & GRETH_RXI)) ||
+ ((status & (GRETH_INT_TE | GRETH_INT_TX)) && (ctrl & GRETH_TXI))) {
+ retval = IRQ_HANDLED;
+
+ /* Disable interrupts and schedule poll() */
+ greth_disable_irqs(greth);
+ napi_schedule(&greth->napi);
+ }
+
+ mmiowb();
+ spin_unlock(&greth->devlock);
+
+ return retval;
+}
+
+static void greth_clean_tx(struct net_device *dev)
+{
+ struct greth_private *greth;
+ struct greth_bd *bdp;
+ u32 stat;
+
+ greth = netdev_priv(dev);
+
+ while (1) {
+ bdp = greth->tx_bd_base + greth->tx_last;
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
+ mb();
+ stat = greth_read_bd(&bdp->stat);
+
+ if (unlikely(stat & GRETH_BD_EN))
+ break;
+
+ if (greth->tx_free == GRETH_TXBD_NUM)
+ break;
+
+ /* Check status for errors */
+ if (unlikely(stat & GRETH_TXBD_STATUS)) {
+ dev->stats.tx_errors++;
+ if (stat & GRETH_TXBD_ERR_AL)
+ dev->stats.tx_aborted_errors++;
+ if (stat & GRETH_TXBD_ERR_UE)
+ dev->stats.tx_fifo_errors++;
+ }
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += greth->tx_bufs_length[greth->tx_last];
+ greth->tx_last = NEXT_TX(greth->tx_last);
+ greth->tx_free++;
+ }
+
+ if (greth->tx_free > 0) {
+ netif_wake_queue(dev);
+ }
+
+}
+
+static inline void greth_update_tx_stats(struct net_device *dev, u32 stat)
+{
+ /* Check status for errors */
+ if (unlikely(stat & GRETH_TXBD_STATUS)) {
+ dev->stats.tx_errors++;
+ if (stat & GRETH_TXBD_ERR_AL)
+ dev->stats.tx_aborted_errors++;
+ if (stat & GRETH_TXBD_ERR_UE)
+ dev->stats.tx_fifo_errors++;
+ if (stat & GRETH_TXBD_ERR_LC)
+ dev->stats.tx_aborted_errors++;
+ }
+ dev->stats.tx_packets++;
+}
+
+static void greth_clean_tx_gbit(struct net_device *dev)
+{
+ struct greth_private *greth;
+ struct greth_bd *bdp, *bdp_last_frag;
+ struct sk_buff *skb;
+ u32 stat;
+ int nr_frags, i;
+
+ greth = netdev_priv(dev);
+
+ while (greth->tx_free < GRETH_TXBD_NUM) {
+
+ skb = greth->tx_skbuff[greth->tx_last];
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ /* We only clean fully completed SKBs */
+ bdp_last_frag = greth->tx_bd_base + SKIP_TX(greth->tx_last, nr_frags);
+
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
+ mb();
+ stat = greth_read_bd(&bdp_last_frag->stat);
+
+ if (stat & GRETH_BD_EN)
+ break;
+
+ greth->tx_skbuff[greth->tx_last] = NULL;
+
+ greth_update_tx_stats(dev, stat);
+ dev->stats.tx_bytes += skb->len;
+
+ bdp = greth->tx_bd_base + greth->tx_last;
+
+ greth->tx_last = NEXT_TX(greth->tx_last);
+
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&bdp->addr),
+ skb_headlen(skb),
+ DMA_TO_DEVICE);
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ bdp = greth->tx_bd_base + greth->tx_last;
+
+ dma_unmap_page(greth->dev,
+ greth_read_bd(&bdp->addr),
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+
+ greth->tx_last = NEXT_TX(greth->tx_last);
+ }
+ greth->tx_free += nr_frags+1;
+ dev_kfree_skb(skb);
+ }
+
+ if (netif_queue_stopped(dev) && (greth->tx_free > (MAX_SKB_FRAGS+1)))
+ netif_wake_queue(dev);
+}
+
+static int greth_rx(struct net_device *dev, int limit)
+{
+ struct greth_private *greth;
+ struct greth_bd *bdp;
+ struct sk_buff *skb;
+ int pkt_len;
+ int bad, count;
+ u32 status, dma_addr;
+ unsigned long flags;
+
+ greth = netdev_priv(dev);
+
+ for (count = 0; count < limit; ++count) {
+
+ bdp = greth->rx_bd_base + greth->rx_cur;
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
+ mb();
+ status = greth_read_bd(&bdp->stat);
+
+ if (unlikely(status & GRETH_BD_EN)) {
+ break;
+ }
+
+ dma_addr = greth_read_bd(&bdp->addr);
+ bad = 0;
+
+ /* Check status for errors. */
+ if (unlikely(status & GRETH_RXBD_STATUS)) {
+ if (status & GRETH_RXBD_ERR_FT) {
+ dev->stats.rx_length_errors++;
+ bad = 1;
+ }
+ if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) {
+ dev->stats.rx_frame_errors++;
+ bad = 1;
+ }
+ if (status & GRETH_RXBD_ERR_CRC) {
+ dev->stats.rx_crc_errors++;
+ bad = 1;
+ }
+ }
+ if (unlikely(bad)) {
+ dev->stats.rx_errors++;
+
+ } else {
+
+ pkt_len = status & GRETH_BD_LEN;
+
+ skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN);
+
+ if (unlikely(skb == NULL)) {
+
+ if (net_ratelimit())
+ dev_warn(&dev->dev, "low on memory - " "packet dropped\n");
+
+ dev->stats.rx_dropped++;
+
+ } else {
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ dma_sync_single_for_cpu(greth->dev,
+ dma_addr,
+ pkt_len,
+ DMA_FROM_DEVICE);
+
+ if (netif_msg_pktdata(greth))
+ greth_print_rx_packet(phys_to_virt(dma_addr), pkt_len);
+
+ memcpy(skb_put(skb, pkt_len), phys_to_virt(dma_addr), pkt_len);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ dev->stats.rx_bytes += pkt_len;
+ dev->stats.rx_packets++;
+ netif_receive_skb(skb);
+ }
+ }
+
+ status = GRETH_BD_EN | GRETH_BD_IE;
+ if (greth->rx_cur == GRETH_RXBD_NUM_MASK) {
+ status |= GRETH_BD_WR;
+ }
+
+ wmb();
+ greth_write_bd(&bdp->stat, status);
+
+ dma_sync_single_for_device(greth->dev, dma_addr, MAX_FRAME_SIZE, DMA_FROM_DEVICE);
+
+ spin_lock_irqsave(&greth->devlock, flags); /* save from XMIT */
+ greth_enable_rx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+
+ greth->rx_cur = NEXT_RX(greth->rx_cur);
+ }
+
+ return count;
+}
+
+static inline int hw_checksummed(u32 status)
+{
+
+ if (status & GRETH_RXBD_IP_FRAG)
+ return 0;
+
+ if (status & GRETH_RXBD_IP && status & GRETH_RXBD_IP_CSERR)
+ return 0;
+
+ if (status & GRETH_RXBD_UDP && status & GRETH_RXBD_UDP_CSERR)
+ return 0;
+
+ if (status & GRETH_RXBD_TCP && status & GRETH_RXBD_TCP_CSERR)
+ return 0;
+
+ return 1;
+}
+
+static int greth_rx_gbit(struct net_device *dev, int limit)
+{
+ struct greth_private *greth;
+ struct greth_bd *bdp;
+ struct sk_buff *skb, *newskb;
+ int pkt_len;
+ int bad, count = 0;
+ u32 status, dma_addr;
+ unsigned long flags;
+
+ greth = netdev_priv(dev);
+
+ for (count = 0; count < limit; ++count) {
+
+ bdp = greth->rx_bd_base + greth->rx_cur;
+ skb = greth->rx_skbuff[greth->rx_cur];
+ GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
+ mb();
+ status = greth_read_bd(&bdp->stat);
+ bad = 0;
+
+ if (status & GRETH_BD_EN)
+ break;
+
+ /* Check status for errors. */
+ if (unlikely(status & GRETH_RXBD_STATUS)) {
+
+ if (status & GRETH_RXBD_ERR_FT) {
+ dev->stats.rx_length_errors++;
+ bad = 1;
+ } else if (status &
+ (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE | GRETH_RXBD_ERR_LE)) {
+ dev->stats.rx_frame_errors++;
+ bad = 1;
+ } else if (status & GRETH_RXBD_ERR_CRC) {
+ dev->stats.rx_crc_errors++;
+ bad = 1;
+ }
+ }
+
+ /* Allocate new skb to replace current, not needed if the
+ * current skb can be reused */
+ if (!bad && (newskb=netdev_alloc_skb(dev, MAX_FRAME_SIZE + NET_IP_ALIGN))) {
+ skb_reserve(newskb, NET_IP_ALIGN);
+
+ dma_addr = dma_map_single(greth->dev,
+ newskb->data,
+ MAX_FRAME_SIZE + NET_IP_ALIGN,
+ DMA_FROM_DEVICE);
+
+ if (!dma_mapping_error(greth->dev, dma_addr)) {
+ /* Process the incoming frame. */
+ pkt_len = status & GRETH_BD_LEN;
+
+ dma_unmap_single(greth->dev,
+ greth_read_bd(&bdp->addr),
+ MAX_FRAME_SIZE + NET_IP_ALIGN,
+ DMA_FROM_DEVICE);
+
+ if (netif_msg_pktdata(greth))
+ greth_print_rx_packet(phys_to_virt(greth_read_bd(&bdp->addr)), pkt_len);
+
+ skb_put(skb, pkt_len);
+
+ if (dev->features & NETIF_F_RXCSUM && hw_checksummed(status))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ netif_receive_skb(skb);
+
+ greth->rx_skbuff[greth->rx_cur] = newskb;
+ greth_write_bd(&bdp->addr, dma_addr);
+ } else {
+ if (net_ratelimit())
+ dev_warn(greth->dev, "Could not create DMA mapping, dropping packet\n");
+ dev_kfree_skb(newskb);
+ /* reusing current skb, so it is a drop */
+ dev->stats.rx_dropped++;
+ }
+ } else if (bad) {
+ /* Bad Frame transfer, the skb is reused */
+ dev->stats.rx_dropped++;
+ } else {
+ /* Failed Allocating a new skb. This is rather stupid
+ * but the current "filled" skb is reused, as if
+ * transfer failure. One could argue that RX descriptor
+ * table handling should be divided into cleaning and
+ * filling as the TX part of the driver
+ */
+ if (net_ratelimit())
+ dev_warn(greth->dev, "Could not allocate SKB, dropping packet\n");
+ /* reusing current skb, so it is a drop */
+ dev->stats.rx_dropped++;
+ }
+
+ status = GRETH_BD_EN | GRETH_BD_IE;
+ if (greth->rx_cur == GRETH_RXBD_NUM_MASK) {
+ status |= GRETH_BD_WR;
+ }
+
+ wmb();
+ greth_write_bd(&bdp->stat, status);
+ spin_lock_irqsave(&greth->devlock, flags);
+ greth_enable_rx(greth);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ greth->rx_cur = NEXT_RX(greth->rx_cur);
+ }
+
+ return count;
+
+}
+
+static int greth_poll(struct napi_struct *napi, int budget)
+{
+ struct greth_private *greth;
+ int work_done = 0;
+ unsigned long flags;
+ u32 mask, ctrl;
+ greth = container_of(napi, struct greth_private, napi);
+
+restart_txrx_poll:
+ if (netif_queue_stopped(greth->netdev)) {
+ if (greth->gbit_mac)
+ greth_clean_tx_gbit(greth->netdev);
+ else
+ greth_clean_tx(greth->netdev);
+ }
+
+ if (greth->gbit_mac) {
+ work_done += greth_rx_gbit(greth->netdev, budget - work_done);
+ } else {
+ work_done += greth_rx(greth->netdev, budget - work_done);
+ }
+
+ if (work_done < budget) {
+
+ spin_lock_irqsave(&greth->devlock, flags);
+
+ ctrl = GRETH_REGLOAD(greth->regs->control);
+ if (netif_queue_stopped(greth->netdev)) {
+ GRETH_REGSAVE(greth->regs->control,
+ ctrl | GRETH_TXI | GRETH_RXI);
+ mask = GRETH_INT_RX | GRETH_INT_RE |
+ GRETH_INT_TX | GRETH_INT_TE;
+ } else {
+ GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_RXI);
+ mask = GRETH_INT_RX | GRETH_INT_RE;
+ }
+
+ if (GRETH_REGLOAD(greth->regs->status) & mask) {
+ GRETH_REGSAVE(greth->regs->control, ctrl);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ goto restart_txrx_poll;
+ } else {
+ __napi_complete(napi);
+ spin_unlock_irqrestore(&greth->devlock, flags);
+ }
+ }
+
+ return work_done;
+}
+
+static int greth_set_mac_add(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct greth_private *greth;
+ struct greth_regs *regs;
+
+ greth = netdev_priv(dev);
+ regs = greth->regs;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ GRETH_REGSAVE(regs->esa_msb, dev->dev_addr[0] << 8 | dev->dev_addr[1]);
+ GRETH_REGSAVE(regs->esa_lsb, dev->dev_addr[2] << 24 | dev->dev_addr[3] << 16 |
+ dev->dev_addr[4] << 8 | dev->dev_addr[5]);
+
+ return 0;
+}
+
+static u32 greth_hash_get_index(__u8 *addr)
+{
+ return (ether_crc(6, addr)) & 0x3F;
+}
+
+static void greth_set_hash_filter(struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ struct greth_private *greth = netdev_priv(dev);
+ struct greth_regs *regs = greth->regs;
+ u32 mc_filter[2];
+ unsigned int bitnr;
+
+ mc_filter[0] = mc_filter[1] = 0;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ bitnr = greth_hash_get_index(ha->addr);
+ mc_filter[bitnr >> 5] |= 1 << (bitnr & 31);
+ }
+
+ GRETH_REGSAVE(regs->hash_msb, mc_filter[1]);
+ GRETH_REGSAVE(regs->hash_lsb, mc_filter[0]);
+}
+
+static void greth_set_multicast_list(struct net_device *dev)
+{
+ int cfg;
+ struct greth_private *greth = netdev_priv(dev);
+ struct greth_regs *regs = greth->regs;
+
+ cfg = GRETH_REGLOAD(regs->control);
+ if (dev->flags & IFF_PROMISC)
+ cfg |= GRETH_CTRL_PR;
+ else
+ cfg &= ~GRETH_CTRL_PR;
+
+ if (greth->multicast) {
+ if (dev->flags & IFF_ALLMULTI) {
+ GRETH_REGSAVE(regs->hash_msb, -1);
+ GRETH_REGSAVE(regs->hash_lsb, -1);
+ cfg |= GRETH_CTRL_MCEN;
+ GRETH_REGSAVE(regs->control, cfg);
+ return;
+ }
+
+ if (netdev_mc_empty(dev)) {
+ cfg &= ~GRETH_CTRL_MCEN;
+ GRETH_REGSAVE(regs->control, cfg);
+ return;
+ }
+
+ /* Setup multicast filter */
+ greth_set_hash_filter(dev);
+ cfg |= GRETH_CTRL_MCEN;
+ }
+ GRETH_REGSAVE(regs->control, cfg);
+}
+
+static u32 greth_get_msglevel(struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ return greth->msg_enable;
+}
+
+static void greth_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ greth->msg_enable = value;
+}
+static int greth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct phy_device *phy = greth->phy;
+
+ if (!phy)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phy, cmd);
+}
+
+static int greth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct phy_device *phy = greth->phy;
+
+ if (!phy)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phy, cmd);
+}
+
+static int greth_get_regs_len(struct net_device *dev)
+{
+ return sizeof(struct greth_regs);
+}
+
+static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct greth_private *greth = netdev_priv(dev);
+
+ strlcpy(info->driver, dev_driver_string(greth->dev),
+ sizeof(info->driver));
+ strlcpy(info->version, "revision: 1.0", sizeof(info->version));
+ strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info));
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+ info->eedump_len = 0;
+ info->regdump_len = sizeof(struct greth_regs);
+}
+
+static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
+{
+ int i;
+ struct greth_private *greth = netdev_priv(dev);
+ u32 __iomem *greth_regs = (u32 __iomem *) greth->regs;
+ u32 *buff = p;
+
+ for (i = 0; i < sizeof(struct greth_regs) / sizeof(u32); i++)
+ buff[i] = greth_read_bd(&greth_regs[i]);
+}
+
+static const struct ethtool_ops greth_ethtool_ops = {
+ .get_msglevel = greth_get_msglevel,
+ .set_msglevel = greth_set_msglevel,
+ .get_settings = greth_get_settings,
+ .set_settings = greth_set_settings,
+ .get_drvinfo = greth_get_drvinfo,
+ .get_regs_len = greth_get_regs_len,
+ .get_regs = greth_get_regs,
+ .get_link = ethtool_op_get_link,
+};
+
+static struct net_device_ops greth_netdev_ops = {
+ .ndo_open = greth_open,
+ .ndo_stop = greth_close,
+ .ndo_start_xmit = greth_start_xmit,
+ .ndo_set_mac_address = greth_set_mac_add,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static inline int wait_for_mdio(struct greth_private *greth)
+{
+ unsigned long timeout = jiffies + 4*HZ/100;
+ while (GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_BUSY) {
+ if (time_after(jiffies, timeout))
+ return 0;
+ }
+ return 1;
+}
+
+static int greth_mdio_read(struct mii_bus *bus, int phy, int reg)
+{
+ struct greth_private *greth = bus->priv;
+ int data;
+
+ if (!wait_for_mdio(greth))
+ return -EBUSY;
+
+ GRETH_REGSAVE(greth->regs->mdio, ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 2);
+
+ if (!wait_for_mdio(greth))
+ return -EBUSY;
+
+ if (!(GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_NVALID)) {
+ data = (GRETH_REGLOAD(greth->regs->mdio) >> 16) & 0xFFFF;
+ return data;
+
+ } else {
+ return -1;
+ }
+}
+
+static int greth_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+ struct greth_private *greth = bus->priv;
+
+ if (!wait_for_mdio(greth))
+ return -EBUSY;
+
+ GRETH_REGSAVE(greth->regs->mdio,
+ ((val & 0xFFFF) << 16) | ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 1);
+
+ if (!wait_for_mdio(greth))
+ return -EBUSY;
+
+ return 0;
+}
+
+static void greth_link_change(struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct phy_device *phydev = greth->phy;
+ unsigned long flags;
+ int status_change = 0;
+ u32 ctrl;
+
+ spin_lock_irqsave(&greth->devlock, flags);
+
+ if (phydev->link) {
+
+ if ((greth->speed != phydev->speed) || (greth->duplex != phydev->duplex)) {
+ ctrl = GRETH_REGLOAD(greth->regs->control) &
+ ~(GRETH_CTRL_FD | GRETH_CTRL_SP | GRETH_CTRL_GB);
+
+ if (phydev->duplex)
+ ctrl |= GRETH_CTRL_FD;
+
+ if (phydev->speed == SPEED_100)
+ ctrl |= GRETH_CTRL_SP;
+ else if (phydev->speed == SPEED_1000)
+ ctrl |= GRETH_CTRL_GB;
+
+ GRETH_REGSAVE(greth->regs->control, ctrl);
+ greth->speed = phydev->speed;
+ greth->duplex = phydev->duplex;
+ status_change = 1;
+ }
+ }
+
+ if (phydev->link != greth->link) {
+ if (!phydev->link) {
+ greth->speed = 0;
+ greth->duplex = -1;
+ }
+ greth->link = phydev->link;
+
+ status_change = 1;
+ }
+
+ spin_unlock_irqrestore(&greth->devlock, flags);
+
+ if (status_change) {
+ if (phydev->link)
+ pr_debug("%s: link up (%d/%s)\n",
+ dev->name, phydev->speed,
+ DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
+ else
+ pr_debug("%s: link down\n", dev->name);
+ }
+}
+
+static int greth_mdio_probe(struct net_device *dev)
+{
+ struct greth_private *greth = netdev_priv(dev);
+ struct phy_device *phy = NULL;
+ int ret;
+
+ /* Find the first PHY */
+ phy = phy_find_first(greth->mdio);
+
+ if (!phy) {
+ if (netif_msg_probe(greth))
+ dev_err(&dev->dev, "no PHY found\n");
+ return -ENXIO;
+ }
+
+ ret = phy_connect_direct(dev, phy, &greth_link_change,
+ greth->gbit_mac ? PHY_INTERFACE_MODE_GMII : PHY_INTERFACE_MODE_MII);
+ if (ret) {
+ if (netif_msg_ifup(greth))
+ dev_err(&dev->dev, "could not attach to PHY\n");
+ return ret;
+ }
+
+ if (greth->gbit_mac)
+ phy->supported &= PHY_GBIT_FEATURES;
+ else
+ phy->supported &= PHY_BASIC_FEATURES;
+
+ phy->advertising = phy->supported;
+
+ greth->link = 0;
+ greth->speed = 0;
+ greth->duplex = -1;
+ greth->phy = phy;
+
+ return 0;
+}
+
+static inline int phy_aneg_done(struct phy_device *phydev)
+{
+ int retval;
+
+ retval = phy_read(phydev, MII_BMSR);
+
+ return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
+}
+
+static int greth_mdio_init(struct greth_private *greth)
+{
+ int ret, phy;
+ unsigned long timeout;
+
+ greth->mdio = mdiobus_alloc();
+ if (!greth->mdio) {
+ return -ENOMEM;
+ }
+
+ greth->mdio->name = "greth-mdio";
+ snprintf(greth->mdio->id, MII_BUS_ID_SIZE, "%s-%d", greth->mdio->name, greth->irq);
+ greth->mdio->read = greth_mdio_read;
+ greth->mdio->write = greth_mdio_write;
+ greth->mdio->priv = greth;
+
+ greth->mdio->irq = greth->mdio_irqs;
+
+ for (phy = 0; phy < PHY_MAX_ADDR; phy++)
+ greth->mdio->irq[phy] = PHY_POLL;
+
+ ret = mdiobus_register(greth->mdio);
+ if (ret) {
+ goto error;
+ }
+
+ ret = greth_mdio_probe(greth->netdev);
+ if (ret) {
+ if (netif_msg_probe(greth))
+ dev_err(&greth->netdev->dev, "failed to probe MDIO bus\n");
+ goto unreg_mdio;
+ }
+
+ phy_start(greth->phy);
+
+ /* If Ethernet debug link is used make autoneg happen right away */
+ if (greth->edcl && greth_edcl == 1) {
+ phy_start_aneg(greth->phy);
+ timeout = jiffies + 6*HZ;
+ while (!phy_aneg_done(greth->phy) && time_before(jiffies, timeout)) {
+ }
+ phy_read_status(greth->phy);
+ greth_link_change(greth->netdev);
+ }
+
+ return 0;
+
+unreg_mdio:
+ mdiobus_unregister(greth->mdio);
+error:
+ mdiobus_free(greth->mdio);
+ return ret;
+}
+
+/* Initialize the GRETH MAC */
+static int greth_of_probe(struct platform_device *ofdev)
+{
+ struct net_device *dev;
+ struct greth_private *greth;
+ struct greth_regs *regs;
+
+ int i;
+ int err;
+ int tmp;
+ unsigned long timeout;
+
+ dev = alloc_etherdev(sizeof(struct greth_private));
+
+ if (dev == NULL)
+ return -ENOMEM;
+
+ greth = netdev_priv(dev);
+ greth->netdev = dev;
+ greth->dev = &ofdev->dev;
+
+ if (greth_debug > 0)
+ greth->msg_enable = greth_debug;
+ else
+ greth->msg_enable = GRETH_DEF_MSG_ENABLE;
+
+ spin_lock_init(&greth->devlock);
+
+ greth->regs = of_ioremap(&ofdev->resource[0], 0,
+ resource_size(&ofdev->resource[0]),
+ "grlib-greth regs");
+
+ if (greth->regs == NULL) {
+ if (netif_msg_probe(greth))
+ dev_err(greth->dev, "ioremap failure.\n");
+ err = -EIO;
+ goto error1;
+ }
+
+ regs = greth->regs;
+ greth->irq = ofdev->archdata.irqs[0];
+
+ dev_set_drvdata(greth->dev, dev);
+ SET_NETDEV_DEV(dev, greth->dev);
+
+ if (netif_msg_probe(greth))
+ dev_dbg(greth->dev, "resetting controller.\n");
+
+ /* Reset the controller. */
+ GRETH_REGSAVE(regs->control, GRETH_RESET);
+
+ /* Wait for MAC to reset itself */
+ timeout = jiffies + HZ/100;
+ while (GRETH_REGLOAD(regs->control) & GRETH_RESET) {
+ if (time_after(jiffies, timeout)) {
+ err = -EIO;
+ if (netif_msg_probe(greth))
+ dev_err(greth->dev, "timeout when waiting for reset.\n");
+ goto error2;
+ }
+ }
+
+ /* Get default PHY address */
+ greth->phyaddr = (GRETH_REGLOAD(regs->mdio) >> 11) & 0x1F;
+
+ /* Check if we have GBIT capable MAC */
+ tmp = GRETH_REGLOAD(regs->control);
+ greth->gbit_mac = (tmp >> 27) & 1;
+
+ /* Check for multicast capability */
+ greth->multicast = (tmp >> 25) & 1;
+
+ greth->edcl = (tmp >> 31) & 1;
+
+ /* If we have EDCL we disable the EDCL speed-duplex FSM so
+ * it doesn't interfere with the software */
+ if (greth->edcl != 0)
+ GRETH_REGORIN(regs->control, GRETH_CTRL_DISDUPLEX);
+
+ /* Check if MAC can handle MDIO interrupts */
+ greth->mdio_int_en = (tmp >> 26) & 1;
+
+ err = greth_mdio_init(greth);
+ if (err) {
+ if (netif_msg_probe(greth))
+ dev_err(greth->dev, "failed to register MDIO bus\n");
+ goto error2;
+ }
+
+ /* Allocate TX descriptor ring in coherent memory */
+ greth->tx_bd_base = dma_zalloc_coherent(greth->dev, 1024,
+ &greth->tx_bd_base_phys,
+ GFP_KERNEL);
+ if (!greth->tx_bd_base) {
+ err = -ENOMEM;
+ goto error3;
+ }
+
+ /* Allocate RX descriptor ring in coherent memory */
+ greth->rx_bd_base = dma_zalloc_coherent(greth->dev, 1024,
+ &greth->rx_bd_base_phys,
+ GFP_KERNEL);
+ if (!greth->rx_bd_base) {
+ err = -ENOMEM;
+ goto error4;
+ }
+
+ /* Get MAC address from: module param, OF property or ID prom */
+ for (i = 0; i < 6; i++) {
+ if (macaddr[i] != 0)
+ break;
+ }
+ if (i == 6) {
+ const unsigned char *addr;
+ int len;
+ addr = of_get_property(ofdev->dev.of_node, "local-mac-address",
+ &len);
+ if (addr != NULL && len == 6) {
+ for (i = 0; i < 6; i++)
+ macaddr[i] = (unsigned int) addr[i];
+ } else {
+#ifdef CONFIG_SPARC
+ for (i = 0; i < 6; i++)
+ macaddr[i] = (unsigned int) idprom->id_ethaddr[i];
+#endif
+ }
+ }
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = macaddr[i];
+
+ macaddr[5]++;
+
+ if (!is_valid_ether_addr(&dev->dev_addr[0])) {
+ if (netif_msg_probe(greth))
+ dev_err(greth->dev, "no valid ethernet address, aborting.\n");
+ err = -EINVAL;
+ goto error5;
+ }
+
+ GRETH_REGSAVE(regs->esa_msb, dev->dev_addr[0] << 8 | dev->dev_addr[1]);
+ GRETH_REGSAVE(regs->esa_lsb, dev->dev_addr[2] << 24 | dev->dev_addr[3] << 16 |
+ dev->dev_addr[4] << 8 | dev->dev_addr[5]);
+
+ /* Clear all pending interrupts except PHY irq */
+ GRETH_REGSAVE(regs->status, 0xFF);
+
+ if (greth->gbit_mac) {
+ dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_RXCSUM;
+ dev->features = dev->hw_features | NETIF_F_HIGHDMA;
+ greth_netdev_ops.ndo_start_xmit = greth_start_xmit_gbit;
+ }
+
+ if (greth->multicast) {
+ greth_netdev_ops.ndo_set_rx_mode = greth_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+ } else {
+ dev->flags &= ~IFF_MULTICAST;
+ }
+
+ dev->netdev_ops = &greth_netdev_ops;
+ dev->ethtool_ops = &greth_ethtool_ops;
+
+ err = register_netdev(dev);
+ if (err) {
+ if (netif_msg_probe(greth))
+ dev_err(greth->dev, "netdevice registration failed.\n");
+ goto error5;
+ }
+
+ /* setup NAPI */
+ netif_napi_add(dev, &greth->napi, greth_poll, 64);
+
+ return 0;
+
+error5:
+ dma_free_coherent(greth->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys);
+error4:
+ dma_free_coherent(greth->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys);
+error3:
+ mdiobus_unregister(greth->mdio);
+error2:
+ of_iounmap(&ofdev->resource[0], greth->regs, resource_size(&ofdev->resource[0]));
+error1:
+ free_netdev(dev);
+ return err;
+}
+
+static int greth_of_remove(struct platform_device *of_dev)
+{
+ struct net_device *ndev = platform_get_drvdata(of_dev);
+ struct greth_private *greth = netdev_priv(ndev);
+
+ /* Free descriptor areas */
+ dma_free_coherent(&of_dev->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys);
+
+ dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys);
+
+ if (greth->phy)
+ phy_stop(greth->phy);
+ mdiobus_unregister(greth->mdio);
+
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+
+ of_iounmap(&of_dev->resource[0], greth->regs, resource_size(&of_dev->resource[0]));
+
+ return 0;
+}
+
+static struct of_device_id greth_of_match[] = {
+ {
+ .name = "GAISLER_ETHMAC",
+ },
+ {
+ .name = "01_01d",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, greth_of_match);
+
+static struct platform_driver greth_of_driver = {
+ .driver = {
+ .name = "grlib-greth",
+ .owner = THIS_MODULE,
+ .of_match_table = greth_of_match,
+ },
+ .probe = greth_of_probe,
+ .remove = greth_of_remove,
+};
+
+module_platform_driver(greth_of_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION("Aeroflex Gaisler Ethernet MAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/aeroflex/greth.h b/drivers/net/ethernet/aeroflex/greth.h
new file mode 100644
index 00000000000..232a622a85b
--- /dev/null
+++ b/drivers/net/ethernet/aeroflex/greth.h
@@ -0,0 +1,142 @@
+#ifndef GRETH_H
+#define GRETH_H
+
+#include <linux/phy.h>
+
+/* Register bits and masks */
+#define GRETH_RESET 0x40
+#define GRETH_MII_BUSY 0x8
+#define GRETH_MII_NVALID 0x10
+
+#define GRETH_CTRL_FD 0x10
+#define GRETH_CTRL_PR 0x20
+#define GRETH_CTRL_SP 0x80
+#define GRETH_CTRL_GB 0x100
+#define GRETH_CTRL_PSTATIEN 0x400
+#define GRETH_CTRL_MCEN 0x800
+#define GRETH_CTRL_DISDUPLEX 0x1000
+#define GRETH_STATUS_PHYSTAT 0x100
+
+#define GRETH_BD_EN 0x800
+#define GRETH_BD_WR 0x1000
+#define GRETH_BD_IE 0x2000
+#define GRETH_BD_LEN 0x7FF
+
+#define GRETH_TXEN 0x1
+#define GRETH_INT_TE 0x2
+#define GRETH_INT_TX 0x8
+#define GRETH_TXI 0x4
+#define GRETH_TXBD_STATUS 0x0001C000
+#define GRETH_TXBD_MORE 0x20000
+#define GRETH_TXBD_IPCS 0x40000
+#define GRETH_TXBD_TCPCS 0x80000
+#define GRETH_TXBD_UDPCS 0x100000
+#define GRETH_TXBD_CSALL (GRETH_TXBD_IPCS | GRETH_TXBD_TCPCS | GRETH_TXBD_UDPCS)
+#define GRETH_TXBD_ERR_LC 0x10000
+#define GRETH_TXBD_ERR_UE 0x4000
+#define GRETH_TXBD_ERR_AL 0x8000
+
+#define GRETH_INT_RE 0x1
+#define GRETH_INT_RX 0x4
+#define GRETH_RXEN 0x2
+#define GRETH_RXI 0x8
+#define GRETH_RXBD_STATUS 0xFFFFC000
+#define GRETH_RXBD_ERR_AE 0x4000
+#define GRETH_RXBD_ERR_FT 0x8000
+#define GRETH_RXBD_ERR_CRC 0x10000
+#define GRETH_RXBD_ERR_OE 0x20000
+#define GRETH_RXBD_ERR_LE 0x40000
+#define GRETH_RXBD_IP 0x80000
+#define GRETH_RXBD_IP_CSERR 0x100000
+#define GRETH_RXBD_UDP 0x200000
+#define GRETH_RXBD_UDP_CSERR 0x400000
+#define GRETH_RXBD_TCP 0x800000
+#define GRETH_RXBD_TCP_CSERR 0x1000000
+#define GRETH_RXBD_IP_FRAG 0x2000000
+#define GRETH_RXBD_MCAST 0x4000000
+
+/* Descriptor parameters */
+#define GRETH_TXBD_NUM 128
+#define GRETH_TXBD_NUM_MASK (GRETH_TXBD_NUM-1)
+#define GRETH_TX_BUF_SIZE 2048
+#define GRETH_RXBD_NUM 128
+#define GRETH_RXBD_NUM_MASK (GRETH_RXBD_NUM-1)
+#define GRETH_RX_BUF_SIZE 2048
+
+/* Buffers per page */
+#define GRETH_RX_BUF_PPGAE (PAGE_SIZE/GRETH_RX_BUF_SIZE)
+#define GRETH_TX_BUF_PPGAE (PAGE_SIZE/GRETH_TX_BUF_SIZE)
+
+/* How many pages are needed for buffers */
+#define GRETH_RX_BUF_PAGE_NUM (GRETH_RXBD_NUM/GRETH_RX_BUF_PPGAE)
+#define GRETH_TX_BUF_PAGE_NUM (GRETH_TXBD_NUM/GRETH_TX_BUF_PPGAE)
+
+/* Buffer size.
+ * Gbit MAC uses tagged maximum frame size which is 1518 excluding CRC.
+ * Set to 1520 to make all buffers word aligned for non-gbit MAC.
+ */
+#define MAX_FRAME_SIZE 1520
+
+/* GRETH APB registers */
+struct greth_regs {
+ u32 control;
+ u32 status;
+ u32 esa_msb;
+ u32 esa_lsb;
+ u32 mdio;
+ u32 tx_desc_p;
+ u32 rx_desc_p;
+ u32 edclip;
+ u32 hash_msb;
+ u32 hash_lsb;
+};
+
+/* GRETH buffer descriptor */
+struct greth_bd {
+ u32 stat;
+ u32 addr;
+};
+
+struct greth_private {
+ struct sk_buff *rx_skbuff[GRETH_RXBD_NUM];
+ struct sk_buff *tx_skbuff[GRETH_TXBD_NUM];
+
+ unsigned char *tx_bufs[GRETH_TXBD_NUM];
+ unsigned char *rx_bufs[GRETH_RXBD_NUM];
+ u16 tx_bufs_length[GRETH_TXBD_NUM];
+
+ u16 tx_next;
+ u16 tx_last;
+ u16 tx_free;
+ u16 rx_cur;
+
+ struct greth_regs *regs; /* Address of controller registers. */
+ struct greth_bd *rx_bd_base; /* Address of Rx BDs. */
+ struct greth_bd *tx_bd_base; /* Address of Tx BDs. */
+ dma_addr_t rx_bd_base_phys;
+ dma_addr_t tx_bd_base_phys;
+
+ int irq;
+
+ struct device *dev; /* Pointer to platform_device->dev */
+ struct net_device *netdev;
+ struct napi_struct napi;
+ spinlock_t devlock;
+
+ struct phy_device *phy;
+ struct mii_bus *mdio;
+ int mdio_irqs[PHY_MAX_ADDR];
+ unsigned int link;
+ unsigned int speed;
+ unsigned int duplex;
+
+ u32 msg_enable;
+
+ u8 phyaddr;
+ u8 multicast;
+ u8 gbit_mac;
+ u8 mdio_int_en;
+ u8 edcl;
+};
+
+#endif
diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig
new file mode 100644
index 00000000000..d8d95d4cd45
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/Kconfig
@@ -0,0 +1,37 @@
+#
+# Allwinner device configuration
+#
+
+config NET_VENDOR_ALLWINNER
+ bool "Allwinner devices"
+ default y
+
+ depends on ARCH_SUNXI
+ ---help---
+ If you have a network (Ethernet) card belonging to this
+ class, say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly
+ affect the kernel: saying N will just cause the configurator
+ to skip all the questions about Allwinner cards. If you say Y,
+ you will be asked for your specific card in the following
+ questions.
+
+if NET_VENDOR_ALLWINNER
+
+config SUN4I_EMAC
+ tristate "Allwinner A10 EMAC support"
+ depends on ARCH_SUNXI
+ depends on OF
+ select CRC32
+ select MII
+ select PHYLIB
+ select MDIO_SUN4I
+ ---help---
+ Support for Allwinner A10 EMAC ethernet driver.
+
+ To compile this driver as a module, choose M here. The module
+ will be called sun4i-emac.
+
+endif # NET_VENDOR_ALLWINNER
diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile
new file mode 100644
index 00000000000..03129f79651
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Allwinner device drivers.
+#
+
+obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
new file mode 100644
index 00000000000..d81e7167a8b
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -0,0 +1,970 @@
+/*
+ * Allwinner EMAC Fast Ethernet driver for Linux.
+ *
+ * Copyright 2012-2013 Stefan Roese <sr@denx.de>
+ * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Linux driver provided by Allwinner:
+ * Copyright (C) 1997 Sten Wang
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+
+#include "sun4i-emac.h"
+
+#define DRV_NAME "sun4i-emac"
+#define DRV_VERSION "1.02"
+
+#define EMAC_MAX_FRAME_LEN 0x0600
+
+/* Transmit timeout, default 5 seconds. */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+/* EMAC register address locking.
+ *
+ * The EMAC uses an address register to control where data written
+ * to the data register goes. This means that the address register
+ * must be preserved over interrupts or similar calls.
+ *
+ * During interrupt and other critical calls, a spinlock is used to
+ * protect the system, but the calls themselves save the address
+ * in the address register in case they are interrupting another
+ * access to the device.
+ *
+ * For general accesses a lock is provided so that calls which are
+ * allowed to sleep are serialised so that the address register does
+ * not need to be saved. This lock also serves to serialise access
+ * to the EEPROM and PHY access registers which are shared between
+ * these two devices.
+ */
+
+/* The driver supports the original EMACE, and now the two newer
+ * devices, EMACA and EMACB.
+ */
+
+struct emac_board_info {
+ struct clk *clk;
+ struct device *dev;
+ struct platform_device *pdev;
+ spinlock_t lock;
+ void __iomem *membase;
+ u32 msg_enable;
+ struct net_device *ndev;
+ struct sk_buff *skb_last;
+ u16 tx_fifo_stat;
+
+ int emacrx_completed_flag;
+
+ struct phy_device *phy_dev;
+ struct device_node *phy_node;
+ unsigned int link;
+ unsigned int speed;
+ unsigned int duplex;
+
+ phy_interface_t phy_interface;
+};
+
+static void emac_update_speed(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ unsigned int reg_val;
+
+ /* set EMAC SPEED, depend on PHY */
+ reg_val = readl(db->membase + EMAC_MAC_SUPP_REG);
+ reg_val &= ~(0x1 << 8);
+ if (db->speed == SPEED_100)
+ reg_val |= 1 << 8;
+ writel(reg_val, db->membase + EMAC_MAC_SUPP_REG);
+}
+
+static void emac_update_duplex(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ unsigned int reg_val;
+
+ /* set duplex depend on phy */
+ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
+ reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN;
+ if (db->duplex)
+ reg_val |= EMAC_MAC_CTL1_DUPLEX_EN;
+ writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
+}
+
+static void emac_handle_link_change(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ struct phy_device *phydev = db->phy_dev;
+ unsigned long flags;
+ int status_change = 0;
+
+ if (phydev->link) {
+ if (db->speed != phydev->speed) {
+ spin_lock_irqsave(&db->lock, flags);
+ db->speed = phydev->speed;
+ emac_update_speed(dev);
+ spin_unlock_irqrestore(&db->lock, flags);
+ status_change = 1;
+ }
+
+ if (db->duplex != phydev->duplex) {
+ spin_lock_irqsave(&db->lock, flags);
+ db->duplex = phydev->duplex;
+ emac_update_duplex(dev);
+ spin_unlock_irqrestore(&db->lock, flags);
+ status_change = 1;
+ }
+ }
+
+ if (phydev->link != db->link) {
+ if (!phydev->link) {
+ db->speed = 0;
+ db->duplex = -1;
+ }
+ db->link = phydev->link;
+
+ status_change = 1;
+ }
+
+ if (status_change)
+ phy_print_status(phydev);
+}
+
+static int emac_mdio_probe(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+
+ /* to-do: PHY interrupts are currently not supported */
+
+ /* attach the mac to the phy */
+ db->phy_dev = of_phy_connect(db->ndev, db->phy_node,
+ &emac_handle_link_change, 0,
+ db->phy_interface);
+ if (!db->phy_dev) {
+ netdev_err(db->ndev, "could not find the PHY\n");
+ return -ENODEV;
+ }
+
+ /* mask with MAC supported features */
+ db->phy_dev->supported &= PHY_BASIC_FEATURES;
+ db->phy_dev->advertising = db->phy_dev->supported;
+
+ db->link = 0;
+ db->speed = 0;
+ db->duplex = -1;
+
+ return 0;
+}
+
+static void emac_mdio_remove(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+
+ phy_disconnect(db->phy_dev);
+ db->phy_dev = NULL;
+}
+
+static void emac_reset(struct emac_board_info *db)
+{
+ dev_dbg(db->dev, "resetting device\n");
+
+ /* RESET device */
+ writel(0, db->membase + EMAC_CTL_REG);
+ udelay(200);
+ writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG);
+ udelay(200);
+}
+
+static void emac_outblk_32bit(void __iomem *reg, void *data, int count)
+{
+ writesl(reg, data, round_up(count, 4) / 4);
+}
+
+static void emac_inblk_32bit(void __iomem *reg, void *data, int count)
+{
+ readsl(reg, data, round_up(count, 4) / 4);
+}
+
+static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct emac_board_info *dm = netdev_priv(dev);
+ struct phy_device *phydev = dm->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phydev, rq, cmd);
+}
+
+/* ethtool ops */
+static void emac_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME));
+ strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION));
+ strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info));
+}
+
+static int emac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct emac_board_info *dm = netdev_priv(dev);
+ struct phy_device *phydev = dm->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+static int emac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct emac_board_info *dm = netdev_priv(dev);
+ struct phy_device *phydev = dm->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+static const struct ethtool_ops emac_ethtool_ops = {
+ .get_drvinfo = emac_get_drvinfo,
+ .get_settings = emac_get_settings,
+ .set_settings = emac_set_settings,
+ .get_link = ethtool_op_get_link,
+};
+
+static unsigned int emac_setup(struct net_device *ndev)
+{
+ struct emac_board_info *db = netdev_priv(ndev);
+ unsigned int reg_val;
+
+ /* set up TX */
+ reg_val = readl(db->membase + EMAC_TX_MODE_REG);
+
+ writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN,
+ db->membase + EMAC_TX_MODE_REG);
+
+ /* set MAC */
+ /* set MAC CTL0 */
+ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
+ writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN |
+ EMAC_MAC_CTL0_TX_FLOW_CTL_EN,
+ db->membase + EMAC_MAC_CTL0_REG);
+
+ /* set MAC CTL1 */
+ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
+ reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN;
+ reg_val |= EMAC_MAC_CTL1_CRC_EN;
+ reg_val |= EMAC_MAC_CTL1_PAD_EN;
+ writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
+
+ /* set up IPGT */
+ writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG);
+
+ /* set up IPGR */
+ writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2,
+ db->membase + EMAC_MAC_IPGR_REG);
+
+ /* set up Collison window */
+ writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM,
+ db->membase + EMAC_MAC_CLRT_REG);
+
+ /* set up Max Frame Length */
+ writel(EMAC_MAX_FRAME_LEN,
+ db->membase + EMAC_MAC_MAXF_REG);
+
+ return 0;
+}
+
+static void emac_set_rx_mode(struct net_device *ndev)
+{
+ struct emac_board_info *db = netdev_priv(ndev);
+ unsigned int reg_val;
+
+ /* set up RX */
+ reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+
+ if (ndev->flags & IFF_PROMISC)
+ reg_val |= EMAC_RX_CTL_PASS_ALL_EN;
+ else
+ reg_val &= ~EMAC_RX_CTL_PASS_ALL_EN;
+
+ writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN |
+ EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN |
+ EMAC_RX_CTL_ACCEPT_MULTICAST_EN |
+ EMAC_RX_CTL_ACCEPT_BROADCAST_EN,
+ db->membase + EMAC_RX_CTL_REG);
+}
+
+static unsigned int emac_powerup(struct net_device *ndev)
+{
+ struct emac_board_info *db = netdev_priv(ndev);
+ unsigned int reg_val;
+
+ /* initial EMAC */
+ /* flush RX FIFO */
+ reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+ reg_val |= 0x8;
+ writel(reg_val, db->membase + EMAC_RX_CTL_REG);
+ udelay(1);
+
+ /* initial MAC */
+ /* soft reset MAC */
+ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
+ reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET;
+ writel(reg_val, db->membase + EMAC_MAC_CTL0_REG);
+
+ /* set MII clock */
+ reg_val = readl(db->membase + EMAC_MAC_MCFG_REG);
+ reg_val &= (~(0xf << 2));
+ reg_val |= (0xD << 2);
+ writel(reg_val, db->membase + EMAC_MAC_MCFG_REG);
+
+ /* clear RX counter */
+ writel(0x0, db->membase + EMAC_RX_FBC_REG);
+
+ /* disable all interrupt and clear interrupt status */
+ writel(0, db->membase + EMAC_INT_CTL_REG);
+ reg_val = readl(db->membase + EMAC_INT_STA_REG);
+ writel(reg_val, db->membase + EMAC_INT_STA_REG);
+
+ udelay(1);
+
+ /* set up EMAC */
+ emac_setup(ndev);
+
+ /* set mac_address to chip */
+ writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev->
+ dev_addr[2], db->membase + EMAC_MAC_A1_REG);
+ writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev->
+ dev_addr[5], db->membase + EMAC_MAC_A0_REG);
+
+ mdelay(1);
+
+ return 0;
+}
+
+static int emac_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct emac_board_info *db = netdev_priv(dev);
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+ writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev->
+ dev_addr[2], db->membase + EMAC_MAC_A1_REG);
+ writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev->
+ dev_addr[5], db->membase + EMAC_MAC_A0_REG);
+
+ return 0;
+}
+
+/* Initialize emac board */
+static void emac_init_device(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int reg_val;
+
+ spin_lock_irqsave(&db->lock, flags);
+
+ emac_update_speed(dev);
+ emac_update_duplex(dev);
+
+ /* enable RX/TX */
+ reg_val = readl(db->membase + EMAC_CTL_REG);
+ writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN,
+ db->membase + EMAC_CTL_REG);
+
+ /* enable RX/TX0/RX Hlevel interrup */
+ reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+ reg_val |= (0xf << 0) | (0x01 << 8);
+ writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+ spin_unlock_irqrestore(&db->lock, flags);
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void emac_timeout(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ unsigned long flags;
+
+ if (netif_msg_timer(db))
+ dev_err(db->dev, "tx time out.\n");
+
+ /* Save previous register address */
+ spin_lock_irqsave(&db->lock, flags);
+
+ netif_stop_queue(dev);
+ emac_reset(db);
+ emac_init_device(dev);
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+
+ /* Restore previous register address */
+ spin_unlock_irqrestore(&db->lock, flags);
+}
+
+/* Hardware start transmission.
+ * Send a packet to media from the upper layer.
+ */
+static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ unsigned long channel;
+ unsigned long flags;
+
+ channel = db->tx_fifo_stat & 3;
+ if (channel == 3)
+ return 1;
+
+ channel = (channel == 1 ? 1 : 0);
+
+ spin_lock_irqsave(&db->lock, flags);
+
+ writel(channel, db->membase + EMAC_TX_INS_REG);
+
+ emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG,
+ skb->data, skb->len);
+ dev->stats.tx_bytes += skb->len;
+
+ db->tx_fifo_stat |= 1 << channel;
+ /* TX control: First packet immediately send, second packet queue */
+ if (channel == 0) {
+ /* set TX len */
+ writel(skb->len, db->membase + EMAC_TX_PL0_REG);
+ /* start translate from fifo to phy */
+ writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1,
+ db->membase + EMAC_TX_CTL0_REG);
+
+ /* save the time stamp */
+ dev->trans_start = jiffies;
+ } else if (channel == 1) {
+ /* set TX len */
+ writel(skb->len, db->membase + EMAC_TX_PL1_REG);
+ /* start translate from fifo to phy */
+ writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1,
+ db->membase + EMAC_TX_CTL1_REG);
+
+ /* save the time stamp */
+ dev->trans_start = jiffies;
+ }
+
+ if ((db->tx_fifo_stat & 3) == 3) {
+ /* Second packet */
+ netif_stop_queue(dev);
+ }
+
+ spin_unlock_irqrestore(&db->lock, flags);
+
+ /* free this SKB */
+ dev_consume_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/* EMAC interrupt handler
+ * receive the packet to upper layer, free the transmitted packet
+ */
+static void emac_tx_done(struct net_device *dev, struct emac_board_info *db,
+ unsigned int tx_status)
+{
+ /* One packet sent complete */
+ db->tx_fifo_stat &= ~(tx_status & 3);
+ if (3 == (tx_status & 3))
+ dev->stats.tx_packets += 2;
+ else
+ dev->stats.tx_packets++;
+
+ if (netif_msg_tx_done(db))
+ dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
+
+ netif_wake_queue(dev);
+}
+
+/* Received a packet and pass to upper layer
+ */
+static void emac_rx(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ struct sk_buff *skb;
+ u8 *rdptr;
+ bool good_packet;
+ static int rxlen_last;
+ unsigned int reg_val;
+ u32 rxhdr, rxstatus, rxcount, rxlen;
+
+ /* Check packet ready or not */
+ while (1) {
+ /* race warning: the first packet might arrive with
+ * the interrupts disabled, but the second will fix
+ * it
+ */
+ rxcount = readl(db->membase + EMAC_RX_FBC_REG);
+
+ if (netif_msg_rx_status(db))
+ dev_dbg(db->dev, "RXCount: %x\n", rxcount);
+
+ if ((db->skb_last != NULL) && (rxlen_last > 0)) {
+ dev->stats.rx_bytes += rxlen_last;
+
+ /* Pass to upper layer */
+ db->skb_last->protocol = eth_type_trans(db->skb_last,
+ dev);
+ netif_rx(db->skb_last);
+ dev->stats.rx_packets++;
+ db->skb_last = NULL;
+ rxlen_last = 0;
+
+ reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+ reg_val &= ~EMAC_RX_CTL_DMA_EN;
+ writel(reg_val, db->membase + EMAC_RX_CTL_REG);
+ }
+
+ if (!rxcount) {
+ db->emacrx_completed_flag = 1;
+ reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+ reg_val |= (0xf << 0) | (0x01 << 8);
+ writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+ /* had one stuck? */
+ rxcount = readl(db->membase + EMAC_RX_FBC_REG);
+ if (!rxcount)
+ return;
+ }
+
+ reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG);
+ if (netif_msg_rx_status(db))
+ dev_dbg(db->dev, "receive header: %x\n", reg_val);
+ if (reg_val != EMAC_UNDOCUMENTED_MAGIC) {
+ /* disable RX */
+ reg_val = readl(db->membase + EMAC_CTL_REG);
+ writel(reg_val & ~EMAC_CTL_RX_EN,
+ db->membase + EMAC_CTL_REG);
+
+ /* Flush RX FIFO */
+ reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+ writel(reg_val | (1 << 3),
+ db->membase + EMAC_RX_CTL_REG);
+
+ do {
+ reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+ } while (reg_val & (1 << 3));
+
+ /* enable RX */
+ reg_val = readl(db->membase + EMAC_CTL_REG);
+ writel(reg_val | EMAC_CTL_RX_EN,
+ db->membase + EMAC_CTL_REG);
+ reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+ reg_val |= (0xf << 0) | (0x01 << 8);
+ writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+ db->emacrx_completed_flag = 1;
+
+ return;
+ }
+
+ /* A packet ready now & Get status/length */
+ good_packet = true;
+
+ emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
+ &rxhdr, sizeof(rxhdr));
+
+ if (netif_msg_rx_status(db))
+ dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr)));
+
+ rxlen = EMAC_RX_IO_DATA_LEN(rxhdr);
+ rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr);
+
+ if (netif_msg_rx_status(db))
+ dev_dbg(db->dev, "RX: status %02x, length %04x\n",
+ rxstatus, rxlen);
+
+ /* Packet Status check */
+ if (rxlen < 0x40) {
+ good_packet = false;
+ if (netif_msg_rx_err(db))
+ dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
+ }
+
+ if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) {
+ good_packet = false;
+
+ if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) {
+ if (netif_msg_rx_err(db))
+ dev_dbg(db->dev, "crc error\n");
+ dev->stats.rx_crc_errors++;
+ }
+
+ if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) {
+ if (netif_msg_rx_err(db))
+ dev_dbg(db->dev, "length error\n");
+ dev->stats.rx_length_errors++;
+ }
+ }
+
+ /* Move data from EMAC */
+ skb = dev_alloc_skb(rxlen + 4);
+ if (good_packet && skb) {
+ skb_reserve(skb, 2);
+ rdptr = (u8 *) skb_put(skb, rxlen - 4);
+
+ /* Read received packet from RX SRAM */
+ if (netif_msg_rx_status(db))
+ dev_dbg(db->dev, "RxLen %x\n", rxlen);
+
+ emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
+ rdptr, rxlen);
+ dev->stats.rx_bytes += rxlen;
+
+ /* Pass to upper layer */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+ }
+}
+
+static irqreturn_t emac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct emac_board_info *db = netdev_priv(dev);
+ int int_status;
+ unsigned long flags;
+ unsigned int reg_val;
+
+ /* A real interrupt coming */
+
+ /* holders of db->lock must always block IRQs */
+ spin_lock_irqsave(&db->lock, flags);
+
+ /* Disable all interrupts */
+ writel(0, db->membase + EMAC_INT_CTL_REG);
+
+ /* Got EMAC interrupt status */
+ /* Got ISR */
+ int_status = readl(db->membase + EMAC_INT_STA_REG);
+ /* Clear ISR status */
+ writel(int_status, db->membase + EMAC_INT_STA_REG);
+
+ if (netif_msg_intr(db))
+ dev_dbg(db->dev, "emac interrupt %02x\n", int_status);
+
+ /* Received the coming packet */
+ if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) {
+ /* carrier lost */
+ db->emacrx_completed_flag = 0;
+ emac_rx(dev);
+ }
+
+ /* Transmit Interrupt check */
+ if (int_status & (0x01 | 0x02))
+ emac_tx_done(dev, db, int_status);
+
+ if (int_status & (0x04 | 0x08))
+ netdev_info(dev, " ab : %x\n", int_status);
+
+ /* Re-enable interrupt mask */
+ if (db->emacrx_completed_flag == 1) {
+ reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+ reg_val |= (0xf << 0) | (0x01 << 8);
+ writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+ }
+ spin_unlock_irqrestore(&db->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Used by netconsole
+ */
+static void emac_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ emac_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+/* Open the interface.
+ * The interface is opened whenever "ifconfig" actives it.
+ */
+static int emac_open(struct net_device *dev)
+{
+ struct emac_board_info *db = netdev_priv(dev);
+ int ret;
+
+ if (netif_msg_ifup(db))
+ dev_dbg(db->dev, "enabling %s\n", dev->name);
+
+ if (request_irq(dev->irq, &emac_interrupt, 0, dev->name, dev))
+ return -EAGAIN;
+
+ /* Initialize EMAC board */
+ emac_reset(db);
+ emac_init_device(dev);
+
+ ret = emac_mdio_probe(dev);
+ if (ret < 0) {
+ free_irq(dev->irq, dev);
+ netdev_err(dev, "cannot probe MDIO bus\n");
+ return ret;
+ }
+
+ phy_start(db->phy_dev);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void emac_shutdown(struct net_device *dev)
+{
+ unsigned int reg_val;
+ struct emac_board_info *db = netdev_priv(dev);
+
+ /* Disable all interrupt */
+ writel(0, db->membase + EMAC_INT_CTL_REG);
+
+ /* clear interupt status */
+ reg_val = readl(db->membase + EMAC_INT_STA_REG);
+ writel(reg_val, db->membase + EMAC_INT_STA_REG);
+
+ /* Disable RX/TX */
+ reg_val = readl(db->membase + EMAC_CTL_REG);
+ reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET);
+ writel(reg_val, db->membase + EMAC_CTL_REG);
+}
+
+/* Stop the interface.
+ * The interface is stopped when it is brought.
+ */
+static int emac_stop(struct net_device *ndev)
+{
+ struct emac_board_info *db = netdev_priv(ndev);
+
+ if (netif_msg_ifdown(db))
+ dev_dbg(db->dev, "shutting down %s\n", ndev->name);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ phy_stop(db->phy_dev);
+
+ emac_mdio_remove(ndev);
+
+ emac_shutdown(ndev);
+
+ free_irq(ndev->irq, ndev);
+
+ return 0;
+}
+
+static const struct net_device_ops emac_netdev_ops = {
+ .ndo_open = emac_open,
+ .ndo_stop = emac_stop,
+ .ndo_start_xmit = emac_start_xmit,
+ .ndo_tx_timeout = emac_timeout,
+ .ndo_set_rx_mode = emac_set_rx_mode,
+ .ndo_do_ioctl = emac_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = emac_set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = emac_poll_controller,
+#endif
+};
+
+/* Search EMAC board, allocate space and register it
+ */
+static int emac_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct emac_board_info *db;
+ struct net_device *ndev;
+ int ret = 0;
+ const char *mac_addr;
+
+ ndev = alloc_etherdev(sizeof(struct emac_board_info));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ db = netdev_priv(ndev);
+ memset(db, 0, sizeof(*db));
+
+ db->dev = &pdev->dev;
+ db->ndev = ndev;
+ db->pdev = pdev;
+
+ spin_lock_init(&db->lock);
+
+ db->membase = of_iomap(np, 0);
+ if (!db->membase) {
+ dev_err(&pdev->dev, "failed to remap registers\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* fill in parameters for net-dev structure */
+ ndev->base_addr = (unsigned long)db->membase;
+ ndev->irq = irq_of_parse_and_map(np, 0);
+ if (ndev->irq == -ENXIO) {
+ netdev_err(ndev, "No irq resource\n");
+ ret = ndev->irq;
+ goto out;
+ }
+
+ db->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(db->clk))
+ goto out;
+
+ clk_prepare_enable(db->clk);
+
+ db->phy_node = of_parse_phandle(np, "phy", 0);
+ if (!db->phy_node) {
+ dev_err(&pdev->dev, "no associated PHY\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* Read MAC-address from DT */
+ mac_addr = of_get_mac_address(np);
+ if (mac_addr)
+ memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+
+ /* Check if the MAC address is valid, if not get a random one */
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ eth_hw_addr_random(ndev);
+ dev_warn(&pdev->dev, "using random MAC address %pM\n",
+ ndev->dev_addr);
+ }
+
+ db->emacrx_completed_flag = 1;
+ emac_powerup(ndev);
+ emac_reset(db);
+
+ ether_setup(ndev);
+
+ ndev->netdev_ops = &emac_netdev_ops;
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ ndev->ethtool_ops = &emac_ethtool_ops;
+
+ platform_set_drvdata(pdev, ndev);
+
+ /* Carrier starts down, phylib will bring it up */
+ netif_carrier_off(ndev);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "Registering netdev failed!\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n",
+ ndev->name, db->membase, ndev->irq, ndev->dev_addr);
+
+ return 0;
+
+out:
+ dev_err(db->dev, "not found (%d).\n", ret);
+
+ free_netdev(ndev);
+
+ return ret;
+}
+
+static int emac_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static int emac_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+
+ netif_carrier_off(ndev);
+ netif_device_detach(ndev);
+ emac_shutdown(ndev);
+
+ return 0;
+}
+
+static int emac_resume(struct platform_device *dev)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+ struct emac_board_info *db = netdev_priv(ndev);
+
+ emac_reset(db);
+ emac_init_device(ndev);
+ netif_device_attach(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id emac_of_match[] = {
+ {.compatible = "allwinner,sun4i-a10-emac",},
+
+ /* Deprecated */
+ {.compatible = "allwinner,sun4i-emac",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, emac_of_match);
+
+static struct platform_driver emac_driver = {
+ .driver = {
+ .name = "sun4i-emac",
+ .of_match_table = emac_of_match,
+ },
+ .probe = emac_probe,
+ .remove = emac_remove,
+ .suspend = emac_suspend,
+ .resume = emac_resume,
+};
+
+module_platform_driver(emac_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 emac network driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.h b/drivers/net/ethernet/allwinner/sun4i-emac.h
new file mode 100644
index 00000000000..38c72d9ec60
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.h
@@ -0,0 +1,108 @@
+/*
+ * Allwinner EMAC Fast Ethernet driver for Linux.
+ *
+ * Copyright 2012 Stefan Roese <sr@denx.de>
+ * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Linux driver provided by Allwinner:
+ * Copyright (C) 1997 Sten Wang
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _SUN4I_EMAC_H_
+#define _SUN4I_EMAC_H_
+
+#define EMAC_CTL_REG (0x00)
+#define EMAC_CTL_RESET (1 << 0)
+#define EMAC_CTL_TX_EN (1 << 1)
+#define EMAC_CTL_RX_EN (1 << 2)
+#define EMAC_TX_MODE_REG (0x04)
+#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0)
+#define EMAC_TX_MODE_DMA_EN (1 << 1)
+#define EMAC_TX_FLOW_REG (0x08)
+#define EMAC_TX_CTL0_REG (0x0c)
+#define EMAC_TX_CTL1_REG (0x10)
+#define EMAC_TX_INS_REG (0x14)
+#define EMAC_TX_PL0_REG (0x18)
+#define EMAC_TX_PL1_REG (0x1c)
+#define EMAC_TX_STA_REG (0x20)
+#define EMAC_TX_IO_DATA_REG (0x24)
+#define EMAC_TX_IO_DATA1_REG (0x28)
+#define EMAC_TX_TSVL0_REG (0x2c)
+#define EMAC_TX_TSVH0_REG (0x30)
+#define EMAC_TX_TSVL1_REG (0x34)
+#define EMAC_TX_TSVH1_REG (0x38)
+#define EMAC_RX_CTL_REG (0x3c)
+#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1)
+#define EMAC_RX_CTL_DMA_EN (1 << 2)
+#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4)
+#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5)
+#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6)
+#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7)
+#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8)
+#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16)
+#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17)
+#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20)
+#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21)
+#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22)
+#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24)
+#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25)
+#define EMAC_RX_HASH0_REG (0x40)
+#define EMAC_RX_HASH1_REG (0x44)
+#define EMAC_RX_STA_REG (0x48)
+#define EMAC_RX_IO_DATA_REG (0x4c)
+#define EMAC_RX_IO_DATA_LEN(x) (x & 0xffff)
+#define EMAC_RX_IO_DATA_STATUS(x) ((x >> 16) & 0xffff)
+#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
+#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
+#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7)
+#define EMAC_RX_FBC_REG (0x50)
+#define EMAC_INT_CTL_REG (0x54)
+#define EMAC_INT_STA_REG (0x58)
+#define EMAC_MAC_CTL0_REG (0x5c)
+#define EMAC_MAC_CTL0_RX_FLOW_CTL_EN (1 << 2)
+#define EMAC_MAC_CTL0_TX_FLOW_CTL_EN (1 << 3)
+#define EMAC_MAC_CTL0_SOFT_RESET (1 << 15)
+#define EMAC_MAC_CTL1_REG (0x60)
+#define EMAC_MAC_CTL1_DUPLEX_EN (1 << 0)
+#define EMAC_MAC_CTL1_LEN_CHECK_EN (1 << 1)
+#define EMAC_MAC_CTL1_HUGE_FRAME_EN (1 << 2)
+#define EMAC_MAC_CTL1_DELAYED_CRC_EN (1 << 3)
+#define EMAC_MAC_CTL1_CRC_EN (1 << 4)
+#define EMAC_MAC_CTL1_PAD_EN (1 << 5)
+#define EMAC_MAC_CTL1_PAD_CRC_EN (1 << 6)
+#define EMAC_MAC_CTL1_AD_SHORT_FRAME_EN (1 << 7)
+#define EMAC_MAC_CTL1_BACKOFF_DIS (1 << 12)
+#define EMAC_MAC_IPGT_REG (0x64)
+#define EMAC_MAC_IPGT_HALF_DUPLEX (0x12)
+#define EMAC_MAC_IPGT_FULL_DUPLEX (0x15)
+#define EMAC_MAC_IPGR_REG (0x68)
+#define EMAC_MAC_IPGR_IPG1 (0x0c)
+#define EMAC_MAC_IPGR_IPG2 (0x12)
+#define EMAC_MAC_CLRT_REG (0x6c)
+#define EMAC_MAC_CLRT_COLLISION_WINDOW (0x37)
+#define EMAC_MAC_CLRT_RM (0x0f)
+#define EMAC_MAC_MAXF_REG (0x70)
+#define EMAC_MAC_SUPP_REG (0x74)
+#define EMAC_MAC_TEST_REG (0x78)
+#define EMAC_MAC_MCFG_REG (0x7c)
+#define EMAC_MAC_A0_REG (0x98)
+#define EMAC_MAC_A1_REG (0x9c)
+#define EMAC_MAC_A2_REG (0xa0)
+#define EMAC_SAFX_L_REG0 (0xa4)
+#define EMAC_SAFX_H_REG0 (0xa8)
+#define EMAC_SAFX_L_REG1 (0xac)
+#define EMAC_SAFX_H_REG1 (0xb0)
+#define EMAC_SAFX_L_REG2 (0xb4)
+#define EMAC_SAFX_H_REG2 (0xb8)
+#define EMAC_SAFX_L_REG3 (0xbc)
+#define EMAC_SAFX_H_REG3 (0xc0)
+
+#define EMAC_PHY_DUPLEX (1 << 8)
+
+#define EMAC_EEPROM_MAGIC (0x444d394b)
+#define EMAC_UNDOCUMENTED_MAGIC (0x0143414d)
+#endif /* _SUN4I_EMAC_H_ */
diff --git a/drivers/net/ethernet/alteon/Kconfig b/drivers/net/ethernet/alteon/Kconfig
new file mode 100644
index 00000000000..799a8528207
--- /dev/null
+++ b/drivers/net/ethernet/alteon/Kconfig
@@ -0,0 +1,48 @@
+#
+# Alteon network device configuration
+#
+
+config NET_VENDOR_ALTEON
+ bool "Alteon devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Alteon cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_ALTEON
+
+config ACENIC
+ tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support"
+ depends on PCI
+ ---help---
+ Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear
+ GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet
+ adapter. The driver allows for using the Jumbo Frame option (9000
+ bytes/frame) however it requires that your switches can handle this
+ as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig
+ line.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acenic.
+
+config ACENIC_OMIT_TIGON_I
+ bool "Omit support for old Tigon I based AceNICs"
+ depends on ACENIC
+ ---help---
+ Say Y here if you only have Tigon II based AceNICs and want to leave
+ out support for the older Tigon I based cards which are no longer
+ being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B
+ version)). This will reduce the size of the driver object by
+ app. 100KB. If you are not sure whether your card is a Tigon I or a
+ Tigon II, say N here.
+
+ The safe and default value for this is N.
+
+endif # NET_VENDOR_ALTEON
diff --git a/drivers/net/ethernet/alteon/Makefile b/drivers/net/ethernet/alteon/Makefile
new file mode 100644
index 00000000000..a2ca173f2a5
--- /dev/null
+++ b/drivers/net/ethernet/alteon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Alteon network device drivers.
+#
+
+obj-$(CONFIG_ACENIC) += acenic.o
diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c
new file mode 100644
index 00000000000..9a6991be974
--- /dev/null
+++ b/drivers/net/ethernet/alteon/acenic.c
@@ -0,0 +1,3189 @@
+/*
+ * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card
+ * and other Tigon based cards.
+ *
+ * Copyright 1998-2002 by Jes Sorensen, <jes@trained-monkey.org>.
+ *
+ * Thanks to Alteon and 3Com for providing hardware and documentation
+ * enabling me to write this driver.
+ *
+ * A mailing list for discussing the use of this driver has been
+ * setup, please subscribe to the lists if you have any questions
+ * about the driver. Send mail to linux-acenic-help@sunsite.auc.dk to
+ * see how to subscribe.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Additional credits:
+ * Pete Wyckoff <wyckoff@ca.sandia.gov>: Initial Linux/Alpha and trace
+ * dump support. The trace dump support has not been
+ * integrated yet however.
+ * Troy Benjegerdes: Big Endian (PPC) patches.
+ * Nate Stahl: Better out of memory handling and stats support.
+ * Aman Singla: Nasty race between interrupt handler and tx code dealing
+ * with 'testing the tx_ret_csm and setting tx_full'
+ * David S. Miller <davem@redhat.com>: conversion to new PCI dma mapping
+ * infrastructure and Sparc support
+ * Pierrick Pinasseau (CERN): For lending me an Ultra 5 to test the
+ * driver under Linux/Sparc64
+ * Matt Domsch <Matt_Domsch@dell.com>: Detect Alteon 1000baseT cards
+ * ETHTOOL_GDRVINFO support
+ * Chip Salzenberg <chip@valinux.com>: Fix race condition between tx
+ * handler and close() cleanup.
+ * Ken Aaker <kdaaker@rchland.vnet.ibm.com>: Correct check for whether
+ * memory mapped IO is enabled to
+ * make the driver work on RS/6000.
+ * Takayoshi Kouchi <kouchi@hpc.bs1.fc.nec.co.jp>: Identifying problem
+ * where the driver would disable
+ * bus master mode if it had to disable
+ * write and invalidate.
+ * Stephen Hack <stephen_hack@hp.com>: Fixed ace_set_mac_addr for little
+ * endian systems.
+ * Val Henson <vhenson@esscom.com>: Reset Jumbo skb producer and
+ * rx producer index when
+ * flushing the Jumbo ring.
+ * Hans Grobler <grobh@sun.ac.za>: Memory leak fixes in the
+ * driver init path.
+ * Grant Grundler <grundler@cup.hp.com>: PCI write posting fixes.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/sockios.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/prefetch.h>
+#include <linux/if_vlan.h>
+
+#ifdef SIOCETHTOOL
+#include <linux/ethtool.h>
+#endif
+
+#include <net/sock.h>
+#include <net/ip.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+
+#define DRV_NAME "acenic"
+
+#undef INDEX_DEBUG
+
+#ifdef CONFIG_ACENIC_OMIT_TIGON_I
+#define ACE_IS_TIGON_I(ap) 0
+#define ACE_TX_RING_ENTRIES(ap) MAX_TX_RING_ENTRIES
+#else
+#define ACE_IS_TIGON_I(ap) (ap->version == 1)
+#define ACE_TX_RING_ENTRIES(ap) ap->tx_ring_entries
+#endif
+
+#ifndef PCI_VENDOR_ID_ALTEON
+#define PCI_VENDOR_ID_ALTEON 0x12ae
+#endif
+#ifndef PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE
+#define PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE 0x0001
+#define PCI_DEVICE_ID_ALTEON_ACENIC_COPPER 0x0002
+#endif
+#ifndef PCI_DEVICE_ID_3COM_3C985
+#define PCI_DEVICE_ID_3COM_3C985 0x0001
+#endif
+#ifndef PCI_VENDOR_ID_NETGEAR
+#define PCI_VENDOR_ID_NETGEAR 0x1385
+#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a
+#endif
+#ifndef PCI_DEVICE_ID_NETGEAR_GA620T
+#define PCI_DEVICE_ID_NETGEAR_GA620T 0x630a
+#endif
+
+
+/*
+ * Farallon used the DEC vendor ID by mistake and they seem not
+ * to care - stinky!
+ */
+#ifndef PCI_DEVICE_ID_FARALLON_PN9000SX
+#define PCI_DEVICE_ID_FARALLON_PN9000SX 0x1a
+#endif
+#ifndef PCI_DEVICE_ID_FARALLON_PN9100T
+#define PCI_DEVICE_ID_FARALLON_PN9100T 0xfa
+#endif
+#ifndef PCI_VENDOR_ID_SGI
+#define PCI_VENDOR_ID_SGI 0x10a9
+#endif
+#ifndef PCI_DEVICE_ID_SGI_ACENIC
+#define PCI_DEVICE_ID_SGI_ACENIC 0x0009
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(acenic_pci_tbl) = {
+ { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ /*
+ * Farallon used the DEC vendor ID on their cards incorrectly,
+ * then later Alteon's ID.
+ */
+ { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_FARALLON_PN9100T,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC,
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
+
+#define ace_sync_irq(irq) synchronize_irq(irq)
+
+#ifndef offset_in_page
+#define offset_in_page(ptr) ((unsigned long)(ptr) & ~PAGE_MASK)
+#endif
+
+#define ACE_MAX_MOD_PARMS 8
+#define BOARD_IDX_STATIC 0
+#define BOARD_IDX_OVERFLOW -1
+
+#include "acenic.h"
+
+/*
+ * These must be defined before the firmware is included.
+ */
+#define MAX_TEXT_LEN 96*1024
+#define MAX_RODATA_LEN 8*1024
+#define MAX_DATA_LEN 2*1024
+
+#ifndef tigon2FwReleaseLocal
+#define tigon2FwReleaseLocal 0
+#endif
+
+/*
+ * This driver currently supports Tigon I and Tigon II based cards
+ * including the Alteon AceNIC, the 3Com 3C985[B] and NetGear
+ * GA620. The driver should also work on the SGI, DEC and Farallon
+ * versions of the card, however I have not been able to test that
+ * myself.
+ *
+ * This card is really neat, it supports receive hardware checksumming
+ * and jumbo frames (up to 9000 bytes) and does a lot of work in the
+ * firmware. Also the programming interface is quite neat, except for
+ * the parts dealing with the i2c eeprom on the card ;-)
+ *
+ * Using jumbo frames:
+ *
+ * To enable jumbo frames, simply specify an mtu between 1500 and 9000
+ * bytes to ifconfig. Jumbo frames can be enabled or disabled at any time
+ * by running `ifconfig eth<X> mtu <MTU>' with <X> being the Ethernet
+ * interface number and <MTU> being the MTU value.
+ *
+ * Module parameters:
+ *
+ * When compiled as a loadable module, the driver allows for a number
+ * of module parameters to be specified. The driver supports the
+ * following module parameters:
+ *
+ * trace=<val> - Firmware trace level. This requires special traced
+ * firmware to replace the firmware supplied with
+ * the driver - for debugging purposes only.
+ *
+ * link=<val> - Link state. Normally you want to use the default link
+ * parameters set by the driver. This can be used to
+ * override these in case your switch doesn't negotiate
+ * the link properly. Valid values are:
+ * 0x0001 - Force half duplex link.
+ * 0x0002 - Do not negotiate line speed with the other end.
+ * 0x0010 - 10Mbit/sec link.
+ * 0x0020 - 100Mbit/sec link.
+ * 0x0040 - 1000Mbit/sec link.
+ * 0x0100 - Do not negotiate flow control.
+ * 0x0200 - Enable RX flow control Y
+ * 0x0400 - Enable TX flow control Y (Tigon II NICs only).
+ * Default value is 0x0270, ie. enable link+flow
+ * control negotiation. Negotiating the highest
+ * possible link speed with RX flow control enabled.
+ *
+ * When disabling link speed negotiation, only one link
+ * speed is allowed to be specified!
+ *
+ * tx_coal_tick=<val> - number of coalescing clock ticks (us) allowed
+ * to wait for more packets to arive before
+ * interrupting the host, from the time the first
+ * packet arrives.
+ *
+ * rx_coal_tick=<val> - number of coalescing clock ticks (us) allowed
+ * to wait for more packets to arive in the transmit ring,
+ * before interrupting the host, after transmitting the
+ * first packet in the ring.
+ *
+ * max_tx_desc=<val> - maximum number of transmit descriptors
+ * (packets) transmitted before interrupting the host.
+ *
+ * max_rx_desc=<val> - maximum number of receive descriptors
+ * (packets) received before interrupting the host.
+ *
+ * tx_ratio=<val> - 7 bit value (0 - 63) specifying the split in 64th
+ * increments of the NIC's on board memory to be used for
+ * transmit and receive buffers. For the 1MB NIC app. 800KB
+ * is available, on the 1/2MB NIC app. 300KB is available.
+ * 68KB will always be available as a minimum for both
+ * directions. The default value is a 50/50 split.
+ * dis_pci_mem_inval=<val> - disable PCI memory write and invalidate
+ * operations, default (1) is to always disable this as
+ * that is what Alteon does on NT. I have not been able
+ * to measure any real performance differences with
+ * this on my systems. Set <val>=0 if you want to
+ * enable these operations.
+ *
+ * If you use more than one NIC, specify the parameters for the
+ * individual NICs with a comma, ie. trace=0,0x00001fff,0 you want to
+ * run tracing on NIC #2 but not on NIC #1 and #3.
+ *
+ * TODO:
+ *
+ * - Proper multicast support.
+ * - NIC dump support.
+ * - More tuning parameters.
+ *
+ * The mini ring is not used under Linux and I am not sure it makes sense
+ * to actually use it.
+ *
+ * New interrupt handler strategy:
+ *
+ * The old interrupt handler worked using the traditional method of
+ * replacing an skbuff with a new one when a packet arrives. However
+ * the rx rings do not need to contain a static number of buffer
+ * descriptors, thus it makes sense to move the memory allocation out
+ * of the main interrupt handler and do it in a bottom half handler
+ * and only allocate new buffers when the number of buffers in the
+ * ring is below a certain threshold. In order to avoid starving the
+ * NIC under heavy load it is however necessary to force allocation
+ * when hitting a minimum threshold. The strategy for alloction is as
+ * follows:
+ *
+ * RX_LOW_BUF_THRES - allocate buffers in the bottom half
+ * RX_PANIC_LOW_THRES - we are very low on buffers, allocate
+ * the buffers in the interrupt handler
+ * RX_RING_THRES - maximum number of buffers in the rx ring
+ * RX_MINI_THRES - maximum number of buffers in the mini ring
+ * RX_JUMBO_THRES - maximum number of buffers in the jumbo ring
+ *
+ * One advantagous side effect of this allocation approach is that the
+ * entire rx processing can be done without holding any spin lock
+ * since the rx rings and registers are totally independent of the tx
+ * ring and its registers. This of course includes the kmalloc's of
+ * new skb's. Thus start_xmit can run in parallel with rx processing
+ * and the memory allocation on SMP systems.
+ *
+ * Note that running the skb reallocation in a bottom half opens up
+ * another can of races which needs to be handled properly. In
+ * particular it can happen that the interrupt handler tries to run
+ * the reallocation while the bottom half is either running on another
+ * CPU or was interrupted on the same CPU. To get around this the
+ * driver uses bitops to prevent the reallocation routines from being
+ * reentered.
+ *
+ * TX handling can also be done without holding any spin lock, wheee
+ * this is fun! since tx_ret_csm is only written to by the interrupt
+ * handler. The case to be aware of is when shutting down the device
+ * and cleaning up where it is necessary to make sure that
+ * start_xmit() is not running while this is happening. Well DaveM
+ * informs me that this case is already protected against ... bye bye
+ * Mr. Spin Lock, it was nice to know you.
+ *
+ * TX interrupts are now partly disabled so the NIC will only generate
+ * TX interrupts for the number of coal ticks, not for the number of
+ * TX packets in the queue. This should reduce the number of TX only,
+ * ie. when no RX processing is done, interrupts seen.
+ */
+
+/*
+ * Threshold values for RX buffer allocation - the low water marks for
+ * when to start refilling the rings are set to 75% of the ring
+ * sizes. It seems to make sense to refill the rings entirely from the
+ * intrrupt handler once it gets below the panic threshold, that way
+ * we don't risk that the refilling is moved to another CPU when the
+ * one running the interrupt handler just got the slab code hot in its
+ * cache.
+ */
+#define RX_RING_SIZE 72
+#define RX_MINI_SIZE 64
+#define RX_JUMBO_SIZE 48
+
+#define RX_PANIC_STD_THRES 16
+#define RX_PANIC_STD_REFILL (3*RX_PANIC_STD_THRES)/2
+#define RX_LOW_STD_THRES (3*RX_RING_SIZE)/4
+#define RX_PANIC_MINI_THRES 12
+#define RX_PANIC_MINI_REFILL (3*RX_PANIC_MINI_THRES)/2
+#define RX_LOW_MINI_THRES (3*RX_MINI_SIZE)/4
+#define RX_PANIC_JUMBO_THRES 6
+#define RX_PANIC_JUMBO_REFILL (3*RX_PANIC_JUMBO_THRES)/2
+#define RX_LOW_JUMBO_THRES (3*RX_JUMBO_SIZE)/4
+
+
+/*
+ * Size of the mini ring entries, basically these just should be big
+ * enough to take TCP ACKs
+ */
+#define ACE_MINI_SIZE 100
+
+#define ACE_MINI_BUFSIZE ACE_MINI_SIZE
+#define ACE_STD_BUFSIZE (ACE_STD_MTU + ETH_HLEN + 4)
+#define ACE_JUMBO_BUFSIZE (ACE_JUMBO_MTU + ETH_HLEN + 4)
+
+/*
+ * There seems to be a magic difference in the effect between 995 and 996
+ * but little difference between 900 and 995 ... no idea why.
+ *
+ * There is now a default set of tuning parameters which is set, depending
+ * on whether or not the user enables Jumbo frames. It's assumed that if
+ * Jumbo frames are enabled, the user wants optimal tuning for that case.
+ */
+#define DEF_TX_COAL 400 /* 996 */
+#define DEF_TX_MAX_DESC 60 /* was 40 */
+#define DEF_RX_COAL 120 /* 1000 */
+#define DEF_RX_MAX_DESC 25
+#define DEF_TX_RATIO 21 /* 24 */
+
+#define DEF_JUMBO_TX_COAL 20
+#define DEF_JUMBO_TX_MAX_DESC 60
+#define DEF_JUMBO_RX_COAL 30
+#define DEF_JUMBO_RX_MAX_DESC 6
+#define DEF_JUMBO_TX_RATIO 21
+
+#if tigon2FwReleaseLocal < 20001118
+/*
+ * Standard firmware and early modifications duplicate
+ * IRQ load without this flag (coal timer is never reset).
+ * Note that with this flag tx_coal should be less than
+ * time to xmit full tx ring.
+ * 400usec is not so bad for tx ring size of 128.
+ */
+#define TX_COAL_INTS_ONLY 1 /* worth it */
+#else
+/*
+ * With modified firmware, this is not necessary, but still useful.
+ */
+#define TX_COAL_INTS_ONLY 1
+#endif
+
+#define DEF_TRACE 0
+#define DEF_STAT (2 * TICKS_PER_SEC)
+
+
+static int link_state[ACE_MAX_MOD_PARMS];
+static int trace[ACE_MAX_MOD_PARMS];
+static int tx_coal_tick[ACE_MAX_MOD_PARMS];
+static int rx_coal_tick[ACE_MAX_MOD_PARMS];
+static int max_tx_desc[ACE_MAX_MOD_PARMS];
+static int max_rx_desc[ACE_MAX_MOD_PARMS];
+static int tx_ratio[ACE_MAX_MOD_PARMS];
+static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
+
+MODULE_AUTHOR("Jes Sorensen <jes@trained-monkey.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
+#ifndef CONFIG_ACENIC_OMIT_TIGON_I
+MODULE_FIRMWARE("acenic/tg1.bin");
+#endif
+MODULE_FIRMWARE("acenic/tg2.bin");
+
+module_param_array_named(link, link_state, int, NULL, 0);
+module_param_array(trace, int, NULL, 0);
+module_param_array(tx_coal_tick, int, NULL, 0);
+module_param_array(max_tx_desc, int, NULL, 0);
+module_param_array(rx_coal_tick, int, NULL, 0);
+module_param_array(max_rx_desc, int, NULL, 0);
+module_param_array(tx_ratio, int, NULL, 0);
+MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state");
+MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level");
+MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives");
+MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait");
+MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives");
+MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait");
+MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)");
+
+
+static const char version[] =
+ "acenic.c: v0.92 08/05/2002 Jes Sorensen, linux-acenic@SunSITE.dk\n"
+ " http://home.cern.ch/~jes/gige/acenic.html\n";
+
+static int ace_get_settings(struct net_device *, struct ethtool_cmd *);
+static int ace_set_settings(struct net_device *, struct ethtool_cmd *);
+static void ace_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
+
+static const struct ethtool_ops ace_ethtool_ops = {
+ .get_settings = ace_get_settings,
+ .set_settings = ace_set_settings,
+ .get_drvinfo = ace_get_drvinfo,
+};
+
+static void ace_watchdog(struct net_device *dev);
+
+static const struct net_device_ops ace_netdev_ops = {
+ .ndo_open = ace_open,
+ .ndo_stop = ace_close,
+ .ndo_tx_timeout = ace_watchdog,
+ .ndo_get_stats = ace_get_stats,
+ .ndo_start_xmit = ace_start_xmit,
+ .ndo_set_rx_mode = ace_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = ace_set_mac_addr,
+ .ndo_change_mtu = ace_change_mtu,
+};
+
+static int acenic_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct net_device *dev;
+ struct ace_private *ap;
+ static int boards_found;
+
+ dev = alloc_etherdev(sizeof(struct ace_private));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ ap = netdev_priv(dev);
+ ap->pdev = pdev;
+ ap->name = pci_name(pdev);
+
+ dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+ dev->watchdog_timeo = 5*HZ;
+
+ dev->netdev_ops = &ace_netdev_ops;
+ dev->ethtool_ops = &ace_ethtool_ops;
+
+ /* we only display this string ONCE */
+ if (!boards_found)
+ printk(version);
+
+ if (pci_enable_device(pdev))
+ goto fail_free_netdev;
+
+ /*
+ * Enable master mode before we start playing with the
+ * pci_command word since pci_set_master() will modify
+ * it.
+ */
+ pci_set_master(pdev);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command);
+
+ /* OpenFirmware on Mac's does not set this - DOH.. */
+ if (!(ap->pci_command & PCI_COMMAND_MEMORY)) {
+ printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
+ "access - was not enabled by BIOS/Firmware\n",
+ ap->name);
+ ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY;
+ pci_write_config_word(ap->pdev, PCI_COMMAND,
+ ap->pci_command);
+ wmb();
+ }
+
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &ap->pci_latency);
+ if (ap->pci_latency <= 0x40) {
+ ap->pci_latency = 0x40;
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ap->pci_latency);
+ }
+
+ /*
+ * Remap the regs into kernel space - this is abuse of
+ * dev->base_addr since it was means for I/O port
+ * addresses but who gives a damn.
+ */
+ dev->base_addr = pci_resource_start(pdev, 0);
+ ap->regs = ioremap(dev->base_addr, 0x4000);
+ if (!ap->regs) {
+ printk(KERN_ERR "%s: Unable to map I/O register, "
+ "AceNIC %i will be disabled.\n",
+ ap->name, boards_found);
+ goto fail_free_netdev;
+ }
+
+ switch(pdev->vendor) {
+ case PCI_VENDOR_ID_ALTEON:
+ if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) {
+ printk(KERN_INFO "%s: Farallon PN9100-T ",
+ ap->name);
+ } else {
+ printk(KERN_INFO "%s: Alteon AceNIC ",
+ ap->name);
+ }
+ break;
+ case PCI_VENDOR_ID_3COM:
+ printk(KERN_INFO "%s: 3Com 3C985 ", ap->name);
+ break;
+ case PCI_VENDOR_ID_NETGEAR:
+ printk(KERN_INFO "%s: NetGear GA620 ", ap->name);
+ break;
+ case PCI_VENDOR_ID_DEC:
+ if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) {
+ printk(KERN_INFO "%s: Farallon PN9000-SX ",
+ ap->name);
+ break;
+ }
+ case PCI_VENDOR_ID_SGI:
+ printk(KERN_INFO "%s: SGI AceNIC ", ap->name);
+ break;
+ default:
+ printk(KERN_INFO "%s: Unknown AceNIC ", ap->name);
+ break;
+ }
+
+ printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr);
+ printk("irq %d\n", pdev->irq);
+
+#ifdef CONFIG_ACENIC_OMIT_TIGON_I
+ if ((readl(&ap->regs->HostCtrl) >> 28) == 4) {
+ printk(KERN_ERR "%s: Driver compiled without Tigon I"
+ " support - NIC disabled\n", dev->name);
+ goto fail_uninit;
+ }
+#endif
+
+ if (ace_allocate_descriptors(dev))
+ goto fail_free_netdev;
+
+#ifdef MODULE
+ if (boards_found >= ACE_MAX_MOD_PARMS)
+ ap->board_idx = BOARD_IDX_OVERFLOW;
+ else
+ ap->board_idx = boards_found;
+#else
+ ap->board_idx = BOARD_IDX_STATIC;
+#endif
+
+ if (ace_init(dev))
+ goto fail_free_netdev;
+
+ if (register_netdev(dev)) {
+ printk(KERN_ERR "acenic: device registration failed\n");
+ goto fail_uninit;
+ }
+ ap->name = dev->name;
+
+ if (ap->pci_using_dac)
+ dev->features |= NETIF_F_HIGHDMA;
+
+ pci_set_drvdata(pdev, dev);
+
+ boards_found++;
+ return 0;
+
+ fail_uninit:
+ ace_init_cleanup(dev);
+ fail_free_netdev:
+ free_netdev(dev);
+ return -ENODEV;
+}
+
+static void acenic_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ short i;
+
+ unregister_netdev(dev);
+
+ writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);
+ if (ap->version >= 2)
+ writel(readl(&regs->CpuBCtrl) | CPU_HALT, &regs->CpuBCtrl);
+
+ /*
+ * This clears any pending interrupts
+ */
+ writel(1, &regs->Mb0Lo);
+ readl(&regs->CpuCtrl); /* flush */
+
+ /*
+ * Make sure no other CPUs are processing interrupts
+ * on the card before the buffers are being released.
+ * Otherwise one might experience some `interesting'
+ * effects.
+ *
+ * Then release the RX buffers - jumbo buffers were
+ * already released in ace_close().
+ */
+ ace_sync_irq(dev->irq);
+
+ for (i = 0; i < RX_STD_RING_ENTRIES; i++) {
+ struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
+
+ if (skb) {
+ struct ring_info *ringp;
+ dma_addr_t mapping;
+
+ ringp = &ap->skb->rx_std_skbuff[i];
+ mapping = dma_unmap_addr(ringp, mapping);
+ pci_unmap_page(ap->pdev, mapping,
+ ACE_STD_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ ap->rx_std_ring[i].size = 0;
+ ap->skb->rx_std_skbuff[i].skb = NULL;
+ dev_kfree_skb(skb);
+ }
+ }
+
+ if (ap->version >= 2) {
+ for (i = 0; i < RX_MINI_RING_ENTRIES; i++) {
+ struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
+
+ if (skb) {
+ struct ring_info *ringp;
+ dma_addr_t mapping;
+
+ ringp = &ap->skb->rx_mini_skbuff[i];
+ mapping = dma_unmap_addr(ringp,mapping);
+ pci_unmap_page(ap->pdev, mapping,
+ ACE_MINI_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ ap->rx_mini_ring[i].size = 0;
+ ap->skb->rx_mini_skbuff[i].skb = NULL;
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+ for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
+ struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
+ if (skb) {
+ struct ring_info *ringp;
+ dma_addr_t mapping;
+
+ ringp = &ap->skb->rx_jumbo_skbuff[i];
+ mapping = dma_unmap_addr(ringp, mapping);
+ pci_unmap_page(ap->pdev, mapping,
+ ACE_JUMBO_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ ap->rx_jumbo_ring[i].size = 0;
+ ap->skb->rx_jumbo_skbuff[i].skb = NULL;
+ dev_kfree_skb(skb);
+ }
+ }
+
+ ace_init_cleanup(dev);
+ free_netdev(dev);
+}
+
+static struct pci_driver acenic_pci_driver = {
+ .name = "acenic",
+ .id_table = acenic_pci_tbl,
+ .probe = acenic_probe_one,
+ .remove = acenic_remove_one,
+};
+
+static void ace_free_descriptors(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ int size;
+
+ if (ap->rx_std_ring != NULL) {
+ size = (sizeof(struct rx_desc) *
+ (RX_STD_RING_ENTRIES +
+ RX_JUMBO_RING_ENTRIES +
+ RX_MINI_RING_ENTRIES +
+ RX_RETURN_RING_ENTRIES));
+ pci_free_consistent(ap->pdev, size, ap->rx_std_ring,
+ ap->rx_ring_base_dma);
+ ap->rx_std_ring = NULL;
+ ap->rx_jumbo_ring = NULL;
+ ap->rx_mini_ring = NULL;
+ ap->rx_return_ring = NULL;
+ }
+ if (ap->evt_ring != NULL) {
+ size = (sizeof(struct event) * EVT_RING_ENTRIES);
+ pci_free_consistent(ap->pdev, size, ap->evt_ring,
+ ap->evt_ring_dma);
+ ap->evt_ring = NULL;
+ }
+ if (ap->tx_ring != NULL && !ACE_IS_TIGON_I(ap)) {
+ size = (sizeof(struct tx_desc) * MAX_TX_RING_ENTRIES);
+ pci_free_consistent(ap->pdev, size, ap->tx_ring,
+ ap->tx_ring_dma);
+ }
+ ap->tx_ring = NULL;
+
+ if (ap->evt_prd != NULL) {
+ pci_free_consistent(ap->pdev, sizeof(u32),
+ (void *)ap->evt_prd, ap->evt_prd_dma);
+ ap->evt_prd = NULL;
+ }
+ if (ap->rx_ret_prd != NULL) {
+ pci_free_consistent(ap->pdev, sizeof(u32),
+ (void *)ap->rx_ret_prd,
+ ap->rx_ret_prd_dma);
+ ap->rx_ret_prd = NULL;
+ }
+ if (ap->tx_csm != NULL) {
+ pci_free_consistent(ap->pdev, sizeof(u32),
+ (void *)ap->tx_csm, ap->tx_csm_dma);
+ ap->tx_csm = NULL;
+ }
+}
+
+
+static int ace_allocate_descriptors(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ int size;
+
+ size = (sizeof(struct rx_desc) *
+ (RX_STD_RING_ENTRIES +
+ RX_JUMBO_RING_ENTRIES +
+ RX_MINI_RING_ENTRIES +
+ RX_RETURN_RING_ENTRIES));
+
+ ap->rx_std_ring = pci_alloc_consistent(ap->pdev, size,
+ &ap->rx_ring_base_dma);
+ if (ap->rx_std_ring == NULL)
+ goto fail;
+
+ ap->rx_jumbo_ring = ap->rx_std_ring + RX_STD_RING_ENTRIES;
+ ap->rx_mini_ring = ap->rx_jumbo_ring + RX_JUMBO_RING_ENTRIES;
+ ap->rx_return_ring = ap->rx_mini_ring + RX_MINI_RING_ENTRIES;
+
+ size = (sizeof(struct event) * EVT_RING_ENTRIES);
+
+ ap->evt_ring = pci_alloc_consistent(ap->pdev, size, &ap->evt_ring_dma);
+
+ if (ap->evt_ring == NULL)
+ goto fail;
+
+ /*
+ * Only allocate a host TX ring for the Tigon II, the Tigon I
+ * has to use PCI registers for this ;-(
+ */
+ if (!ACE_IS_TIGON_I(ap)) {
+ size = (sizeof(struct tx_desc) * MAX_TX_RING_ENTRIES);
+
+ ap->tx_ring = pci_alloc_consistent(ap->pdev, size,
+ &ap->tx_ring_dma);
+
+ if (ap->tx_ring == NULL)
+ goto fail;
+ }
+
+ ap->evt_prd = pci_alloc_consistent(ap->pdev, sizeof(u32),
+ &ap->evt_prd_dma);
+ if (ap->evt_prd == NULL)
+ goto fail;
+
+ ap->rx_ret_prd = pci_alloc_consistent(ap->pdev, sizeof(u32),
+ &ap->rx_ret_prd_dma);
+ if (ap->rx_ret_prd == NULL)
+ goto fail;
+
+ ap->tx_csm = pci_alloc_consistent(ap->pdev, sizeof(u32),
+ &ap->tx_csm_dma);
+ if (ap->tx_csm == NULL)
+ goto fail;
+
+ return 0;
+
+fail:
+ /* Clean up. */
+ ace_init_cleanup(dev);
+ return 1;
+}
+
+
+/*
+ * Generic cleanup handling data allocated during init. Used when the
+ * module is unloaded or if an error occurs during initialization
+ */
+static void ace_init_cleanup(struct net_device *dev)
+{
+ struct ace_private *ap;
+
+ ap = netdev_priv(dev);
+
+ ace_free_descriptors(dev);
+
+ if (ap->info)
+ pci_free_consistent(ap->pdev, sizeof(struct ace_info),
+ ap->info, ap->info_dma);
+ kfree(ap->skb);
+ kfree(ap->trace_buf);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
+ iounmap(ap->regs);
+}
+
+
+/*
+ * Commands are considered to be slow.
+ */
+static inline void ace_issue_cmd(struct ace_regs __iomem *regs, struct cmd *cmd)
+{
+ u32 idx;
+
+ idx = readl(&regs->CmdPrd);
+
+ writel(*(u32 *)(cmd), &regs->CmdRng[idx]);
+ idx = (idx + 1) % CMD_RING_ENTRIES;
+
+ writel(idx, &regs->CmdPrd);
+}
+
+
+static int ace_init(struct net_device *dev)
+{
+ struct ace_private *ap;
+ struct ace_regs __iomem *regs;
+ struct ace_info *info = NULL;
+ struct pci_dev *pdev;
+ unsigned long myjif;
+ u64 tmp_ptr;
+ u32 tig_ver, mac1, mac2, tmp, pci_state;
+ int board_idx, ecode = 0;
+ short i;
+ unsigned char cache_size;
+
+ ap = netdev_priv(dev);
+ regs = ap->regs;
+
+ board_idx = ap->board_idx;
+
+ /*
+ * aman@sgi.com - its useful to do a NIC reset here to
+ * address the `Firmware not running' problem subsequent
+ * to any crashes involving the NIC
+ */
+ writel(HW_RESET | (HW_RESET << 24), &regs->HostCtrl);
+ readl(&regs->HostCtrl); /* PCI write posting */
+ udelay(5);
+
+ /*
+ * Don't access any other registers before this point!
+ */
+#ifdef __BIG_ENDIAN
+ /*
+ * This will most likely need BYTE_SWAP once we switch
+ * to using __raw_writel()
+ */
+ writel((WORD_SWAP | CLR_INT | ((WORD_SWAP | CLR_INT) << 24)),
+ &regs->HostCtrl);
+#else
+ writel((CLR_INT | WORD_SWAP | ((CLR_INT | WORD_SWAP) << 24)),
+ &regs->HostCtrl);
+#endif
+ readl(&regs->HostCtrl); /* PCI write posting */
+
+ /*
+ * Stop the NIC CPU and clear pending interrupts
+ */
+ writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);
+ readl(&regs->CpuCtrl); /* PCI write posting */
+ writel(0, &regs->Mb0Lo);
+
+ tig_ver = readl(&regs->HostCtrl) >> 28;
+
+ switch(tig_ver){
+#ifndef CONFIG_ACENIC_OMIT_TIGON_I
+ case 4:
+ case 5:
+ printk(KERN_INFO " Tigon I (Rev. %i), Firmware: %i.%i.%i, ",
+ tig_ver, ap->firmware_major, ap->firmware_minor,
+ ap->firmware_fix);
+ writel(0, &regs->LocalCtrl);
+ ap->version = 1;
+ ap->tx_ring_entries = TIGON_I_TX_RING_ENTRIES;
+ break;
+#endif
+ case 6:
+ printk(KERN_INFO " Tigon II (Rev. %i), Firmware: %i.%i.%i, ",
+ tig_ver, ap->firmware_major, ap->firmware_minor,
+ ap->firmware_fix);
+ writel(readl(&regs->CpuBCtrl) | CPU_HALT, &regs->CpuBCtrl);
+ readl(&regs->CpuBCtrl); /* PCI write posting */
+ /*
+ * The SRAM bank size does _not_ indicate the amount
+ * of memory on the card, it controls the _bank_ size!
+ * Ie. a 1MB AceNIC will have two banks of 512KB.
+ */
+ writel(SRAM_BANK_512K, &regs->LocalCtrl);
+ writel(SYNC_SRAM_TIMING, &regs->MiscCfg);
+ ap->version = 2;
+ ap->tx_ring_entries = MAX_TX_RING_ENTRIES;
+ break;
+ default:
+ printk(KERN_WARNING " Unsupported Tigon version detected "
+ "(%i)\n", tig_ver);
+ ecode = -ENODEV;
+ goto init_error;
+ }
+
+ /*
+ * ModeStat _must_ be set after the SRAM settings as this change
+ * seems to corrupt the ModeStat and possible other registers.
+ * The SRAM settings survive resets and setting it to the same
+ * value a second time works as well. This is what caused the
+ * `Firmware not running' problem on the Tigon II.
+ */
+#ifdef __BIG_ENDIAN
+ writel(ACE_BYTE_SWAP_DMA | ACE_WARN | ACE_FATAL | ACE_BYTE_SWAP_BD |
+ ACE_WORD_SWAP_BD | ACE_NO_JUMBO_FRAG, &regs->ModeStat);
+#else
+ writel(ACE_BYTE_SWAP_DMA | ACE_WARN | ACE_FATAL |
+ ACE_WORD_SWAP_BD | ACE_NO_JUMBO_FRAG, &regs->ModeStat);
+#endif
+ readl(&regs->ModeStat); /* PCI write posting */
+
+ mac1 = 0;
+ for(i = 0; i < 4; i++) {
+ int t;
+
+ mac1 = mac1 << 8;
+ t = read_eeprom_byte(dev, 0x8c+i);
+ if (t < 0) {
+ ecode = -EIO;
+ goto init_error;
+ } else
+ mac1 |= (t & 0xff);
+ }
+ mac2 = 0;
+ for(i = 4; i < 8; i++) {
+ int t;
+
+ mac2 = mac2 << 8;
+ t = read_eeprom_byte(dev, 0x8c+i);
+ if (t < 0) {
+ ecode = -EIO;
+ goto init_error;
+ } else
+ mac2 |= (t & 0xff);
+ }
+
+ writel(mac1, &regs->MacAddrHi);
+ writel(mac2, &regs->MacAddrLo);
+
+ dev->dev_addr[0] = (mac1 >> 8) & 0xff;
+ dev->dev_addr[1] = mac1 & 0xff;
+ dev->dev_addr[2] = (mac2 >> 24) & 0xff;
+ dev->dev_addr[3] = (mac2 >> 16) & 0xff;
+ dev->dev_addr[4] = (mac2 >> 8) & 0xff;
+ dev->dev_addr[5] = mac2 & 0xff;
+
+ printk("MAC: %pM\n", dev->dev_addr);
+
+ /*
+ * Looks like this is necessary to deal with on all architectures,
+ * even this %$#%$# N440BX Intel based thing doesn't get it right.
+ * Ie. having two NICs in the machine, one will have the cache
+ * line set at boot time, the other will not.
+ */
+ pdev = ap->pdev;
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_size);
+ cache_size <<= 2;
+ if (cache_size != SMP_CACHE_BYTES) {
+ printk(KERN_INFO " PCI cache line size set incorrectly "
+ "(%i bytes) by BIOS/FW, ", cache_size);
+ if (cache_size > SMP_CACHE_BYTES)
+ printk("expecting %i\n", SMP_CACHE_BYTES);
+ else {
+ printk("correcting to %i\n", SMP_CACHE_BYTES);
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ SMP_CACHE_BYTES >> 2);
+ }
+ }
+
+ pci_state = readl(&regs->PciState);
+ printk(KERN_INFO " PCI bus width: %i bits, speed: %iMHz, "
+ "latency: %i clks\n",
+ (pci_state & PCI_32BIT) ? 32 : 64,
+ (pci_state & PCI_66MHZ) ? 66 : 33,
+ ap->pci_latency);
+
+ /*
+ * Set the max DMA transfer size. Seems that for most systems
+ * the performance is better when no MAX parameter is
+ * set. However for systems enabling PCI write and invalidate,
+ * DMA writes must be set to the L1 cache line size to get
+ * optimal performance.
+ *
+ * The default is now to turn the PCI write and invalidate off
+ * - that is what Alteon does for NT.
+ */
+ tmp = READ_CMD_MEM | WRITE_CMD_MEM;
+ if (ap->version >= 2) {
+ tmp |= (MEM_READ_MULTIPLE | (pci_state & PCI_66MHZ));
+ /*
+ * Tuning parameters only supported for 8 cards
+ */
+ if (board_idx == BOARD_IDX_OVERFLOW ||
+ dis_pci_mem_inval[board_idx]) {
+ if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
+ ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
+ pci_write_config_word(pdev, PCI_COMMAND,
+ ap->pci_command);
+ printk(KERN_INFO " Disabling PCI memory "
+ "write and invalidate\n");
+ }
+ } else if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
+ printk(KERN_INFO " PCI memory write & invalidate "
+ "enabled by BIOS, enabling counter measures\n");
+
+ switch(SMP_CACHE_BYTES) {
+ case 16:
+ tmp |= DMA_WRITE_MAX_16;
+ break;
+ case 32:
+ tmp |= DMA_WRITE_MAX_32;
+ break;
+ case 64:
+ tmp |= DMA_WRITE_MAX_64;
+ break;
+ case 128:
+ tmp |= DMA_WRITE_MAX_128;
+ break;
+ default:
+ printk(KERN_INFO " Cache line size %i not "
+ "supported, PCI write and invalidate "
+ "disabled\n", SMP_CACHE_BYTES);
+ ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
+ pci_write_config_word(pdev, PCI_COMMAND,
+ ap->pci_command);
+ }
+ }
+ }
+
+#ifdef __sparc__
+ /*
+ * On this platform, we know what the best dma settings
+ * are. We use 64-byte maximum bursts, because if we
+ * burst larger than the cache line size (or even cross
+ * a 64byte boundary in a single burst) the UltraSparc
+ * PCI controller will disconnect at 64-byte multiples.
+ *
+ * Read-multiple will be properly enabled above, and when
+ * set will give the PCI controller proper hints about
+ * prefetching.
+ */
+ tmp &= ~DMA_READ_WRITE_MASK;
+ tmp |= DMA_READ_MAX_64;
+ tmp |= DMA_WRITE_MAX_64;
+#endif
+#ifdef __alpha__
+ tmp &= ~DMA_READ_WRITE_MASK;
+ tmp |= DMA_READ_MAX_128;
+ /*
+ * All the docs say MUST NOT. Well, I did.
+ * Nothing terrible happens, if we load wrong size.
+ * Bit w&i still works better!
+ */
+ tmp |= DMA_WRITE_MAX_128;
+#endif
+ writel(tmp, &regs->PciState);
+
+#if 0
+ /*
+ * The Host PCI bus controller driver has to set FBB.
+ * If all devices on that PCI bus support FBB, then the controller
+ * can enable FBB support in the Host PCI Bus controller (or on
+ * the PCI-PCI bridge if that applies).
+ * -ggg
+ */
+ /*
+ * I have received reports from people having problems when this
+ * bit is enabled.
+ */
+ if (!(ap->pci_command & PCI_COMMAND_FAST_BACK)) {
+ printk(KERN_INFO " Enabling PCI Fast Back to Back\n");
+ ap->pci_command |= PCI_COMMAND_FAST_BACK;
+ pci_write_config_word(pdev, PCI_COMMAND, ap->pci_command);
+ }
+#endif
+
+ /*
+ * Configure DMA attributes.
+ */
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ ap->pci_using_dac = 1;
+ } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ ap->pci_using_dac = 0;
+ } else {
+ ecode = -ENODEV;
+ goto init_error;
+ }
+
+ /*
+ * Initialize the generic info block and the command+event rings
+ * and the control blocks for the transmit and receive rings
+ * as they need to be setup once and for all.
+ */
+ if (!(info = pci_alloc_consistent(ap->pdev, sizeof(struct ace_info),
+ &ap->info_dma))) {
+ ecode = -EAGAIN;
+ goto init_error;
+ }
+ ap->info = info;
+
+ /*
+ * Get the memory for the skb rings.
+ */
+ if (!(ap->skb = kmalloc(sizeof(struct ace_skb), GFP_KERNEL))) {
+ ecode = -EAGAIN;
+ goto init_error;
+ }
+
+ ecode = request_irq(pdev->irq, ace_interrupt, IRQF_SHARED,
+ DRV_NAME, dev);
+ if (ecode) {
+ printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
+ DRV_NAME, pdev->irq);
+ goto init_error;
+ } else
+ dev->irq = pdev->irq;
+
+#ifdef INDEX_DEBUG
+ spin_lock_init(&ap->debug_lock);
+ ap->last_tx = ACE_TX_RING_ENTRIES(ap) - 1;
+ ap->last_std_rx = 0;
+ ap->last_mini_rx = 0;
+#endif
+
+ memset(ap->info, 0, sizeof(struct ace_info));
+ memset(ap->skb, 0, sizeof(struct ace_skb));
+
+ ecode = ace_load_firmware(dev);
+ if (ecode)
+ goto init_error;
+
+ ap->fw_running = 0;
+
+ tmp_ptr = ap->info_dma;
+ writel(tmp_ptr >> 32, &regs->InfoPtrHi);
+ writel(tmp_ptr & 0xffffffff, &regs->InfoPtrLo);
+
+ memset(ap->evt_ring, 0, EVT_RING_ENTRIES * sizeof(struct event));
+
+ set_aceaddr(&info->evt_ctrl.rngptr, ap->evt_ring_dma);
+ info->evt_ctrl.flags = 0;
+
+ *(ap->evt_prd) = 0;
+ wmb();
+ set_aceaddr(&info->evt_prd_ptr, ap->evt_prd_dma);
+ writel(0, &regs->EvtCsm);
+
+ set_aceaddr(&info->cmd_ctrl.rngptr, 0x100);
+ info->cmd_ctrl.flags = 0;
+ info->cmd_ctrl.max_len = 0;
+
+ for (i = 0; i < CMD_RING_ENTRIES; i++)
+ writel(0, &regs->CmdRng[i]);
+
+ writel(0, &regs->CmdPrd);
+ writel(0, &regs->CmdCsm);
+
+ tmp_ptr = ap->info_dma;
+ tmp_ptr += (unsigned long) &(((struct ace_info *)0)->s.stats);
+ set_aceaddr(&info->stats2_ptr, (dma_addr_t) tmp_ptr);
+
+ set_aceaddr(&info->rx_std_ctrl.rngptr, ap->rx_ring_base_dma);
+ info->rx_std_ctrl.max_len = ACE_STD_BUFSIZE;
+ info->rx_std_ctrl.flags =
+ RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
+
+ memset(ap->rx_std_ring, 0,
+ RX_STD_RING_ENTRIES * sizeof(struct rx_desc));
+
+ for (i = 0; i < RX_STD_RING_ENTRIES; i++)
+ ap->rx_std_ring[i].flags = BD_FLG_TCP_UDP_SUM;
+
+ ap->rx_std_skbprd = 0;
+ atomic_set(&ap->cur_rx_bufs, 0);
+
+ set_aceaddr(&info->rx_jumbo_ctrl.rngptr,
+ (ap->rx_ring_base_dma +
+ (sizeof(struct rx_desc) * RX_STD_RING_ENTRIES)));
+ info->rx_jumbo_ctrl.max_len = 0;
+ info->rx_jumbo_ctrl.flags =
+ RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
+
+ memset(ap->rx_jumbo_ring, 0,
+ RX_JUMBO_RING_ENTRIES * sizeof(struct rx_desc));
+
+ for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++)
+ ap->rx_jumbo_ring[i].flags = BD_FLG_TCP_UDP_SUM | BD_FLG_JUMBO;
+
+ ap->rx_jumbo_skbprd = 0;
+ atomic_set(&ap->cur_jumbo_bufs, 0);
+
+ memset(ap->rx_mini_ring, 0,
+ RX_MINI_RING_ENTRIES * sizeof(struct rx_desc));
+
+ if (ap->version >= 2) {
+ set_aceaddr(&info->rx_mini_ctrl.rngptr,
+ (ap->rx_ring_base_dma +
+ (sizeof(struct rx_desc) *
+ (RX_STD_RING_ENTRIES +
+ RX_JUMBO_RING_ENTRIES))));
+ info->rx_mini_ctrl.max_len = ACE_MINI_SIZE;
+ info->rx_mini_ctrl.flags =
+ RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|RCB_FLG_VLAN_ASSIST;
+
+ for (i = 0; i < RX_MINI_RING_ENTRIES; i++)
+ ap->rx_mini_ring[i].flags =
+ BD_FLG_TCP_UDP_SUM | BD_FLG_MINI;
+ } else {
+ set_aceaddr(&info->rx_mini_ctrl.rngptr, 0);
+ info->rx_mini_ctrl.flags = RCB_FLG_RNG_DISABLE;
+ info->rx_mini_ctrl.max_len = 0;
+ }
+
+ ap->rx_mini_skbprd = 0;
+ atomic_set(&ap->cur_mini_bufs, 0);
+
+ set_aceaddr(&info->rx_return_ctrl.rngptr,
+ (ap->rx_ring_base_dma +
+ (sizeof(struct rx_desc) *
+ (RX_STD_RING_ENTRIES +
+ RX_JUMBO_RING_ENTRIES +
+ RX_MINI_RING_ENTRIES))));
+ info->rx_return_ctrl.flags = 0;
+ info->rx_return_ctrl.max_len = RX_RETURN_RING_ENTRIES;
+
+ memset(ap->rx_return_ring, 0,
+ RX_RETURN_RING_ENTRIES * sizeof(struct rx_desc));
+
+ set_aceaddr(&info->rx_ret_prd_ptr, ap->rx_ret_prd_dma);
+ *(ap->rx_ret_prd) = 0;
+
+ writel(TX_RING_BASE, &regs->WinBase);
+
+ if (ACE_IS_TIGON_I(ap)) {
+ ap->tx_ring = (__force struct tx_desc *) regs->Window;
+ for (i = 0; i < (TIGON_I_TX_RING_ENTRIES
+ * sizeof(struct tx_desc)) / sizeof(u32); i++)
+ writel(0, (__force void __iomem *)ap->tx_ring + i * 4);
+
+ set_aceaddr(&info->tx_ctrl.rngptr, TX_RING_BASE);
+ } else {
+ memset(ap->tx_ring, 0,
+ MAX_TX_RING_ENTRIES * sizeof(struct tx_desc));
+
+ set_aceaddr(&info->tx_ctrl.rngptr, ap->tx_ring_dma);
+ }
+
+ info->tx_ctrl.max_len = ACE_TX_RING_ENTRIES(ap);
+ tmp = RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | RCB_FLG_VLAN_ASSIST;
+
+ /*
+ * The Tigon I does not like having the TX ring in host memory ;-(
+ */
+ if (!ACE_IS_TIGON_I(ap))
+ tmp |= RCB_FLG_TX_HOST_RING;
+#if TX_COAL_INTS_ONLY
+ tmp |= RCB_FLG_COAL_INT_ONLY;
+#endif
+ info->tx_ctrl.flags = tmp;
+
+ set_aceaddr(&info->tx_csm_ptr, ap->tx_csm_dma);
+
+ /*
+ * Potential item for tuning parameter
+ */
+#if 0 /* NO */
+ writel(DMA_THRESH_16W, &regs->DmaReadCfg);
+ writel(DMA_THRESH_16W, &regs->DmaWriteCfg);
+#else
+ writel(DMA_THRESH_8W, &regs->DmaReadCfg);
+ writel(DMA_THRESH_8W, &regs->DmaWriteCfg);
+#endif
+
+ writel(0, &regs->MaskInt);
+ writel(1, &regs->IfIdx);
+#if 0
+ /*
+ * McKinley boxes do not like us fiddling with AssistState
+ * this early
+ */
+ writel(1, &regs->AssistState);
+#endif
+
+ writel(DEF_STAT, &regs->TuneStatTicks);
+ writel(DEF_TRACE, &regs->TuneTrace);
+
+ ace_set_rxtx_parms(dev, 0);
+
+ if (board_idx == BOARD_IDX_OVERFLOW) {
+ printk(KERN_WARNING "%s: more than %i NICs detected, "
+ "ignoring module parameters!\n",
+ ap->name, ACE_MAX_MOD_PARMS);
+ } else if (board_idx >= 0) {
+ if (tx_coal_tick[board_idx])
+ writel(tx_coal_tick[board_idx],
+ &regs->TuneTxCoalTicks);
+ if (max_tx_desc[board_idx])
+ writel(max_tx_desc[board_idx], &regs->TuneMaxTxDesc);
+
+ if (rx_coal_tick[board_idx])
+ writel(rx_coal_tick[board_idx],
+ &regs->TuneRxCoalTicks);
+ if (max_rx_desc[board_idx])
+ writel(max_rx_desc[board_idx], &regs->TuneMaxRxDesc);
+
+ if (trace[board_idx])
+ writel(trace[board_idx], &regs->TuneTrace);
+
+ if ((tx_ratio[board_idx] > 0) && (tx_ratio[board_idx] < 64))
+ writel(tx_ratio[board_idx], &regs->TxBufRat);
+ }
+
+ /*
+ * Default link parameters
+ */
+ tmp = LNK_ENABLE | LNK_FULL_DUPLEX | LNK_1000MB | LNK_100MB |
+ LNK_10MB | LNK_RX_FLOW_CTL_Y | LNK_NEG_FCTL | LNK_NEGOTIATE;
+ if(ap->version >= 2)
+ tmp |= LNK_TX_FLOW_CTL_Y;
+
+ /*
+ * Override link default parameters
+ */
+ if ((board_idx >= 0) && link_state[board_idx]) {
+ int option = link_state[board_idx];
+
+ tmp = LNK_ENABLE;
+
+ if (option & 0x01) {
+ printk(KERN_INFO "%s: Setting half duplex link\n",
+ ap->name);
+ tmp &= ~LNK_FULL_DUPLEX;
+ }
+ if (option & 0x02)
+ tmp &= ~LNK_NEGOTIATE;
+ if (option & 0x10)
+ tmp |= LNK_10MB;
+ if (option & 0x20)
+ tmp |= LNK_100MB;
+ if (option & 0x40)
+ tmp |= LNK_1000MB;
+ if ((option & 0x70) == 0) {
+ printk(KERN_WARNING "%s: No media speed specified, "
+ "forcing auto negotiation\n", ap->name);
+ tmp |= LNK_NEGOTIATE | LNK_1000MB |
+ LNK_100MB | LNK_10MB;
+ }
+ if ((option & 0x100) == 0)
+ tmp |= LNK_NEG_FCTL;
+ else
+ printk(KERN_INFO "%s: Disabling flow control "
+ "negotiation\n", ap->name);
+ if (option & 0x200)
+ tmp |= LNK_RX_FLOW_CTL_Y;
+ if ((option & 0x400) && (ap->version >= 2)) {
+ printk(KERN_INFO "%s: Enabling TX flow control\n",
+ ap->name);
+ tmp |= LNK_TX_FLOW_CTL_Y;
+ }
+ }
+
+ ap->link = tmp;
+ writel(tmp, &regs->TuneLink);
+ if (ap->version >= 2)
+ writel(tmp, &regs->TuneFastLink);
+
+ writel(ap->firmware_start, &regs->Pc);
+
+ writel(0, &regs->Mb0Lo);
+
+ /*
+ * Set tx_csm before we start receiving interrupts, otherwise
+ * the interrupt handler might think it is supposed to process
+ * tx ints before we are up and running, which may cause a null
+ * pointer access in the int handler.
+ */
+ ap->cur_rx = 0;
+ ap->tx_prd = *(ap->tx_csm) = ap->tx_ret_csm = 0;
+
+ wmb();
+ ace_set_txprd(regs, ap, 0);
+ writel(0, &regs->RxRetCsm);
+
+ /*
+ * Enable DMA engine now.
+ * If we do this sooner, Mckinley box pukes.
+ * I assume it's because Tigon II DMA engine wants to check
+ * *something* even before the CPU is started.
+ */
+ writel(1, &regs->AssistState); /* enable DMA */
+
+ /*
+ * Start the NIC CPU
+ */
+ writel(readl(&regs->CpuCtrl) & ~(CPU_HALT|CPU_TRACE), &regs->CpuCtrl);
+ readl(&regs->CpuCtrl);
+
+ /*
+ * Wait for the firmware to spin up - max 3 seconds.
+ */
+ myjif = jiffies + 3 * HZ;
+ while (time_before(jiffies, myjif) && !ap->fw_running)
+ cpu_relax();
+
+ if (!ap->fw_running) {
+ printk(KERN_ERR "%s: Firmware NOT running!\n", ap->name);
+
+ ace_dump_trace(ap);
+ writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);
+ readl(&regs->CpuCtrl);
+
+ /* aman@sgi.com - account for badly behaving firmware/NIC:
+ * - have observed that the NIC may continue to generate
+ * interrupts for some reason; attempt to stop it - halt
+ * second CPU for Tigon II cards, and also clear Mb0
+ * - if we're a module, we'll fail to load if this was
+ * the only GbE card in the system => if the kernel does
+ * see an interrupt from the NIC, code to handle it is
+ * gone and OOps! - so free_irq also
+ */
+ if (ap->version >= 2)
+ writel(readl(&regs->CpuBCtrl) | CPU_HALT,
+ &regs->CpuBCtrl);
+ writel(0, &regs->Mb0Lo);
+ readl(&regs->Mb0Lo);
+
+ ecode = -EBUSY;
+ goto init_error;
+ }
+
+ /*
+ * We load the ring here as there seem to be no way to tell the
+ * firmware to wipe the ring without re-initializing it.
+ */
+ if (!test_and_set_bit(0, &ap->std_refill_busy))
+ ace_load_std_rx_ring(dev, RX_RING_SIZE);
+ else
+ printk(KERN_ERR "%s: Someone is busy refilling the RX ring\n",
+ ap->name);
+ if (ap->version >= 2) {
+ if (!test_and_set_bit(0, &ap->mini_refill_busy))
+ ace_load_mini_rx_ring(dev, RX_MINI_SIZE);
+ else
+ printk(KERN_ERR "%s: Someone is busy refilling "
+ "the RX mini ring\n", ap->name);
+ }
+ return 0;
+
+ init_error:
+ ace_init_cleanup(dev);
+ return ecode;
+}
+
+
+static void ace_set_rxtx_parms(struct net_device *dev, int jumbo)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ int board_idx = ap->board_idx;
+
+ if (board_idx >= 0) {
+ if (!jumbo) {
+ if (!tx_coal_tick[board_idx])
+ writel(DEF_TX_COAL, &regs->TuneTxCoalTicks);
+ if (!max_tx_desc[board_idx])
+ writel(DEF_TX_MAX_DESC, &regs->TuneMaxTxDesc);
+ if (!rx_coal_tick[board_idx])
+ writel(DEF_RX_COAL, &regs->TuneRxCoalTicks);
+ if (!max_rx_desc[board_idx])
+ writel(DEF_RX_MAX_DESC, &regs->TuneMaxRxDesc);
+ if (!tx_ratio[board_idx])
+ writel(DEF_TX_RATIO, &regs->TxBufRat);
+ } else {
+ if (!tx_coal_tick[board_idx])
+ writel(DEF_JUMBO_TX_COAL,
+ &regs->TuneTxCoalTicks);
+ if (!max_tx_desc[board_idx])
+ writel(DEF_JUMBO_TX_MAX_DESC,
+ &regs->TuneMaxTxDesc);
+ if (!rx_coal_tick[board_idx])
+ writel(DEF_JUMBO_RX_COAL,
+ &regs->TuneRxCoalTicks);
+ if (!max_rx_desc[board_idx])
+ writel(DEF_JUMBO_RX_MAX_DESC,
+ &regs->TuneMaxRxDesc);
+ if (!tx_ratio[board_idx])
+ writel(DEF_JUMBO_TX_RATIO, &regs->TxBufRat);
+ }
+ }
+}
+
+
+static void ace_watchdog(struct net_device *data)
+{
+ struct net_device *dev = data;
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+
+ /*
+ * We haven't received a stats update event for more than 2.5
+ * seconds and there is data in the transmit queue, thus we
+ * assume the card is stuck.
+ */
+ if (*ap->tx_csm != ap->tx_ret_csm) {
+ printk(KERN_WARNING "%s: Transmitter is stuck, %08x\n",
+ dev->name, (unsigned int)readl(&regs->HostCtrl));
+ /* This can happen due to ieee flow control. */
+ } else {
+ printk(KERN_DEBUG "%s: BUG... transmitter died. Kicking it.\n",
+ dev->name);
+#if 0
+ netif_wake_queue(dev);
+#endif
+ }
+}
+
+
+static void ace_tasklet(unsigned long arg)
+{
+ struct net_device *dev = (struct net_device *) arg;
+ struct ace_private *ap = netdev_priv(dev);
+ int cur_size;
+
+ cur_size = atomic_read(&ap->cur_rx_bufs);
+ if ((cur_size < RX_LOW_STD_THRES) &&
+ !test_and_set_bit(0, &ap->std_refill_busy)) {
+#ifdef DEBUG
+ printk("refilling buffers (current %i)\n", cur_size);
+#endif
+ ace_load_std_rx_ring(dev, RX_RING_SIZE - cur_size);
+ }
+
+ if (ap->version >= 2) {
+ cur_size = atomic_read(&ap->cur_mini_bufs);
+ if ((cur_size < RX_LOW_MINI_THRES) &&
+ !test_and_set_bit(0, &ap->mini_refill_busy)) {
+#ifdef DEBUG
+ printk("refilling mini buffers (current %i)\n",
+ cur_size);
+#endif
+ ace_load_mini_rx_ring(dev, RX_MINI_SIZE - cur_size);
+ }
+ }
+
+ cur_size = atomic_read(&ap->cur_jumbo_bufs);
+ if (ap->jumbo && (cur_size < RX_LOW_JUMBO_THRES) &&
+ !test_and_set_bit(0, &ap->jumbo_refill_busy)) {
+#ifdef DEBUG
+ printk("refilling jumbo buffers (current %i)\n", cur_size);
+#endif
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE - cur_size);
+ }
+ ap->tasklet_pending = 0;
+}
+
+
+/*
+ * Copy the contents of the NIC's trace buffer to kernel memory.
+ */
+static void ace_dump_trace(struct ace_private *ap)
+{
+#if 0
+ if (!ap->trace_buf)
+ if (!(ap->trace_buf = kmalloc(ACE_TRACE_SIZE, GFP_KERNEL)))
+ return;
+#endif
+}
+
+
+/*
+ * Load the standard rx ring.
+ *
+ * Loading rings is safe without holding the spin lock since this is
+ * done only before the device is enabled, thus no interrupts are
+ * generated and by the interrupt handler/tasklet handler.
+ */
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ short i, idx;
+
+
+ prefetchw(&ap->cur_rx_bufs);
+
+ idx = ap->rx_std_skbprd;
+
+ for (i = 0; i < nr_bufs; i++) {
+ struct sk_buff *skb;
+ struct rx_desc *rd;
+ dma_addr_t mapping;
+
+ skb = netdev_alloc_skb_ip_align(dev, ACE_STD_BUFSIZE);
+ if (!skb)
+ break;
+
+ mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
+ offset_in_page(skb->data),
+ ACE_STD_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+ ap->skb->rx_std_skbuff[idx].skb = skb;
+ dma_unmap_addr_set(&ap->skb->rx_std_skbuff[idx],
+ mapping, mapping);
+
+ rd = &ap->rx_std_ring[idx];
+ set_aceaddr(&rd->addr, mapping);
+ rd->size = ACE_STD_BUFSIZE;
+ rd->idx = idx;
+ idx = (idx + 1) % RX_STD_RING_ENTRIES;
+ }
+
+ if (!i)
+ goto error_out;
+
+ atomic_add(i, &ap->cur_rx_bufs);
+ ap->rx_std_skbprd = idx;
+
+ if (ACE_IS_TIGON_I(ap)) {
+ struct cmd cmd;
+ cmd.evt = C_SET_RX_PRD_IDX;
+ cmd.code = 0;
+ cmd.idx = ap->rx_std_skbprd;
+ ace_issue_cmd(regs, &cmd);
+ } else {
+ writel(idx, &regs->RxStdPrd);
+ wmb();
+ }
+
+ out:
+ clear_bit(0, &ap->std_refill_busy);
+ return;
+
+ error_out:
+ printk(KERN_INFO "Out of memory when allocating "
+ "standard receive buffers\n");
+ goto out;
+}
+
+
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ short i, idx;
+
+ prefetchw(&ap->cur_mini_bufs);
+
+ idx = ap->rx_mini_skbprd;
+ for (i = 0; i < nr_bufs; i++) {
+ struct sk_buff *skb;
+ struct rx_desc *rd;
+ dma_addr_t mapping;
+
+ skb = netdev_alloc_skb_ip_align(dev, ACE_MINI_BUFSIZE);
+ if (!skb)
+ break;
+
+ mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
+ offset_in_page(skb->data),
+ ACE_MINI_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+ ap->skb->rx_mini_skbuff[idx].skb = skb;
+ dma_unmap_addr_set(&ap->skb->rx_mini_skbuff[idx],
+ mapping, mapping);
+
+ rd = &ap->rx_mini_ring[idx];
+ set_aceaddr(&rd->addr, mapping);
+ rd->size = ACE_MINI_BUFSIZE;
+ rd->idx = idx;
+ idx = (idx + 1) % RX_MINI_RING_ENTRIES;
+ }
+
+ if (!i)
+ goto error_out;
+
+ atomic_add(i, &ap->cur_mini_bufs);
+
+ ap->rx_mini_skbprd = idx;
+
+ writel(idx, &regs->RxMiniPrd);
+ wmb();
+
+ out:
+ clear_bit(0, &ap->mini_refill_busy);
+ return;
+ error_out:
+ printk(KERN_INFO "Out of memory when allocating "
+ "mini receive buffers\n");
+ goto out;
+}
+
+
+/*
+ * Load the jumbo rx ring, this may happen at any time if the MTU
+ * is changed to a value > 1500.
+ */
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ short i, idx;
+
+ idx = ap->rx_jumbo_skbprd;
+
+ for (i = 0; i < nr_bufs; i++) {
+ struct sk_buff *skb;
+ struct rx_desc *rd;
+ dma_addr_t mapping;
+
+ skb = netdev_alloc_skb_ip_align(dev, ACE_JUMBO_BUFSIZE);
+ if (!skb)
+ break;
+
+ mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
+ offset_in_page(skb->data),
+ ACE_JUMBO_BUFSIZE,
+ PCI_DMA_FROMDEVICE);
+ ap->skb->rx_jumbo_skbuff[idx].skb = skb;
+ dma_unmap_addr_set(&ap->skb->rx_jumbo_skbuff[idx],
+ mapping, mapping);
+
+ rd = &ap->rx_jumbo_ring[idx];
+ set_aceaddr(&rd->addr, mapping);
+ rd->size = ACE_JUMBO_BUFSIZE;
+ rd->idx = idx;
+ idx = (idx + 1) % RX_JUMBO_RING_ENTRIES;
+ }
+
+ if (!i)
+ goto error_out;
+
+ atomic_add(i, &ap->cur_jumbo_bufs);
+ ap->rx_jumbo_skbprd = idx;
+
+ if (ACE_IS_TIGON_I(ap)) {
+ struct cmd cmd;
+ cmd.evt = C_SET_RX_JUMBO_PRD_IDX;
+ cmd.code = 0;
+ cmd.idx = ap->rx_jumbo_skbprd;
+ ace_issue_cmd(regs, &cmd);
+ } else {
+ writel(idx, &regs->RxJumboPrd);
+ wmb();
+ }
+
+ out:
+ clear_bit(0, &ap->jumbo_refill_busy);
+ return;
+ error_out:
+ if (net_ratelimit())
+ printk(KERN_INFO "Out of memory when allocating "
+ "jumbo receive buffers\n");
+ goto out;
+}
+
+
+/*
+ * All events are considered to be slow (RX/TX ints do not generate
+ * events) and are handled here, outside the main interrupt handler,
+ * to reduce the size of the handler.
+ */
+static u32 ace_handle_event(struct net_device *dev, u32 evtcsm, u32 evtprd)
+{
+ struct ace_private *ap;
+
+ ap = netdev_priv(dev);
+
+ while (evtcsm != evtprd) {
+ switch (ap->evt_ring[evtcsm].evt) {
+ case E_FW_RUNNING:
+ printk(KERN_INFO "%s: Firmware up and running\n",
+ ap->name);
+ ap->fw_running = 1;
+ wmb();
+ break;
+ case E_STATS_UPDATED:
+ break;
+ case E_LNK_STATE:
+ {
+ u16 code = ap->evt_ring[evtcsm].code;
+ switch (code) {
+ case E_C_LINK_UP:
+ {
+ u32 state = readl(&ap->regs->GigLnkState);
+ printk(KERN_WARNING "%s: Optical link UP "
+ "(%s Duplex, Flow Control: %s%s)\n",
+ ap->name,
+ state & LNK_FULL_DUPLEX ? "Full":"Half",
+ state & LNK_TX_FLOW_CTL_Y ? "TX " : "",
+ state & LNK_RX_FLOW_CTL_Y ? "RX" : "");
+ break;
+ }
+ case E_C_LINK_DOWN:
+ printk(KERN_WARNING "%s: Optical link DOWN\n",
+ ap->name);
+ break;
+ case E_C_LINK_10_100:
+ printk(KERN_WARNING "%s: 10/100BaseT link "
+ "UP\n", ap->name);
+ break;
+ default:
+ printk(KERN_ERR "%s: Unknown optical link "
+ "state %02x\n", ap->name, code);
+ }
+ break;
+ }
+ case E_ERROR:
+ switch(ap->evt_ring[evtcsm].code) {
+ case E_C_ERR_INVAL_CMD:
+ printk(KERN_ERR "%s: invalid command error\n",
+ ap->name);
+ break;
+ case E_C_ERR_UNIMP_CMD:
+ printk(KERN_ERR "%s: unimplemented command "
+ "error\n", ap->name);
+ break;
+ case E_C_ERR_BAD_CFG:
+ printk(KERN_ERR "%s: bad config error\n",
+ ap->name);
+ break;
+ default:
+ printk(KERN_ERR "%s: unknown error %02x\n",
+ ap->name, ap->evt_ring[evtcsm].code);
+ }
+ break;
+ case E_RESET_JUMBO_RNG:
+ {
+ int i;
+ for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
+ if (ap->skb->rx_jumbo_skbuff[i].skb) {
+ ap->rx_jumbo_ring[i].size = 0;
+ set_aceaddr(&ap->rx_jumbo_ring[i].addr, 0);
+ dev_kfree_skb(ap->skb->rx_jumbo_skbuff[i].skb);
+ ap->skb->rx_jumbo_skbuff[i].skb = NULL;
+ }
+ }
+
+ if (ACE_IS_TIGON_I(ap)) {
+ struct cmd cmd;
+ cmd.evt = C_SET_RX_JUMBO_PRD_IDX;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(ap->regs, &cmd);
+ } else {
+ writel(0, &((ap->regs)->RxJumboPrd));
+ wmb();
+ }
+
+ ap->jumbo = 0;
+ ap->rx_jumbo_skbprd = 0;
+ printk(KERN_INFO "%s: Jumbo ring flushed\n",
+ ap->name);
+ clear_bit(0, &ap->jumbo_refill_busy);
+ break;
+ }
+ default:
+ printk(KERN_ERR "%s: Unhandled event 0x%02x\n",
+ ap->name, ap->evt_ring[evtcsm].evt);
+ }
+ evtcsm = (evtcsm + 1) % EVT_RING_ENTRIES;
+ }
+
+ return evtcsm;
+}
+
+
+static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ u32 idx;
+ int mini_count = 0, std_count = 0;
+
+ idx = rxretcsm;
+
+ prefetchw(&ap->cur_rx_bufs);
+ prefetchw(&ap->cur_mini_bufs);
+
+ while (idx != rxretprd) {
+ struct ring_info *rip;
+ struct sk_buff *skb;
+ struct rx_desc *rxdesc, *retdesc;
+ u32 skbidx;
+ int bd_flags, desc_type, mapsize;
+ u16 csum;
+
+
+ /* make sure the rx descriptor isn't read before rxretprd */
+ if (idx == rxretcsm)
+ rmb();
+
+ retdesc = &ap->rx_return_ring[idx];
+ skbidx = retdesc->idx;
+ bd_flags = retdesc->flags;
+ desc_type = bd_flags & (BD_FLG_JUMBO | BD_FLG_MINI);
+
+ switch(desc_type) {
+ /*
+ * Normal frames do not have any flags set
+ *
+ * Mini and normal frames arrive frequently,
+ * so use a local counter to avoid doing
+ * atomic operations for each packet arriving.
+ */
+ case 0:
+ rip = &ap->skb->rx_std_skbuff[skbidx];
+ mapsize = ACE_STD_BUFSIZE;
+ rxdesc = &ap->rx_std_ring[skbidx];
+ std_count++;
+ break;
+ case BD_FLG_JUMBO:
+ rip = &ap->skb->rx_jumbo_skbuff[skbidx];
+ mapsize = ACE_JUMBO_BUFSIZE;
+ rxdesc = &ap->rx_jumbo_ring[skbidx];
+ atomic_dec(&ap->cur_jumbo_bufs);
+ break;
+ case BD_FLG_MINI:
+ rip = &ap->skb->rx_mini_skbuff[skbidx];
+ mapsize = ACE_MINI_BUFSIZE;
+ rxdesc = &ap->rx_mini_ring[skbidx];
+ mini_count++;
+ break;
+ default:
+ printk(KERN_INFO "%s: unknown frame type (0x%02x) "
+ "returned by NIC\n", dev->name,
+ retdesc->flags);
+ goto error;
+ }
+
+ skb = rip->skb;
+ rip->skb = NULL;
+ pci_unmap_page(ap->pdev,
+ dma_unmap_addr(rip, mapping),
+ mapsize,
+ PCI_DMA_FROMDEVICE);
+ skb_put(skb, retdesc->size);
+
+ /*
+ * Fly baby, fly!
+ */
+ csum = retdesc->tcp_udp_csum;
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ /*
+ * Instead of forcing the poor tigon mips cpu to calculate
+ * pseudo hdr checksum, we do this ourselves.
+ */
+ if (bd_flags & BD_FLG_TCP_UDP_SUM) {
+ skb->csum = htons(csum);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ } else {
+ skb_checksum_none_assert(skb);
+ }
+
+ /* send it up */
+ if ((bd_flags & BD_FLG_VLAN_TAG))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), retdesc->vlan);
+ netif_rx(skb);
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += retdesc->size;
+
+ idx = (idx + 1) % RX_RETURN_RING_ENTRIES;
+ }
+
+ atomic_sub(std_count, &ap->cur_rx_bufs);
+ if (!ACE_IS_TIGON_I(ap))
+ atomic_sub(mini_count, &ap->cur_mini_bufs);
+
+ out:
+ /*
+ * According to the documentation RxRetCsm is obsolete with
+ * the 12.3.x Firmware - my Tigon I NICs seem to disagree!
+ */
+ if (ACE_IS_TIGON_I(ap)) {
+ writel(idx, &ap->regs->RxRetCsm);
+ }
+ ap->cur_rx = idx;
+
+ return;
+ error:
+ idx = rxretprd;
+ goto out;
+}
+
+
+static inline void ace_tx_int(struct net_device *dev,
+ u32 txcsm, u32 idx)
+{
+ struct ace_private *ap = netdev_priv(dev);
+
+ do {
+ struct sk_buff *skb;
+ struct tx_ring_info *info;
+
+ info = ap->skb->tx_skbuff + idx;
+ skb = info->skb;
+
+ if (dma_unmap_len(info, maplen)) {
+ pci_unmap_page(ap->pdev, dma_unmap_addr(info, mapping),
+ dma_unmap_len(info, maplen),
+ PCI_DMA_TODEVICE);
+ dma_unmap_len_set(info, maplen, 0);
+ }
+
+ if (skb) {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev_kfree_skb_irq(skb);
+ info->skb = NULL;
+ }
+
+ idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
+ } while (idx != txcsm);
+
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ wmb();
+ ap->tx_ret_csm = txcsm;
+
+ /* So... tx_ret_csm is advanced _after_ check for device wakeup.
+ *
+ * We could try to make it before. In this case we would get
+ * the following race condition: hard_start_xmit on other cpu
+ * enters after we advanced tx_ret_csm and fills space,
+ * which we have just freed, so that we make illegal device wakeup.
+ * There is no good way to workaround this (at entry
+ * to ace_start_xmit detects this condition and prevents
+ * ring corruption, but it is not a good workaround.)
+ *
+ * When tx_ret_csm is advanced after, we wake up device _only_
+ * if we really have some space in ring (though the core doing
+ * hard_start_xmit can see full ring for some period and has to
+ * synchronize.) Superb.
+ * BUT! We get another subtle race condition. hard_start_xmit
+ * may think that ring is full between wakeup and advancing
+ * tx_ret_csm and will stop device instantly! It is not so bad.
+ * We are guaranteed that there is something in ring, so that
+ * the next irq will resume transmission. To speedup this we could
+ * mark descriptor, which closes ring with BD_FLG_COAL_NOW
+ * (see ace_start_xmit).
+ *
+ * Well, this dilemma exists in all lock-free devices.
+ * We, following scheme used in drivers by Donald Becker,
+ * select the least dangerous.
+ * --ANK
+ */
+}
+
+
+static irqreturn_t ace_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ u32 idx;
+ u32 txcsm, rxretcsm, rxretprd;
+ u32 evtcsm, evtprd;
+
+ /*
+ * In case of PCI shared interrupts or spurious interrupts,
+ * we want to make sure it is actually our interrupt before
+ * spending any time in here.
+ */
+ if (!(readl(&regs->HostCtrl) & IN_INT))
+ return IRQ_NONE;
+
+ /*
+ * ACK intr now. Otherwise we will lose updates to rx_ret_prd,
+ * which happened _after_ rxretprd = *ap->rx_ret_prd; but before
+ * writel(0, &regs->Mb0Lo).
+ *
+ * "IRQ avoidance" recommended in docs applies to IRQs served
+ * threads and it is wrong even for that case.
+ */
+ writel(0, &regs->Mb0Lo);
+ readl(&regs->Mb0Lo);
+
+ /*
+ * There is no conflict between transmit handling in
+ * start_xmit and receive processing, thus there is no reason
+ * to take a spin lock for RX handling. Wait until we start
+ * working on the other stuff - hey we don't need a spin lock
+ * anymore.
+ */
+ rxretprd = *ap->rx_ret_prd;
+ rxretcsm = ap->cur_rx;
+
+ if (rxretprd != rxretcsm)
+ ace_rx_int(dev, rxretprd, rxretcsm);
+
+ txcsm = *ap->tx_csm;
+ idx = ap->tx_ret_csm;
+
+ if (txcsm != idx) {
+ /*
+ * If each skb takes only one descriptor this check degenerates
+ * to identity, because new space has just been opened.
+ * But if skbs are fragmented we must check that this index
+ * update releases enough of space, otherwise we just
+ * wait for device to make more work.
+ */
+ if (!tx_ring_full(ap, txcsm, ap->tx_prd))
+ ace_tx_int(dev, txcsm, idx);
+ }
+
+ evtcsm = readl(&regs->EvtCsm);
+ evtprd = *ap->evt_prd;
+
+ if (evtcsm != evtprd) {
+ evtcsm = ace_handle_event(dev, evtcsm, evtprd);
+ writel(evtcsm, &regs->EvtCsm);
+ }
+
+ /*
+ * This has to go last in the interrupt handler and run with
+ * the spin lock released ... what lock?
+ */
+ if (netif_running(dev)) {
+ int cur_size;
+ int run_tasklet = 0;
+
+ cur_size = atomic_read(&ap->cur_rx_bufs);
+ if (cur_size < RX_LOW_STD_THRES) {
+ if ((cur_size < RX_PANIC_STD_THRES) &&
+ !test_and_set_bit(0, &ap->std_refill_busy)) {
+#ifdef DEBUG
+ printk("low on std buffers %i\n", cur_size);
+#endif
+ ace_load_std_rx_ring(dev,
+ RX_RING_SIZE - cur_size);
+ } else
+ run_tasklet = 1;
+ }
+
+ if (!ACE_IS_TIGON_I(ap)) {
+ cur_size = atomic_read(&ap->cur_mini_bufs);
+ if (cur_size < RX_LOW_MINI_THRES) {
+ if ((cur_size < RX_PANIC_MINI_THRES) &&
+ !test_and_set_bit(0,
+ &ap->mini_refill_busy)) {
+#ifdef DEBUG
+ printk("low on mini buffers %i\n",
+ cur_size);
+#endif
+ ace_load_mini_rx_ring(dev,
+ RX_MINI_SIZE - cur_size);
+ } else
+ run_tasklet = 1;
+ }
+ }
+
+ if (ap->jumbo) {
+ cur_size = atomic_read(&ap->cur_jumbo_bufs);
+ if (cur_size < RX_LOW_JUMBO_THRES) {
+ if ((cur_size < RX_PANIC_JUMBO_THRES) &&
+ !test_and_set_bit(0,
+ &ap->jumbo_refill_busy)){
+#ifdef DEBUG
+ printk("low on jumbo buffers %i\n",
+ cur_size);
+#endif
+ ace_load_jumbo_rx_ring(dev,
+ RX_JUMBO_SIZE - cur_size);
+ } else
+ run_tasklet = 1;
+ }
+ }
+ if (run_tasklet && !ap->tasklet_pending) {
+ ap->tasklet_pending = 1;
+ tasklet_schedule(&ap->ace_tasklet);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ace_open(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ struct cmd cmd;
+
+ if (!(ap->fw_running)) {
+ printk(KERN_WARNING "%s: Firmware not running!\n", dev->name);
+ return -EBUSY;
+ }
+
+ writel(dev->mtu + ETH_HLEN + 4, &regs->IfMtu);
+
+ cmd.evt = C_CLEAR_STATS;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+
+ cmd.evt = C_HOST_STATE;
+ cmd.code = C_C_STACK_UP;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+
+ if (ap->jumbo &&
+ !test_and_set_bit(0, &ap->jumbo_refill_busy))
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
+
+ if (dev->flags & IFF_PROMISC) {
+ cmd.evt = C_SET_PROMISC_MODE;
+ cmd.code = C_C_PROMISC_ENABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+
+ ap->promisc = 1;
+ }else
+ ap->promisc = 0;
+ ap->mcast_all = 0;
+
+#if 0
+ cmd.evt = C_LNK_NEGOTIATION;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+#endif
+
+ netif_start_queue(dev);
+
+ /*
+ * Setup the bottom half rx ring refill handler
+ */
+ tasklet_init(&ap->ace_tasklet, ace_tasklet, (unsigned long)dev);
+ return 0;
+}
+
+
+static int ace_close(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ struct cmd cmd;
+ unsigned long flags;
+ short i;
+
+ /*
+ * Without (or before) releasing irq and stopping hardware, this
+ * is an absolute non-sense, by the way. It will be reset instantly
+ * by the first irq.
+ */
+ netif_stop_queue(dev);
+
+
+ if (ap->promisc) {
+ cmd.evt = C_SET_PROMISC_MODE;
+ cmd.code = C_C_PROMISC_DISABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ ap->promisc = 0;
+ }
+
+ cmd.evt = C_HOST_STATE;
+ cmd.code = C_C_STACK_DOWN;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+
+ tasklet_kill(&ap->ace_tasklet);
+
+ /*
+ * Make sure one CPU is not processing packets while
+ * buffers are being released by another.
+ */
+
+ local_irq_save(flags);
+ ace_mask_irq(dev);
+
+ for (i = 0; i < ACE_TX_RING_ENTRIES(ap); i++) {
+ struct sk_buff *skb;
+ struct tx_ring_info *info;
+
+ info = ap->skb->tx_skbuff + i;
+ skb = info->skb;
+
+ if (dma_unmap_len(info, maplen)) {
+ if (ACE_IS_TIGON_I(ap)) {
+ /* NB: TIGON_1 is special, tx_ring is in io space */
+ struct tx_desc __iomem *tx;
+ tx = (__force struct tx_desc __iomem *) &ap->tx_ring[i];
+ writel(0, &tx->addr.addrhi);
+ writel(0, &tx->addr.addrlo);
+ writel(0, &tx->flagsize);
+ } else
+ memset(ap->tx_ring + i, 0,
+ sizeof(struct tx_desc));
+ pci_unmap_page(ap->pdev, dma_unmap_addr(info, mapping),
+ dma_unmap_len(info, maplen),
+ PCI_DMA_TODEVICE);
+ dma_unmap_len_set(info, maplen, 0);
+ }
+ if (skb) {
+ dev_kfree_skb(skb);
+ info->skb = NULL;
+ }
+ }
+
+ if (ap->jumbo) {
+ cmd.evt = C_RESET_JUMBO_RNG;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ }
+
+ ace_unmask_irq(dev);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+static inline dma_addr_t
+ace_map_tx_skb(struct ace_private *ap, struct sk_buff *skb,
+ struct sk_buff *tail, u32 idx)
+{
+ dma_addr_t mapping;
+ struct tx_ring_info *info;
+
+ mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
+ offset_in_page(skb->data),
+ skb->len, PCI_DMA_TODEVICE);
+
+ info = ap->skb->tx_skbuff + idx;
+ info->skb = tail;
+ dma_unmap_addr_set(info, mapping, mapping);
+ dma_unmap_len_set(info, maplen, skb->len);
+ return mapping;
+}
+
+
+static inline void
+ace_load_tx_bd(struct ace_private *ap, struct tx_desc *desc, u64 addr,
+ u32 flagsize, u32 vlan_tag)
+{
+#if !USE_TX_COAL_NOW
+ flagsize &= ~BD_FLG_COAL_NOW;
+#endif
+
+ if (ACE_IS_TIGON_I(ap)) {
+ struct tx_desc __iomem *io = (__force struct tx_desc __iomem *) desc;
+ writel(addr >> 32, &io->addr.addrhi);
+ writel(addr & 0xffffffff, &io->addr.addrlo);
+ writel(flagsize, &io->flagsize);
+ writel(vlan_tag, &io->vlanres);
+ } else {
+ desc->addr.addrhi = addr >> 32;
+ desc->addr.addrlo = addr;
+ desc->flagsize = flagsize;
+ desc->vlanres = vlan_tag;
+ }
+}
+
+
+static netdev_tx_t ace_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ struct tx_desc *desc;
+ u32 idx, flagsize;
+ unsigned long maxjiff = jiffies + 3*HZ;
+
+restart:
+ idx = ap->tx_prd;
+
+ if (tx_ring_full(ap, ap->tx_ret_csm, idx))
+ goto overflow;
+
+ if (!skb_shinfo(skb)->nr_frags) {
+ dma_addr_t mapping;
+ u32 vlan_tag = 0;
+
+ mapping = ace_map_tx_skb(ap, skb, skb, idx);
+ flagsize = (skb->len << 16) | (BD_FLG_END);
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ flagsize |= BD_FLG_TCP_UDP_SUM;
+ if (vlan_tx_tag_present(skb)) {
+ flagsize |= BD_FLG_VLAN_TAG;
+ vlan_tag = vlan_tx_tag_get(skb);
+ }
+ desc = ap->tx_ring + idx;
+ idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
+
+ /* Look at ace_tx_int for explanations. */
+ if (tx_ring_full(ap, ap->tx_ret_csm, idx))
+ flagsize |= BD_FLG_COAL_NOW;
+
+ ace_load_tx_bd(ap, desc, mapping, flagsize, vlan_tag);
+ } else {
+ dma_addr_t mapping;
+ u32 vlan_tag = 0;
+ int i, len = 0;
+
+ mapping = ace_map_tx_skb(ap, skb, NULL, idx);
+ flagsize = (skb_headlen(skb) << 16);
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ flagsize |= BD_FLG_TCP_UDP_SUM;
+ if (vlan_tx_tag_present(skb)) {
+ flagsize |= BD_FLG_VLAN_TAG;
+ vlan_tag = vlan_tx_tag_get(skb);
+ }
+
+ ace_load_tx_bd(ap, ap->tx_ring + idx, mapping, flagsize, vlan_tag);
+
+ idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ struct tx_ring_info *info;
+
+ len += skb_frag_size(frag);
+ info = ap->skb->tx_skbuff + idx;
+ desc = ap->tx_ring + idx;
+
+ mapping = skb_frag_dma_map(&ap->pdev->dev, frag, 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+
+ flagsize = skb_frag_size(frag) << 16;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ flagsize |= BD_FLG_TCP_UDP_SUM;
+ idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
+
+ if (i == skb_shinfo(skb)->nr_frags - 1) {
+ flagsize |= BD_FLG_END;
+ if (tx_ring_full(ap, ap->tx_ret_csm, idx))
+ flagsize |= BD_FLG_COAL_NOW;
+
+ /*
+ * Only the last fragment frees
+ * the skb!
+ */
+ info->skb = skb;
+ } else {
+ info->skb = NULL;
+ }
+ dma_unmap_addr_set(info, mapping, mapping);
+ dma_unmap_len_set(info, maplen, skb_frag_size(frag));
+ ace_load_tx_bd(ap, desc, mapping, flagsize, vlan_tag);
+ }
+ }
+
+ wmb();
+ ap->tx_prd = idx;
+ ace_set_txprd(regs, ap, idx);
+
+ if (flagsize & BD_FLG_COAL_NOW) {
+ netif_stop_queue(dev);
+
+ /*
+ * A TX-descriptor producer (an IRQ) might have gotten
+ * between, making the ring free again. Since xmit is
+ * serialized, this is the only situation we have to
+ * re-test.
+ */
+ if (!tx_ring_full(ap, ap->tx_ret_csm, idx))
+ netif_wake_queue(dev);
+ }
+
+ return NETDEV_TX_OK;
+
+overflow:
+ /*
+ * This race condition is unavoidable with lock-free drivers.
+ * We wake up the queue _before_ tx_prd is advanced, so that we can
+ * enter hard_start_xmit too early, while tx ring still looks closed.
+ * This happens ~1-4 times per 100000 packets, so that we can allow
+ * to loop syncing to other CPU. Probably, we need an additional
+ * wmb() in ace_tx_intr as well.
+ *
+ * Note that this race is relieved by reserving one more entry
+ * in tx ring than it is necessary (see original non-SG driver).
+ * However, with SG we need to reserve 2*MAX_SKB_FRAGS+1, which
+ * is already overkill.
+ *
+ * Alternative is to return with 1 not throttling queue. In this
+ * case loop becomes longer, no more useful effects.
+ */
+ if (time_before(jiffies, maxjiff)) {
+ barrier();
+ cpu_relax();
+ goto restart;
+ }
+
+ /* The ring is stuck full. */
+ printk(KERN_WARNING "%s: Transmit ring stuck full\n", dev->name);
+ return NETDEV_TX_BUSY;
+}
+
+
+static int ace_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+
+ if (new_mtu > ACE_JUMBO_MTU)
+ return -EINVAL;
+
+ writel(new_mtu + ETH_HLEN + 4, &regs->IfMtu);
+ dev->mtu = new_mtu;
+
+ if (new_mtu > ACE_STD_MTU) {
+ if (!(ap->jumbo)) {
+ printk(KERN_INFO "%s: Enabling Jumbo frame "
+ "support\n", dev->name);
+ ap->jumbo = 1;
+ if (!test_and_set_bit(0, &ap->jumbo_refill_busy))
+ ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
+ ace_set_rxtx_parms(dev, 1);
+ }
+ } else {
+ while (test_and_set_bit(0, &ap->jumbo_refill_busy));
+ ace_sync_irq(dev->irq);
+ ace_set_rxtx_parms(dev, 0);
+ if (ap->jumbo) {
+ struct cmd cmd;
+
+ cmd.evt = C_RESET_JUMBO_RNG;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ }
+ }
+
+ return 0;
+}
+
+static int ace_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ u32 link;
+
+ memset(ecmd, 0, sizeof(struct ethtool_cmd));
+ ecmd->supported =
+ (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_FIBRE);
+
+ ecmd->port = PORT_FIBRE;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ link = readl(&regs->GigLnkState);
+ if (link & LNK_1000MB)
+ ethtool_cmd_speed_set(ecmd, SPEED_1000);
+ else {
+ link = readl(&regs->FastLnkState);
+ if (link & LNK_100MB)
+ ethtool_cmd_speed_set(ecmd, SPEED_100);
+ else if (link & LNK_10MB)
+ ethtool_cmd_speed_set(ecmd, SPEED_10);
+ else
+ ethtool_cmd_speed_set(ecmd, 0);
+ }
+ if (link & LNK_FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+
+ if (link & LNK_NEGOTIATE)
+ ecmd->autoneg = AUTONEG_ENABLE;
+ else
+ ecmd->autoneg = AUTONEG_DISABLE;
+
+#if 0
+ /*
+ * Current struct ethtool_cmd is insufficient
+ */
+ ecmd->trace = readl(&regs->TuneTrace);
+
+ ecmd->txcoal = readl(&regs->TuneTxCoalTicks);
+ ecmd->rxcoal = readl(&regs->TuneRxCoalTicks);
+#endif
+ ecmd->maxtxpkt = readl(&regs->TuneMaxTxDesc);
+ ecmd->maxrxpkt = readl(&regs->TuneMaxRxDesc);
+
+ return 0;
+}
+
+static int ace_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ u32 link, speed;
+
+ link = readl(&regs->GigLnkState);
+ if (link & LNK_1000MB)
+ speed = SPEED_1000;
+ else {
+ link = readl(&regs->FastLnkState);
+ if (link & LNK_100MB)
+ speed = SPEED_100;
+ else if (link & LNK_10MB)
+ speed = SPEED_10;
+ else
+ speed = SPEED_100;
+ }
+
+ link = LNK_ENABLE | LNK_1000MB | LNK_100MB | LNK_10MB |
+ LNK_RX_FLOW_CTL_Y | LNK_NEG_FCTL;
+ if (!ACE_IS_TIGON_I(ap))
+ link |= LNK_TX_FLOW_CTL_Y;
+ if (ecmd->autoneg == AUTONEG_ENABLE)
+ link |= LNK_NEGOTIATE;
+ if (ethtool_cmd_speed(ecmd) != speed) {
+ link &= ~(LNK_1000MB | LNK_100MB | LNK_10MB);
+ switch (ethtool_cmd_speed(ecmd)) {
+ case SPEED_1000:
+ link |= LNK_1000MB;
+ break;
+ case SPEED_100:
+ link |= LNK_100MB;
+ break;
+ case SPEED_10:
+ link |= LNK_10MB;
+ break;
+ }
+ }
+
+ if (ecmd->duplex == DUPLEX_FULL)
+ link |= LNK_FULL_DUPLEX;
+
+ if (link != ap->link) {
+ struct cmd cmd;
+ printk(KERN_INFO "%s: Renegotiating link state\n",
+ dev->name);
+
+ ap->link = link;
+ writel(link, &regs->TuneLink);
+ if (!ACE_IS_TIGON_I(ap))
+ writel(link, &regs->TuneFastLink);
+ wmb();
+
+ cmd.evt = C_LNK_NEGOTIATION;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ }
+ return 0;
+}
+
+static void ace_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ace_private *ap = netdev_priv(dev);
+
+ strlcpy(info->driver, "acenic", sizeof(info->driver));
+ snprintf(info->version, sizeof(info->version), "%i.%i.%i",
+ ap->firmware_major, ap->firmware_minor,
+ ap->firmware_fix);
+
+ if (ap->pdev)
+ strlcpy(info->bus_info, pci_name(ap->pdev),
+ sizeof(info->bus_info));
+
+}
+
+/*
+ * Set the hardware MAC address.
+ */
+static int ace_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ struct sockaddr *addr=p;
+ u8 *da;
+ struct cmd cmd;
+
+ if(netif_running(dev))
+ return -EBUSY;
+
+ memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
+
+ da = (u8 *)dev->dev_addr;
+
+ writel(da[0] << 8 | da[1], &regs->MacAddrHi);
+ writel((da[2] << 24) | (da[3] << 16) | (da[4] << 8) | da[5],
+ &regs->MacAddrLo);
+
+ cmd.evt = C_SET_MAC_ADDR;
+ cmd.code = 0;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+
+ return 0;
+}
+
+
+static void ace_set_multicast_list(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ struct cmd cmd;
+
+ if ((dev->flags & IFF_ALLMULTI) && !(ap->mcast_all)) {
+ cmd.evt = C_SET_MULTICAST_MODE;
+ cmd.code = C_C_MCAST_ENABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ ap->mcast_all = 1;
+ } else if (ap->mcast_all) {
+ cmd.evt = C_SET_MULTICAST_MODE;
+ cmd.code = C_C_MCAST_DISABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ ap->mcast_all = 0;
+ }
+
+ if ((dev->flags & IFF_PROMISC) && !(ap->promisc)) {
+ cmd.evt = C_SET_PROMISC_MODE;
+ cmd.code = C_C_PROMISC_ENABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ ap->promisc = 1;
+ }else if (!(dev->flags & IFF_PROMISC) && (ap->promisc)) {
+ cmd.evt = C_SET_PROMISC_MODE;
+ cmd.code = C_C_PROMISC_DISABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ ap->promisc = 0;
+ }
+
+ /*
+ * For the time being multicast relies on the upper layers
+ * filtering it properly. The Firmware does not allow one to
+ * set the entire multicast list at a time and keeping track of
+ * it here is going to be messy.
+ */
+ if (!netdev_mc_empty(dev) && !ap->mcast_all) {
+ cmd.evt = C_SET_MULTICAST_MODE;
+ cmd.code = C_C_MCAST_ENABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ }else if (!ap->mcast_all) {
+ cmd.evt = C_SET_MULTICAST_MODE;
+ cmd.code = C_C_MCAST_DISABLE;
+ cmd.idx = 0;
+ ace_issue_cmd(regs, &cmd);
+ }
+}
+
+
+static struct net_device_stats *ace_get_stats(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_mac_stats __iomem *mac_stats =
+ (struct ace_mac_stats __iomem *)ap->regs->Stats;
+
+ dev->stats.rx_missed_errors = readl(&mac_stats->drop_space);
+ dev->stats.multicast = readl(&mac_stats->kept_mc);
+ dev->stats.collisions = readl(&mac_stats->coll);
+
+ return &dev->stats;
+}
+
+
+static void ace_copy(struct ace_regs __iomem *regs, const __be32 *src,
+ u32 dest, int size)
+{
+ void __iomem *tdest;
+ short tsize, i;
+
+ if (size <= 0)
+ return;
+
+ while (size > 0) {
+ tsize = min_t(u32, ((~dest & (ACE_WINDOW_SIZE - 1)) + 1),
+ min_t(u32, size, ACE_WINDOW_SIZE));
+ tdest = (void __iomem *) &regs->Window +
+ (dest & (ACE_WINDOW_SIZE - 1));
+ writel(dest & ~(ACE_WINDOW_SIZE - 1), &regs->WinBase);
+ for (i = 0; i < (tsize / 4); i++) {
+ /* Firmware is big-endian */
+ writel(be32_to_cpup(src), tdest);
+ src++;
+ tdest += 4;
+ dest += 4;
+ size -= 4;
+ }
+ }
+}
+
+
+static void ace_clear(struct ace_regs __iomem *regs, u32 dest, int size)
+{
+ void __iomem *tdest;
+ short tsize = 0, i;
+
+ if (size <= 0)
+ return;
+
+ while (size > 0) {
+ tsize = min_t(u32, ((~dest & (ACE_WINDOW_SIZE - 1)) + 1),
+ min_t(u32, size, ACE_WINDOW_SIZE));
+ tdest = (void __iomem *) &regs->Window +
+ (dest & (ACE_WINDOW_SIZE - 1));
+ writel(dest & ~(ACE_WINDOW_SIZE - 1), &regs->WinBase);
+
+ for (i = 0; i < (tsize / 4); i++) {
+ writel(0, tdest + i*4);
+ }
+
+ dest += tsize;
+ size -= tsize;
+ }
+}
+
+
+/*
+ * Download the firmware into the SRAM on the NIC
+ *
+ * This operation requires the NIC to be halted and is performed with
+ * interrupts disabled and with the spinlock hold.
+ */
+static int ace_load_firmware(struct net_device *dev)
+{
+ const struct firmware *fw;
+ const char *fw_name = "acenic/tg2.bin";
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ const __be32 *fw_data;
+ u32 load_addr;
+ int ret;
+
+ if (!(readl(&regs->CpuCtrl) & CPU_HALTED)) {
+ printk(KERN_ERR "%s: trying to download firmware while the "
+ "CPU is running!\n", ap->name);
+ return -EFAULT;
+ }
+
+ if (ACE_IS_TIGON_I(ap))
+ fw_name = "acenic/tg1.bin";
+
+ ret = request_firmware(&fw, fw_name, &ap->pdev->dev);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n",
+ ap->name, fw_name);
+ return ret;
+ }
+
+ fw_data = (void *)fw->data;
+
+ /* Firmware blob starts with version numbers, followed by
+ load and start address. Remainder is the blob to be loaded
+ contiguously from load address. We don't bother to represent
+ the BSS/SBSS sections any more, since we were clearing the
+ whole thing anyway. */
+ ap->firmware_major = fw->data[0];
+ ap->firmware_minor = fw->data[1];
+ ap->firmware_fix = fw->data[2];
+
+ ap->firmware_start = be32_to_cpu(fw_data[1]);
+ if (ap->firmware_start < 0x4000 || ap->firmware_start >= 0x80000) {
+ printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n",
+ ap->name, ap->firmware_start, fw_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ load_addr = be32_to_cpu(fw_data[2]);
+ if (load_addr < 0x4000 || load_addr >= 0x80000) {
+ printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n",
+ ap->name, load_addr, fw_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Do not try to clear more than 512KiB or we end up seeing
+ * funny things on NICs with only 512KiB SRAM
+ */
+ ace_clear(regs, 0x2000, 0x80000-0x2000);
+ ace_copy(regs, &fw_data[3], load_addr, fw->size-12);
+ out:
+ release_firmware(fw);
+ return ret;
+}
+
+
+/*
+ * The eeprom on the AceNIC is an Atmel i2c EEPROM.
+ *
+ * Accessing the EEPROM is `interesting' to say the least - don't read
+ * this code right after dinner.
+ *
+ * This is all about black magic and bit-banging the device .... I
+ * wonder in what hospital they have put the guy who designed the i2c
+ * specs.
+ *
+ * Oh yes, this is only the beginning!
+ *
+ * Thanks to Stevarino Webinski for helping tracking down the bugs in the
+ * code i2c readout code by beta testing all my hacks.
+ */
+static void eeprom_start(struct ace_regs __iomem *regs)
+{
+ u32 local;
+
+ readl(&regs->LocalCtrl);
+ udelay(ACE_SHORT_DELAY);
+ local = readl(&regs->LocalCtrl);
+ local |= EEPROM_DATA_OUT | EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local |= EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local &= ~EEPROM_DATA_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local &= ~EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+}
+
+
+static void eeprom_prep(struct ace_regs __iomem *regs, u8 magic)
+{
+ short i;
+ u32 local;
+
+ udelay(ACE_SHORT_DELAY);
+ local = readl(&regs->LocalCtrl);
+ local &= ~EEPROM_DATA_OUT;
+ local |= EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+
+ for (i = 0; i < 8; i++, magic <<= 1) {
+ udelay(ACE_SHORT_DELAY);
+ if (magic & 0x80)
+ local |= EEPROM_DATA_OUT;
+ else
+ local &= ~EEPROM_DATA_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+
+ udelay(ACE_SHORT_DELAY);
+ local |= EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local &= ~(EEPROM_CLK_OUT | EEPROM_DATA_OUT);
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ }
+}
+
+
+static int eeprom_check_ack(struct ace_regs __iomem *regs)
+{
+ int state;
+ u32 local;
+
+ local = readl(&regs->LocalCtrl);
+ local &= ~EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_LONG_DELAY);
+ local |= EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ /* sample data in middle of high clk */
+ state = (readl(&regs->LocalCtrl) & EEPROM_DATA_IN) != 0;
+ udelay(ACE_SHORT_DELAY);
+ mb();
+ writel(readl(&regs->LocalCtrl) & ~EEPROM_CLK_OUT, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+
+ return state;
+}
+
+
+static void eeprom_stop(struct ace_regs __iomem *regs)
+{
+ u32 local;
+
+ udelay(ACE_SHORT_DELAY);
+ local = readl(&regs->LocalCtrl);
+ local |= EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local &= ~EEPROM_DATA_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local |= EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ local |= EEPROM_DATA_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_LONG_DELAY);
+ local &= ~EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ mb();
+}
+
+
+/*
+ * Read a whole byte from the EEPROM.
+ */
+static int read_eeprom_byte(struct net_device *dev, unsigned long offset)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+ unsigned long flags;
+ u32 local;
+ int result = 0;
+ short i;
+
+ /*
+ * Don't take interrupts on this CPU will bit banging
+ * the %#%#@$ I2C device
+ */
+ local_irq_save(flags);
+
+ eeprom_start(regs);
+
+ eeprom_prep(regs, EEPROM_WRITE_SELECT);
+ if (eeprom_check_ack(regs)) {
+ local_irq_restore(flags);
+ printk(KERN_ERR "%s: Unable to sync eeprom\n", ap->name);
+ result = -EIO;
+ goto eeprom_read_error;
+ }
+
+ eeprom_prep(regs, (offset >> 8) & 0xff);
+ if (eeprom_check_ack(regs)) {
+ local_irq_restore(flags);
+ printk(KERN_ERR "%s: Unable to set address byte 0\n",
+ ap->name);
+ result = -EIO;
+ goto eeprom_read_error;
+ }
+
+ eeprom_prep(regs, offset & 0xff);
+ if (eeprom_check_ack(regs)) {
+ local_irq_restore(flags);
+ printk(KERN_ERR "%s: Unable to set address byte 1\n",
+ ap->name);
+ result = -EIO;
+ goto eeprom_read_error;
+ }
+
+ eeprom_start(regs);
+ eeprom_prep(regs, EEPROM_READ_SELECT);
+ if (eeprom_check_ack(regs)) {
+ local_irq_restore(flags);
+ printk(KERN_ERR "%s: Unable to set READ_SELECT\n",
+ ap->name);
+ result = -EIO;
+ goto eeprom_read_error;
+ }
+
+ for (i = 0; i < 8; i++) {
+ local = readl(&regs->LocalCtrl);
+ local &= ~EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ udelay(ACE_LONG_DELAY);
+ mb();
+ local |= EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ /* sample data mid high clk */
+ result = (result << 1) |
+ ((readl(&regs->LocalCtrl) & EEPROM_DATA_IN) != 0);
+ udelay(ACE_SHORT_DELAY);
+ mb();
+ local = readl(&regs->LocalCtrl);
+ local &= ~EEPROM_CLK_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ udelay(ACE_SHORT_DELAY);
+ mb();
+ if (i == 7) {
+ local |= EEPROM_WRITE_ENABLE;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ }
+ }
+
+ local |= EEPROM_DATA_OUT;
+ writel(local, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ writel(readl(&regs->LocalCtrl) | EEPROM_CLK_OUT, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ udelay(ACE_LONG_DELAY);
+ writel(readl(&regs->LocalCtrl) & ~EEPROM_CLK_OUT, &regs->LocalCtrl);
+ readl(&regs->LocalCtrl);
+ mb();
+ udelay(ACE_SHORT_DELAY);
+ eeprom_stop(regs);
+
+ local_irq_restore(flags);
+ out:
+ return result;
+
+ eeprom_read_error:
+ printk(KERN_ERR "%s: Unable to read eeprom byte 0x%02lx\n",
+ ap->name, offset);
+ goto out;
+}
+
+module_pci_driver(acenic_pci_driver);
diff --git a/drivers/net/ethernet/alteon/acenic.h b/drivers/net/ethernet/alteon/acenic.h
new file mode 100644
index 00000000000..51c486cfbb8
--- /dev/null
+++ b/drivers/net/ethernet/alteon/acenic.h
@@ -0,0 +1,790 @@
+#ifndef _ACENIC_H_
+#define _ACENIC_H_
+#include <linux/interrupt.h>
+
+
+/*
+ * Generate TX index update each time, when TX ring is closed.
+ * Normally, this is not useful, because results in more dma (and irqs
+ * without TX_COAL_INTS_ONLY).
+ */
+#define USE_TX_COAL_NOW 0
+
+/*
+ * Addressing:
+ *
+ * The Tigon uses 64-bit host addresses, regardless of their actual
+ * length, and it expects a big-endian format. For 32 bit systems the
+ * upper 32 bits of the address are simply ignored (zero), however for
+ * little endian 64 bit systems (Alpha) this looks strange with the
+ * two parts of the address word being swapped.
+ *
+ * The addresses are split in two 32 bit words for all architectures
+ * as some of them are in PCI shared memory and it is necessary to use
+ * readl/writel to access them.
+ *
+ * The addressing code is derived from Pete Wyckoff's work, but
+ * modified to deal properly with readl/writel usage.
+ */
+
+struct ace_regs {
+ u32 pad0[16]; /* PCI control registers */
+
+ u32 HostCtrl; /* 0x40 */
+ u32 LocalCtrl;
+
+ u32 pad1[2];
+
+ u32 MiscCfg; /* 0x50 */
+
+ u32 pad2[2];
+
+ u32 PciState;
+
+ u32 pad3[2]; /* 0x60 */
+
+ u32 WinBase;
+ u32 WinData;
+
+ u32 pad4[12]; /* 0x70 */
+
+ u32 DmaWriteState; /* 0xa0 */
+ u32 pad5[3];
+ u32 DmaReadState; /* 0xb0 */
+
+ u32 pad6[26];
+
+ u32 AssistState;
+
+ u32 pad7[8]; /* 0x120 */
+
+ u32 CpuCtrl; /* 0x140 */
+ u32 Pc;
+
+ u32 pad8[3];
+
+ u32 SramAddr; /* 0x154 */
+ u32 SramData;
+
+ u32 pad9[49];
+
+ u32 MacRxState; /* 0x220 */
+
+ u32 pad10[7];
+
+ u32 CpuBCtrl; /* 0x240 */
+ u32 PcB;
+
+ u32 pad11[3];
+
+ u32 SramBAddr; /* 0x254 */
+ u32 SramBData;
+
+ u32 pad12[105];
+
+ u32 pad13[32]; /* 0x400 */
+ u32 Stats[32];
+
+ u32 Mb0Hi; /* 0x500 */
+ u32 Mb0Lo;
+ u32 Mb1Hi;
+ u32 CmdPrd;
+ u32 Mb2Hi;
+ u32 TxPrd;
+ u32 Mb3Hi;
+ u32 RxStdPrd;
+ u32 Mb4Hi;
+ u32 RxJumboPrd;
+ u32 Mb5Hi;
+ u32 RxMiniPrd;
+ u32 Mb6Hi;
+ u32 Mb6Lo;
+ u32 Mb7Hi;
+ u32 Mb7Lo;
+ u32 Mb8Hi;
+ u32 Mb8Lo;
+ u32 Mb9Hi;
+ u32 Mb9Lo;
+ u32 MbAHi;
+ u32 MbALo;
+ u32 MbBHi;
+ u32 MbBLo;
+ u32 MbCHi;
+ u32 MbCLo;
+ u32 MbDHi;
+ u32 MbDLo;
+ u32 MbEHi;
+ u32 MbELo;
+ u32 MbFHi;
+ u32 MbFLo;
+
+ u32 pad14[32];
+
+ u32 MacAddrHi; /* 0x600 */
+ u32 MacAddrLo;
+ u32 InfoPtrHi;
+ u32 InfoPtrLo;
+ u32 MultiCastHi; /* 0x610 */
+ u32 MultiCastLo;
+ u32 ModeStat;
+ u32 DmaReadCfg;
+ u32 DmaWriteCfg; /* 0x620 */
+ u32 TxBufRat;
+ u32 EvtCsm;
+ u32 CmdCsm;
+ u32 TuneRxCoalTicks;/* 0x630 */
+ u32 TuneTxCoalTicks;
+ u32 TuneStatTicks;
+ u32 TuneMaxTxDesc;
+ u32 TuneMaxRxDesc; /* 0x640 */
+ u32 TuneTrace;
+ u32 TuneLink;
+ u32 TuneFastLink;
+ u32 TracePtr; /* 0x650 */
+ u32 TraceStrt;
+ u32 TraceLen;
+ u32 IfIdx;
+ u32 IfMtu; /* 0x660 */
+ u32 MaskInt;
+ u32 GigLnkState;
+ u32 FastLnkState;
+ u32 pad16[4]; /* 0x670 */
+ u32 RxRetCsm; /* 0x680 */
+
+ u32 pad17[31];
+
+ u32 CmdRng[64]; /* 0x700 */
+ u32 Window[0x200];
+};
+
+
+typedef struct {
+ u32 addrhi;
+ u32 addrlo;
+} aceaddr;
+
+
+#define ACE_WINDOW_SIZE 0x800
+
+#define ACE_JUMBO_MTU 9000
+#define ACE_STD_MTU 1500
+
+#define ACE_TRACE_SIZE 0x8000
+
+/*
+ * Host control register bits.
+ */
+
+#define IN_INT 0x01
+#define CLR_INT 0x02
+#define HW_RESET 0x08
+#define BYTE_SWAP 0x10
+#define WORD_SWAP 0x20
+#define MASK_INTS 0x40
+
+/*
+ * Local control register bits.
+ */
+
+#define EEPROM_DATA_IN 0x800000
+#define EEPROM_DATA_OUT 0x400000
+#define EEPROM_WRITE_ENABLE 0x200000
+#define EEPROM_CLK_OUT 0x100000
+
+#define EEPROM_BASE 0xa0000000
+
+#define EEPROM_WRITE_SELECT 0xa0
+#define EEPROM_READ_SELECT 0xa1
+
+#define SRAM_BANK_512K 0x200
+
+
+/*
+ * udelay() values for when clocking the eeprom
+ */
+#define ACE_SHORT_DELAY 2
+#define ACE_LONG_DELAY 4
+
+
+/*
+ * Misc Config bits
+ */
+
+#define SYNC_SRAM_TIMING 0x100000
+
+
+/*
+ * CPU state bits.
+ */
+
+#define CPU_RESET 0x01
+#define CPU_TRACE 0x02
+#define CPU_PROM_FAILED 0x10
+#define CPU_HALT 0x00010000
+#define CPU_HALTED 0xffff0000
+
+
+/*
+ * PCI State bits.
+ */
+
+#define DMA_READ_MAX_4 0x04
+#define DMA_READ_MAX_16 0x08
+#define DMA_READ_MAX_32 0x0c
+#define DMA_READ_MAX_64 0x10
+#define DMA_READ_MAX_128 0x14
+#define DMA_READ_MAX_256 0x18
+#define DMA_READ_MAX_1K 0x1c
+#define DMA_WRITE_MAX_4 0x20
+#define DMA_WRITE_MAX_16 0x40
+#define DMA_WRITE_MAX_32 0x60
+#define DMA_WRITE_MAX_64 0x80
+#define DMA_WRITE_MAX_128 0xa0
+#define DMA_WRITE_MAX_256 0xc0
+#define DMA_WRITE_MAX_1K 0xe0
+#define DMA_READ_WRITE_MASK 0xfc
+#define MEM_READ_MULTIPLE 0x00020000
+#define PCI_66MHZ 0x00080000
+#define PCI_32BIT 0x00100000
+#define DMA_WRITE_ALL_ALIGN 0x00800000
+#define READ_CMD_MEM 0x06000000
+#define WRITE_CMD_MEM 0x70000000
+
+
+/*
+ * Mode status
+ */
+
+#define ACE_BYTE_SWAP_BD 0x02
+#define ACE_WORD_SWAP_BD 0x04 /* not actually used */
+#define ACE_WARN 0x08
+#define ACE_BYTE_SWAP_DMA 0x10
+#define ACE_NO_JUMBO_FRAG 0x200
+#define ACE_FATAL 0x40000000
+
+
+/*
+ * DMA config
+ */
+
+#define DMA_THRESH_1W 0x10
+#define DMA_THRESH_2W 0x20
+#define DMA_THRESH_4W 0x40
+#define DMA_THRESH_8W 0x80
+#define DMA_THRESH_16W 0x100
+#define DMA_THRESH_32W 0x0 /* not described in doc, but exists. */
+
+
+/*
+ * Tuning parameters
+ */
+
+#define TICKS_PER_SEC 1000000
+
+
+/*
+ * Link bits
+ */
+
+#define LNK_PREF 0x00008000
+#define LNK_10MB 0x00010000
+#define LNK_100MB 0x00020000
+#define LNK_1000MB 0x00040000
+#define LNK_FULL_DUPLEX 0x00080000
+#define LNK_HALF_DUPLEX 0x00100000
+#define LNK_TX_FLOW_CTL_Y 0x00200000
+#define LNK_NEG_ADVANCED 0x00400000
+#define LNK_RX_FLOW_CTL_Y 0x00800000
+#define LNK_NIC 0x01000000
+#define LNK_JAM 0x02000000
+#define LNK_JUMBO 0x04000000
+#define LNK_ALTEON 0x08000000
+#define LNK_NEG_FCTL 0x10000000
+#define LNK_NEGOTIATE 0x20000000
+#define LNK_ENABLE 0x40000000
+#define LNK_UP 0x80000000
+
+
+/*
+ * Event definitions
+ */
+
+#define EVT_RING_ENTRIES 256
+#define EVT_RING_SIZE (EVT_RING_ENTRIES * sizeof(struct event))
+
+struct event {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u32 idx:12;
+ u32 code:12;
+ u32 evt:8;
+#else
+ u32 evt:8;
+ u32 code:12;
+ u32 idx:12;
+#endif
+ u32 pad;
+};
+
+
+/*
+ * Events
+ */
+
+#define E_FW_RUNNING 0x01
+#define E_STATS_UPDATED 0x04
+
+#define E_STATS_UPDATE 0x04
+
+#define E_LNK_STATE 0x06
+#define E_C_LINK_UP 0x01
+#define E_C_LINK_DOWN 0x02
+#define E_C_LINK_10_100 0x03
+
+#define E_ERROR 0x07
+#define E_C_ERR_INVAL_CMD 0x01
+#define E_C_ERR_UNIMP_CMD 0x02
+#define E_C_ERR_BAD_CFG 0x03
+
+#define E_MCAST_LIST 0x08
+#define E_C_MCAST_ADDR_ADD 0x01
+#define E_C_MCAST_ADDR_DEL 0x02
+
+#define E_RESET_JUMBO_RNG 0x09
+
+
+/*
+ * Commands
+ */
+
+#define CMD_RING_ENTRIES 64
+
+struct cmd {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u32 idx:12;
+ u32 code:12;
+ u32 evt:8;
+#else
+ u32 evt:8;
+ u32 code:12;
+ u32 idx:12;
+#endif
+};
+
+
+#define C_HOST_STATE 0x01
+#define C_C_STACK_UP 0x01
+#define C_C_STACK_DOWN 0x02
+
+#define C_FDR_FILTERING 0x02
+#define C_C_FDR_FILT_ENABLE 0x01
+#define C_C_FDR_FILT_DISABLE 0x02
+
+#define C_SET_RX_PRD_IDX 0x03
+#define C_UPDATE_STATS 0x04
+#define C_RESET_JUMBO_RNG 0x05
+#define C_ADD_MULTICAST_ADDR 0x08
+#define C_DEL_MULTICAST_ADDR 0x09
+
+#define C_SET_PROMISC_MODE 0x0a
+#define C_C_PROMISC_ENABLE 0x01
+#define C_C_PROMISC_DISABLE 0x02
+
+#define C_LNK_NEGOTIATION 0x0b
+#define C_C_NEGOTIATE_BOTH 0x00
+#define C_C_NEGOTIATE_GIG 0x01
+#define C_C_NEGOTIATE_10_100 0x02
+
+#define C_SET_MAC_ADDR 0x0c
+#define C_CLEAR_PROFILE 0x0d
+
+#define C_SET_MULTICAST_MODE 0x0e
+#define C_C_MCAST_ENABLE 0x01
+#define C_C_MCAST_DISABLE 0x02
+
+#define C_CLEAR_STATS 0x0f
+#define C_SET_RX_JUMBO_PRD_IDX 0x10
+#define C_REFRESH_STATS 0x11
+
+
+/*
+ * Descriptor flags
+ */
+#define BD_FLG_TCP_UDP_SUM 0x01
+#define BD_FLG_IP_SUM 0x02
+#define BD_FLG_END 0x04
+#define BD_FLG_MORE 0x08
+#define BD_FLG_JUMBO 0x10
+#define BD_FLG_UCAST 0x20
+#define BD_FLG_MCAST 0x40
+#define BD_FLG_BCAST 0x60
+#define BD_FLG_TYP_MASK 0x60
+#define BD_FLG_IP_FRAG 0x80
+#define BD_FLG_IP_FRAG_END 0x100
+#define BD_FLG_VLAN_TAG 0x200
+#define BD_FLG_FRAME_ERROR 0x400
+#define BD_FLG_COAL_NOW 0x800
+#define BD_FLG_MINI 0x1000
+
+
+/*
+ * Ring Control block flags
+ */
+#define RCB_FLG_TCP_UDP_SUM 0x01
+#define RCB_FLG_IP_SUM 0x02
+#define RCB_FLG_NO_PSEUDO_HDR 0x08
+#define RCB_FLG_VLAN_ASSIST 0x10
+#define RCB_FLG_COAL_INT_ONLY 0x20
+#define RCB_FLG_TX_HOST_RING 0x40
+#define RCB_FLG_IEEE_SNAP_SUM 0x80
+#define RCB_FLG_EXT_RX_BD 0x100
+#define RCB_FLG_RNG_DISABLE 0x200
+
+
+/*
+ * TX ring - maximum TX ring entries for Tigon I's is 128
+ */
+#define MAX_TX_RING_ENTRIES 256
+#define TIGON_I_TX_RING_ENTRIES 128
+#define TX_RING_SIZE (MAX_TX_RING_ENTRIES * sizeof(struct tx_desc))
+#define TX_RING_BASE 0x3800
+
+struct tx_desc{
+ aceaddr addr;
+ u32 flagsize;
+#if 0
+/*
+ * This is in PCI shared mem and must be accessed with readl/writel
+ * real layout is:
+ */
+#if __LITTLE_ENDIAN
+ u16 flags;
+ u16 size;
+ u16 vlan;
+ u16 reserved;
+#else
+ u16 size;
+ u16 flags;
+ u16 reserved;
+ u16 vlan;
+#endif
+#endif
+ u32 vlanres;
+};
+
+
+#define RX_STD_RING_ENTRIES 512
+#define RX_STD_RING_SIZE (RX_STD_RING_ENTRIES * sizeof(struct rx_desc))
+
+#define RX_JUMBO_RING_ENTRIES 256
+#define RX_JUMBO_RING_SIZE (RX_JUMBO_RING_ENTRIES *sizeof(struct rx_desc))
+
+#define RX_MINI_RING_ENTRIES 1024
+#define RX_MINI_RING_SIZE (RX_MINI_RING_ENTRIES *sizeof(struct rx_desc))
+
+#define RX_RETURN_RING_ENTRIES 2048
+#define RX_RETURN_RING_SIZE (RX_MAX_RETURN_RING_ENTRIES * \
+ sizeof(struct rx_desc))
+
+struct rx_desc{
+ aceaddr addr;
+#ifdef __LITTLE_ENDIAN
+ u16 size;
+ u16 idx;
+#else
+ u16 idx;
+ u16 size;
+#endif
+#ifdef __LITTLE_ENDIAN
+ u16 flags;
+ u16 type;
+#else
+ u16 type;
+ u16 flags;
+#endif
+#ifdef __LITTLE_ENDIAN
+ u16 tcp_udp_csum;
+ u16 ip_csum;
+#else
+ u16 ip_csum;
+ u16 tcp_udp_csum;
+#endif
+#ifdef __LITTLE_ENDIAN
+ u16 vlan;
+ u16 err_flags;
+#else
+ u16 err_flags;
+ u16 vlan;
+#endif
+ u32 reserved;
+ u32 opague;
+};
+
+
+/*
+ * This struct is shared with the NIC firmware.
+ */
+struct ring_ctrl {
+ aceaddr rngptr;
+#ifdef __LITTLE_ENDIAN
+ u16 flags;
+ u16 max_len;
+#else
+ u16 max_len;
+ u16 flags;
+#endif
+ u32 pad;
+};
+
+
+struct ace_mac_stats {
+ u32 excess_colls;
+ u32 coll_1;
+ u32 coll_2;
+ u32 coll_3;
+ u32 coll_4;
+ u32 coll_5;
+ u32 coll_6;
+ u32 coll_7;
+ u32 coll_8;
+ u32 coll_9;
+ u32 coll_10;
+ u32 coll_11;
+ u32 coll_12;
+ u32 coll_13;
+ u32 coll_14;
+ u32 coll_15;
+ u32 late_coll;
+ u32 defers;
+ u32 crc_err;
+ u32 underrun;
+ u32 crs_err;
+ u32 pad[3];
+ u32 drop_ula;
+ u32 drop_mc;
+ u32 drop_fc;
+ u32 drop_space;
+ u32 coll;
+ u32 kept_bc;
+ u32 kept_mc;
+ u32 kept_uc;
+};
+
+
+struct ace_info {
+ union {
+ u32 stats[256];
+ } s;
+ struct ring_ctrl evt_ctrl;
+ struct ring_ctrl cmd_ctrl;
+ struct ring_ctrl tx_ctrl;
+ struct ring_ctrl rx_std_ctrl;
+ struct ring_ctrl rx_jumbo_ctrl;
+ struct ring_ctrl rx_mini_ctrl;
+ struct ring_ctrl rx_return_ctrl;
+ aceaddr evt_prd_ptr;
+ aceaddr rx_ret_prd_ptr;
+ aceaddr tx_csm_ptr;
+ aceaddr stats2_ptr;
+};
+
+
+struct ring_info {
+ struct sk_buff *skb;
+ DEFINE_DMA_UNMAP_ADDR(mapping);
+};
+
+
+/*
+ * Funny... As soon as we add maplen on alpha, it starts to work
+ * much slower. Hmm... is it because struct does not fit to one cacheline?
+ * So, split tx_ring_info.
+ */
+struct tx_ring_info {
+ struct sk_buff *skb;
+ DEFINE_DMA_UNMAP_ADDR(mapping);
+ DEFINE_DMA_UNMAP_LEN(maplen);
+};
+
+
+/*
+ * struct ace_skb holding the rings of skb's. This is an awful lot of
+ * pointers, but I don't see any other smart mode to do this in an
+ * efficient manner ;-(
+ */
+struct ace_skb
+{
+ struct tx_ring_info tx_skbuff[MAX_TX_RING_ENTRIES];
+ struct ring_info rx_std_skbuff[RX_STD_RING_ENTRIES];
+ struct ring_info rx_mini_skbuff[RX_MINI_RING_ENTRIES];
+ struct ring_info rx_jumbo_skbuff[RX_JUMBO_RING_ENTRIES];
+};
+
+
+/*
+ * Struct private for the AceNIC.
+ *
+ * Elements are grouped so variables used by the tx handling goes
+ * together, and will go into the same cache lines etc. in order to
+ * avoid cache line contention between the rx and tx handling on SMP.
+ *
+ * Frequently accessed variables are put at the beginning of the
+ * struct to help the compiler generate better/shorter code.
+ */
+struct ace_private
+{
+ struct ace_info *info;
+ struct ace_regs __iomem *regs; /* register base */
+ struct ace_skb *skb;
+ dma_addr_t info_dma; /* 32/64 bit */
+
+ int version, link;
+ int promisc, mcast_all;
+
+ /*
+ * TX elements
+ */
+ struct tx_desc *tx_ring;
+ u32 tx_prd;
+ volatile u32 tx_ret_csm;
+ int tx_ring_entries;
+
+ /*
+ * RX elements
+ */
+ unsigned long std_refill_busy
+ __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ unsigned long mini_refill_busy, jumbo_refill_busy;
+ atomic_t cur_rx_bufs;
+ atomic_t cur_mini_bufs;
+ atomic_t cur_jumbo_bufs;
+ u32 rx_std_skbprd, rx_mini_skbprd, rx_jumbo_skbprd;
+ u32 cur_rx;
+
+ struct rx_desc *rx_std_ring;
+ struct rx_desc *rx_jumbo_ring;
+ struct rx_desc *rx_mini_ring;
+ struct rx_desc *rx_return_ring;
+
+ int tasklet_pending, jumbo;
+ struct tasklet_struct ace_tasklet;
+
+ struct event *evt_ring;
+
+ volatile u32 *evt_prd, *rx_ret_prd, *tx_csm;
+
+ dma_addr_t tx_ring_dma; /* 32/64 bit */
+ dma_addr_t rx_ring_base_dma;
+ dma_addr_t evt_ring_dma;
+ dma_addr_t evt_prd_dma, rx_ret_prd_dma, tx_csm_dma;
+
+ unsigned char *trace_buf;
+ struct pci_dev *pdev;
+ struct net_device *next;
+ volatile int fw_running;
+ int board_idx;
+ u16 pci_command;
+ u8 pci_latency;
+ const char *name;
+#ifdef INDEX_DEBUG
+ spinlock_t debug_lock
+ __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ u32 last_tx, last_std_rx, last_mini_rx;
+#endif
+ int pci_using_dac;
+ u8 firmware_major;
+ u8 firmware_minor;
+ u8 firmware_fix;
+ u32 firmware_start;
+};
+
+
+#define TX_RESERVED MAX_SKB_FRAGS
+
+static inline int tx_space (struct ace_private *ap, u32 csm, u32 prd)
+{
+ return (csm - prd - 1) & (ACE_TX_RING_ENTRIES(ap) - 1);
+}
+
+#define tx_free(ap) tx_space((ap)->tx_ret_csm, (ap)->tx_prd, ap)
+#define tx_ring_full(ap, csm, prd) (tx_space(ap, csm, prd) <= TX_RESERVED)
+
+static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr)
+{
+ u64 baddr = (u64) addr;
+ aa->addrlo = baddr & 0xffffffff;
+ aa->addrhi = baddr >> 32;
+ wmb();
+}
+
+
+static inline void ace_set_txprd(struct ace_regs __iomem *regs,
+ struct ace_private *ap, u32 value)
+{
+#ifdef INDEX_DEBUG
+ unsigned long flags;
+ spin_lock_irqsave(&ap->debug_lock, flags);
+ writel(value, &regs->TxPrd);
+ if (value == ap->last_tx)
+ printk(KERN_ERR "AceNIC RACE ALERT! writing identical value "
+ "to tx producer (%i)\n", value);
+ ap->last_tx = value;
+ spin_unlock_irqrestore(&ap->debug_lock, flags);
+#else
+ writel(value, &regs->TxPrd);
+#endif
+ wmb();
+}
+
+
+static inline void ace_mask_irq(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+
+ if (ACE_IS_TIGON_I(ap))
+ writel(1, &regs->MaskInt);
+ else
+ writel(readl(&regs->HostCtrl) | MASK_INTS, &regs->HostCtrl);
+
+ ace_sync_irq(dev->irq);
+}
+
+
+static inline void ace_unmask_irq(struct net_device *dev)
+{
+ struct ace_private *ap = netdev_priv(dev);
+ struct ace_regs __iomem *regs = ap->regs;
+
+ if (ACE_IS_TIGON_I(ap))
+ writel(0, &regs->MaskInt);
+ else
+ writel(readl(&regs->HostCtrl) & ~MASK_INTS, &regs->HostCtrl);
+}
+
+
+/*
+ * Prototypes
+ */
+static int ace_init(struct net_device *dev);
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs);
+static irqreturn_t ace_interrupt(int irq, void *dev_id);
+static int ace_load_firmware(struct net_device *dev);
+static int ace_open(struct net_device *dev);
+static netdev_tx_t ace_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static int ace_close(struct net_device *dev);
+static void ace_tasklet(unsigned long dev);
+static void ace_dump_trace(struct ace_private *ap);
+static void ace_set_multicast_list(struct net_device *dev);
+static int ace_change_mtu(struct net_device *dev, int new_mtu);
+static int ace_set_mac_addr(struct net_device *dev, void *p);
+static void ace_set_rxtx_parms(struct net_device *dev, int jumbo);
+static int ace_allocate_descriptors(struct net_device *dev);
+static void ace_free_descriptors(struct net_device *dev);
+static void ace_init_cleanup(struct net_device *dev);
+static struct net_device_stats *ace_get_stats(struct net_device *dev);
+static int read_eeprom_byte(struct net_device *dev, unsigned long offset);
+
+#endif /* _ACENIC_H_ */
diff --git a/drivers/net/ethernet/altera/Kconfig b/drivers/net/ethernet/altera/Kconfig
new file mode 100644
index 00000000000..fdddba51473
--- /dev/null
+++ b/drivers/net/ethernet/altera/Kconfig
@@ -0,0 +1,9 @@
+config ALTERA_TSE
+ tristate "Altera Triple-Speed Ethernet MAC support"
+ depends on HAS_DMA
+ select PHYLIB
+ ---help---
+ This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
+
+ To compile this driver as a module, choose M here. The module
+ will be called alteratse.
diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
new file mode 100644
index 00000000000..3eff2fd3997
--- /dev/null
+++ b/drivers/net/ethernet/altera/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Altera device drivers.
+#
+
+obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
+altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
+altera_msgdma.o altera_sgdma.o altera_utils.o
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/ethernet/altera/altera_msgdma.c b/drivers/net/ethernet/altera/altera_msgdma.c
new file mode 100644
index 00000000000..0fb986ba329
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma.c
@@ -0,0 +1,206 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/netdevice.h>
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_msgdmahw.h"
+#include "altera_msgdma.h"
+
+/* No initialization work to do for MSGDMA */
+int msgdma_initialize(struct altera_tse_private *priv)
+{
+ return 0;
+}
+
+void msgdma_uninitialize(struct altera_tse_private *priv)
+{
+}
+
+void msgdma_start_rxdma(struct altera_tse_private *priv)
+{
+}
+
+void msgdma_reset(struct altera_tse_private *priv)
+{
+ int counter;
+
+ /* Reset Rx mSGDMA */
+ csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr,
+ msgdma_csroffs(status));
+ csrwr32(MSGDMA_CSR_CTL_RESET, priv->rx_dma_csr,
+ msgdma_csroffs(control));
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(priv->rx_dma_csr, msgdma_csroffs(status),
+ MSGDMA_CSR_STAT_RESETTING))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+ netif_warn(priv, drv, priv->dev,
+ "TSE Rx mSGDMA resetting bit never cleared!\n");
+
+ /* clear all status bits */
+ csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr, msgdma_csroffs(status));
+
+ /* Reset Tx mSGDMA */
+ csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr,
+ msgdma_csroffs(status));
+
+ csrwr32(MSGDMA_CSR_CTL_RESET, priv->tx_dma_csr,
+ msgdma_csroffs(control));
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(priv->tx_dma_csr, msgdma_csroffs(status),
+ MSGDMA_CSR_STAT_RESETTING))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+ netif_warn(priv, drv, priv->dev,
+ "TSE Tx mSGDMA resetting bit never cleared!\n");
+
+ /* clear all status bits */
+ csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr, msgdma_csroffs(status));
+}
+
+void msgdma_disable_rxirq(struct altera_tse_private *priv)
+{
+ tse_clear_bit(priv->rx_dma_csr, msgdma_csroffs(control),
+ MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_enable_rxirq(struct altera_tse_private *priv)
+{
+ tse_set_bit(priv->rx_dma_csr, msgdma_csroffs(control),
+ MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_disable_txirq(struct altera_tse_private *priv)
+{
+ tse_clear_bit(priv->tx_dma_csr, msgdma_csroffs(control),
+ MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_enable_txirq(struct altera_tse_private *priv)
+{
+ tse_set_bit(priv->tx_dma_csr, msgdma_csroffs(control),
+ MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_clear_rxirq(struct altera_tse_private *priv)
+{
+ csrwr32(MSGDMA_CSR_STAT_IRQ, priv->rx_dma_csr, msgdma_csroffs(status));
+}
+
+void msgdma_clear_txirq(struct altera_tse_private *priv)
+{
+ csrwr32(MSGDMA_CSR_STAT_IRQ, priv->tx_dma_csr, msgdma_csroffs(status));
+}
+
+/* return 0 to indicate transmit is pending */
+int msgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ csrwr32(lower_32_bits(buffer->dma_addr), priv->tx_dma_desc,
+ msgdma_descroffs(read_addr_lo));
+ csrwr32(upper_32_bits(buffer->dma_addr), priv->tx_dma_desc,
+ msgdma_descroffs(read_addr_hi));
+ csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_lo));
+ csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_hi));
+ csrwr32(buffer->len, priv->tx_dma_desc, msgdma_descroffs(len));
+ csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(burst_seq_num));
+ csrwr32(MSGDMA_DESC_TX_STRIDE, priv->tx_dma_desc,
+ msgdma_descroffs(stride));
+ csrwr32(MSGDMA_DESC_CTL_TX_SINGLE, priv->tx_dma_desc,
+ msgdma_descroffs(control));
+ return 0;
+}
+
+u32 msgdma_tx_completions(struct altera_tse_private *priv)
+{
+ u32 ready = 0;
+ u32 inuse;
+ u32 status;
+
+ /* Get number of sent descriptors */
+ inuse = csrrd32(priv->tx_dma_csr, msgdma_csroffs(rw_fill_level))
+ & 0xffff;
+
+ if (inuse) { /* Tx FIFO is not empty */
+ ready = priv->tx_prod - priv->tx_cons - inuse - 1;
+ } else {
+ /* Check for buffered last packet */
+ status = csrrd32(priv->tx_dma_csr, msgdma_csroffs(status));
+ if (status & MSGDMA_CSR_STAT_BUSY)
+ ready = priv->tx_prod - priv->tx_cons - 1;
+ else
+ ready = priv->tx_prod - priv->tx_cons;
+ }
+ return ready;
+}
+
+/* Put buffer to the mSGDMA RX FIFO
+ */
+void msgdma_add_rx_desc(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ u32 len = priv->rx_dma_buf_sz;
+ dma_addr_t dma_addr = rxbuffer->dma_addr;
+ u32 control = (MSGDMA_DESC_CTL_END_ON_EOP
+ | MSGDMA_DESC_CTL_END_ON_LEN
+ | MSGDMA_DESC_CTL_TR_COMP_IRQ
+ | MSGDMA_DESC_CTL_EARLY_IRQ
+ | MSGDMA_DESC_CTL_TR_ERR_IRQ
+ | MSGDMA_DESC_CTL_GO);
+
+ csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_lo));
+ csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_hi));
+ csrwr32(lower_32_bits(dma_addr), priv->rx_dma_desc,
+ msgdma_descroffs(write_addr_lo));
+ csrwr32(upper_32_bits(dma_addr), priv->rx_dma_desc,
+ msgdma_descroffs(write_addr_hi));
+ csrwr32(len, priv->rx_dma_desc, msgdma_descroffs(len));
+ csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(burst_seq_num));
+ csrwr32(0x00010001, priv->rx_dma_desc, msgdma_descroffs(stride));
+ csrwr32(control, priv->rx_dma_desc, msgdma_descroffs(control));
+}
+
+/* status is returned on upper 16 bits,
+ * length is returned in lower 16 bits
+ */
+u32 msgdma_rx_status(struct altera_tse_private *priv)
+{
+ u32 rxstatus = 0;
+ u32 pktlength;
+ u32 pktstatus;
+
+ if (csrrd32(priv->rx_dma_csr, msgdma_csroffs(resp_fill_level))
+ & 0xffff) {
+ pktlength = csrrd32(priv->rx_dma_resp,
+ msgdma_respoffs(bytes_transferred));
+ pktstatus = csrrd32(priv->rx_dma_resp,
+ msgdma_respoffs(status));
+ rxstatus = pktstatus;
+ rxstatus = rxstatus << 16;
+ rxstatus |= (pktlength & 0xffff);
+ }
+ return rxstatus;
+}
diff --git a/drivers/net/ethernet/altera/altera_msgdma.h b/drivers/net/ethernet/altera/altera_msgdma.h
new file mode 100644
index 00000000000..42cf61c8105
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma.h
@@ -0,0 +1,35 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_MSGDMA_H__
+#define __ALTERA_MSGDMA_H__
+
+void msgdma_reset(struct altera_tse_private *);
+void msgdma_enable_txirq(struct altera_tse_private *);
+void msgdma_enable_rxirq(struct altera_tse_private *);
+void msgdma_disable_rxirq(struct altera_tse_private *);
+void msgdma_disable_txirq(struct altera_tse_private *);
+void msgdma_clear_rxirq(struct altera_tse_private *);
+void msgdma_clear_txirq(struct altera_tse_private *);
+u32 msgdma_tx_completions(struct altera_tse_private *);
+void msgdma_add_rx_desc(struct altera_tse_private *, struct tse_buffer *);
+int msgdma_tx_buffer(struct altera_tse_private *, struct tse_buffer *);
+u32 msgdma_rx_status(struct altera_tse_private *);
+int msgdma_initialize(struct altera_tse_private *);
+void msgdma_uninitialize(struct altera_tse_private *);
+void msgdma_start_rxdma(struct altera_tse_private *);
+
+#endif /* __ALTERA_MSGDMA_H__ */
diff --git a/drivers/net/ethernet/altera/altera_msgdmahw.h b/drivers/net/ethernet/altera/altera_msgdmahw.h
new file mode 100644
index 00000000000..e335626e1b6
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdmahw.h
@@ -0,0 +1,162 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_MSGDMAHW_H__
+#define __ALTERA_MSGDMAHW_H__
+
+/* mSGDMA extended descriptor format
+ */
+struct msgdma_extended_desc {
+ u32 read_addr_lo; /* data buffer source address low bits */
+ u32 write_addr_lo; /* data buffer destination address low bits */
+ u32 len; /* the number of bytes to transfer
+ * per descriptor
+ */
+ u32 burst_seq_num; /* bit 31:24 write burst
+ * bit 23:16 read burst
+ * bit 15:0 sequence number
+ */
+ u32 stride; /* bit 31:16 write stride
+ * bit 15:0 read stride
+ */
+ u32 read_addr_hi; /* data buffer source address high bits */
+ u32 write_addr_hi; /* data buffer destination address high bits */
+ u32 control; /* characteristics of the transfer */
+};
+
+/* mSGDMA descriptor control field bit definitions
+ */
+#define MSGDMA_DESC_CTL_SET_CH(x) ((x) & 0xff)
+#define MSGDMA_DESC_CTL_GEN_SOP BIT(8)
+#define MSGDMA_DESC_CTL_GEN_EOP BIT(9)
+#define MSGDMA_DESC_CTL_PARK_READS BIT(10)
+#define MSGDMA_DESC_CTL_PARK_WRITES BIT(11)
+#define MSGDMA_DESC_CTL_END_ON_EOP BIT(12)
+#define MSGDMA_DESC_CTL_END_ON_LEN BIT(13)
+#define MSGDMA_DESC_CTL_TR_COMP_IRQ BIT(14)
+#define MSGDMA_DESC_CTL_EARLY_IRQ BIT(15)
+#define MSGDMA_DESC_CTL_TR_ERR_IRQ (0xff << 16)
+#define MSGDMA_DESC_CTL_EARLY_DONE BIT(24)
+/* Writing ‘1’ to the ‘go’ bit commits the entire descriptor into the
+ * descriptor FIFO(s)
+ */
+#define MSGDMA_DESC_CTL_GO BIT(31)
+
+/* Tx buffer control flags
+ */
+#define MSGDMA_DESC_CTL_TX_FIRST (MSGDMA_DESC_CTL_GEN_SOP | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_MIDDLE (MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_LAST (MSGDMA_DESC_CTL_GEN_EOP | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_SINGLE (MSGDMA_DESC_CTL_GEN_SOP | \
+ MSGDMA_DESC_CTL_GEN_EOP | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_RX_SINGLE (MSGDMA_DESC_CTL_END_ON_EOP | \
+ MSGDMA_DESC_CTL_END_ON_LEN | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_EARLY_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+/* mSGDMA extended descriptor stride definitions
+ */
+#define MSGDMA_DESC_TX_STRIDE (0x00010001)
+#define MSGDMA_DESC_RX_STRIDE (0x00010001)
+
+/* mSGDMA dispatcher control and status register map
+ */
+struct msgdma_csr {
+ u32 status; /* Read/Clear */
+ u32 control; /* Read/Write */
+ u32 rw_fill_level; /* bit 31:16 - write fill level
+ * bit 15:0 - read fill level
+ */
+ u32 resp_fill_level; /* bit 15:0 */
+ u32 rw_seq_num; /* bit 31:16 - write sequence number
+ * bit 15:0 - read sequence number
+ */
+ u32 pad[3]; /* reserved */
+};
+
+/* mSGDMA CSR status register bit definitions
+ */
+#define MSGDMA_CSR_STAT_BUSY BIT(0)
+#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY BIT(1)
+#define MSGDMA_CSR_STAT_DESC_BUF_FULL BIT(2)
+#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY BIT(3)
+#define MSGDMA_CSR_STAT_RESP_BUF_FULL BIT(4)
+#define MSGDMA_CSR_STAT_STOPPED BIT(5)
+#define MSGDMA_CSR_STAT_RESETTING BIT(6)
+#define MSGDMA_CSR_STAT_STOPPED_ON_ERR BIT(7)
+#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY BIT(8)
+#define MSGDMA_CSR_STAT_IRQ BIT(9)
+#define MSGDMA_CSR_STAT_MASK 0x3FF
+#define MSGDMA_CSR_STAT_MASK_WITHOUT_IRQ 0x1FF
+
+#define MSGDMA_CSR_STAT_BUSY_GET(v) GET_BIT_VALUE(v, 0)
+#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 1)
+#define MSGDMA_CSR_STAT_DESC_BUF_FULL_GET(v) GET_BIT_VALUE(v, 2)
+#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 3)
+#define MSGDMA_CSR_STAT_RESP_BUF_FULL_GET(v) GET_BIT_VALUE(v, 4)
+#define MSGDMA_CSR_STAT_STOPPED_GET(v) GET_BIT_VALUE(v, 5)
+#define MSGDMA_CSR_STAT_RESETTING_GET(v) GET_BIT_VALUE(v, 6)
+#define MSGDMA_CSR_STAT_STOPPED_ON_ERR_GET(v) GET_BIT_VALUE(v, 7)
+#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY_GET(v) GET_BIT_VALUE(v, 8)
+#define MSGDMA_CSR_STAT_IRQ_GET(v) GET_BIT_VALUE(v, 9)
+
+/* mSGDMA CSR control register bit definitions
+ */
+#define MSGDMA_CSR_CTL_STOP BIT(0)
+#define MSGDMA_CSR_CTL_RESET BIT(1)
+#define MSGDMA_CSR_CTL_STOP_ON_ERR BIT(2)
+#define MSGDMA_CSR_CTL_STOP_ON_EARLY BIT(3)
+#define MSGDMA_CSR_CTL_GLOBAL_INTR BIT(4)
+#define MSGDMA_CSR_CTL_STOP_DESCS BIT(5)
+
+/* mSGDMA CSR fill level bits
+ */
+#define MSGDMA_CSR_WR_FILL_LEVEL_GET(v) (((v) & 0xffff0000) >> 16)
+#define MSGDMA_CSR_RD_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
+#define MSGDMA_CSR_RESP_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
+
+/* mSGDMA response register map
+ */
+struct msgdma_response {
+ u32 bytes_transferred;
+ u32 status;
+};
+
+#define msgdma_respoffs(a) (offsetof(struct msgdma_response, a))
+#define msgdma_csroffs(a) (offsetof(struct msgdma_csr, a))
+#define msgdma_descroffs(a) (offsetof(struct msgdma_extended_desc, a))
+
+/* mSGDMA response register bit definitions
+ */
+#define MSGDMA_RESP_EARLY_TERM BIT(8)
+#define MSGDMA_RESP_ERR_MASK 0xFF
+
+#endif /* __ALTERA_MSGDMA_H__*/
diff --git a/drivers/net/ethernet/altera/altera_sgdma.c b/drivers/net/ethernet/altera/altera_sgdma.c
new file mode 100644
index 00000000000..580553d42d3
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdma.c
@@ -0,0 +1,540 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/list.h>
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_sgdmahw.h"
+#include "altera_sgdma.h"
+
+static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc,
+ struct sgdma_descrip __iomem *ndesc,
+ dma_addr_t ndesc_phys,
+ dma_addr_t raddr,
+ dma_addr_t waddr,
+ u16 length,
+ int generate_eop,
+ int rfixed,
+ int wfixed);
+
+static int sgdma_async_write(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc);
+
+static int sgdma_async_read(struct altera_tse_private *priv);
+
+static dma_addr_t
+sgdma_txphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc);
+
+static dma_addr_t
+sgdma_rxphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc);
+
+static int sgdma_txbusy(struct altera_tse_private *priv);
+
+static int sgdma_rxbusy(struct altera_tse_private *priv);
+
+static void
+queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer);
+
+static void
+queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer);
+
+static struct tse_buffer *
+dequeue_tx(struct altera_tse_private *priv);
+
+static struct tse_buffer *
+dequeue_rx(struct altera_tse_private *priv);
+
+static struct tse_buffer *
+queue_rx_peekhead(struct altera_tse_private *priv);
+
+int sgdma_initialize(struct altera_tse_private *priv)
+{
+ priv->txctrlreg = SGDMA_CTRLREG_ILASTD |
+ SGDMA_CTRLREG_INTEN;
+
+ priv->rxctrlreg = SGDMA_CTRLREG_IDESCRIP |
+ SGDMA_CTRLREG_INTEN |
+ SGDMA_CTRLREG_ILASTD;
+
+ priv->sgdmadesclen = sizeof(struct sgdma_descrip);
+
+ INIT_LIST_HEAD(&priv->txlisthd);
+ INIT_LIST_HEAD(&priv->rxlisthd);
+
+ priv->rxdescphys = (dma_addr_t) 0;
+ priv->txdescphys = (dma_addr_t) 0;
+
+ priv->rxdescphys = dma_map_single(priv->device,
+ (void __force *)priv->rx_dma_desc,
+ priv->rxdescmem, DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(priv->device, priv->rxdescphys)) {
+ sgdma_uninitialize(priv);
+ netdev_err(priv->dev, "error mapping rx descriptor memory\n");
+ return -EINVAL;
+ }
+
+ priv->txdescphys = dma_map_single(priv->device,
+ (void __force *)priv->tx_dma_desc,
+ priv->txdescmem, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(priv->device, priv->txdescphys)) {
+ sgdma_uninitialize(priv);
+ netdev_err(priv->dev, "error mapping tx descriptor memory\n");
+ return -EINVAL;
+ }
+
+ /* Initialize descriptor memory to all 0's, sync memory to cache */
+ memset_io(priv->tx_dma_desc, 0, priv->txdescmem);
+ memset_io(priv->rx_dma_desc, 0, priv->rxdescmem);
+
+ dma_sync_single_for_device(priv->device, priv->txdescphys,
+ priv->txdescmem, DMA_TO_DEVICE);
+
+ dma_sync_single_for_device(priv->device, priv->rxdescphys,
+ priv->rxdescmem, DMA_TO_DEVICE);
+
+ return 0;
+}
+
+void sgdma_uninitialize(struct altera_tse_private *priv)
+{
+ if (priv->rxdescphys)
+ dma_unmap_single(priv->device, priv->rxdescphys,
+ priv->rxdescmem, DMA_BIDIRECTIONAL);
+
+ if (priv->txdescphys)
+ dma_unmap_single(priv->device, priv->txdescphys,
+ priv->txdescmem, DMA_TO_DEVICE);
+}
+
+/* This function resets the SGDMA controller and clears the
+ * descriptor memory used for transmits and receives.
+ */
+void sgdma_reset(struct altera_tse_private *priv)
+{
+ /* Initialize descriptor memory to 0 */
+ memset_io(priv->tx_dma_desc, 0, priv->txdescmem);
+ memset_io(priv->rx_dma_desc, 0, priv->rxdescmem);
+
+ csrwr32(SGDMA_CTRLREG_RESET, priv->tx_dma_csr, sgdma_csroffs(control));
+ csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control));
+
+ csrwr32(SGDMA_CTRLREG_RESET, priv->rx_dma_csr, sgdma_csroffs(control));
+ csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control));
+}
+
+/* For SGDMA, interrupts remain enabled after initially enabling,
+ * so no need to provide implementations for abstract enable
+ * and disable
+ */
+
+void sgdma_enable_rxirq(struct altera_tse_private *priv)
+{
+}
+
+void sgdma_enable_txirq(struct altera_tse_private *priv)
+{
+}
+
+void sgdma_disable_rxirq(struct altera_tse_private *priv)
+{
+}
+
+void sgdma_disable_txirq(struct altera_tse_private *priv)
+{
+}
+
+void sgdma_clear_rxirq(struct altera_tse_private *priv)
+{
+ tse_set_bit(priv->rx_dma_csr, sgdma_csroffs(control),
+ SGDMA_CTRLREG_CLRINT);
+}
+
+void sgdma_clear_txirq(struct altera_tse_private *priv)
+{
+ tse_set_bit(priv->tx_dma_csr, sgdma_csroffs(control),
+ SGDMA_CTRLREG_CLRINT);
+}
+
+/* transmits buffer through SGDMA. Returns number of buffers
+ * transmitted, 0 if not possible.
+ *
+ * tx_lock is held by the caller
+ */
+int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ struct sgdma_descrip __iomem *descbase =
+ (struct sgdma_descrip __iomem *)priv->tx_dma_desc;
+
+ struct sgdma_descrip __iomem *cdesc = &descbase[0];
+ struct sgdma_descrip __iomem *ndesc = &descbase[1];
+
+ /* wait 'til the tx sgdma is ready for the next transmit request */
+ if (sgdma_txbusy(priv))
+ return 0;
+
+ sgdma_setup_descrip(cdesc, /* current descriptor */
+ ndesc, /* next descriptor */
+ sgdma_txphysaddr(priv, ndesc),
+ buffer->dma_addr, /* address of packet to xmit */
+ 0, /* write addr 0 for tx dma */
+ buffer->len, /* length of packet */
+ SGDMA_CONTROL_EOP, /* Generate EOP */
+ 0, /* read fixed */
+ SGDMA_CONTROL_WR_FIXED); /* Generate SOP */
+
+ sgdma_async_write(priv, cdesc);
+
+ /* enqueue the request to the pending transmit queue */
+ queue_tx(priv, buffer);
+
+ return 1;
+}
+
+
+/* tx_lock held to protect access to queued tx list
+ */
+u32 sgdma_tx_completions(struct altera_tse_private *priv)
+{
+ u32 ready = 0;
+
+ if (!sgdma_txbusy(priv) &&
+ ((csrrd8(priv->tx_dma_desc, sgdma_descroffs(control))
+ & SGDMA_CONTROL_HW_OWNED) == 0) &&
+ (dequeue_tx(priv))) {
+ ready = 1;
+ }
+
+ return ready;
+}
+
+void sgdma_start_rxdma(struct altera_tse_private *priv)
+{
+ sgdma_async_read(priv);
+}
+
+void sgdma_add_rx_desc(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ queue_rx(priv, rxbuffer);
+}
+
+/* status is returned on upper 16 bits,
+ * length is returned in lower 16 bits
+ */
+u32 sgdma_rx_status(struct altera_tse_private *priv)
+{
+ struct sgdma_descrip __iomem *base =
+ (struct sgdma_descrip __iomem *)priv->rx_dma_desc;
+ struct sgdma_descrip __iomem *desc = NULL;
+ struct tse_buffer *rxbuffer = NULL;
+ unsigned int rxstatus = 0;
+
+ u32 sts = csrrd32(priv->rx_dma_csr, sgdma_csroffs(status));
+
+ desc = &base[0];
+ if (sts & SGDMA_STSREG_EOP) {
+ unsigned int pktlength = 0;
+ unsigned int pktstatus = 0;
+ dma_sync_single_for_cpu(priv->device,
+ priv->rxdescphys,
+ priv->sgdmadesclen,
+ DMA_FROM_DEVICE);
+
+ pktlength = csrrd16(desc, sgdma_descroffs(bytes_xferred));
+ pktstatus = csrrd8(desc, sgdma_descroffs(status));
+ rxstatus = pktstatus & ~SGDMA_STATUS_EOP;
+ rxstatus = rxstatus << 16;
+ rxstatus |= (pktlength & 0xffff);
+
+ if (rxstatus) {
+ csrwr8(0, desc, sgdma_descroffs(status));
+
+ rxbuffer = dequeue_rx(priv);
+ if (rxbuffer == NULL)
+ netdev_info(priv->dev,
+ "sgdma rx and rx queue empty!\n");
+
+ /* Clear control */
+ csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control));
+ /* clear status */
+ csrwr32(0xf, priv->rx_dma_csr, sgdma_csroffs(status));
+
+ /* kick the rx sgdma after reaping this descriptor */
+ sgdma_async_read(priv);
+
+ } else {
+ /* If the SGDMA indicated an end of packet on recv,
+ * then it's expected that the rxstatus from the
+ * descriptor is non-zero - meaning a valid packet
+ * with a nonzero length, or an error has been
+ * indicated. if not, then all we can do is signal
+ * an error and return no packet received. Most likely
+ * there is a system design error, or an error in the
+ * underlying kernel (cache or cache management problem)
+ */
+ netdev_err(priv->dev,
+ "SGDMA RX Error Info: %x, %x, %x\n",
+ sts, csrrd8(desc, sgdma_descroffs(status)),
+ rxstatus);
+ }
+ } else if (sts == 0) {
+ sgdma_async_read(priv);
+ }
+
+ return rxstatus;
+}
+
+
+/* Private functions */
+static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc,
+ struct sgdma_descrip __iomem *ndesc,
+ dma_addr_t ndesc_phys,
+ dma_addr_t raddr,
+ dma_addr_t waddr,
+ u16 length,
+ int generate_eop,
+ int rfixed,
+ int wfixed)
+{
+ /* Clear the next descriptor as not owned by hardware */
+
+ u32 ctrl = csrrd8(ndesc, sgdma_descroffs(control));
+ ctrl &= ~SGDMA_CONTROL_HW_OWNED;
+ csrwr8(ctrl, ndesc, sgdma_descroffs(control));
+
+ ctrl = SGDMA_CONTROL_HW_OWNED;
+ ctrl |= generate_eop;
+ ctrl |= rfixed;
+ ctrl |= wfixed;
+
+ /* Channel is implicitly zero, initialized to 0 by default */
+ csrwr32(lower_32_bits(raddr), desc, sgdma_descroffs(raddr));
+ csrwr32(lower_32_bits(waddr), desc, sgdma_descroffs(waddr));
+
+ csrwr32(0, desc, sgdma_descroffs(pad1));
+ csrwr32(0, desc, sgdma_descroffs(pad2));
+ csrwr32(lower_32_bits(ndesc_phys), desc, sgdma_descroffs(next));
+
+ csrwr8(ctrl, desc, sgdma_descroffs(control));
+ csrwr8(0, desc, sgdma_descroffs(status));
+ csrwr8(0, desc, sgdma_descroffs(wburst));
+ csrwr8(0, desc, sgdma_descroffs(rburst));
+ csrwr16(length, desc, sgdma_descroffs(bytes));
+ csrwr16(0, desc, sgdma_descroffs(bytes_xferred));
+}
+
+/* If hardware is busy, don't restart async read.
+ * if status register is 0 - meaning initial state, restart async read,
+ * probably for the first time when populating a receive buffer.
+ * If read status indicate not busy and a status, restart the async
+ * DMA read.
+ */
+static int sgdma_async_read(struct altera_tse_private *priv)
+{
+ struct sgdma_descrip __iomem *descbase =
+ (struct sgdma_descrip __iomem *)priv->rx_dma_desc;
+
+ struct sgdma_descrip __iomem *cdesc = &descbase[0];
+ struct sgdma_descrip __iomem *ndesc = &descbase[1];
+ struct tse_buffer *rxbuffer = NULL;
+
+ if (!sgdma_rxbusy(priv)) {
+ rxbuffer = queue_rx_peekhead(priv);
+ if (rxbuffer == NULL) {
+ netdev_err(priv->dev, "no rx buffers available\n");
+ return 0;
+ }
+
+ sgdma_setup_descrip(cdesc, /* current descriptor */
+ ndesc, /* next descriptor */
+ sgdma_rxphysaddr(priv, ndesc),
+ 0, /* read addr 0 for rx dma */
+ rxbuffer->dma_addr, /* write addr for rx dma */
+ 0, /* read 'til EOP */
+ 0, /* EOP: NA for rx dma */
+ 0, /* read fixed: NA for rx dma */
+ 0); /* SOP: NA for rx DMA */
+
+ dma_sync_single_for_device(priv->device,
+ priv->rxdescphys,
+ priv->sgdmadesclen,
+ DMA_TO_DEVICE);
+
+ csrwr32(lower_32_bits(sgdma_rxphysaddr(priv, cdesc)),
+ priv->rx_dma_csr,
+ sgdma_csroffs(next_descrip));
+
+ csrwr32((priv->rxctrlreg | SGDMA_CTRLREG_START),
+ priv->rx_dma_csr,
+ sgdma_csroffs(control));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int sgdma_async_write(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc)
+{
+ if (sgdma_txbusy(priv))
+ return 0;
+
+ /* clear control and status */
+ csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control));
+ csrwr32(0x1f, priv->tx_dma_csr, sgdma_csroffs(status));
+
+ dma_sync_single_for_device(priv->device, priv->txdescphys,
+ priv->sgdmadesclen, DMA_TO_DEVICE);
+
+ csrwr32(lower_32_bits(sgdma_txphysaddr(priv, desc)),
+ priv->tx_dma_csr,
+ sgdma_csroffs(next_descrip));
+
+ csrwr32((priv->txctrlreg | SGDMA_CTRLREG_START),
+ priv->tx_dma_csr,
+ sgdma_csroffs(control));
+
+ return 1;
+}
+
+static dma_addr_t
+sgdma_txphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc)
+{
+ dma_addr_t paddr = priv->txdescmem_busaddr;
+ uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->tx_dma_desc;
+ return (dma_addr_t)((uintptr_t)paddr + offs);
+}
+
+static dma_addr_t
+sgdma_rxphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip __iomem *desc)
+{
+ dma_addr_t paddr = priv->rxdescmem_busaddr;
+ uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->rx_dma_desc;
+ return (dma_addr_t)((uintptr_t)paddr + offs);
+}
+
+#define list_remove_head(list, entry, type, member) \
+ do { \
+ entry = NULL; \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ list_del_init(&entry->member); \
+ } \
+ } while (0)
+
+#define list_peek_head(list, entry, type, member) \
+ do { \
+ entry = NULL; \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ } \
+ } while (0)
+
+/* adds a tse_buffer to the tail of a tx buffer list.
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static void
+queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ list_add_tail(&buffer->lh, &priv->txlisthd);
+}
+
+
+/* adds a tse_buffer to the tail of a rx buffer list
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static void
+queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ list_add_tail(&buffer->lh, &priv->rxlisthd);
+}
+
+/* dequeues a tse_buffer from the transmit buffer list, otherwise
+ * returns NULL if empty.
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static struct tse_buffer *
+dequeue_tx(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_remove_head(&priv->txlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* dequeues a tse_buffer from the receive buffer list, otherwise
+ * returns NULL if empty
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static struct tse_buffer *
+dequeue_rx(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_remove_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* dequeues a tse_buffer from the receive buffer list, otherwise
+ * returns NULL if empty
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list while the
+ * head is being examined.
+ */
+static struct tse_buffer *
+queue_rx_peekhead(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_peek_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* check and return rx sgdma status without polling
+ */
+static int sgdma_rxbusy(struct altera_tse_private *priv)
+{
+ return csrrd32(priv->rx_dma_csr, sgdma_csroffs(status))
+ & SGDMA_STSREG_BUSY;
+}
+
+/* waits for the tx sgdma to finish it's current operation, returns 0
+ * when it transitions to nonbusy, returns 1 if the operation times out
+ */
+static int sgdma_txbusy(struct altera_tse_private *priv)
+{
+ int delay = 0;
+
+ /* if DMA is busy, wait for current transactino to finish */
+ while ((csrrd32(priv->tx_dma_csr, sgdma_csroffs(status))
+ & SGDMA_STSREG_BUSY) && (delay++ < 100))
+ udelay(1);
+
+ if (csrrd32(priv->tx_dma_csr, sgdma_csroffs(status))
+ & SGDMA_STSREG_BUSY) {
+ netdev_err(priv->dev, "timeout waiting for tx dma\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/drivers/net/ethernet/altera/altera_sgdma.h b/drivers/net/ethernet/altera/altera_sgdma.h
new file mode 100644
index 00000000000..584977e29ef
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdma.h
@@ -0,0 +1,36 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_SGDMA_H__
+#define __ALTERA_SGDMA_H__
+
+void sgdma_reset(struct altera_tse_private *);
+void sgdma_enable_txirq(struct altera_tse_private *);
+void sgdma_enable_rxirq(struct altera_tse_private *);
+void sgdma_disable_rxirq(struct altera_tse_private *);
+void sgdma_disable_txirq(struct altera_tse_private *);
+void sgdma_clear_rxirq(struct altera_tse_private *);
+void sgdma_clear_txirq(struct altera_tse_private *);
+int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *);
+u32 sgdma_tx_completions(struct altera_tse_private *);
+void sgdma_add_rx_desc(struct altera_tse_private *priv, struct tse_buffer *);
+void sgdma_status(struct altera_tse_private *);
+u32 sgdma_rx_status(struct altera_tse_private *);
+int sgdma_initialize(struct altera_tse_private *);
+void sgdma_uninitialize(struct altera_tse_private *);
+void sgdma_start_rxdma(struct altera_tse_private *);
+
+#endif /* __ALTERA_SGDMA_H__ */
diff --git a/drivers/net/ethernet/altera/altera_sgdmahw.h b/drivers/net/ethernet/altera/altera_sgdmahw.h
new file mode 100644
index 00000000000..85bc33b218d
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdmahw.h
@@ -0,0 +1,126 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_SGDMAHW_H__
+#define __ALTERA_SGDMAHW_H__
+
+/* SGDMA descriptor structure */
+struct sgdma_descrip {
+ u32 raddr; /* address of data to be read */
+ u32 pad1;
+ u32 waddr;
+ u32 pad2;
+ u32 next;
+ u32 pad3;
+ u16 bytes;
+ u8 rburst;
+ u8 wburst;
+ u16 bytes_xferred; /* 16 bits, bytes xferred */
+
+ /* bit 0: error
+ * bit 1: length error
+ * bit 2: crc error
+ * bit 3: truncated error
+ * bit 4: phy error
+ * bit 5: collision error
+ * bit 6: reserved
+ * bit 7: status eop for recv case
+ */
+ u8 status;
+
+ /* bit 0: eop
+ * bit 1: read_fixed
+ * bit 2: write fixed
+ * bits 3,4,5,6: Channel (always 0)
+ * bit 7: hardware owned
+ */
+ u8 control;
+} __packed;
+
+
+#define SGDMA_STATUS_ERR BIT(0)
+#define SGDMA_STATUS_LENGTH_ERR BIT(1)
+#define SGDMA_STATUS_CRC_ERR BIT(2)
+#define SGDMA_STATUS_TRUNC_ERR BIT(3)
+#define SGDMA_STATUS_PHY_ERR BIT(4)
+#define SGDMA_STATUS_COLL_ERR BIT(5)
+#define SGDMA_STATUS_EOP BIT(7)
+
+#define SGDMA_CONTROL_EOP BIT(0)
+#define SGDMA_CONTROL_RD_FIXED BIT(1)
+#define SGDMA_CONTROL_WR_FIXED BIT(2)
+
+/* Channel is always 0, so just zero initialize it */
+
+#define SGDMA_CONTROL_HW_OWNED BIT(7)
+
+/* SGDMA register space */
+struct sgdma_csr {
+ /* bit 0: error
+ * bit 1: eop
+ * bit 2: descriptor completed
+ * bit 3: chain completed
+ * bit 4: busy
+ * remainder reserved
+ */
+ u32 status;
+ u32 pad1[3];
+
+ /* bit 0: interrupt on error
+ * bit 1: interrupt on eop
+ * bit 2: interrupt after every descriptor
+ * bit 3: interrupt after last descrip in a chain
+ * bit 4: global interrupt enable
+ * bit 5: starts descriptor processing
+ * bit 6: stop core on dma error
+ * bit 7: interrupt on max descriptors
+ * bits 8-15: max descriptors to generate interrupt
+ * bit 16: Software reset
+ * bit 17: clears owned by hardware if 0, does not clear otherwise
+ * bit 18: enables descriptor polling mode
+ * bit 19-26: clocks before polling again
+ * bit 27-30: reserved
+ * bit 31: clear interrupt
+ */
+ u32 control;
+ u32 pad2[3];
+ u32 next_descrip;
+ u32 pad3[3];
+};
+
+#define sgdma_csroffs(a) (offsetof(struct sgdma_csr, a))
+#define sgdma_descroffs(a) (offsetof(struct sgdma_descrip, a))
+
+#define SGDMA_STSREG_ERR BIT(0) /* Error */
+#define SGDMA_STSREG_EOP BIT(1) /* EOP */
+#define SGDMA_STSREG_DESCRIP BIT(2) /* Descriptor completed */
+#define SGDMA_STSREG_CHAIN BIT(3) /* Chain completed */
+#define SGDMA_STSREG_BUSY BIT(4) /* Controller busy */
+
+#define SGDMA_CTRLREG_IOE BIT(0) /* Interrupt on error */
+#define SGDMA_CTRLREG_IOEOP BIT(1) /* Interrupt on EOP */
+#define SGDMA_CTRLREG_IDESCRIP BIT(2) /* Interrupt after every descriptor */
+#define SGDMA_CTRLREG_ILASTD BIT(3) /* Interrupt after last descriptor */
+#define SGDMA_CTRLREG_INTEN BIT(4) /* Global Interrupt enable */
+#define SGDMA_CTRLREG_START BIT(5) /* starts descriptor processing */
+#define SGDMA_CTRLREG_STOPERR BIT(6) /* stop on dma error */
+#define SGDMA_CTRLREG_INTMAX BIT(7) /* Interrupt on max descriptors */
+#define SGDMA_CTRLREG_RESET BIT(16)/* Software reset */
+#define SGDMA_CTRLREG_COBHW BIT(17)/* Clears owned by hardware */
+#define SGDMA_CTRLREG_POLL BIT(18)/* enables descriptor polling mode */
+#define SGDMA_CTRLREG_CLRINT BIT(31)/* Clears interrupt */
+
+#endif /* __ALTERA_SGDMAHW_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
new file mode 100644
index 00000000000..2adb24d4523
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -0,0 +1,537 @@
+/* Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ * Matthew Gerlach
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_TSE_H__
+#define __ALTERA_TSE_H__
+
+#define ALTERA_TSE_RESOURCE_NAME "altera_tse"
+
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000
+#define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in
+ * bytes
+ */
+/* Rx FIFO default settings */
+#define ALTERA_TSE_RX_SECTION_EMPTY 16
+#define ALTERA_TSE_RX_SECTION_FULL 0
+#define ALTERA_TSE_RX_ALMOST_EMPTY 8
+#define ALTERA_TSE_RX_ALMOST_FULL 8
+
+/* Tx FIFO default settings */
+#define ALTERA_TSE_TX_SECTION_EMPTY 16
+#define ALTERA_TSE_TX_SECTION_FULL 0
+#define ALTERA_TSE_TX_ALMOST_EMPTY 8
+#define ALTERA_TSE_TX_ALMOST_FULL 3
+
+/* MAC function configuration default settings */
+#define ALTERA_TSE_TX_IPG_LENGTH 12
+
+#define ALTERA_TSE_PAUSE_QUANTA 0xffff
+
+#define GET_BIT_VALUE(v, bit) (((v) >> (bit)) & 0x1)
+
+/* MAC Command_Config Register Bit Definitions
+ */
+#define MAC_CMDCFG_TX_ENA BIT(0)
+#define MAC_CMDCFG_RX_ENA BIT(1)
+#define MAC_CMDCFG_XON_GEN BIT(2)
+#define MAC_CMDCFG_ETH_SPEED BIT(3)
+#define MAC_CMDCFG_PROMIS_EN BIT(4)
+#define MAC_CMDCFG_PAD_EN BIT(5)
+#define MAC_CMDCFG_CRC_FWD BIT(6)
+#define MAC_CMDCFG_PAUSE_FWD BIT(7)
+#define MAC_CMDCFG_PAUSE_IGNORE BIT(8)
+#define MAC_CMDCFG_TX_ADDR_INS BIT(9)
+#define MAC_CMDCFG_HD_ENA BIT(10)
+#define MAC_CMDCFG_EXCESS_COL BIT(11)
+#define MAC_CMDCFG_LATE_COL BIT(12)
+#define MAC_CMDCFG_SW_RESET BIT(13)
+#define MAC_CMDCFG_MHASH_SEL BIT(14)
+#define MAC_CMDCFG_LOOP_ENA BIT(15)
+#define MAC_CMDCFG_TX_ADDR_SEL(v) (((v) & 0x7) << 16)
+#define MAC_CMDCFG_MAGIC_ENA BIT(19)
+#define MAC_CMDCFG_SLEEP BIT(20)
+#define MAC_CMDCFG_WAKEUP BIT(21)
+#define MAC_CMDCFG_XOFF_GEN BIT(22)
+#define MAC_CMDCFG_CNTL_FRM_ENA BIT(23)
+#define MAC_CMDCFG_NO_LGTH_CHECK BIT(24)
+#define MAC_CMDCFG_ENA_10 BIT(25)
+#define MAC_CMDCFG_RX_ERR_DISC BIT(26)
+#define MAC_CMDCFG_DISABLE_READ_TIMEOUT BIT(27)
+#define MAC_CMDCFG_CNT_RESET BIT(31)
+
+#define MAC_CMDCFG_TX_ENA_GET(v) GET_BIT_VALUE(v, 0)
+#define MAC_CMDCFG_RX_ENA_GET(v) GET_BIT_VALUE(v, 1)
+#define MAC_CMDCFG_XON_GEN_GET(v) GET_BIT_VALUE(v, 2)
+#define MAC_CMDCFG_ETH_SPEED_GET(v) GET_BIT_VALUE(v, 3)
+#define MAC_CMDCFG_PROMIS_EN_GET(v) GET_BIT_VALUE(v, 4)
+#define MAC_CMDCFG_PAD_EN_GET(v) GET_BIT_VALUE(v, 5)
+#define MAC_CMDCFG_CRC_FWD_GET(v) GET_BIT_VALUE(v, 6)
+#define MAC_CMDCFG_PAUSE_FWD_GET(v) GET_BIT_VALUE(v, 7)
+#define MAC_CMDCFG_PAUSE_IGNORE_GET(v) GET_BIT_VALUE(v, 8)
+#define MAC_CMDCFG_TX_ADDR_INS_GET(v) GET_BIT_VALUE(v, 9)
+#define MAC_CMDCFG_HD_ENA_GET(v) GET_BIT_VALUE(v, 10)
+#define MAC_CMDCFG_EXCESS_COL_GET(v) GET_BIT_VALUE(v, 11)
+#define MAC_CMDCFG_LATE_COL_GET(v) GET_BIT_VALUE(v, 12)
+#define MAC_CMDCFG_SW_RESET_GET(v) GET_BIT_VALUE(v, 13)
+#define MAC_CMDCFG_MHASH_SEL_GET(v) GET_BIT_VALUE(v, 14)
+#define MAC_CMDCFG_LOOP_ENA_GET(v) GET_BIT_VALUE(v, 15)
+#define MAC_CMDCFG_TX_ADDR_SEL_GET(v) (((v) >> 16) & 0x7)
+#define MAC_CMDCFG_MAGIC_ENA_GET(v) GET_BIT_VALUE(v, 19)
+#define MAC_CMDCFG_SLEEP_GET(v) GET_BIT_VALUE(v, 20)
+#define MAC_CMDCFG_WAKEUP_GET(v) GET_BIT_VALUE(v, 21)
+#define MAC_CMDCFG_XOFF_GEN_GET(v) GET_BIT_VALUE(v, 22)
+#define MAC_CMDCFG_CNTL_FRM_ENA_GET(v) GET_BIT_VALUE(v, 23)
+#define MAC_CMDCFG_NO_LGTH_CHECK_GET(v) GET_BIT_VALUE(v, 24)
+#define MAC_CMDCFG_ENA_10_GET(v) GET_BIT_VALUE(v, 25)
+#define MAC_CMDCFG_RX_ERR_DISC_GET(v) GET_BIT_VALUE(v, 26)
+#define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27)
+#define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31)
+
+/* MDIO registers within MAC register Space
+ */
+struct altera_tse_mdio {
+ u32 control; /* PHY device operation control register */
+ u32 status; /* PHY device operation status register */
+ u32 phy_id1; /* Bits 31:16 of PHY identifier */
+ u32 phy_id2; /* Bits 15:0 of PHY identifier */
+ u32 auto_negotiation_advertisement; /* Auto-negotiation
+ * advertisement
+ * register
+ */
+ u32 remote_partner_base_page_ability;
+
+ u32 reg6;
+ u32 reg7;
+ u32 reg8;
+ u32 reg9;
+ u32 rega;
+ u32 regb;
+ u32 regc;
+ u32 regd;
+ u32 rege;
+ u32 regf;
+ u32 reg10;
+ u32 reg11;
+ u32 reg12;
+ u32 reg13;
+ u32 reg14;
+ u32 reg15;
+ u32 reg16;
+ u32 reg17;
+ u32 reg18;
+ u32 reg19;
+ u32 reg1a;
+ u32 reg1b;
+ u32 reg1c;
+ u32 reg1d;
+ u32 reg1e;
+ u32 reg1f;
+};
+
+/* MAC register Space. Note that some of these registers may or may not be
+ * present depending upon options chosen by the user when the core was
+ * configured and built. Please consult the Altera Triple Speed Ethernet User
+ * Guide for details.
+ */
+struct altera_tse_mac {
+ /* Bits 15:0: MegaCore function revision (0x0800). Bit 31:16: Customer
+ * specific revision
+ */
+ u32 megacore_revision;
+ /* Provides a memory location for user applications to test the device
+ * memory operation.
+ */
+ u32 scratch_pad;
+ /* The host processor uses this register to control and configure the
+ * MAC block
+ */
+ u32 command_config;
+ /* 32-bit primary MAC address word 0 bits 0 to 31 of the primary
+ * MAC address
+ */
+ u32 mac_addr_0;
+ /* 32-bit primary MAC address word 1 bits 32 to 47 of the primary
+ * MAC address
+ */
+ u32 mac_addr_1;
+ /* 14-bit maximum frame length. The MAC receive logic */
+ u32 frm_length;
+ /* The pause quanta is used in each pause frame sent to a remote
+ * Ethernet device, in increments of 512 Ethernet bit times
+ */
+ u32 pause_quanta;
+ /* 12-bit receive FIFO section-empty threshold */
+ u32 rx_section_empty;
+ /* 12-bit receive FIFO section-full threshold */
+ u32 rx_section_full;
+ /* 12-bit transmit FIFO section-empty threshold */
+ u32 tx_section_empty;
+ /* 12-bit transmit FIFO section-full threshold */
+ u32 tx_section_full;
+ /* 12-bit receive FIFO almost-empty threshold */
+ u32 rx_almost_empty;
+ /* 12-bit receive FIFO almost-full threshold */
+ u32 rx_almost_full;
+ /* 12-bit transmit FIFO almost-empty threshold */
+ u32 tx_almost_empty;
+ /* 12-bit transmit FIFO almost-full threshold */
+ u32 tx_almost_full;
+ /* MDIO address of PHY Device 0. Bits 0 to 4 hold a 5-bit PHY address */
+ u32 mdio_phy0_addr;
+ /* MDIO address of PHY Device 1. Bits 0 to 4 hold a 5-bit PHY address */
+ u32 mdio_phy1_addr;
+
+ /* Bit[15:0]—16-bit holdoff quanta */
+ u32 holdoff_quant;
+
+ /* only if 100/1000 BaseX PCS, reserved otherwise */
+ u32 reserved1[5];
+
+ /* Minimum IPG between consecutive transmit frame in terms of bytes */
+ u32 tx_ipg_length;
+
+ /* IEEE 802.3 oEntity Managed Object Support */
+
+ /* The MAC addresses */
+ u32 mac_id_1;
+ u32 mac_id_2;
+
+ /* Number of frames transmitted without error including pause frames */
+ u32 frames_transmitted_ok;
+ /* Number of frames received without error including pause frames */
+ u32 frames_received_ok;
+ /* Number of frames received with a CRC error */
+ u32 frames_check_sequence_errors;
+ /* Frame received with an alignment error */
+ u32 alignment_errors;
+ /* Sum of payload and padding octets of frames transmitted without
+ * error
+ */
+ u32 octets_transmitted_ok;
+ /* Sum of payload and padding octets of frames received without error */
+ u32 octets_received_ok;
+
+ /* IEEE 802.3 oPausedEntity Managed Object Support */
+
+ /* Number of transmitted pause frames */
+ u32 tx_pause_mac_ctrl_frames;
+ /* Number of Received pause frames */
+ u32 rx_pause_mac_ctrl_frames;
+
+ /* IETF MIB (MIB-II) Object Support */
+
+ /* Number of frames received with error */
+ u32 if_in_errors;
+ /* Number of frames transmitted with error */
+ u32 if_out_errors;
+ /* Number of valid received unicast frames */
+ u32 if_in_ucast_pkts;
+ /* Number of valid received multicasts frames (without pause) */
+ u32 if_in_multicast_pkts;
+ /* Number of valid received broadcast frames */
+ u32 if_in_broadcast_pkts;
+ u32 if_out_discards;
+ /* The number of valid unicast frames transmitted */
+ u32 if_out_ucast_pkts;
+ /* The number of valid multicast frames transmitted,
+ * excluding pause frames
+ */
+ u32 if_out_multicast_pkts;
+ u32 if_out_broadcast_pkts;
+
+ /* IETF RMON MIB Object Support */
+
+ /* Counts the number of dropped packets due to internal errors
+ * of the MAC client.
+ */
+ u32 ether_stats_drop_events;
+ /* Total number of bytes received. Good and bad frames. */
+ u32 ether_stats_octets;
+ /* Total number of packets received. Counts good and bad packets. */
+ u32 ether_stats_pkts;
+ /* Number of packets received with less than 64 bytes. */
+ u32 ether_stats_undersize_pkts;
+ /* The number of frames received that are longer than the
+ * value configured in the frm_length register
+ */
+ u32 ether_stats_oversize_pkts;
+ /* Number of received packet with 64 bytes */
+ u32 ether_stats_pkts_64_octets;
+ /* Frames (good and bad) with 65 to 127 bytes */
+ u32 ether_stats_pkts_65to127_octets;
+ /* Frames (good and bad) with 128 to 255 bytes */
+ u32 ether_stats_pkts_128to255_octets;
+ /* Frames (good and bad) with 256 to 511 bytes */
+ u32 ether_stats_pkts_256to511_octets;
+ /* Frames (good and bad) with 512 to 1023 bytes */
+ u32 ether_stats_pkts_512to1023_octets;
+ /* Frames (good and bad) with 1024 to 1518 bytes */
+ u32 ether_stats_pkts_1024to1518_octets;
+
+ /* Any frame length from 1519 to the maximum length configured in the
+ * frm_length register, if it is greater than 1518
+ */
+ u32 ether_stats_pkts_1519tox_octets;
+ /* Too long frames with CRC error */
+ u32 ether_stats_jabbers;
+ /* Too short frames with CRC error */
+ u32 ether_stats_fragments;
+
+ u32 reserved2;
+
+ /* FIFO control register */
+ u32 tx_cmd_stat;
+ u32 rx_cmd_stat;
+
+ /* Extended Statistics Counters */
+ u32 msb_octets_transmitted_ok;
+ u32 msb_octets_received_ok;
+ u32 msb_ether_stats_octets;
+
+ u32 reserved3;
+
+ /* Multicast address resolution table, mapped in the controller address
+ * space
+ */
+ u32 hash_table[64];
+
+ /* Registers 0 to 31 within PHY device 0/1 connected to the MDIO PHY
+ * management interface
+ */
+ struct altera_tse_mdio mdio_phy0;
+ struct altera_tse_mdio mdio_phy1;
+
+ /* 4 Supplemental MAC Addresses */
+ u32 supp_mac_addr_0_0;
+ u32 supp_mac_addr_0_1;
+ u32 supp_mac_addr_1_0;
+ u32 supp_mac_addr_1_1;
+ u32 supp_mac_addr_2_0;
+ u32 supp_mac_addr_2_1;
+ u32 supp_mac_addr_3_0;
+ u32 supp_mac_addr_3_1;
+
+ u32 reserved4[8];
+
+ /* IEEE 1588v2 Feature */
+ u32 tx_period;
+ u32 tx_adjust_fns;
+ u32 tx_adjust_ns;
+ u32 rx_period;
+ u32 rx_adjust_fns;
+ u32 rx_adjust_ns;
+
+ u32 reserved5[42];
+};
+
+#define tse_csroffs(a) (offsetof(struct altera_tse_mac, a))
+
+/* Transmit and Receive Command Registers Bit Definitions
+ */
+#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC BIT(17)
+#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 BIT(18)
+#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 BIT(25)
+
+/* Wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer
+ */
+struct tse_buffer {
+ struct list_head lh;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ u32 len;
+ int mapped_as_page;
+};
+
+struct altera_tse_private;
+
+#define ALTERA_DTYPE_SGDMA 1
+#define ALTERA_DTYPE_MSGDMA 2
+
+/* standard DMA interface for SGDMA and MSGDMA */
+struct altera_dmaops {
+ int altera_dtype;
+ int dmamask;
+ void (*reset_dma)(struct altera_tse_private *);
+ void (*enable_txirq)(struct altera_tse_private *);
+ void (*enable_rxirq)(struct altera_tse_private *);
+ void (*disable_txirq)(struct altera_tse_private *);
+ void (*disable_rxirq)(struct altera_tse_private *);
+ void (*clear_txirq)(struct altera_tse_private *);
+ void (*clear_rxirq)(struct altera_tse_private *);
+ int (*tx_buffer)(struct altera_tse_private *, struct tse_buffer *);
+ u32 (*tx_completions)(struct altera_tse_private *);
+ void (*add_rx_desc)(struct altera_tse_private *, struct tse_buffer *);
+ u32 (*get_rx_status)(struct altera_tse_private *);
+ int (*init_dma)(struct altera_tse_private *);
+ void (*uninit_dma)(struct altera_tse_private *);
+ void (*start_rxdma)(struct altera_tse_private *);
+};
+
+/* This structure is private to each device.
+ */
+struct altera_tse_private {
+ struct net_device *dev;
+ struct device *device;
+ struct napi_struct napi;
+
+ /* MAC address space */
+ struct altera_tse_mac __iomem *mac_dev;
+
+ /* TSE Revision */
+ u32 revision;
+
+ /* mSGDMA Rx Dispatcher address space */
+ void __iomem *rx_dma_csr;
+ void __iomem *rx_dma_desc;
+ void __iomem *rx_dma_resp;
+
+ /* mSGDMA Tx Dispatcher address space */
+ void __iomem *tx_dma_csr;
+ void __iomem *tx_dma_desc;
+
+ /* Rx buffers queue */
+ struct tse_buffer *rx_ring;
+ u32 rx_cons;
+ u32 rx_prod;
+ u32 rx_ring_size;
+ u32 rx_dma_buf_sz;
+
+ /* Tx ring buffer */
+ struct tse_buffer *tx_ring;
+ u32 tx_prod;
+ u32 tx_cons;
+ u32 tx_ring_size;
+
+ /* Interrupts */
+ u32 tx_irq;
+ u32 rx_irq;
+
+ /* RX/TX MAC FIFO configs */
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
+ u32 max_mtu;
+
+ /* Hash filter settings */
+ u32 hash_filter;
+ u32 added_unicast;
+
+ /* Descriptor memory info for managing SGDMA */
+ u32 txdescmem;
+ u32 rxdescmem;
+ dma_addr_t rxdescmem_busaddr;
+ dma_addr_t txdescmem_busaddr;
+ u32 txctrlreg;
+ u32 rxctrlreg;
+ dma_addr_t rxdescphys;
+ dma_addr_t txdescphys;
+ size_t sgdmadesclen;
+
+ struct list_head txlisthd;
+ struct list_head rxlisthd;
+
+ /* MAC command_config register protection */
+ spinlock_t mac_cfg_lock;
+ /* Tx path protection */
+ spinlock_t tx_lock;
+ /* Rx DMA & interrupt control protection */
+ spinlock_t rxdma_irq_lock;
+
+ /* PHY */
+ int phy_addr; /* PHY's MDIO address, -1 for autodetection */
+ phy_interface_t phy_iface;
+ struct mii_bus *mdio;
+ struct phy_device *phydev;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
+
+ /* ethtool msglvl option */
+ u32 msg_enable;
+
+ struct altera_dmaops *dmaops;
+};
+
+/* Function prototypes
+ */
+void altera_tse_set_ethtool_ops(struct net_device *);
+
+static inline
+u32 csrrd32(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+ return readl(paddr);
+}
+
+static inline
+u16 csrrd16(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+ return readw(paddr);
+}
+
+static inline
+u8 csrrd8(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+ return readb(paddr);
+}
+
+static inline
+void csrwr32(u32 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writel(val, paddr);
+}
+
+static inline
+void csrwr16(u16 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writew(val, paddr);
+}
+
+static inline
+void csrwr8(u8 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writeb(val, paddr);
+}
+
+#endif /* __ALTERA_TSE_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c
new file mode 100644
index 00000000000..be72e1e6452
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c
@@ -0,0 +1,275 @@
+/* Ethtool support for Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "altera_tse.h"
+
+#define TSE_STATS_LEN 31
+#define TSE_NUM_REGS 128
+
+static char const stat_gstrings[][ETH_GSTRING_LEN] = {
+ "tx_packets",
+ "rx_packets",
+ "rx_crc_errors",
+ "rx_align_errors",
+ "tx_bytes",
+ "rx_bytes",
+ "tx_pause",
+ "rx_pause",
+ "rx_errors",
+ "tx_errors",
+ "rx_unicast",
+ "rx_multicast",
+ "rx_broadcast",
+ "tx_discards",
+ "tx_unicast",
+ "tx_multicast",
+ "tx_broadcast",
+ "ether_drops",
+ "rx_total_bytes",
+ "rx_total_packets",
+ "rx_undersize",
+ "rx_oversize",
+ "rx_64_bytes",
+ "rx_65_127_bytes",
+ "rx_128_255_bytes",
+ "rx_256_511_bytes",
+ "rx_512_1023_bytes",
+ "rx_1024_1518_bytes",
+ "rx_gte_1519_bytes",
+ "rx_jabbers",
+ "rx_runts",
+};
+
+static void tse_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ u32 rev = ioread32(&priv->mac_dev->megacore_revision);
+
+ strcpy(info->driver, "altera_tse");
+ strcpy(info->version, "v8.0");
+ snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "v%d.%d",
+ rev & 0xFFFF, (rev & 0xFFFF0000) >> 16);
+ sprintf(info->bus_info, "platform");
+}
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats
+ */
+static void tse_gstrings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+ memcpy(buf, stat_gstrings, TSE_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+static void tse_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+ u64 *buf)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ u64 ext;
+
+ buf[0] = csrrd32(priv->mac_dev,
+ tse_csroffs(frames_transmitted_ok));
+ buf[1] = csrrd32(priv->mac_dev,
+ tse_csroffs(frames_received_ok));
+ buf[2] = csrrd32(priv->mac_dev,
+ tse_csroffs(frames_check_sequence_errors));
+ buf[3] = csrrd32(priv->mac_dev,
+ tse_csroffs(alignment_errors));
+
+ /* Extended aOctetsTransmittedOK counter */
+ ext = (u64) csrrd32(priv->mac_dev,
+ tse_csroffs(msb_octets_transmitted_ok)) << 32;
+
+ ext |= csrrd32(priv->mac_dev,
+ tse_csroffs(octets_transmitted_ok));
+ buf[4] = ext;
+
+ /* Extended aOctetsReceivedOK counter */
+ ext = (u64) csrrd32(priv->mac_dev,
+ tse_csroffs(msb_octets_received_ok)) << 32;
+
+ ext |= csrrd32(priv->mac_dev,
+ tse_csroffs(octets_received_ok));
+ buf[5] = ext;
+
+ buf[6] = csrrd32(priv->mac_dev,
+ tse_csroffs(tx_pause_mac_ctrl_frames));
+ buf[7] = csrrd32(priv->mac_dev,
+ tse_csroffs(rx_pause_mac_ctrl_frames));
+ buf[8] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_in_errors));
+ buf[9] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_out_errors));
+ buf[10] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_in_ucast_pkts));
+ buf[11] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_in_multicast_pkts));
+ buf[12] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_in_broadcast_pkts));
+ buf[13] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_out_discards));
+ buf[14] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_out_ucast_pkts));
+ buf[15] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_out_multicast_pkts));
+ buf[16] = csrrd32(priv->mac_dev,
+ tse_csroffs(if_out_broadcast_pkts));
+ buf[17] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_drop_events));
+
+ /* Extended etherStatsOctets counter */
+ ext = (u64) csrrd32(priv->mac_dev,
+ tse_csroffs(msb_ether_stats_octets)) << 32;
+ ext |= csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_octets));
+ buf[18] = ext;
+
+ buf[19] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts));
+ buf[20] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_undersize_pkts));
+ buf[21] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_oversize_pkts));
+ buf[22] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_64_octets));
+ buf[23] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_65to127_octets));
+ buf[24] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_128to255_octets));
+ buf[25] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_256to511_octets));
+ buf[26] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_512to1023_octets));
+ buf[27] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_1024to1518_octets));
+ buf[28] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_pkts_1519tox_octets));
+ buf[29] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_jabbers));
+ buf[30] = csrrd32(priv->mac_dev,
+ tse_csroffs(ether_stats_fragments));
+}
+
+static int tse_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return TSE_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static u32 tse_get_msglevel(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ return priv->msg_enable;
+}
+
+static void tse_set_msglevel(struct net_device *dev, uint32_t data)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ priv->msg_enable = data;
+}
+
+static int tse_reglen(struct net_device *dev)
+{
+ return TSE_NUM_REGS * sizeof(u32);
+}
+
+static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *regbuf)
+{
+ int i;
+ struct altera_tse_private *priv = netdev_priv(dev);
+ u32 *buf = regbuf;
+
+ /* Set version to a known value, so ethtool knows
+ * how to do any special formatting of this data.
+ * This version number will need to change if and
+ * when this register table is changed.
+ *
+ * version[31:0] = 1: Dump the first 128 TSE Registers
+ * Upper bits are all 0 by default
+ *
+ * Upper 16-bits will indicate feature presence for
+ * Ethtool register decoding in future version.
+ */
+
+ regs->version = 1;
+
+ for (i = 0; i < TSE_NUM_REGS; i++)
+ buf[i] = csrrd32(priv->mac_dev, i * 4);
+}
+
+static int tse_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (phydev == NULL)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+static int tse_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (phydev == NULL)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+static const struct ethtool_ops tse_ethtool_ops = {
+ .get_drvinfo = tse_get_drvinfo,
+ .get_regs_len = tse_reglen,
+ .get_regs = tse_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_settings = tse_get_settings,
+ .set_settings = tse_set_settings,
+ .get_strings = tse_gstrings,
+ .get_sset_count = tse_sset_count,
+ .get_ethtool_stats = tse_fill_stats,
+ .get_msglevel = tse_get_msglevel,
+ .set_msglevel = tse_set_msglevel,
+};
+
+void altera_tse_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &tse_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
new file mode 100644
index 00000000000..7330681574d
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -0,0 +1,1577 @@
+/* Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ * Matthew Gerlach
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <asm/cacheflush.h>
+
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_sgdma.h"
+#include "altera_msgdma.h"
+
+static atomic_t instance_count = ATOMIC_INIT(~0);
+/* Module parameters */
+static int debug = -1;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
+
+static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_IFUP |
+ NETIF_MSG_IFDOWN);
+
+#define RX_DESCRIPTORS 64
+static int dma_rx_num = RX_DESCRIPTORS;
+module_param(dma_rx_num, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dma_rx_num, "Number of descriptors in the RX list");
+
+#define TX_DESCRIPTORS 64
+static int dma_tx_num = TX_DESCRIPTORS;
+module_param(dma_tx_num, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dma_tx_num, "Number of descriptors in the TX list");
+
+
+#define POLL_PHY (-1)
+
+/* Make sure DMA buffer size is larger than the max frame size
+ * plus some alignment offset and a VLAN header. If the max frame size is
+ * 1518, a VLAN header would be additional 4 bytes and additional
+ * headroom for alignment is 2 bytes, 2048 is just fine.
+ */
+#define ALTERA_RXDMABUFFER_SIZE 2048
+
+/* Allow network stack to resume queueing packets after we've
+ * finished transmitting at least 1/4 of the packets in the queue.
+ */
+#define TSE_TX_THRESH(x) (x->tx_ring_size / 4)
+
+#define TXQUEUESTOP_THRESHHOLD 2
+
+static struct of_device_id altera_tse_ids[];
+
+static inline u32 tse_tx_avail(struct altera_tse_private *priv)
+{
+ return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1;
+}
+
+/* MDIO specific functions
+ */
+static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct net_device *ndev = bus->priv;
+ struct altera_tse_private *priv = netdev_priv(ndev);
+
+ /* set MDIO address */
+ csrwr32((mii_id & 0x1f), priv->mac_dev,
+ tse_csroffs(mdio_phy0_addr));
+
+ /* get the data */
+ return csrrd32(priv->mac_dev,
+ tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff;
+}
+
+static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+ u16 value)
+{
+ struct net_device *ndev = bus->priv;
+ struct altera_tse_private *priv = netdev_priv(ndev);
+
+ /* set MDIO address */
+ csrwr32((mii_id & 0x1f), priv->mac_dev,
+ tse_csroffs(mdio_phy0_addr));
+
+ /* write the data */
+ csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4);
+ return 0;
+}
+
+static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int ret;
+ int i;
+ struct device_node *mdio_node = NULL;
+ struct mii_bus *mdio = NULL;
+ struct device_node *child_node = NULL;
+
+ for_each_child_of_node(priv->device->of_node, child_node) {
+ if (of_device_is_compatible(child_node, "altr,tse-mdio")) {
+ mdio_node = child_node;
+ break;
+ }
+ }
+
+ if (mdio_node) {
+ netdev_dbg(dev, "FOUND MDIO subnode\n");
+ } else {
+ netdev_dbg(dev, "NO MDIO subnode\n");
+ return 0;
+ }
+
+ mdio = mdiobus_alloc();
+ if (mdio == NULL) {
+ netdev_err(dev, "Error allocating MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ mdio->name = ALTERA_TSE_RESOURCE_NAME;
+ mdio->read = &altera_tse_mdio_read;
+ mdio->write = &altera_tse_mdio_write;
+ snprintf(mdio->id, MII_BUS_ID_SIZE, "%s-%u", mdio->name, id);
+
+ mdio->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
+ if (mdio->irq == NULL) {
+ ret = -ENOMEM;
+ goto out_free_mdio;
+ }
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ mdio->irq[i] = PHY_POLL;
+
+ mdio->priv = dev;
+ mdio->parent = priv->device;
+
+ ret = of_mdiobus_register(mdio, mdio_node);
+ if (ret != 0) {
+ netdev_err(dev, "Cannot register MDIO bus %s\n",
+ mdio->id);
+ goto out_free_mdio_irq;
+ }
+
+ if (netif_msg_drv(priv))
+ netdev_info(dev, "MDIO bus %s: created\n", mdio->id);
+
+ priv->mdio = mdio;
+ return 0;
+out_free_mdio_irq:
+ kfree(mdio->irq);
+out_free_mdio:
+ mdiobus_free(mdio);
+ mdio = NULL;
+ return ret;
+}
+
+static void altera_tse_mdio_destroy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ if (priv->mdio == NULL)
+ return;
+
+ if (netif_msg_drv(priv))
+ netdev_info(dev, "MDIO bus %s: removed\n",
+ priv->mdio->id);
+
+ mdiobus_unregister(priv->mdio);
+ kfree(priv->mdio->irq);
+ mdiobus_free(priv->mdio);
+ priv->mdio = NULL;
+}
+
+static int tse_init_rx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer, int len)
+{
+ rxbuffer->skb = netdev_alloc_skb_ip_align(priv->dev, len);
+ if (!rxbuffer->skb)
+ return -ENOMEM;
+
+ rxbuffer->dma_addr = dma_map_single(priv->device, rxbuffer->skb->data,
+ len,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(priv->device, rxbuffer->dma_addr)) {
+ netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
+ dev_kfree_skb_any(rxbuffer->skb);
+ return -EINVAL;
+ }
+ rxbuffer->dma_addr &= (dma_addr_t)~3;
+ rxbuffer->len = len;
+ return 0;
+}
+
+static void tse_free_rx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ struct sk_buff *skb = rxbuffer->skb;
+ dma_addr_t dma_addr = rxbuffer->dma_addr;
+
+ if (skb != NULL) {
+ if (dma_addr)
+ dma_unmap_single(priv->device, dma_addr,
+ rxbuffer->len,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ rxbuffer->skb = NULL;
+ rxbuffer->dma_addr = 0;
+ }
+}
+
+/* Unmap and free Tx buffer resources
+ */
+static void tse_free_tx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *buffer)
+{
+ if (buffer->dma_addr) {
+ if (buffer->mapped_as_page)
+ dma_unmap_page(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+ buffer->dma_addr = 0;
+ }
+ if (buffer->skb) {
+ dev_kfree_skb_any(buffer->skb);
+ buffer->skb = NULL;
+ }
+}
+
+static int alloc_init_skbufs(struct altera_tse_private *priv)
+{
+ unsigned int rx_descs = priv->rx_ring_size;
+ unsigned int tx_descs = priv->tx_ring_size;
+ int ret = -ENOMEM;
+ int i;
+
+ /* Create Rx ring buffer */
+ priv->rx_ring = kcalloc(rx_descs, sizeof(struct tse_buffer),
+ GFP_KERNEL);
+ if (!priv->rx_ring)
+ goto err_rx_ring;
+
+ /* Create Tx ring buffer */
+ priv->tx_ring = kcalloc(tx_descs, sizeof(struct tse_buffer),
+ GFP_KERNEL);
+ if (!priv->tx_ring)
+ goto err_tx_ring;
+
+ priv->tx_cons = 0;
+ priv->tx_prod = 0;
+
+ /* Init Rx ring */
+ for (i = 0; i < rx_descs; i++) {
+ ret = tse_init_rx_buffer(priv, &priv->rx_ring[i],
+ priv->rx_dma_buf_sz);
+ if (ret)
+ goto err_init_rx_buffers;
+ }
+
+ priv->rx_cons = 0;
+ priv->rx_prod = 0;
+
+ return 0;
+err_init_rx_buffers:
+ while (--i >= 0)
+ tse_free_rx_buffer(priv, &priv->rx_ring[i]);
+ kfree(priv->tx_ring);
+err_tx_ring:
+ kfree(priv->rx_ring);
+err_rx_ring:
+ return ret;
+}
+
+static void free_skbufs(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int rx_descs = priv->rx_ring_size;
+ unsigned int tx_descs = priv->tx_ring_size;
+ int i;
+
+ /* Release the DMA TX/RX socket buffers */
+ for (i = 0; i < rx_descs; i++)
+ tse_free_rx_buffer(priv, &priv->rx_ring[i]);
+ for (i = 0; i < tx_descs; i++)
+ tse_free_tx_buffer(priv, &priv->tx_ring[i]);
+
+
+ kfree(priv->tx_ring);
+}
+
+/* Reallocate the skb for the reception process
+ */
+static inline void tse_rx_refill(struct altera_tse_private *priv)
+{
+ unsigned int rxsize = priv->rx_ring_size;
+ unsigned int entry;
+ int ret;
+
+ for (; priv->rx_cons - priv->rx_prod > 0;
+ priv->rx_prod++) {
+ entry = priv->rx_prod % rxsize;
+ if (likely(priv->rx_ring[entry].skb == NULL)) {
+ ret = tse_init_rx_buffer(priv, &priv->rx_ring[entry],
+ priv->rx_dma_buf_sz);
+ if (unlikely(ret != 0))
+ break;
+ priv->dmaops->add_rx_desc(priv, &priv->rx_ring[entry]);
+ }
+ }
+}
+
+/* Pull out the VLAN tag and fix up the packet
+ */
+static inline void tse_rx_vlan(struct net_device *dev, struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr;
+ u16 vid;
+ if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ !__vlan_get_tag(skb, &vid)) {
+ eth_hdr = (struct ethhdr *)skb->data;
+ memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
+ skb_pull(skb, VLAN_HLEN);
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+ }
+}
+
+/* Receive a packet: retrieve and pass over to upper levels
+ */
+static int tse_rx(struct altera_tse_private *priv, int limit)
+{
+ unsigned int count = 0;
+ unsigned int next_entry;
+ struct sk_buff *skb;
+ unsigned int entry = priv->rx_cons % priv->rx_ring_size;
+ u32 rxstatus;
+ u16 pktlength;
+ u16 pktstatus;
+
+ while ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) {
+ pktstatus = rxstatus >> 16;
+ pktlength = rxstatus & 0xffff;
+
+ if ((pktstatus & 0xFF) || (pktlength == 0))
+ netdev_err(priv->dev,
+ "RCV pktstatus %08X pktlength %08X\n",
+ pktstatus, pktlength);
+
+ count++;
+ next_entry = (++priv->rx_cons) % priv->rx_ring_size;
+
+ skb = priv->rx_ring[entry].skb;
+ if (unlikely(!skb)) {
+ netdev_err(priv->dev,
+ "%s: Inconsistent Rx descriptor chain\n",
+ __func__);
+ priv->dev->stats.rx_dropped++;
+ break;
+ }
+ priv->rx_ring[entry].skb = NULL;
+
+ skb_put(skb, pktlength);
+
+ /* make cache consistent with receive packet buffer */
+ dma_sync_single_for_cpu(priv->device,
+ priv->rx_ring[entry].dma_addr,
+ priv->rx_ring[entry].len,
+ DMA_FROM_DEVICE);
+
+ dma_unmap_single(priv->device, priv->rx_ring[entry].dma_addr,
+ priv->rx_ring[entry].len, DMA_FROM_DEVICE);
+
+ if (netif_msg_pktdata(priv)) {
+ netdev_info(priv->dev, "frame received %d bytes\n",
+ pktlength);
+ print_hex_dump(KERN_ERR, "data: ", DUMP_PREFIX_OFFSET,
+ 16, 1, skb->data, pktlength, true);
+ }
+
+ tse_rx_vlan(priv->dev, skb);
+
+ skb->protocol = eth_type_trans(skb, priv->dev);
+ skb_checksum_none_assert(skb);
+
+ napi_gro_receive(&priv->napi, skb);
+
+ priv->dev->stats.rx_packets++;
+ priv->dev->stats.rx_bytes += pktlength;
+
+ entry = next_entry;
+
+ tse_rx_refill(priv);
+ }
+
+ return count;
+}
+
+/* Reclaim resources after transmission completes
+ */
+static int tse_tx_complete(struct altera_tse_private *priv)
+{
+ unsigned int txsize = priv->tx_ring_size;
+ u32 ready;
+ unsigned int entry;
+ struct tse_buffer *tx_buff;
+ int txcomplete = 0;
+
+ spin_lock(&priv->tx_lock);
+
+ ready = priv->dmaops->tx_completions(priv);
+
+ /* Free sent buffers */
+ while (ready && (priv->tx_cons != priv->tx_prod)) {
+ entry = priv->tx_cons % txsize;
+ tx_buff = &priv->tx_ring[entry];
+
+ if (netif_msg_tx_done(priv))
+ netdev_dbg(priv->dev, "%s: curr %d, dirty %d\n",
+ __func__, priv->tx_prod, priv->tx_cons);
+
+ if (likely(tx_buff->skb))
+ priv->dev->stats.tx_packets++;
+
+ tse_free_tx_buffer(priv, tx_buff);
+ priv->tx_cons++;
+
+ txcomplete++;
+ ready--;
+ }
+
+ if (unlikely(netif_queue_stopped(priv->dev) &&
+ tse_tx_avail(priv) > TSE_TX_THRESH(priv))) {
+ netif_tx_lock(priv->dev);
+ if (netif_queue_stopped(priv->dev) &&
+ tse_tx_avail(priv) > TSE_TX_THRESH(priv)) {
+ if (netif_msg_tx_done(priv))
+ netdev_dbg(priv->dev, "%s: restart transmit\n",
+ __func__);
+ netif_wake_queue(priv->dev);
+ }
+ netif_tx_unlock(priv->dev);
+ }
+
+ spin_unlock(&priv->tx_lock);
+ return txcomplete;
+}
+
+/* NAPI polling function
+ */
+static int tse_poll(struct napi_struct *napi, int budget)
+{
+ struct altera_tse_private *priv =
+ container_of(napi, struct altera_tse_private, napi);
+ int rxcomplete = 0;
+ int txcomplete = 0;
+ unsigned long int flags;
+
+ txcomplete = tse_tx_complete(priv);
+
+ rxcomplete = tse_rx(priv, budget);
+
+ if (rxcomplete >= budget || txcomplete > 0)
+ return rxcomplete;
+
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+
+ netdev_dbg(priv->dev,
+ "NAPI Complete, did %d packets with budget %d\n",
+ txcomplete+rxcomplete, budget);
+
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->enable_rxirq(priv);
+ priv->dmaops->enable_txirq(priv);
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+ return rxcomplete + txcomplete;
+}
+
+/* DMA TX & RX FIFO interrupt routing
+ */
+static irqreturn_t altera_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct altera_tse_private *priv;
+ unsigned long int flags;
+
+ if (unlikely(!dev)) {
+ pr_err("%s: invalid dev pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ priv = netdev_priv(dev);
+
+ /* turn off desc irqs and enable napi rx */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+
+ if (likely(napi_schedule_prep(&priv->napi))) {
+ priv->dmaops->disable_rxirq(priv);
+ priv->dmaops->disable_txirq(priv);
+ __napi_schedule(&priv->napi);
+ }
+
+ /* reset IRQs */
+ priv->dmaops->clear_rxirq(priv);
+ priv->dmaops->clear_txirq(priv);
+
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/* Transmit a packet (called by the kernel). Dispatches
+ * either the SGDMA method for transmitting or the
+ * MSGDMA method, assumes no scatter/gather support,
+ * implying an assumption that there's only one
+ * physically contiguous fragment starting at
+ * skb->data, for length of skb_headlen(skb).
+ */
+static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int txsize = priv->tx_ring_size;
+ unsigned int entry;
+ struct tse_buffer *buffer = NULL;
+ int nfrags = skb_shinfo(skb)->nr_frags;
+ unsigned int nopaged_len = skb_headlen(skb);
+ enum netdev_tx ret = NETDEV_TX_OK;
+ dma_addr_t dma_addr;
+
+ spin_lock_bh(&priv->tx_lock);
+
+ if (unlikely(tse_tx_avail(priv) < nfrags + 1)) {
+ if (!netif_queue_stopped(dev)) {
+ netif_stop_queue(dev);
+ /* This is a hard error, log it. */
+ netdev_err(priv->dev,
+ "%s: Tx list full when queue awake\n",
+ __func__);
+ }
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ /* Map the first skb fragment */
+ entry = priv->tx_prod % txsize;
+ buffer = &priv->tx_ring[entry];
+
+ dma_addr = dma_map_single(priv->device, skb->data, nopaged_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, dma_addr)) {
+ netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+
+ buffer->skb = skb;
+ buffer->dma_addr = dma_addr;
+ buffer->len = nopaged_len;
+
+ /* Push data out of the cache hierarchy into main memory */
+ dma_sync_single_for_device(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+
+ priv->dmaops->tx_buffer(priv, buffer);
+
+ skb_tx_timestamp(skb);
+
+ priv->tx_prod++;
+ dev->stats.tx_bytes += skb->len;
+
+ if (unlikely(tse_tx_avail(priv) <= TXQUEUESTOP_THRESHHOLD)) {
+ if (netif_msg_hw(priv))
+ netdev_dbg(priv->dev, "%s: stop transmitted packets\n",
+ __func__);
+ netif_stop_queue(dev);
+ }
+
+out:
+ spin_unlock_bh(&priv->tx_lock);
+
+ return ret;
+}
+
+/* Called every time the controller might need to be made
+ * aware of new link state. The PHY code conveys this
+ * information through variables in the phydev structure, and this
+ * function converts those variables into the appropriate
+ * register values, and can bring down the device if needed.
+ */
+static void altera_tse_adjust_link(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ int new_state = 0;
+
+ /* only change config if there is a link */
+ spin_lock(&priv->mac_cfg_lock);
+ if (phydev->link) {
+ /* Read old config */
+ u32 cfg_reg = ioread32(&priv->mac_dev->command_config);
+
+ /* Check duplex */
+ if (phydev->duplex != priv->oldduplex) {
+ new_state = 1;
+ if (!(phydev->duplex))
+ cfg_reg |= MAC_CMDCFG_HD_ENA;
+ else
+ cfg_reg &= ~MAC_CMDCFG_HD_ENA;
+
+ netdev_dbg(priv->dev, "%s: Link duplex = 0x%x\n",
+ dev->name, phydev->duplex);
+
+ priv->oldduplex = phydev->duplex;
+ }
+
+ /* Check speed */
+ if (phydev->speed != priv->oldspeed) {
+ new_state = 1;
+ switch (phydev->speed) {
+ case 1000:
+ cfg_reg |= MAC_CMDCFG_ETH_SPEED;
+ cfg_reg &= ~MAC_CMDCFG_ENA_10;
+ break;
+ case 100:
+ cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
+ cfg_reg &= ~MAC_CMDCFG_ENA_10;
+ break;
+ case 10:
+ cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
+ cfg_reg |= MAC_CMDCFG_ENA_10;
+ break;
+ default:
+ if (netif_msg_link(priv))
+ netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n",
+ phydev->speed);
+ break;
+ }
+ priv->oldspeed = phydev->speed;
+ }
+ iowrite32(cfg_reg, &priv->mac_dev->command_config);
+
+ if (!priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 1;
+ }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+ }
+
+ if (new_state && netif_msg_link(priv))
+ phy_print_status(phydev);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+static struct phy_device *connect_local_phy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ char phy_id_fmt[MII_BUS_ID_SIZE + 3];
+
+ if (priv->phy_addr != POLL_PHY) {
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
+ priv->mdio->id, priv->phy_addr);
+
+ netdev_dbg(dev, "trying to attach to %s\n", phy_id_fmt);
+
+ phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link,
+ priv->phy_iface);
+ if (IS_ERR(phydev))
+ netdev_err(dev, "Could not attach to PHY\n");
+
+ } else {
+ int ret;
+ phydev = phy_find_first(priv->mdio);
+ if (phydev == NULL) {
+ netdev_err(dev, "No PHY found\n");
+ return phydev;
+ }
+
+ ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link,
+ priv->phy_iface);
+ if (ret != 0) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ phydev = NULL;
+ }
+ }
+ return phydev;
+}
+
+/* Initialize driver's PHY state, and attach to the PHY
+ */
+static int init_phy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev;
+ struct device_node *phynode;
+
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+
+ phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0);
+
+ if (!phynode) {
+ netdev_dbg(dev, "no phy-handle found\n");
+ if (!priv->mdio) {
+ netdev_err(dev,
+ "No phy-handle nor local mdio specified\n");
+ return -ENODEV;
+ }
+ phydev = connect_local_phy(dev);
+ } else {
+ netdev_dbg(dev, "phy-handle found\n");
+ phydev = of_phy_connect(dev, phynode,
+ &altera_tse_adjust_link, 0, priv->phy_iface);
+ }
+
+ if (!phydev) {
+ netdev_err(dev, "Could not find the PHY\n");
+ return -ENODEV;
+ }
+
+ /* Stop Advertising 1000BASE Capability if interface is not GMII
+ * Note: Checkpatch throws CHECKs for the camel case defines below,
+ * it's ok to ignore.
+ */
+ if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) ||
+ (priv->phy_iface == PHY_INTERFACE_MODE_RMII))
+ phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+
+ /* Broken HW is sometimes missing the pull-up resistor on the
+ * MDIO line, which results in reads to non-existent devices returning
+ * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
+ * device as well.
+ * Note: phydev->phy_id is the result of reading the UID PHY registers.
+ */
+ if (phydev->phy_id == 0) {
+ netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id);
+ phy_disconnect(phydev);
+ return -ENODEV;
+ }
+
+ netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n",
+ phydev->addr, phydev->phy_id, phydev->link);
+
+ priv->phydev = phydev;
+ return 0;
+}
+
+static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr)
+{
+ u32 msb;
+ u32 lsb;
+
+ msb = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+ lsb = ((addr[5] << 8) | addr[4]) & 0xffff;
+
+ /* Set primary MAC address */
+ csrwr32(msb, priv->mac_dev, tse_csroffs(mac_addr_0));
+ csrwr32(lsb, priv->mac_dev, tse_csroffs(mac_addr_1));
+}
+
+/* MAC software reset.
+ * When reset is triggered, the MAC function completes the current
+ * transmission or reception, and subsequently disables the transmit and
+ * receive logic, flushes the receive FIFO buffer, and resets the statistics
+ * counters.
+ */
+static int reset_mac(struct altera_tse_private *priv)
+{
+ int counter;
+ u32 dat;
+
+ dat = csrrd32(priv->mac_dev, tse_csroffs(command_config));
+ dat &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA);
+ dat |= MAC_CMDCFG_SW_RESET | MAC_CMDCFG_CNT_RESET;
+ csrwr32(dat, priv->mac_dev, tse_csroffs(command_config));
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(priv->mac_dev, tse_csroffs(command_config),
+ MAC_CMDCFG_SW_RESET))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ dat = csrrd32(priv->mac_dev, tse_csroffs(command_config));
+ dat &= ~MAC_CMDCFG_SW_RESET;
+ csrwr32(dat, priv->mac_dev, tse_csroffs(command_config));
+ return -1;
+ }
+ return 0;
+}
+
+/* Initialize MAC core registers
+*/
+static int init_mac(struct altera_tse_private *priv)
+{
+ unsigned int cmd = 0;
+ u32 frm_length;
+
+ /* Setup Rx FIFO */
+ csrwr32(priv->rx_fifo_depth - ALTERA_TSE_RX_SECTION_EMPTY,
+ priv->mac_dev, tse_csroffs(rx_section_empty));
+
+ csrwr32(ALTERA_TSE_RX_SECTION_FULL, priv->mac_dev,
+ tse_csroffs(rx_section_full));
+
+ csrwr32(ALTERA_TSE_RX_ALMOST_EMPTY, priv->mac_dev,
+ tse_csroffs(rx_almost_empty));
+
+ csrwr32(ALTERA_TSE_RX_ALMOST_FULL, priv->mac_dev,
+ tse_csroffs(rx_almost_full));
+
+ /* Setup Tx FIFO */
+ csrwr32(priv->tx_fifo_depth - ALTERA_TSE_TX_SECTION_EMPTY,
+ priv->mac_dev, tse_csroffs(tx_section_empty));
+
+ csrwr32(ALTERA_TSE_TX_SECTION_FULL, priv->mac_dev,
+ tse_csroffs(tx_section_full));
+
+ csrwr32(ALTERA_TSE_TX_ALMOST_EMPTY, priv->mac_dev,
+ tse_csroffs(tx_almost_empty));
+
+ csrwr32(ALTERA_TSE_TX_ALMOST_FULL, priv->mac_dev,
+ tse_csroffs(tx_almost_full));
+
+ /* MAC Address Configuration */
+ tse_update_mac_addr(priv, priv->dev->dev_addr);
+
+ /* MAC Function Configuration */
+ frm_length = ETH_HLEN + priv->dev->mtu + ETH_FCS_LEN;
+ csrwr32(frm_length, priv->mac_dev, tse_csroffs(frm_length));
+
+ csrwr32(ALTERA_TSE_TX_IPG_LENGTH, priv->mac_dev,
+ tse_csroffs(tx_ipg_length));
+
+ /* Disable RX/TX shift 16 for alignment of all received frames on 16-bit
+ * start address
+ */
+ tse_set_bit(priv->mac_dev, tse_csroffs(rx_cmd_stat),
+ ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16);
+
+ tse_clear_bit(priv->mac_dev, tse_csroffs(tx_cmd_stat),
+ ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 |
+ ALTERA_TSE_TX_CMD_STAT_OMIT_CRC);
+
+ /* Set the MAC options */
+ cmd = csrrd32(priv->mac_dev, tse_csroffs(command_config));
+ cmd &= ~MAC_CMDCFG_PAD_EN; /* No padding Removal on Receive */
+ cmd &= ~MAC_CMDCFG_CRC_FWD; /* CRC Removal */
+ cmd |= MAC_CMDCFG_RX_ERR_DISC; /* Automatically discard frames
+ * with CRC errors
+ */
+ cmd |= MAC_CMDCFG_CNTL_FRM_ENA;
+ cmd &= ~MAC_CMDCFG_TX_ENA;
+ cmd &= ~MAC_CMDCFG_RX_ENA;
+
+ /* Default speed and duplex setting, full/100 */
+ cmd &= ~MAC_CMDCFG_HD_ENA;
+ cmd &= ~MAC_CMDCFG_ETH_SPEED;
+ cmd &= ~MAC_CMDCFG_ENA_10;
+
+ csrwr32(cmd, priv->mac_dev, tse_csroffs(command_config));
+
+ csrwr32(ALTERA_TSE_PAUSE_QUANTA, priv->mac_dev,
+ tse_csroffs(pause_quanta));
+
+ if (netif_msg_hw(priv))
+ dev_dbg(priv->device,
+ "MAC post-initialization: CMD_CONFIG = 0x%08x\n", cmd);
+
+ return 0;
+}
+
+/* Start/stop MAC transmission logic
+ */
+static void tse_set_mac(struct altera_tse_private *priv, bool enable)
+{
+ u32 value = csrrd32(priv->mac_dev, tse_csroffs(command_config));
+
+ if (enable)
+ value |= MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA;
+ else
+ value &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA);
+
+ csrwr32(value, priv->mac_dev, tse_csroffs(command_config));
+}
+
+/* Change the MTU
+ */
+static int tse_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int max_mtu = priv->max_mtu;
+ unsigned int min_mtu = ETH_ZLEN + ETH_FCS_LEN;
+
+ if (netif_running(dev)) {
+ netdev_err(dev, "must be stopped to change its MTU\n");
+ return -EBUSY;
+ }
+
+ if ((new_mtu < min_mtu) || (new_mtu > max_mtu)) {
+ netdev_err(dev, "invalid MTU, max MTU is: %u\n", max_mtu);
+ return -EINVAL;
+ }
+
+ dev->mtu = new_mtu;
+ netdev_update_features(dev);
+
+ return 0;
+}
+
+static void altera_tse_set_mcfilter(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int i;
+ struct netdev_hw_addr *ha;
+
+ /* clear the hash filter */
+ for (i = 0; i < 64; i++)
+ csrwr32(0, priv->mac_dev, tse_csroffs(hash_table) + i * 4);
+
+ netdev_for_each_mc_addr(ha, dev) {
+ unsigned int hash = 0;
+ int mac_octet;
+
+ for (mac_octet = 5; mac_octet >= 0; mac_octet--) {
+ unsigned char xor_bit = 0;
+ unsigned char octet = ha->addr[mac_octet];
+ unsigned int bitshift;
+
+ for (bitshift = 0; bitshift < 8; bitshift++)
+ xor_bit ^= ((octet >> bitshift) & 0x01);
+
+ hash = (hash << 1) | xor_bit;
+ }
+ csrwr32(1, priv->mac_dev, tse_csroffs(hash_table) + hash * 4);
+ }
+}
+
+
+static void altera_tse_set_mcfilterall(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int i;
+
+ /* set the hash filter */
+ for (i = 0; i < 64; i++)
+ csrwr32(1, priv->mac_dev, tse_csroffs(hash_table) + i * 4);
+}
+
+/* Set or clear the multicast filter for this adaptor
+ */
+static void tse_set_rx_mode_hashfilter(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ spin_lock(&priv->mac_cfg_lock);
+
+ if (dev->flags & IFF_PROMISC)
+ tse_set_bit(priv->mac_dev, tse_csroffs(command_config),
+ MAC_CMDCFG_PROMIS_EN);
+
+ if (dev->flags & IFF_ALLMULTI)
+ altera_tse_set_mcfilterall(dev);
+ else
+ altera_tse_set_mcfilter(dev);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+
+/* Set or clear the multicast filter for this adaptor
+ */
+static void tse_set_rx_mode(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ spin_lock(&priv->mac_cfg_lock);
+
+ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+ !netdev_mc_empty(dev) || !netdev_uc_empty(dev))
+ tse_set_bit(priv->mac_dev, tse_csroffs(command_config),
+ MAC_CMDCFG_PROMIS_EN);
+ else
+ tse_clear_bit(priv->mac_dev, tse_csroffs(command_config),
+ MAC_CMDCFG_PROMIS_EN);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+
+/* Open and initialize the interface
+ */
+static int tse_open(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int ret = 0;
+ int i;
+ unsigned long int flags;
+
+ /* Reset and configure TSE MAC and probe associated PHY */
+ ret = priv->dmaops->init_dma(priv);
+ if (ret != 0) {
+ netdev_err(dev, "Cannot initialize DMA\n");
+ goto phy_error;
+ }
+
+ if (netif_msg_ifup(priv))
+ netdev_warn(dev, "device MAC address %pM\n",
+ dev->dev_addr);
+
+ if ((priv->revision < 0xd00) || (priv->revision > 0xe00))
+ netdev_warn(dev, "TSE revision %x\n", priv->revision);
+
+ spin_lock(&priv->mac_cfg_lock);
+ ret = reset_mac(priv);
+ if (ret)
+ netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret);
+
+ ret = init_mac(priv);
+ spin_unlock(&priv->mac_cfg_lock);
+ if (ret) {
+ netdev_err(dev, "Cannot init MAC core (error: %d)\n", ret);
+ goto alloc_skbuf_error;
+ }
+
+ priv->dmaops->reset_dma(priv);
+
+ /* Create and initialize the TX/RX descriptors chains. */
+ priv->rx_ring_size = dma_rx_num;
+ priv->tx_ring_size = dma_tx_num;
+ ret = alloc_init_skbufs(priv);
+ if (ret) {
+ netdev_err(dev, "DMA descriptors initialization failed\n");
+ goto alloc_skbuf_error;
+ }
+
+
+ /* Register RX interrupt */
+ ret = request_irq(priv->rx_irq, altera_isr, IRQF_SHARED,
+ dev->name, dev);
+ if (ret) {
+ netdev_err(dev, "Unable to register RX interrupt %d\n",
+ priv->rx_irq);
+ goto init_error;
+ }
+
+ /* Register TX interrupt */
+ ret = request_irq(priv->tx_irq, altera_isr, IRQF_SHARED,
+ dev->name, dev);
+ if (ret) {
+ netdev_err(dev, "Unable to register TX interrupt %d\n",
+ priv->tx_irq);
+ goto tx_request_irq_error;
+ }
+
+ /* Enable DMA interrupts */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->enable_rxirq(priv);
+ priv->dmaops->enable_txirq(priv);
+
+ /* Setup RX descriptor chain */
+ for (i = 0; i < priv->rx_ring_size; i++)
+ priv->dmaops->add_rx_desc(priv, &priv->rx_ring[i]);
+
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ if (priv->phydev)
+ phy_start(priv->phydev);
+
+ napi_enable(&priv->napi);
+ netif_start_queue(dev);
+
+ priv->dmaops->start_rxdma(priv);
+
+ /* Start MAC Rx/Tx */
+ spin_lock(&priv->mac_cfg_lock);
+ tse_set_mac(priv, true);
+ spin_unlock(&priv->mac_cfg_lock);
+
+ return 0;
+
+tx_request_irq_error:
+ free_irq(priv->rx_irq, dev);
+init_error:
+ free_skbufs(dev);
+alloc_skbuf_error:
+ if (priv->phydev) {
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+ }
+phy_error:
+ return ret;
+}
+
+/* Stop TSE MAC interface and put the device in an inactive state
+ */
+static int tse_shutdown(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int ret;
+ unsigned long int flags;
+
+ /* Stop and disconnect the PHY */
+ if (priv->phydev) {
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+ }
+
+ netif_stop_queue(dev);
+ napi_disable(&priv->napi);
+
+ /* Disable DMA interrupts */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->disable_rxirq(priv);
+ priv->dmaops->disable_txirq(priv);
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ /* Free the IRQ lines */
+ free_irq(priv->rx_irq, dev);
+ free_irq(priv->tx_irq, dev);
+
+ /* disable and reset the MAC, empties fifo */
+ spin_lock(&priv->mac_cfg_lock);
+ spin_lock(&priv->tx_lock);
+
+ ret = reset_mac(priv);
+ if (ret)
+ netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret);
+ priv->dmaops->reset_dma(priv);
+ free_skbufs(dev);
+
+ spin_unlock(&priv->tx_lock);
+ spin_unlock(&priv->mac_cfg_lock);
+
+ priv->dmaops->uninit_dma(priv);
+
+ return 0;
+}
+
+static struct net_device_ops altera_tse_netdev_ops = {
+ .ndo_open = tse_open,
+ .ndo_stop = tse_shutdown,
+ .ndo_start_xmit = tse_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_rx_mode = tse_set_rx_mode,
+ .ndo_change_mtu = tse_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int request_and_map(struct platform_device *pdev, const char *name,
+ struct resource **res, void __iomem **ptr)
+{
+ struct resource *region;
+ struct device *device = &pdev->dev;
+
+ *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (*res == NULL) {
+ dev_err(device, "resource %s not defined\n", name);
+ return -ENODEV;
+ }
+
+ region = devm_request_mem_region(device, (*res)->start,
+ resource_size(*res), dev_name(device));
+ if (region == NULL) {
+ dev_err(device, "unable to request %s\n", name);
+ return -EBUSY;
+ }
+
+ *ptr = devm_ioremap_nocache(device, region->start,
+ resource_size(region));
+ if (*ptr == NULL) {
+ dev_err(device, "ioremap_nocache of %s failed!", name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Probe Altera TSE MAC device
+ */
+static int altera_tse_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ int ret = -ENODEV;
+ struct resource *control_port;
+ struct resource *dma_res;
+ struct altera_tse_private *priv;
+ const unsigned char *macaddr;
+ struct device_node *np = pdev->dev.of_node;
+ void __iomem *descmap;
+ const struct of_device_id *of_id = NULL;
+
+ ndev = alloc_etherdev(sizeof(struct altera_tse_private));
+ if (!ndev) {
+ dev_err(&pdev->dev, "Could not allocate network device\n");
+ return -ENODEV;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ priv = netdev_priv(ndev);
+ priv->device = &pdev->dev;
+ priv->dev = ndev;
+ priv->msg_enable = netif_msg_init(debug, default_msg_level);
+
+ of_id = of_match_device(altera_tse_ids, &pdev->dev);
+
+ if (of_id)
+ priv->dmaops = (struct altera_dmaops *)of_id->data;
+
+
+ if (priv->dmaops &&
+ priv->dmaops->altera_dtype == ALTERA_DTYPE_SGDMA) {
+ /* Get the mapped address to the SGDMA descriptor memory */
+ ret = request_and_map(pdev, "s1", &dma_res, &descmap);
+ if (ret)
+ goto err_free_netdev;
+
+ /* Start of that memory is for transmit descriptors */
+ priv->tx_dma_desc = descmap;
+
+ /* First half is for tx descriptors, other half for tx */
+ priv->txdescmem = resource_size(dma_res)/2;
+
+ priv->txdescmem_busaddr = (dma_addr_t)dma_res->start;
+
+ priv->rx_dma_desc = (void __iomem *)((uintptr_t)(descmap +
+ priv->txdescmem));
+ priv->rxdescmem = resource_size(dma_res)/2;
+ priv->rxdescmem_busaddr = dma_res->start;
+ priv->rxdescmem_busaddr += priv->txdescmem;
+
+ if (upper_32_bits(priv->rxdescmem_busaddr)) {
+ dev_dbg(priv->device,
+ "SGDMA bus addresses greater than 32-bits\n");
+ goto err_free_netdev;
+ }
+ if (upper_32_bits(priv->txdescmem_busaddr)) {
+ dev_dbg(priv->device,
+ "SGDMA bus addresses greater than 32-bits\n");
+ goto err_free_netdev;
+ }
+ } else if (priv->dmaops &&
+ priv->dmaops->altera_dtype == ALTERA_DTYPE_MSGDMA) {
+ ret = request_and_map(pdev, "rx_resp", &dma_res,
+ &priv->rx_dma_resp);
+ if (ret)
+ goto err_free_netdev;
+
+ ret = request_and_map(pdev, "tx_desc", &dma_res,
+ &priv->tx_dma_desc);
+ if (ret)
+ goto err_free_netdev;
+
+ priv->txdescmem = resource_size(dma_res);
+ priv->txdescmem_busaddr = dma_res->start;
+
+ ret = request_and_map(pdev, "rx_desc", &dma_res,
+ &priv->rx_dma_desc);
+ if (ret)
+ goto err_free_netdev;
+
+ priv->rxdescmem = resource_size(dma_res);
+ priv->rxdescmem_busaddr = dma_res->start;
+
+ } else {
+ goto err_free_netdev;
+ }
+
+ if (!dma_set_mask(priv->device, DMA_BIT_MASK(priv->dmaops->dmamask)))
+ dma_set_coherent_mask(priv->device,
+ DMA_BIT_MASK(priv->dmaops->dmamask));
+ else if (!dma_set_mask(priv->device, DMA_BIT_MASK(32)))
+ dma_set_coherent_mask(priv->device, DMA_BIT_MASK(32));
+ else
+ goto err_free_netdev;
+
+ /* MAC address space */
+ ret = request_and_map(pdev, "control_port", &control_port,
+ (void __iomem **)&priv->mac_dev);
+ if (ret)
+ goto err_free_netdev;
+
+ /* xSGDMA Rx Dispatcher address space */
+ ret = request_and_map(pdev, "rx_csr", &dma_res,
+ &priv->rx_dma_csr);
+ if (ret)
+ goto err_free_netdev;
+
+
+ /* xSGDMA Tx Dispatcher address space */
+ ret = request_and_map(pdev, "tx_csr", &dma_res,
+ &priv->tx_dma_csr);
+ if (ret)
+ goto err_free_netdev;
+
+
+ /* Rx IRQ */
+ priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq");
+ if (priv->rx_irq == -ENXIO) {
+ dev_err(&pdev->dev, "cannot obtain Rx IRQ\n");
+ ret = -ENXIO;
+ goto err_free_netdev;
+ }
+
+ /* Tx IRQ */
+ priv->tx_irq = platform_get_irq_byname(pdev, "tx_irq");
+ if (priv->tx_irq == -ENXIO) {
+ dev_err(&pdev->dev, "cannot obtain Tx IRQ\n");
+ ret = -ENXIO;
+ goto err_free_netdev;
+ }
+
+ /* get FIFO depths from device tree */
+ if (of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth",
+ &priv->rx_fifo_depth)) {
+ dev_err(&pdev->dev, "cannot obtain rx-fifo-depth\n");
+ ret = -ENXIO;
+ goto err_free_netdev;
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
+ &priv->rx_fifo_depth)) {
+ dev_err(&pdev->dev, "cannot obtain tx-fifo-depth\n");
+ ret = -ENXIO;
+ goto err_free_netdev;
+ }
+
+ /* get hash filter settings for this instance */
+ priv->hash_filter =
+ of_property_read_bool(pdev->dev.of_node,
+ "altr,has-hash-multicast-filter");
+
+ /* Set hash filter to not set for now until the
+ * multicast filter receive issue is debugged
+ */
+ priv->hash_filter = 0;
+
+ /* get supplemental address settings for this instance */
+ priv->added_unicast =
+ of_property_read_bool(pdev->dev.of_node,
+ "altr,has-supplementary-unicast");
+
+ /* Max MTU is 1500, ETH_DATA_LEN */
+ priv->max_mtu = ETH_DATA_LEN;
+
+ /* Get the max mtu from the device tree. Note that the
+ * "max-frame-size" parameter is actually max mtu. Definition
+ * in the ePAPR v1.1 spec and usage differ, so go with usage.
+ */
+ of_property_read_u32(pdev->dev.of_node, "max-frame-size",
+ &priv->max_mtu);
+
+ /* The DMA buffer size already accounts for an alignment bias
+ * to avoid unaligned access exceptions for the NIOS processor,
+ */
+ priv->rx_dma_buf_sz = ALTERA_RXDMABUFFER_SIZE;
+
+ /* get default MAC address from device tree */
+ macaddr = of_get_mac_address(pdev->dev.of_node);
+ if (macaddr)
+ ether_addr_copy(ndev->dev_addr, macaddr);
+ else
+ eth_hw_addr_random(ndev);
+
+ priv->phy_iface = of_get_phy_mode(np);
+
+ /* try to get PHY address from device tree, use PHY autodetection if
+ * no valid address is given
+ */
+ if (of_property_read_u32(pdev->dev.of_node, "phy-addr",
+ &priv->phy_addr)) {
+ priv->phy_addr = POLL_PHY;
+ }
+
+ if (!((priv->phy_addr == POLL_PHY) ||
+ ((priv->phy_addr >= 0) && (priv->phy_addr < PHY_MAX_ADDR)))) {
+ dev_err(&pdev->dev, "invalid phy-addr specified %d\n",
+ priv->phy_addr);
+ goto err_free_netdev;
+ }
+
+ /* Create/attach to MDIO bus */
+ ret = altera_tse_mdio_create(ndev,
+ atomic_add_return(1, &instance_count));
+
+ if (ret)
+ goto err_free_netdev;
+
+ /* initialize netdev */
+ ether_setup(ndev);
+ ndev->mem_start = control_port->start;
+ ndev->mem_end = control_port->end;
+ ndev->netdev_ops = &altera_tse_netdev_ops;
+ altera_tse_set_ethtool_ops(ndev);
+
+ altera_tse_netdev_ops.ndo_set_rx_mode = tse_set_rx_mode;
+
+ if (priv->hash_filter)
+ altera_tse_netdev_ops.ndo_set_rx_mode =
+ tse_set_rx_mode_hashfilter;
+
+ /* Scatter/gather IO is not supported,
+ * so it is turned off
+ */
+ ndev->hw_features &= ~NETIF_F_SG;
+ ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
+
+ /* VLAN offloading of tagging, stripping and filtering is not
+ * supported by hardware, but driver will accommodate the
+ * extra 4-byte VLAN tag for processing by upper layers
+ */
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+
+ /* setup NAPI interface */
+ netif_napi_add(ndev, &priv->napi, tse_poll, NAPI_POLL_WEIGHT);
+
+ spin_lock_init(&priv->mac_cfg_lock);
+ spin_lock_init(&priv->tx_lock);
+ spin_lock_init(&priv->rxdma_irq_lock);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register TSE net device\n");
+ goto err_register_netdev;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ priv->revision = ioread32(&priv->mac_dev->megacore_revision);
+
+ if (netif_msg_probe(priv))
+ dev_info(&pdev->dev, "Altera TSE MAC version %d.%d at 0x%08lx irq %d/%d\n",
+ (priv->revision >> 8) & 0xff,
+ priv->revision & 0xff,
+ (unsigned long) control_port->start, priv->rx_irq,
+ priv->tx_irq);
+
+ ret = init_phy(ndev);
+ if (ret != 0) {
+ netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret);
+ goto err_init_phy;
+ }
+ return 0;
+
+err_init_phy:
+ unregister_netdev(ndev);
+err_register_netdev:
+ netif_napi_del(&priv->napi);
+ altera_tse_mdio_destroy(ndev);
+err_free_netdev:
+ free_netdev(ndev);
+ return ret;
+}
+
+/* Remove Altera TSE MAC device
+ */
+static int altera_tse_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ altera_tse_mdio_destroy(ndev);
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct altera_dmaops altera_dtype_sgdma = {
+ .altera_dtype = ALTERA_DTYPE_SGDMA,
+ .dmamask = 32,
+ .reset_dma = sgdma_reset,
+ .enable_txirq = sgdma_enable_txirq,
+ .enable_rxirq = sgdma_enable_rxirq,
+ .disable_txirq = sgdma_disable_txirq,
+ .disable_rxirq = sgdma_disable_rxirq,
+ .clear_txirq = sgdma_clear_txirq,
+ .clear_rxirq = sgdma_clear_rxirq,
+ .tx_buffer = sgdma_tx_buffer,
+ .tx_completions = sgdma_tx_completions,
+ .add_rx_desc = sgdma_add_rx_desc,
+ .get_rx_status = sgdma_rx_status,
+ .init_dma = sgdma_initialize,
+ .uninit_dma = sgdma_uninitialize,
+ .start_rxdma = sgdma_start_rxdma,
+};
+
+static const struct altera_dmaops altera_dtype_msgdma = {
+ .altera_dtype = ALTERA_DTYPE_MSGDMA,
+ .dmamask = 64,
+ .reset_dma = msgdma_reset,
+ .enable_txirq = msgdma_enable_txirq,
+ .enable_rxirq = msgdma_enable_rxirq,
+ .disable_txirq = msgdma_disable_txirq,
+ .disable_rxirq = msgdma_disable_rxirq,
+ .clear_txirq = msgdma_clear_txirq,
+ .clear_rxirq = msgdma_clear_rxirq,
+ .tx_buffer = msgdma_tx_buffer,
+ .tx_completions = msgdma_tx_completions,
+ .add_rx_desc = msgdma_add_rx_desc,
+ .get_rx_status = msgdma_rx_status,
+ .init_dma = msgdma_initialize,
+ .uninit_dma = msgdma_uninitialize,
+ .start_rxdma = msgdma_start_rxdma,
+};
+
+static struct of_device_id altera_tse_ids[] = {
+ { .compatible = "altr,tse-msgdma-1.0", .data = &altera_dtype_msgdma, },
+ { .compatible = "altr,tse-1.0", .data = &altera_dtype_sgdma, },
+ { .compatible = "ALTR,tse-1.0", .data = &altera_dtype_sgdma, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altera_tse_ids);
+
+static struct platform_driver altera_tse_driver = {
+ .probe = altera_tse_probe,
+ .remove = altera_tse_remove,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = ALTERA_TSE_RESOURCE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = altera_tse_ids,
+ },
+};
+
+module_platform_driver(altera_tse_driver);
+
+MODULE_AUTHOR("Altera Corporation");
+MODULE_DESCRIPTION("Altera Triple Speed Ethernet MAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/altera/altera_utils.c b/drivers/net/ethernet/altera/altera_utils.c
new file mode 100644
index 00000000000..d7eeb1713ad
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_utils.c
@@ -0,0 +1,44 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "altera_tse.h"
+#include "altera_utils.h"
+
+void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask)
+{
+ u32 value = csrrd32(ioaddr, offs);
+ value |= bit_mask;
+ csrwr32(value, ioaddr, offs);
+}
+
+void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask)
+{
+ u32 value = csrrd32(ioaddr, offs);
+ value &= ~bit_mask;
+ csrwr32(value, ioaddr, offs);
+}
+
+int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask)
+{
+ u32 value = csrrd32(ioaddr, offs);
+ return (value & bit_mask) ? 1 : 0;
+}
+
+int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask)
+{
+ u32 value = csrrd32(ioaddr, offs);
+ return (value & bit_mask) ? 0 : 1;
+}
diff --git a/drivers/net/ethernet/altera/altera_utils.h b/drivers/net/ethernet/altera/altera_utils.h
new file mode 100644
index 00000000000..baf100ccf58
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_utils.h
@@ -0,0 +1,27 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+
+#ifndef __ALTERA_UTILS_H__
+#define __ALTERA_UTILS_H__
+
+void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
+void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
+int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask);
+int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask);
+
+#endif /* __ALTERA_UTILS_H__*/
diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c
new file mode 100644
index 00000000000..98a10d555b7
--- /dev/null
+++ b/drivers/net/ethernet/amd/7990.c
@@ -0,0 +1,666 @@
+/*
+ * 7990.c -- LANCE ethernet IC generic routines.
+ * This is an attempt to separate out the bits of various ethernet
+ * drivers that are common because they all use the AMD 7990 LANCE
+ * (Local Area Network Controller for Ethernet) chip.
+ *
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * Most of this stuff was obtained by looking at other LANCE drivers,
+ * in particular a2065.[ch]. The AMD C-LANCE datasheet was also helpful.
+ * NB: this was made easy by the fact that Jes Sorensen had cleaned up
+ * most of a2025 and sunlance with the aim of merging them, so the
+ * common code was pretty obvious.
+ */
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/route.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <asm/irq.h>
+/* Used for the temporal inet entries and routing */
+#include <linux/socket.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/pgtable.h>
+#ifdef CONFIG_HP300
+#include <asm/blinken.h>
+#endif
+
+#include "7990.h"
+
+#define WRITERAP(lp, x) out_be16(lp->base + LANCE_RAP, (x))
+#define WRITERDP(lp, x) out_be16(lp->base + LANCE_RDP, (x))
+#define READRDP(lp) in_be16(lp->base + LANCE_RDP)
+
+#if defined(CONFIG_HPLANCE) || defined(CONFIG_HPLANCE_MODULE)
+#include "hplance.h"
+
+#undef WRITERAP
+#undef WRITERDP
+#undef READRDP
+
+#if defined(CONFIG_MVME147_NET) || defined(CONFIG_MVME147_NET_MODULE)
+
+/* Lossage Factor Nine, Mr Sulu. */
+#define WRITERAP(lp, x) (lp->writerap(lp, x))
+#define WRITERDP(lp, x) (lp->writerdp(lp, x))
+#define READRDP(lp) (lp->readrdp(lp))
+
+#else
+
+/* These inlines can be used if only CONFIG_HPLANCE is defined */
+static inline void WRITERAP(struct lance_private *lp, __u16 value)
+{
+ do {
+ out_be16(lp->base + HPLANCE_REGOFF + LANCE_RAP, value);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+}
+
+static inline void WRITERDP(struct lance_private *lp, __u16 value)
+{
+ do {
+ out_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP, value);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+}
+
+static inline __u16 READRDP(struct lance_private *lp)
+{
+ __u16 value;
+ do {
+ value = in_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+ return value;
+}
+
+#endif
+#endif /* CONFIG_HPLANCE || CONFIG_HPLANCE_MODULE */
+
+/* debugging output macros, various flavours */
+/* #define TEST_HITS */
+#ifdef UNDEF
+#define PRINT_RINGS() \
+do { \
+ int t; \
+ for (t = 0; t < RX_RING_SIZE; t++) { \
+ printk("R%d: @(%02X %04X) len %04X, mblen %04X, bits %02X\n", \
+ t, ib->brx_ring[t].rmd1_hadr, ib->brx_ring[t].rmd0, \
+ ib->brx_ring[t].length, \
+ ib->brx_ring[t].mblength, ib->brx_ring[t].rmd1_bits); \
+ } \
+ for (t = 0; t < TX_RING_SIZE; t++) { \
+ printk("T%d: @(%02X %04X) len %04X, misc %04X, bits %02X\n", \
+ t, ib->btx_ring[t].tmd1_hadr, ib->btx_ring[t].tmd0, \
+ ib->btx_ring[t].length, \
+ ib->btx_ring[t].misc, ib->btx_ring[t].tmd1_bits); \
+ } \
+} while (0)
+#else
+#define PRINT_RINGS()
+#endif
+
+/* Load the CSR registers. The LANCE has to be STOPped when we do this! */
+static void load_csrs(struct lance_private *lp)
+{
+ volatile struct lance_init_block *aib = lp->lance_init_block;
+ int leptr;
+
+ leptr = LANCE_ADDR(aib);
+
+ WRITERAP(lp, LE_CSR1); /* load address of init block */
+ WRITERDP(lp, leptr & 0xFFFF);
+ WRITERAP(lp, LE_CSR2);
+ WRITERDP(lp, leptr >> 16);
+ WRITERAP(lp, LE_CSR3);
+ WRITERDP(lp, lp->busmaster_regval); /* set byteswap/ALEctrl/byte ctrl */
+
+ /* Point back to csr0 */
+ WRITERAP(lp, LE_CSR0);
+}
+
+/* #define to 0 or 1 appropriately */
+#define DEBUG_IRING 0
+/* Set up the Lance Rx and Tx rings and the init block */
+static void lance_init_ring(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_init_block *aib; /* for LANCE_ADDR computations */
+ int leptr;
+ int i;
+
+ aib = lp->lance_init_block;
+
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ ib->mode = LE_MO_PROM; /* normal, enable Tx & Rx */
+
+ /* Copy the ethernet address to the lance init block
+ * Notice that we do a byteswap if we're big endian.
+ * [I think this is the right criterion; at least, sunlance,
+ * a2065 and atarilance do the byteswap and lance.c (PC) doesn't.
+ * However, the datasheet says that the BSWAP bit doesn't affect
+ * the init block, so surely it should be low byte first for
+ * everybody? Um.]
+ * We could define the ib->physaddr as three 16bit values and
+ * use (addr[1] << 8) | addr[0] & co, but this is more efficient.
+ */
+#ifdef __BIG_ENDIAN
+ ib->phys_addr[0] = dev->dev_addr[1];
+ ib->phys_addr[1] = dev->dev_addr[0];
+ ib->phys_addr[2] = dev->dev_addr[3];
+ ib->phys_addr[3] = dev->dev_addr[2];
+ ib->phys_addr[4] = dev->dev_addr[5];
+ ib->phys_addr[5] = dev->dev_addr[4];
+#else
+ for (i = 0; i < 6; i++)
+ ib->phys_addr[i] = dev->dev_addr[i];
+#endif
+
+ if (DEBUG_IRING)
+ printk("TX rings:\n");
+
+ lp->tx_full = 0;
+ /* Setup the Tx ring entries */
+ for (i = 0; i < (1 << lp->lance_log_tx_bufs); i++) {
+ leptr = LANCE_ADDR(&aib->tx_buf[i][0]);
+ ib->btx_ring[i].tmd0 = leptr;
+ ib->btx_ring[i].tmd1_hadr = leptr >> 16;
+ ib->btx_ring[i].tmd1_bits = 0;
+ ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */
+ ib->btx_ring[i].misc = 0;
+ if (DEBUG_IRING)
+ printk("%d: 0x%8.8x\n", i, leptr);
+ }
+
+ /* Setup the Rx ring entries */
+ if (DEBUG_IRING)
+ printk("RX rings:\n");
+ for (i = 0; i < (1 << lp->lance_log_rx_bufs); i++) {
+ leptr = LANCE_ADDR(&aib->rx_buf[i][0]);
+
+ ib->brx_ring[i].rmd0 = leptr;
+ ib->brx_ring[i].rmd1_hadr = leptr >> 16;
+ ib->brx_ring[i].rmd1_bits = LE_R1_OWN;
+ /* 0xf000 == bits that must be one (reserved, presumably) */
+ ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000;
+ ib->brx_ring[i].mblength = 0;
+ if (DEBUG_IRING)
+ printk("%d: 0x%8.8x\n", i, leptr);
+ }
+
+ /* Setup the initialization block */
+
+ /* Setup rx descriptor pointer */
+ leptr = LANCE_ADDR(&aib->brx_ring);
+ ib->rx_len = (lp->lance_log_rx_bufs << 13) | (leptr >> 16);
+ ib->rx_ptr = leptr;
+ if (DEBUG_IRING)
+ printk("RX ptr: %8.8x\n", leptr);
+
+ /* Setup tx descriptor pointer */
+ leptr = LANCE_ADDR(&aib->btx_ring);
+ ib->tx_len = (lp->lance_log_tx_bufs << 13) | (leptr >> 16);
+ ib->tx_ptr = leptr;
+ if (DEBUG_IRING)
+ printk("TX ptr: %8.8x\n", leptr);
+
+ /* Clear the multicast filter */
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
+ PRINT_RINGS();
+}
+
+/* LANCE must be STOPped before we do this, too... */
+static int init_restart_lance(struct lance_private *lp)
+{
+ int i;
+
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_INIT);
+
+ /* Need a hook here for sunlance ledma stuff */
+
+ /* Wait for the lance to complete initialization */
+ for (i = 0; (i < 100) && !(READRDP(lp) & (LE_C0_ERR | LE_C0_IDON)); i++)
+ barrier();
+ if ((i == 100) || (READRDP(lp) & LE_C0_ERR)) {
+ printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, READRDP(lp));
+ return -1;
+ }
+
+ /* Clear IDON by writing a "1", enable interrupts and start lance */
+ WRITERDP(lp, LE_C0_IDON);
+ WRITERDP(lp, LE_C0_INEA | LE_C0_STRT);
+
+ return 0;
+}
+
+static int lance_reset(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int status;
+
+ /* Stop the lance */
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STOP);
+
+ load_csrs(lp);
+ lance_init_ring(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ status = init_restart_lance(lp);
+#ifdef DEBUG_DRIVER
+ printk("Lance restart=%d\n", status);
+#endif
+ return status;
+}
+
+static int lance_rx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_rx_desc *rd;
+ unsigned char bits;
+#ifdef TEST_HITS
+ int i;
+#endif
+
+#ifdef TEST_HITS
+ printk("[");
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (i == lp->rx_new)
+ printk("%s",
+ ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "_" : "X");
+ else
+ printk("%s",
+ ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "." : "1");
+ }
+ printk("]");
+#endif
+#ifdef CONFIG_HP300
+ blinken_leds(0x40, 0);
+#endif
+ WRITERDP(lp, LE_C0_RINT | LE_C0_INEA); /* ack Rx int, reenable ints */
+ for (rd = &ib->brx_ring[lp->rx_new]; /* For each Rx ring we own... */
+ !((bits = rd->rmd1_bits) & LE_R1_OWN);
+ rd = &ib->brx_ring[lp->rx_new]) {
+
+ /* We got an incomplete frame? */
+ if ((bits & LE_R1_POK) != LE_R1_POK) {
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ continue;
+ } else if (bits & LE_R1_ERR) {
+ /* Count only the end frame as a rx error,
+ * not the beginning
+ */
+ if (bits & LE_R1_BUF)
+ dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC)
+ dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL)
+ dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA)
+ dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP)
+ dev->stats.rx_errors++;
+ } else {
+ int len = (rd->mblength & 0xfff) - 4;
+ struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
+
+ if (!skb) {
+ dev->stats.rx_dropped++;
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
+ return 0;
+ }
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)&(ib->rx_buf[lp->rx_new][0]),
+ len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+ }
+
+ /* Return the packet to the pool */
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
+ }
+ return 0;
+}
+
+static int lance_tx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_tx_desc *td;
+ int i, j;
+ int status;
+
+#ifdef CONFIG_HP300
+ blinken_leds(0x80, 0);
+#endif
+ /* csr0 is 2f3 */
+ WRITERDP(lp, LE_C0_TINT | LE_C0_INEA);
+ /* csr0 is 73 */
+
+ j = lp->tx_old;
+ for (i = j; i != lp->tx_new; i = j) {
+ td = &ib->btx_ring[i];
+
+ /* If we hit a packet not owned by us, stop */
+ if (td->tmd1_bits & LE_T1_OWN)
+ break;
+
+ if (td->tmd1_bits & LE_T1_ERR) {
+ status = td->misc;
+
+ dev->stats.tx_errors++;
+ if (status & LE_T3_RTY)
+ dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL)
+ dev->stats.tx_window_errors++;
+
+ if (status & LE_T3_CLOS) {
+ dev->stats.tx_carrier_errors++;
+ if (lp->auto_select) {
+ lp->tpe = 1 - lp->tpe;
+ printk("%s: Carrier Lost, trying %s\n",
+ dev->name,
+ lp->tpe ? "TPE" : "AUI");
+ /* Stop the lance */
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STOP);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ return 0;
+ }
+ }
+
+ /* buffer errors and underflows turn off the transmitter */
+ /* Restart the adapter */
+ if (status & (LE_T3_BUF|LE_T3_UFL)) {
+ dev->stats.tx_fifo_errors++;
+
+ printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+ dev->name);
+ /* Stop the lance */
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STOP);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ return 0;
+ }
+ } else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
+ /*
+ * So we don't count the packet more than once.
+ */
+ td->tmd1_bits &= ~(LE_T1_POK);
+
+ /* One collision before packet was sent. */
+ if (td->tmd1_bits & LE_T1_EONE)
+ dev->stats.collisions++;
+
+ /* More than one collision, be optimistic. */
+ if (td->tmd1_bits & LE_T1_EMORE)
+ dev->stats.collisions += 2;
+
+ dev->stats.tx_packets++;
+ }
+
+ j = (j + 1) & lp->tx_ring_mod_mask;
+ }
+ lp->tx_old = j;
+ WRITERDP(lp, LE_C0_TINT | LE_C0_INEA);
+ return 0;
+}
+
+static irqreturn_t
+lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ int csr0;
+
+ spin_lock(&lp->devlock);
+
+ WRITERAP(lp, LE_CSR0); /* LANCE Controller Status */
+ csr0 = READRDP(lp);
+
+ PRINT_RINGS();
+
+ if (!(csr0 & LE_C0_INTR)) { /* Check if any interrupt has */
+ spin_unlock(&lp->devlock);
+ return IRQ_NONE; /* been generated by the Lance. */
+ }
+
+ /* Acknowledge all the interrupt sources ASAP */
+ WRITERDP(lp, csr0 & ~(LE_C0_INEA|LE_C0_TDMD|LE_C0_STOP|LE_C0_STRT|LE_C0_INIT));
+
+ if ((csr0 & LE_C0_ERR)) {
+ /* Clear the error condition */
+ WRITERDP(lp, LE_C0_BABL|LE_C0_ERR|LE_C0_MISS|LE_C0_INEA);
+ }
+
+ if (csr0 & LE_C0_RINT)
+ lance_rx(dev);
+
+ if (csr0 & LE_C0_TINT)
+ lance_tx(dev);
+
+ /* Log misc errors. */
+ if (csr0 & LE_C0_BABL)
+ dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & LE_C0_MISS)
+ dev->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & LE_C0_MERR) {
+ printk("%s: Bus master arbitration failure, status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ WRITERDP(lp, LE_C0_STRT);
+ }
+
+ if (lp->tx_full && netif_queue_stopped(dev) && (TX_BUFFS_AVAIL >= 0)) {
+ lp->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_MERR|LE_C0_IDON|LE_C0_INEA);
+
+ spin_unlock(&lp->devlock);
+ return IRQ_HANDLED;
+}
+
+int lance_open(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int res;
+
+ /* Install the Interrupt handler. Or we could shunt this out to specific drivers? */
+ if (request_irq(lp->irq, lance_interrupt, IRQF_SHARED, lp->name, dev))
+ return -EAGAIN;
+
+ res = lance_reset(dev);
+ spin_lock_init(&lp->devlock);
+ netif_start_queue(dev);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(lance_open);
+
+int lance_close(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ /* Stop the LANCE */
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STOP);
+
+ free_irq(lp->irq, dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lance_close);
+
+void lance_tx_timeout(struct net_device *dev)
+{
+ printk("lance_tx_timeout\n");
+ lance_reset(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+EXPORT_SYMBOL_GPL(lance_tx_timeout);
+
+int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ int entry, skblen, len;
+ static int outs;
+ unsigned long flags;
+
+ if (!TX_BUFFS_AVAIL)
+ return NETDEV_TX_LOCKED;
+
+ netif_stop_queue(dev);
+
+ skblen = skb->len;
+
+#ifdef DEBUG_DRIVER
+ /* dump the packet */
+ {
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ if ((i % 16) == 0)
+ printk("\n");
+ printk("%2.2x ", skb->data[i]);
+ }
+ }
+#endif
+ len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
+ entry = lp->tx_new & lp->tx_ring_mod_mask;
+ ib->btx_ring[entry].length = (-len) | 0xf000;
+ ib->btx_ring[entry].misc = 0;
+
+ if (skb->len < ETH_ZLEN)
+ memset((void *)&ib->tx_buf[entry][0], 0, ETH_ZLEN);
+ skb_copy_from_linear_data(skb, (void *)&ib->tx_buf[entry][0], skblen);
+
+ /* Now, give the packet to the lance */
+ ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
+ lp->tx_new = (lp->tx_new + 1) & lp->tx_ring_mod_mask;
+
+ outs++;
+ /* Kick the lance: transmit now */
+ WRITERDP(lp, LE_C0_INEA | LE_C0_TDMD);
+ dev_consume_skb_any(skb);
+
+ spin_lock_irqsave(&lp->devlock, flags);
+ if (TX_BUFFS_AVAIL)
+ netif_start_queue(dev);
+ else
+ lp->tx_full = 1;
+ spin_unlock_irqrestore(&lp->devlock, flags);
+
+ return NETDEV_TX_OK;
+}
+EXPORT_SYMBOL_GPL(lance_start_xmit);
+
+/* taken from the depca driver via a2065.c */
+static void lance_load_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile u16 *mcast_table = (u16 *)&ib->filter;
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI) {
+ ib->filter[0] = 0xffffffff;
+ ib->filter[1] = 0xffffffff;
+ return;
+ }
+ /* clear the multicast filter */
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
+
+ /* Add addresses */
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc_le(6, ha->addr);
+ crc = crc >> 26;
+ mcast_table[crc >> 4] |= 1 << (crc & 0xf);
+ }
+}
+
+
+void lance_set_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ int stopped;
+
+ stopped = netif_queue_stopped(dev);
+ if (!stopped)
+ netif_stop_queue(dev);
+
+ while (lp->tx_old != lp->tx_new)
+ schedule();
+
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STOP);
+ lance_init_ring(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ ib->mode |= LE_MO_PROM;
+ } else {
+ ib->mode &= ~LE_MO_PROM;
+ lance_load_multicast(dev);
+ }
+ load_csrs(lp);
+ init_restart_lance(lp);
+
+ if (!stopped)
+ netif_start_queue(dev);
+}
+EXPORT_SYMBOL_GPL(lance_set_multicast);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void lance_poll(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ spin_lock(&lp->devlock);
+ WRITERAP(lp, LE_CSR0);
+ WRITERDP(lp, LE_C0_STRT);
+ spin_unlock(&lp->devlock);
+ lance_interrupt(dev->irq, dev);
+}
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/amd/7990.h b/drivers/net/ethernet/amd/7990.h
new file mode 100644
index 00000000000..e9e0be31380
--- /dev/null
+++ b/drivers/net/ethernet/amd/7990.h
@@ -0,0 +1,250 @@
+/*
+ * 7990.h -- LANCE ethernet IC generic routines.
+ * This is an attempt to separate out the bits of various ethernet
+ * drivers that are common because they all use the AMD 7990 LANCE
+ * (Local Area Network Controller for Ethernet) chip.
+ *
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * Most of this stuff was obtained by looking at other LANCE drivers,
+ * in particular a2065.[ch]. The AMD C-LANCE datasheet was also helpful.
+ */
+
+#ifndef _7990_H
+#define _7990_H
+
+/* The lance only has two register locations. We communicate mostly via memory. */
+#define LANCE_RDP 0 /* Register Data Port */
+#define LANCE_RAP 2 /* Register Address Port */
+
+/* Transmit/receive ring definitions.
+ * We allow the specific drivers to override these defaults if they want to.
+ * NB: according to lance.c, increasing the number of buffers is a waste
+ * of space and reduces the chance that an upper layer will be able to
+ * reorder queued Tx packets based on priority. [Clearly there is a minimum
+ * limit too: too small and we drop rx packets and can't tx at full speed.]
+ * 4+4 seems to be the usual setting; the atarilance driver uses 3 and 5.
+ */
+
+/* Blast! This won't work. The problem is that we can't specify a default
+ * setting because that would cause the lance_init_block struct to be
+ * too long (and overflow the RAM on shared-memory cards like the HP LANCE.
+ */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 1
+#define LANCE_LOG_RX_BUFFERS 3
+#endif
+
+#define TX_RING_SIZE (1 << LANCE_LOG_TX_BUFFERS)
+#define RX_RING_SIZE (1 << LANCE_LOG_RX_BUFFERS)
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+#define PKT_BUFF_SIZE (1544)
+#define RX_BUFF_SIZE PKT_BUFF_SIZE
+#define TX_BUFF_SIZE PKT_BUFF_SIZE
+
+/* Each receive buffer is described by a receive message descriptor (RMD) */
+struct lance_rx_desc {
+ volatile unsigned short rmd0; /* low address of packet */
+ volatile unsigned char rmd1_bits; /* descriptor bits */
+ volatile unsigned char rmd1_hadr; /* high address of packet */
+ volatile short length; /* This length is 2s complement (negative)!
+ * Buffer length */
+ volatile unsigned short mblength; /* Actual number of bytes received */
+};
+
+/* Ditto for TMD: */
+struct lance_tx_desc {
+ volatile unsigned short tmd0; /* low address of packet */
+ volatile unsigned char tmd1_bits; /* descriptor bits */
+ volatile unsigned char tmd1_hadr; /* high address of packet */
+ volatile short length; /* Length is 2s complement (negative)! */
+ volatile unsigned short misc;
+};
+
+/* There are three memory structures accessed by the LANCE:
+ * the initialization block, the receive and transmit descriptor rings,
+ * and the data buffers themselves. In fact we might as well put the
+ * init block,the Tx and Rx rings and the buffers together in memory:
+ */
+struct lance_init_block {
+ volatile unsigned short mode; /* Pre-set mode (reg. 15) */
+ volatile unsigned char phys_addr[6]; /* Physical ethernet address */
+ volatile unsigned filter[2]; /* Multicast filter (64 bits) */
+
+ /* Receive and transmit ring base, along with extra bits. */
+ volatile unsigned short rx_ptr; /* receive descriptor addr */
+ volatile unsigned short rx_len; /* receive len and high addr */
+ volatile unsigned short tx_ptr; /* transmit descriptor addr */
+ volatile unsigned short tx_len; /* transmit len and high addr */
+
+ /* The Tx and Rx ring entries must be aligned on 8-byte boundaries.
+ * This will be true if this whole struct is 8-byte aligned.
+ */
+ volatile struct lance_tx_desc btx_ring[TX_RING_SIZE];
+ volatile struct lance_rx_desc brx_ring[RX_RING_SIZE];
+
+ volatile char tx_buf[TX_RING_SIZE][TX_BUFF_SIZE];
+ volatile char rx_buf[RX_RING_SIZE][RX_BUFF_SIZE];
+ /* we use this just to make the struct big enough that we can move its startaddr
+ * in order to force alignment to an eight byte boundary.
+ */
+};
+
+/* This is where we keep all the stuff the driver needs to know about.
+ * I'm definitely unhappy about the mechanism for allowing specific
+ * drivers to add things...
+ */
+struct lance_private {
+ const char *name;
+ unsigned long base;
+ volatile struct lance_init_block *init_block; /* CPU address of RAM */
+ volatile struct lance_init_block *lance_init_block; /* LANCE address of RAM */
+
+ int rx_new, tx_new;
+ int rx_old, tx_old;
+
+ int lance_log_rx_bufs, lance_log_tx_bufs;
+ int rx_ring_mod_mask, tx_ring_mod_mask;
+
+ int tpe; /* TPE is selected */
+ int auto_select; /* cable-selection is by carrier */
+ unsigned short busmaster_regval;
+
+ unsigned int irq; /* IRQ to register */
+
+ /* This is because the HP LANCE is disgusting and you have to check
+ * a DIO-specific register every time you read/write the LANCE regs :-<
+ * [could we get away with making these some sort of macro?]
+ */
+ void (*writerap)(void *, unsigned short);
+ void (*writerdp)(void *, unsigned short);
+ unsigned short (*readrdp)(void *);
+ spinlock_t devlock;
+ char tx_full;
+};
+
+/*
+ * Am7990 Control and Status Registers
+ */
+#define LE_CSR0 0x0000 /* LANCE Controller Status */
+#define LE_CSR1 0x0001 /* IADR[15:0] (bit0==0 ie word aligned) */
+#define LE_CSR2 0x0002 /* IADR[23:16] (high bits reserved) */
+#define LE_CSR3 0x0003 /* Misc */
+
+/*
+ * Bit definitions for CSR0 (LANCE Controller Status)
+ */
+#define LE_C0_ERR 0x8000 /* Error = BABL | CERR | MISS | MERR */
+#define LE_C0_BABL 0x4000 /* Babble: Transmitted too many bits */
+#define LE_C0_CERR 0x2000 /* No Heartbeat (10BASE-T) */
+#define LE_C0_MISS 0x1000 /* Missed Frame (no rx buffer to put it in) */
+#define LE_C0_MERR 0x0800 /* Memory Error */
+#define LE_C0_RINT 0x0400 /* Receive Interrupt */
+#define LE_C0_TINT 0x0200 /* Transmit Interrupt */
+#define LE_C0_IDON 0x0100 /* Initialization Done */
+#define LE_C0_INTR 0x0080 /* Interrupt Flag
+ = BABL | MISS | MERR | RINT | TINT | IDON */
+#define LE_C0_INEA 0x0040 /* Interrupt Enable */
+#define LE_C0_RXON 0x0020 /* Receive On */
+#define LE_C0_TXON 0x0010 /* Transmit On */
+#define LE_C0_TDMD 0x0008 /* Transmit Demand */
+#define LE_C0_STOP 0x0004 /* Stop */
+#define LE_C0_STRT 0x0002 /* Start */
+#define LE_C0_INIT 0x0001 /* Initialize */
+
+
+/*
+ * Bit definitions for CSR3
+ */
+#define LE_C3_BSWP 0x0004 /* Byte Swap (on for big endian byte order) */
+#define LE_C3_ACON 0x0002 /* ALE Control (on for active low ALE) */
+#define LE_C3_BCON 0x0001 /* Byte Control */
+
+
+/*
+ * Mode Flags
+ */
+#define LE_MO_PROM 0x8000 /* Promiscuous Mode */
+/* these next ones 0x4000 -- 0x0080 are not available on the LANCE 7990,
+ * but they are in NetBSD's am7990.h, presumably for backwards-compatible chips
+ */
+#define LE_MO_DRCVBC 0x4000 /* disable receive broadcast */
+#define LE_MO_DRCVPA 0x2000 /* disable physical address detection */
+#define LE_MO_DLNKTST 0x1000 /* disable link status */
+#define LE_MO_DAPC 0x0800 /* disable automatic polarity correction */
+#define LE_MO_MENDECL 0x0400 /* MENDEC loopback mode */
+#define LE_MO_LRTTSEL 0x0200 /* lower RX threshold / TX mode selection */
+#define LE_MO_PSEL1 0x0100 /* port selection bit1 */
+#define LE_MO_PSEL0 0x0080 /* port selection bit0 */
+/* and this one is from the C-LANCE data sheet... */
+#define LE_MO_EMBA 0x0080 /* Enable Modified Backoff Algorithm
+ (C-LANCE, not original LANCE) */
+#define LE_MO_INTL 0x0040 /* Internal Loopback */
+#define LE_MO_DRTY 0x0020 /* Disable Retry */
+#define LE_MO_FCOLL 0x0010 /* Force Collision */
+#define LE_MO_DXMTFCS 0x0008 /* Disable Transmit CRC */
+#define LE_MO_LOOP 0x0004 /* Loopback Enable */
+#define LE_MO_DTX 0x0002 /* Disable Transmitter */
+#define LE_MO_DRX 0x0001 /* Disable Receiver */
+
+
+/*
+ * Receive Flags
+ */
+#define LE_R1_OWN 0x80 /* LANCE owns the descriptor */
+#define LE_R1_ERR 0x40 /* Error */
+#define LE_R1_FRA 0x20 /* Framing Error */
+#define LE_R1_OFL 0x10 /* Overflow Error */
+#define LE_R1_CRC 0x08 /* CRC Error */
+#define LE_R1_BUF 0x04 /* Buffer Error */
+#define LE_R1_SOP 0x02 /* Start of Packet */
+#define LE_R1_EOP 0x01 /* End of Packet */
+#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+
+/*
+ * Transmit Flags
+ */
+#define LE_T1_OWN 0x80 /* LANCE owns the descriptor */
+#define LE_T1_ERR 0x40 /* Error */
+#define LE_T1_RES 0x20 /* Reserved, LANCE writes this with a zero */
+#define LE_T1_EMORE 0x10 /* More than one retry needed */
+#define LE_T1_EONE 0x08 /* One retry needed */
+#define LE_T1_EDEF 0x04 /* Deferred */
+#define LE_T1_SOP 0x02 /* Start of Packet */
+#define LE_T1_EOP 0x01 /* End of Packet */
+#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+/*
+ * Error Flags
+ */
+#define LE_T3_BUF 0x8000 /* Buffer Error */
+#define LE_T3_UFL 0x4000 /* Underflow Error */
+#define LE_T3_LCOL 0x1000 /* Late Collision */
+#define LE_T3_CLOS 0x0800 /* Loss of Carrier */
+#define LE_T3_RTY 0x0400 /* Retry Error */
+#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry */
+
+/* Miscellaneous useful macros */
+
+#define TX_BUFFS_AVAIL ((lp->tx_old <= lp->tx_new) ? \
+ lp->tx_old + lp->tx_ring_mod_mask - lp->tx_new : \
+ lp->tx_old - lp->tx_new - 1)
+
+/* The LANCE only uses 24 bit addresses. This does the obvious thing. */
+#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
+
+/* Now the prototypes we export */
+int lance_open(struct net_device *dev);
+int lance_close(struct net_device *dev);
+int lance_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void lance_set_multicast(struct net_device *dev);
+void lance_tx_timeout(struct net_device *dev);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void lance_poll(struct net_device *dev);
+#endif
+
+#endif /* ndef _7990_H */
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
new file mode 100644
index 00000000000..bbaf36d9f5e
--- /dev/null
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -0,0 +1,192 @@
+#
+# AMD network device configuration
+#
+
+config NET_VENDOR_AMD
+ bool "AMD devices"
+ default y
+ depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
+ SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
+ (ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA || ARM64
+ ---help---
+ If you have a network (Ethernet) chipset belonging to this class,
+ say Y.
+
+ Note that the answer to this question does not directly affect
+ the kernel: saying N will just case the configurator to skip all
+ the questions regarding AMD chipsets. If you say Y, you will be asked
+ for your specific chipset/driver in the following questions.
+
+if NET_VENDOR_AMD
+
+config A2065
+ tristate "A2065 support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called a2065.
+
+config AMD8111_ETH
+ tristate "AMD 8111 (new PCI LANCE) support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ If you have an AMD 8111-based PCI LANCE ethernet card,
+ answer Y here and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called amd8111e.
+
+config LANCE
+ tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Some LinkSys cards are
+ of this type.
+
+ To compile this driver as a module, choose M here: the module
+ will be called lance. This is recommended.
+
+config PCNET32
+ tristate "AMD PCnet32 PCI support"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ If you have a PCnet32 or PCnetPCI based network (Ethernet) card,
+ answer Y here and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called pcnet32.
+
+config ARIADNE
+ tristate "Ariadne support"
+ depends on ZORRO
+ ---help---
+ If you have a Village Tronic Ariadne Ethernet adapter, say Y.
+ Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ariadne.
+
+config ARM_AM79C961A
+ bool "ARM EBSA110 AM79C961A support"
+ depends on ARM && ARCH_EBSA110
+ select CRC32
+ ---help---
+ If you wish to compile a kernel for the EBSA-110, then you should
+ always answer Y to this.
+
+config ATARILANCE
+ tristate "Atari LANCE support"
+ depends on ATARI
+ ---help---
+ Say Y to include support for several Atari Ethernet adapters based
+ on the AMD LANCE chipset: RieblCard (with or without battery), or
+ PAMCard VME (also the version by Rhotron, with different addresses).
+
+config DECLANCE
+ tristate "DEC LANCE ethernet controller support"
+ depends on MACH_DECSTATION
+ select CRC32
+ ---help---
+ This driver is for the series of Ethernet controllers produced by
+ DEC (now Compaq) based on the AMD LANCE chipset, including the
+ DEPCA series. (This chipset is better known via the NE2100 cards.)
+
+config HPLANCE
+ bool "HP on-board LANCE support"
+ depends on DIO
+ select CRC32
+ ---help---
+ If you want to use the builtin "LANCE" Ethernet controller on an
+ HP300 machine, say Y here.
+
+config MIPS_AU1X00_ENET
+ tristate "MIPS AU1000 Ethernet support"
+ depends on MIPS_ALCHEMY
+ select PHYLIB
+ select CRC32
+ ---help---
+ If you have an Alchemy Semi AU1X00 based system
+ say Y. Otherwise, say N.
+
+config MVME147_NET
+ tristate "MVME147 (LANCE) Ethernet support"
+ depends on MVME147
+ select CRC32
+ ---help---
+ Support for the on-board Ethernet interface on the Motorola MVME147
+ single-board computer. Say Y here to include the
+ driver for this chip in your kernel.
+ To compile this driver as a module, choose M here.
+
+config PCMCIA_NMCLAN
+ tristate "New Media PCMCIA support"
+ depends on PCMCIA
+ help
+ Say Y here if you intend to attach a New Media Ethernet or LiveWire
+ PCMCIA (PC-card) Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called nmclan_cs. If unsure, say N.
+
+config NI65
+ tristate "NI6510 support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ni65.
+
+config SUN3LANCE
+ tristate "Sun3/Sun3x on-board LANCE support"
+ depends on (SUN3 || SUN3X)
+ ---help---
+ Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80)
+ featured an AMD LANCE 10Mbit Ethernet controller on board; say Y
+ here to compile in the Linux driver for this and enable Ethernet.
+ General Linux information on the Sun 3 and 3x series (now
+ discontinued) is at
+ <http://www.angelfire.com/ca2/tech68k/sun3.html>.
+
+ If you're not building a kernel for a Sun 3, say N.
+
+config SUNLANCE
+ tristate "Sun LANCE support"
+ depends on SBUS
+ select CRC32
+ ---help---
+ This driver supports the "le" interface present on all 32-bit Sparc
+ systems, on some older Ultra systems and as an Sbus option. These
+ cards are based on the AMD LANCE chipset, which is better known
+ via the NE2100 cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sunlance.
+
+config AMD_XGBE
+ tristate "AMD 10GbE Ethernet driver"
+ depends on OF_NET
+ select PHYLIB
+ select AMD_XGBE_PHY
+ ---help---
+ This driver supports the AMD 10GbE Ethernet device found on an
+ AMD SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called amd-xgbe.
+
+endif # NET_VENDOR_AMD
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
new file mode 100644
index 00000000000..a38a2dce3eb
--- /dev/null
+++ b/drivers/net/ethernet/amd/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the AMD network device drivers.
+#
+
+obj-$(CONFIG_A2065) += a2065.o
+obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
+obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
+obj-$(CONFIG_ARIADNE) += ariadne.o
+obj-$(CONFIG_ATARILANCE) += atarilance.o
+obj-$(CONFIG_DECLANCE) += declance.o
+obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
+obj-$(CONFIG_LANCE) += lance.o
+obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
+obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
+obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
+obj-$(CONFIG_NI65) += ni65.o
+obj-$(CONFIG_PCNET32) += pcnet32.o
+obj-$(CONFIG_SUN3LANCE) += sun3lance.o
+obj-$(CONFIG_SUNLANCE) += sunlance.o
+obj-$(CONFIG_AMD_XGBE) += xgbe/
diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c
new file mode 100644
index 00000000000..56139184b80
--- /dev/null
+++ b/drivers/net/ethernet/amd/a2065.c
@@ -0,0 +1,783 @@
+/*
+ * Amiga Linux/68k A2065 Ethernet Driver
+ *
+ * (C) Copyright 1995-2003 by Geert Uytterhoeven <geert@linux-m68k.org>
+ *
+ * Fixes and tips by:
+ * - Janos Farkas (CHEXUM@sparta.banki.hu)
+ * - Jes Degn Soerensen (jds@kom.auc.dk)
+ * - Matt Domsch (Matt_Domsch@dell.com)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ariadne.?: Amiga Linux/68k Ariadne Ethernet Driver
+ * (C) Copyright 1995 by Geert Uytterhoeven,
+ * Peter De Schrijver
+ *
+ * lance.c: An AMD LANCE ethernet driver for linux.
+ * Written 1993-94 by Donald Becker.
+ *
+ * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
+ * Advanced Micro Devices
+ * Publication #16907, Rev. B, Amendment/0, May 1994
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * The A2065 is a Zorro-II board made by Commodore/Ameristar. It contains:
+ *
+ * - an Am7990 Local Area Network Controller for Ethernet (LANCE) with
+ * both 10BASE-2 (thin coax) and AUI (DB-15) connectors
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*#define DEBUG*/
+/*#define TEST_HITS*/
+
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/zorro.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include "a2065.h"
+
+/* Transmit/Receive Ring Definitions */
+
+#define LANCE_LOG_TX_BUFFERS (2)
+#define LANCE_LOG_RX_BUFFERS (4)
+
+#define TX_RING_SIZE (1 << LANCE_LOG_TX_BUFFERS)
+#define RX_RING_SIZE (1 << LANCE_LOG_RX_BUFFERS)
+
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+
+#define PKT_BUF_SIZE (1544)
+#define RX_BUFF_SIZE PKT_BUF_SIZE
+#define TX_BUFF_SIZE PKT_BUF_SIZE
+
+/* Layout of the Lance's RAM Buffer */
+
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode (reg. 15) */
+ unsigned char phys_addr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter. */
+
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned short rx_ptr; /* receive descriptor addr */
+ unsigned short rx_len; /* receive len and high addr */
+ unsigned short tx_ptr; /* transmit descriptor addr */
+ unsigned short tx_len; /* transmit len and high addr */
+
+ /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
+ struct lance_rx_desc brx_ring[RX_RING_SIZE];
+ struct lance_tx_desc btx_ring[TX_RING_SIZE];
+
+ char rx_buf[RX_RING_SIZE][RX_BUFF_SIZE];
+ char tx_buf[TX_RING_SIZE][TX_BUFF_SIZE];
+};
+
+/* Private Device Data */
+
+struct lance_private {
+ char *name;
+ volatile struct lance_regs *ll;
+ volatile struct lance_init_block *init_block; /* Hosts view */
+ volatile struct lance_init_block *lance_init_block; /* Lance view */
+
+ int rx_new, tx_new;
+ int rx_old, tx_old;
+
+ int lance_log_rx_bufs, lance_log_tx_bufs;
+ int rx_ring_mod_mask, tx_ring_mod_mask;
+
+ int tpe; /* cable-selection is TPE */
+ int auto_select; /* cable-selection by carrier */
+ unsigned short busmaster_regval;
+
+#ifdef CONFIG_SUNLANCE
+ struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */
+ int burst_sizes; /* ledma SBus burst sizes */
+#endif
+ struct timer_list multicast_timer;
+};
+
+#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
+
+/* Load the CSR registers */
+static void load_csrs(struct lance_private *lp)
+{
+ volatile struct lance_regs *ll = lp->ll;
+ volatile struct lance_init_block *aib = lp->lance_init_block;
+ int leptr = LANCE_ADDR(aib);
+
+ ll->rap = LE_CSR1;
+ ll->rdp = (leptr & 0xFFFF);
+ ll->rap = LE_CSR2;
+ ll->rdp = leptr >> 16;
+ ll->rap = LE_CSR3;
+ ll->rdp = lp->busmaster_regval;
+
+ /* Point back to csr0 */
+ ll->rap = LE_CSR0;
+}
+
+/* Setup the Lance Rx and Tx rings */
+static void lance_init_ring(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_init_block *aib = lp->lance_init_block;
+ /* for LANCE_ADDR computations */
+ int leptr;
+ int i;
+
+ /* Lock out other processes while setting up hardware */
+ netif_stop_queue(dev);
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ ib->mode = 0;
+
+ /* Copy the ethernet address to the lance init block
+ * Note that on the sparc you need to swap the ethernet address.
+ */
+ ib->phys_addr[0] = dev->dev_addr[1];
+ ib->phys_addr[1] = dev->dev_addr[0];
+ ib->phys_addr[2] = dev->dev_addr[3];
+ ib->phys_addr[3] = dev->dev_addr[2];
+ ib->phys_addr[4] = dev->dev_addr[5];
+ ib->phys_addr[5] = dev->dev_addr[4];
+
+ /* Setup the Tx ring entries */
+ netdev_dbg(dev, "TX rings:\n");
+ for (i = 0; i <= 1 << lp->lance_log_tx_bufs; i++) {
+ leptr = LANCE_ADDR(&aib->tx_buf[i][0]);
+ ib->btx_ring[i].tmd0 = leptr;
+ ib->btx_ring[i].tmd1_hadr = leptr >> 16;
+ ib->btx_ring[i].tmd1_bits = 0;
+ ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */
+ ib->btx_ring[i].misc = 0;
+ if (i < 3)
+ netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
+ }
+
+ /* Setup the Rx ring entries */
+ netdev_dbg(dev, "RX rings:\n");
+ for (i = 0; i < 1 << lp->lance_log_rx_bufs; i++) {
+ leptr = LANCE_ADDR(&aib->rx_buf[i][0]);
+
+ ib->brx_ring[i].rmd0 = leptr;
+ ib->brx_ring[i].rmd1_hadr = leptr >> 16;
+ ib->brx_ring[i].rmd1_bits = LE_R1_OWN;
+ ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000;
+ ib->brx_ring[i].mblength = 0;
+ if (i < 3)
+ netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
+ }
+
+ /* Setup the initialization block */
+
+ /* Setup rx descriptor pointer */
+ leptr = LANCE_ADDR(&aib->brx_ring);
+ ib->rx_len = (lp->lance_log_rx_bufs << 13) | (leptr >> 16);
+ ib->rx_ptr = leptr;
+ netdev_dbg(dev, "RX ptr: %08x\n", leptr);
+
+ /* Setup tx descriptor pointer */
+ leptr = LANCE_ADDR(&aib->btx_ring);
+ ib->tx_len = (lp->lance_log_tx_bufs << 13) | (leptr >> 16);
+ ib->tx_ptr = leptr;
+ netdev_dbg(dev, "TX ptr: %08x\n", leptr);
+
+ /* Clear the multicast filter */
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
+}
+
+static int init_restart_lance(struct lance_private *lp)
+{
+ volatile struct lance_regs *ll = lp->ll;
+ int i;
+
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_INIT;
+
+ /* Wait for the lance to complete initialization */
+ for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++)
+ barrier();
+ if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
+ pr_err("unopened after %d ticks, csr0=%04x\n", i, ll->rdp);
+ return -EIO;
+ }
+
+ /* Clear IDON by writing a "1", enable interrupts and start lance */
+ ll->rdp = LE_C0_IDON;
+ ll->rdp = LE_C0_INEA | LE_C0_STRT;
+
+ return 0;
+}
+
+static int lance_rx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_regs *ll = lp->ll;
+ volatile struct lance_rx_desc *rd;
+ unsigned char bits;
+
+#ifdef TEST_HITS
+ int i;
+ char buf[RX_RING_SIZE + 1];
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ char r1_own = ib->brx_ring[i].rmd1_bits & LE_R1_OWN;
+ if (i == lp->rx_new)
+ buf[i] = r1_own ? '_' : 'X';
+ else
+ buf[i] = r1_own ? '.' : '1';
+ }
+ buf[RX_RING_SIZE] = 0;
+
+ pr_debug("RxRing TestHits: [%s]\n", buf);
+#endif
+
+ ll->rdp = LE_C0_RINT | LE_C0_INEA;
+ for (rd = &ib->brx_ring[lp->rx_new];
+ !((bits = rd->rmd1_bits) & LE_R1_OWN);
+ rd = &ib->brx_ring[lp->rx_new]) {
+
+ /* We got an incomplete frame? */
+ if ((bits & LE_R1_POK) != LE_R1_POK) {
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ continue;
+ } else if (bits & LE_R1_ERR) {
+ /* Count only the end frame as a rx error,
+ * not the beginning
+ */
+ if (bits & LE_R1_BUF)
+ dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC)
+ dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL)
+ dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA)
+ dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP)
+ dev->stats.rx_errors++;
+ } else {
+ int len = (rd->mblength & 0xfff) - 4;
+ struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
+
+ if (!skb) {
+ dev->stats.rx_dropped++;
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
+ return 0;
+ }
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)&ib->rx_buf[lp->rx_new][0],
+ len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+ }
+
+ /* Return the packet to the pool */
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
+ }
+ return 0;
+}
+
+static int lance_tx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_regs *ll = lp->ll;
+ volatile struct lance_tx_desc *td;
+ int i, j;
+ int status;
+
+ /* csr0 is 2f3 */
+ ll->rdp = LE_C0_TINT | LE_C0_INEA;
+ /* csr0 is 73 */
+
+ j = lp->tx_old;
+ for (i = j; i != lp->tx_new; i = j) {
+ td = &ib->btx_ring[i];
+
+ /* If we hit a packet not owned by us, stop */
+ if (td->tmd1_bits & LE_T1_OWN)
+ break;
+
+ if (td->tmd1_bits & LE_T1_ERR) {
+ status = td->misc;
+
+ dev->stats.tx_errors++;
+ if (status & LE_T3_RTY)
+ dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL)
+ dev->stats.tx_window_errors++;
+
+ if (status & LE_T3_CLOS) {
+ dev->stats.tx_carrier_errors++;
+ if (lp->auto_select) {
+ lp->tpe = 1 - lp->tpe;
+ netdev_err(dev, "Carrier Lost, trying %s\n",
+ lp->tpe ? "TPE" : "AUI");
+ /* Stop the lance */
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ return 0;
+ }
+ }
+
+ /* buffer errors and underflows turn off
+ * the transmitter, so restart the adapter
+ */
+ if (status & (LE_T3_BUF | LE_T3_UFL)) {
+ dev->stats.tx_fifo_errors++;
+
+ netdev_err(dev, "Tx: ERR_BUF|ERR_UFL, restarting\n");
+ /* Stop the lance */
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ return 0;
+ }
+ } else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
+ /* So we don't count the packet more than once. */
+ td->tmd1_bits &= ~(LE_T1_POK);
+
+ /* One collision before packet was sent. */
+ if (td->tmd1_bits & LE_T1_EONE)
+ dev->stats.collisions++;
+
+ /* More than one collision, be optimistic. */
+ if (td->tmd1_bits & LE_T1_EMORE)
+ dev->stats.collisions += 2;
+
+ dev->stats.tx_packets++;
+ }
+
+ j = (j + 1) & lp->tx_ring_mod_mask;
+ }
+ lp->tx_old = j;
+ ll->rdp = LE_C0_TINT | LE_C0_INEA;
+ return 0;
+}
+
+static int lance_tx_buffs_avail(struct lance_private *lp)
+{
+ if (lp->tx_old <= lp->tx_new)
+ return lp->tx_old + lp->tx_ring_mod_mask - lp->tx_new;
+ return lp->tx_old - lp->tx_new - 1;
+}
+
+static irqreturn_t lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int csr0;
+
+ ll->rap = LE_CSR0; /* LANCE Controller Status */
+ csr0 = ll->rdp;
+
+ if (!(csr0 & LE_C0_INTR)) /* Check if any interrupt has */
+ return IRQ_NONE; /* been generated by the Lance. */
+
+ /* Acknowledge all the interrupt sources ASAP */
+ ll->rdp = csr0 & ~(LE_C0_INEA | LE_C0_TDMD | LE_C0_STOP | LE_C0_STRT |
+ LE_C0_INIT);
+
+ if (csr0 & LE_C0_ERR) {
+ /* Clear the error condition */
+ ll->rdp = LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | LE_C0_INEA;
+ }
+
+ if (csr0 & LE_C0_RINT)
+ lance_rx(dev);
+
+ if (csr0 & LE_C0_TINT)
+ lance_tx(dev);
+
+ /* Log misc errors. */
+ if (csr0 & LE_C0_BABL)
+ dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & LE_C0_MISS)
+ dev->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & LE_C0_MERR) {
+ netdev_err(dev, "Bus master arbitration failure, status %04x\n",
+ csr0);
+ /* Restart the chip. */
+ ll->rdp = LE_C0_STRT;
+ }
+
+ if (netif_queue_stopped(dev) && lance_tx_buffs_avail(lp) > 0)
+ netif_wake_queue(dev);
+
+ ll->rap = LE_CSR0;
+ ll->rdp = (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR |
+ LE_C0_IDON | LE_C0_INEA);
+ return IRQ_HANDLED;
+}
+
+static int lance_open(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int ret;
+
+ /* Stop the Lance */
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+
+ /* Install the Interrupt handler */
+ ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (ret)
+ return ret;
+
+ load_csrs(lp);
+ lance_init_ring(dev);
+
+ netif_start_queue(dev);
+
+ return init_restart_lance(lp);
+}
+
+static int lance_close(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->multicast_timer);
+
+ /* Stop the card */
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ return 0;
+}
+
+static inline int lance_reset(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int status;
+
+ /* Stop the lance */
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+
+ load_csrs(lp);
+
+ lance_init_ring(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_start_queue(dev);
+
+ status = init_restart_lance(lp);
+ netdev_dbg(dev, "Lance restart=%d\n", status);
+
+ return status;
+}
+
+static void lance_tx_timeout(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+
+ netdev_err(dev, "transmit timed out, status %04x, reset\n", ll->rdp);
+ lance_reset(dev);
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ volatile struct lance_init_block *ib = lp->init_block;
+ int entry, skblen;
+ int status = NETDEV_TX_OK;
+ unsigned long flags;
+
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ skblen = max_t(unsigned, skb->len, ETH_ZLEN);
+
+ local_irq_save(flags);
+
+ if (!lance_tx_buffs_avail(lp)) {
+ local_irq_restore(flags);
+ return NETDEV_TX_LOCKED;
+ }
+
+#ifdef DEBUG
+ /* dump the packet */
+ print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->data, 64, true);
+#endif
+ entry = lp->tx_new & lp->tx_ring_mod_mask;
+ ib->btx_ring[entry].length = (-skblen) | 0xf000;
+ ib->btx_ring[entry].misc = 0;
+
+ skb_copy_from_linear_data(skb, (void *)&ib->tx_buf[entry][0], skblen);
+
+ /* Now, give the packet to the lance */
+ ib->btx_ring[entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
+ lp->tx_new = (lp->tx_new+1) & lp->tx_ring_mod_mask;
+ dev->stats.tx_bytes += skblen;
+
+ if (lance_tx_buffs_avail(lp) <= 0)
+ netif_stop_queue(dev);
+
+ /* Kick the lance: transmit now */
+ ll->rdp = LE_C0_INEA | LE_C0_TDMD;
+ dev_kfree_skb(skb);
+
+ local_irq_restore(flags);
+
+ return status;
+}
+
+/* taken from the depca driver */
+static void lance_load_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile u16 *mcast_table = (u16 *)&ib->filter;
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI) {
+ ib->filter[0] = 0xffffffff;
+ ib->filter[1] = 0xffffffff;
+ return;
+ }
+ /* clear the multicast filter */
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
+
+ /* Add addresses */
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc_le(6, ha->addr);
+ crc = crc >> 26;
+ mcast_table[crc >> 4] |= 1 << (crc & 0xf);
+ }
+}
+
+static void lance_set_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_init_block *ib = lp->init_block;
+ volatile struct lance_regs *ll = lp->ll;
+
+ if (!netif_running(dev))
+ return;
+
+ if (lp->tx_old != lp->tx_new) {
+ mod_timer(&lp->multicast_timer, jiffies + 4);
+ netif_wake_queue(dev);
+ return;
+ }
+
+ netif_stop_queue(dev);
+
+ ll->rap = LE_CSR0;
+ ll->rdp = LE_C0_STOP;
+ lance_init_ring(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ ib->mode |= LE_MO_PROM;
+ } else {
+ ib->mode &= ~LE_MO_PROM;
+ lance_load_multicast(dev);
+ }
+ load_csrs(lp);
+ init_restart_lance(lp);
+ netif_wake_queue(dev);
+}
+
+static int a2065_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent);
+static void a2065_remove_one(struct zorro_dev *z);
+
+
+static struct zorro_device_id a2065_zorro_tbl[] = {
+ { ZORRO_PROD_CBM_A2065_1 },
+ { ZORRO_PROD_CBM_A2065_2 },
+ { ZORRO_PROD_AMERISTAR_A2065 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, a2065_zorro_tbl);
+
+static struct zorro_driver a2065_driver = {
+ .name = "a2065",
+ .id_table = a2065_zorro_tbl,
+ .probe = a2065_init_one,
+ .remove = a2065_remove_one,
+};
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_stop = lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_set_rx_mode = lance_set_multicast,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int a2065_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ struct net_device *dev;
+ struct lance_private *priv;
+ unsigned long board = z->resource.start;
+ unsigned long base_addr = board + A2065_LANCE;
+ unsigned long mem_start = board + A2065_RAM;
+ struct resource *r1, *r2;
+ u32 serial;
+ int err;
+
+ r1 = request_mem_region(base_addr, sizeof(struct lance_regs),
+ "Am7990");
+ if (!r1)
+ return -EBUSY;
+ r2 = request_mem_region(mem_start, A2065_RAM_SIZE, "RAM");
+ if (!r2) {
+ release_mem_region(base_addr, sizeof(struct lance_regs));
+ return -EBUSY;
+ }
+
+ dev = alloc_etherdev(sizeof(struct lance_private));
+ if (dev == NULL) {
+ release_mem_region(base_addr, sizeof(struct lance_regs));
+ release_mem_region(mem_start, A2065_RAM_SIZE);
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(dev);
+
+ r1->name = dev->name;
+ r2->name = dev->name;
+
+ serial = be32_to_cpu(z->rom.er_SerialNumber);
+ dev->dev_addr[0] = 0x00;
+ if (z->id != ZORRO_PROD_AMERISTAR_A2065) { /* Commodore */
+ dev->dev_addr[1] = 0x80;
+ dev->dev_addr[2] = 0x10;
+ } else { /* Ameristar */
+ dev->dev_addr[1] = 0x00;
+ dev->dev_addr[2] = 0x9f;
+ }
+ dev->dev_addr[3] = (serial >> 16) & 0xff;
+ dev->dev_addr[4] = (serial >> 8) & 0xff;
+ dev->dev_addr[5] = serial & 0xff;
+ dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr);
+ dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start);
+ dev->mem_end = dev->mem_start + A2065_RAM_SIZE;
+
+ priv->ll = (volatile struct lance_regs *)dev->base_addr;
+ priv->init_block = (struct lance_init_block *)dev->mem_start;
+ priv->lance_init_block = (struct lance_init_block *)A2065_RAM;
+ priv->auto_select = 0;
+ priv->busmaster_regval = LE_C3_BSWP;
+
+ priv->lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
+ priv->lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
+ priv->rx_ring_mod_mask = RX_RING_MOD_MASK;
+ priv->tx_ring_mod_mask = TX_RING_MOD_MASK;
+
+ dev->netdev_ops = &lance_netdev_ops;
+ dev->watchdog_timeo = 5*HZ;
+ dev->dma = 0;
+
+ init_timer(&priv->multicast_timer);
+ priv->multicast_timer.data = (unsigned long) dev;
+ priv->multicast_timer.function =
+ (void (*)(unsigned long))lance_set_multicast;
+
+ err = register_netdev(dev);
+ if (err) {
+ release_mem_region(base_addr, sizeof(struct lance_regs));
+ release_mem_region(mem_start, A2065_RAM_SIZE);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+
+ netdev_info(dev, "A2065 at 0x%08lx, Ethernet Address %pM\n",
+ board, dev->dev_addr);
+
+ return 0;
+}
+
+
+static void a2065_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr),
+ sizeof(struct lance_regs));
+ release_mem_region(ZTWO_PADDR(dev->mem_start), A2065_RAM_SIZE);
+ free_netdev(dev);
+}
+
+static int __init a2065_init_module(void)
+{
+ return zorro_register_driver(&a2065_driver);
+}
+
+static void __exit a2065_cleanup_module(void)
+{
+ zorro_unregister_driver(&a2065_driver);
+}
+
+module_init(a2065_init_module);
+module_exit(a2065_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/amd/a2065.h b/drivers/net/ethernet/amd/a2065.h
new file mode 100644
index 00000000000..5117759d4e9
--- /dev/null
+++ b/drivers/net/ethernet/amd/a2065.h
@@ -0,0 +1,173 @@
+/*
+ * Amiga Linux/68k A2065 Ethernet Driver
+ *
+ * (C) Copyright 1995 by Geert Uytterhoeven <geert@linux-m68k.org>
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ariadne.?: Amiga Linux/68k Ariadne Ethernet Driver
+ * (C) Copyright 1995 by Geert Uytterhoeven,
+ * Peter De Schrijver
+ *
+ * lance.c: An AMD LANCE ethernet driver for linux.
+ * Written 1993-94 by Donald Becker.
+ *
+ * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
+ * Advanced Micro Devices
+ * Publication #16907, Rev. B, Amendment/0, May 1994
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The A2065 is a Zorro-II board made by Commodore/Ameristar. It contains:
+ *
+ * - an Am7990 Local Area Network Controller for Ethernet (LANCE) with
+ * both 10BASE-2 (thin coax) and AUI (DB-15) connectors
+ */
+
+
+/*
+ * Am7990 Local Area Network Controller for Ethernet (LANCE)
+ */
+
+struct lance_regs {
+ unsigned short rdp; /* Register Data Port */
+ unsigned short rap; /* Register Address Port */
+};
+
+
+/*
+ * Am7990 Control and Status Registers
+ */
+
+#define LE_CSR0 0x0000 /* LANCE Controller Status */
+#define LE_CSR1 0x0001 /* IADR[15:0] */
+#define LE_CSR2 0x0002 /* IADR[23:16] */
+#define LE_CSR3 0x0003 /* Misc */
+
+
+/*
+ * Bit definitions for CSR0 (LANCE Controller Status)
+ */
+
+#define LE_C0_ERR 0x8000 /* Error */
+#define LE_C0_BABL 0x4000 /* Babble: Transmitted too many bits */
+#define LE_C0_CERR 0x2000 /* No Heartbeat (10BASE-T) */
+#define LE_C0_MISS 0x1000 /* Missed Frame */
+#define LE_C0_MERR 0x0800 /* Memory Error */
+#define LE_C0_RINT 0x0400 /* Receive Interrupt */
+#define LE_C0_TINT 0x0200 /* Transmit Interrupt */
+#define LE_C0_IDON 0x0100 /* Initialization Done */
+#define LE_C0_INTR 0x0080 /* Interrupt Flag */
+#define LE_C0_INEA 0x0040 /* Interrupt Enable */
+#define LE_C0_RXON 0x0020 /* Receive On */
+#define LE_C0_TXON 0x0010 /* Transmit On */
+#define LE_C0_TDMD 0x0008 /* Transmit Demand */
+#define LE_C0_STOP 0x0004 /* Stop */
+#define LE_C0_STRT 0x0002 /* Start */
+#define LE_C0_INIT 0x0001 /* Initialize */
+
+
+/*
+ * Bit definitions for CSR3
+ */
+
+#define LE_C3_BSWP 0x0004 /* Byte Swap
+ (on for big endian byte order) */
+#define LE_C3_ACON 0x0002 /* ALE Control
+ (on for active low ALE) */
+#define LE_C3_BCON 0x0001 /* Byte Control */
+
+
+/*
+ * Mode Flags
+ */
+
+#define LE_MO_PROM 0x8000 /* Promiscuous Mode */
+#define LE_MO_INTL 0x0040 /* Internal Loopback */
+#define LE_MO_DRTY 0x0020 /* Disable Retry */
+#define LE_MO_FCOLL 0x0010 /* Force Collision */
+#define LE_MO_DXMTFCS 0x0008 /* Disable Transmit CRC */
+#define LE_MO_LOOP 0x0004 /* Loopback Enable */
+#define LE_MO_DTX 0x0002 /* Disable Transmitter */
+#define LE_MO_DRX 0x0001 /* Disable Receiver */
+
+
+struct lance_rx_desc {
+ unsigned short rmd0; /* low address of packet */
+ unsigned char rmd1_bits; /* descriptor bits */
+ unsigned char rmd1_hadr; /* high address of packet */
+ short length; /* This length is 2s complement (negative)!
+ * Buffer length
+ */
+ unsigned short mblength; /* Aactual number of bytes received */
+};
+
+struct lance_tx_desc {
+ unsigned short tmd0; /* low address of packet */
+ unsigned char tmd1_bits; /* descriptor bits */
+ unsigned char tmd1_hadr; /* high address of packet */
+ short length; /* Length is 2s complement (negative)! */
+ unsigned short misc;
+};
+
+
+/*
+ * Receive Flags
+ */
+
+#define LE_R1_OWN 0x80 /* LANCE owns the descriptor */
+#define LE_R1_ERR 0x40 /* Error */
+#define LE_R1_FRA 0x20 /* Framing Error */
+#define LE_R1_OFL 0x10 /* Overflow Error */
+#define LE_R1_CRC 0x08 /* CRC Error */
+#define LE_R1_BUF 0x04 /* Buffer Error */
+#define LE_R1_SOP 0x02 /* Start of Packet */
+#define LE_R1_EOP 0x01 /* End of Packet */
+#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+
+/*
+ * Transmit Flags
+ */
+
+#define LE_T1_OWN 0x80 /* LANCE owns the descriptor */
+#define LE_T1_ERR 0x40 /* Error */
+#define LE_T1_RES 0x20 /* Reserved,
+ LANCE writes this with a zero */
+#define LE_T1_EMORE 0x10 /* More than one retry needed */
+#define LE_T1_EONE 0x08 /* One retry needed */
+#define LE_T1_EDEF 0x04 /* Deferred */
+#define LE_T1_SOP 0x02 /* Start of Packet */
+#define LE_T1_EOP 0x01 /* End of Packet */
+#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+
+/*
+ * Error Flags
+ */
+
+#define LE_T3_BUF 0x8000 /* Buffer Error */
+#define LE_T3_UFL 0x4000 /* Underflow Error */
+#define LE_T3_LCOL 0x1000 /* Late Collision */
+#define LE_T3_CLOS 0x0800 /* Loss of Carrier */
+#define LE_T3_RTY 0x0400 /* Retry Error */
+#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry */
+
+
+/*
+ * A2065 Expansion Board Structure
+ */
+
+#define A2065_LANCE 0x4000
+
+#define A2065_RAM 0x8000
+#define A2065_RAM_SIZE 0x8000
+
diff --git a/drivers/net/ethernet/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c
new file mode 100644
index 00000000000..87e727b921d
--- /dev/null
+++ b/drivers/net/ethernet/amd/am79c961a.c
@@ -0,0 +1,768 @@
+/*
+ * linux/drivers/net/ethernet/amd/am79c961a.c
+ *
+ * by Russell King <rmk@arm.linux.org.uk> 1995-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Derived from various things including skeleton.c
+ *
+ * This is a special driver for the am79c961A Lance chip used in the
+ * Intel (formally Digital Equipment Corp) EBSA110 platform. Please
+ * note that this can not be built as a module (it doesn't make sense).
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+
+#define TX_BUFFERS 15
+#define RX_BUFFERS 25
+
+#include "am79c961a.h"
+
+static irqreturn_t
+am79c961_interrupt (int irq, void *dev_id);
+
+static unsigned int net_debug = NET_DEBUG;
+
+static const char version[] =
+ "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n";
+
+/* --------------------------------------------------------------------------- */
+
+#ifdef __arm__
+static void write_rreg(u_long base, u_int reg, u_int val)
+{
+ asm volatile(
+ "str%?h %1, [%2] @ NET_RAP\n\t"
+ "str%?h %0, [%2, #-4] @ NET_RDP"
+ :
+ : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
+}
+
+static inline unsigned short read_rreg(u_long base_addr, u_int reg)
+{
+ unsigned short v;
+ asm volatile(
+ "str%?h %1, [%2] @ NET_RAP\n\t"
+ "ldr%?h %0, [%2, #-4] @ NET_RDP"
+ : "=r" (v)
+ : "r" (reg), "r" (ISAIO_BASE + 0x0464));
+ return v;
+}
+
+static inline void write_ireg(u_long base, u_int reg, u_int val)
+{
+ asm volatile(
+ "str%?h %1, [%2] @ NET_RAP\n\t"
+ "str%?h %0, [%2, #8] @ NET_IDP"
+ :
+ : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
+}
+
+static inline unsigned short read_ireg(u_long base_addr, u_int reg)
+{
+ u_short v;
+ asm volatile(
+ "str%?h %1, [%2] @ NAT_RAP\n\t"
+ "ldr%?h %0, [%2, #8] @ NET_IDP\n\t"
+ : "=r" (v)
+ : "r" (reg), "r" (ISAIO_BASE + 0x0464));
+ return v;
+}
+
+#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
+#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1))
+
+static void
+am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
+{
+ offset = ISAMEM_BASE + (offset << 1);
+ length = (length + 1) & ~1;
+ if ((int)buf & 2) {
+ asm volatile("str%?h %2, [%0], #4"
+ : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+ buf += 2;
+ length -= 2;
+ }
+ while (length > 8) {
+ register unsigned int tmp asm("r2"), tmp2 asm("r3");
+ asm volatile(
+ "ldm%?ia %0!, {%1, %2}"
+ : "+r" (buf), "=&r" (tmp), "=&r" (tmp2));
+ length -= 8;
+ asm volatile(
+ "str%?h %1, [%0], #4\n\t"
+ "mov%? %1, %1, lsr #16\n\t"
+ "str%?h %1, [%0], #4\n\t"
+ "str%?h %2, [%0], #4\n\t"
+ "mov%? %2, %2, lsr #16\n\t"
+ "str%?h %2, [%0], #4"
+ : "+r" (offset), "=&r" (tmp), "=&r" (tmp2));
+ }
+ while (length > 0) {
+ asm volatile("str%?h %2, [%0], #4"
+ : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+ buf += 2;
+ length -= 2;
+ }
+}
+
+static void
+am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
+{
+ offset = ISAMEM_BASE + (offset << 1);
+ length = (length + 1) & ~1;
+ if ((int)buf & 2) {
+ unsigned int tmp;
+ asm volatile(
+ "ldr%?h %2, [%0], #4\n\t"
+ "str%?b %2, [%1], #1\n\t"
+ "mov%? %2, %2, lsr #8\n\t"
+ "str%?b %2, [%1], #1"
+ : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
+ length -= 2;
+ }
+ while (length > 8) {
+ register unsigned int tmp asm("r2"), tmp2 asm("r3"), tmp3;
+ asm volatile(
+ "ldr%?h %2, [%0], #4\n\t"
+ "ldr%?h %4, [%0], #4\n\t"
+ "ldr%?h %3, [%0], #4\n\t"
+ "orr%? %2, %2, %4, lsl #16\n\t"
+ "ldr%?h %4, [%0], #4\n\t"
+ "orr%? %3, %3, %4, lsl #16\n\t"
+ "stm%?ia %1!, {%2, %3}"
+ : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
+ : "0" (offset), "1" (buf));
+ length -= 8;
+ }
+ while (length > 0) {
+ unsigned int tmp;
+ asm volatile(
+ "ldr%?h %2, [%0], #4\n\t"
+ "str%?b %2, [%1], #1\n\t"
+ "mov%? %2, %2, lsr #8\n\t"
+ "str%?b %2, [%1], #1"
+ : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
+ length -= 2;
+ }
+}
+#else
+#error Not compatible
+#endif
+
+static int
+am79c961_ramtest(struct net_device *dev, unsigned int val)
+{
+ unsigned char *buffer = kmalloc (65536, GFP_KERNEL);
+ int i, error = 0, errorcount = 0;
+
+ if (!buffer)
+ return 0;
+ memset (buffer, val, 65536);
+ am_writebuffer(dev, 0, buffer, 65536);
+ memset (buffer, val ^ 255, 65536);
+ am_readbuffer(dev, 0, buffer, 65536);
+ for (i = 0; i < 65536; i++) {
+ if (buffer[i] != val && !error) {
+ printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i);
+ error = 1;
+ errorcount ++;
+ } else if (error && buffer[i] == val) {
+ printk ("%05X\n", i);
+ error = 0;
+ }
+ }
+ if (error)
+ printk ("10000\n");
+ kfree (buffer);
+ return errorcount;
+}
+
+static void am79c961_mc_hash(char *addr, u16 *hash)
+{
+ int idx, bit;
+ u32 crc;
+
+ crc = ether_crc_le(ETH_ALEN, addr);
+
+ idx = crc >> 30;
+ bit = (crc >> 26) & 15;
+
+ hash[idx] |= 1 << bit;
+}
+
+static unsigned int am79c961_get_rx_mode(struct net_device *dev, u16 *hash)
+{
+ unsigned int mode = MODE_PORT_10BT;
+
+ if (dev->flags & IFF_PROMISC) {
+ mode |= MODE_PROMISC;
+ memset(hash, 0xff, 4 * sizeof(*hash));
+ } else if (dev->flags & IFF_ALLMULTI) {
+ memset(hash, 0xff, 4 * sizeof(*hash));
+ } else {
+ struct netdev_hw_addr *ha;
+
+ memset(hash, 0, 4 * sizeof(*hash));
+
+ netdev_for_each_mc_addr(ha, dev)
+ am79c961_mc_hash(ha->addr, hash);
+ }
+
+ return mode;
+}
+
+static void
+am79c961_init_for_open(struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ unsigned char *p;
+ u_int hdr_addr, first_free_addr;
+ u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
+ int i;
+
+ /*
+ * Stop the chip.
+ */
+ spin_lock_irqsave(&priv->chip_lock, flags);
+ write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+
+ write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */
+ write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */
+ write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */
+ write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
+
+ for (i = LADRL; i <= LADRH; i++)
+ write_rreg (dev->base_addr, i, multi_hash[i - LADRL]);
+
+ for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
+ write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
+
+ write_rreg (dev->base_addr, MODE, mode);
+ write_rreg (dev->base_addr, POLLINT, 0);
+ write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
+ write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
+
+ first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
+ hdr_addr = 0;
+
+ priv->rxhead = 0;
+ priv->rxtail = 0;
+ priv->rxhdr = hdr_addr;
+
+ for (i = 0; i < RX_BUFFERS; i++) {
+ priv->rxbuffer[i] = first_free_addr;
+ am_writeword (dev, hdr_addr, first_free_addr);
+ am_writeword (dev, hdr_addr + 2, RMD_OWN);
+ am_writeword (dev, hdr_addr + 4, (-1600));
+ am_writeword (dev, hdr_addr + 6, 0);
+ first_free_addr += 1600;
+ hdr_addr += 8;
+ }
+ priv->txhead = 0;
+ priv->txtail = 0;
+ priv->txhdr = hdr_addr;
+ for (i = 0; i < TX_BUFFERS; i++) {
+ priv->txbuffer[i] = first_free_addr;
+ am_writeword (dev, hdr_addr, first_free_addr);
+ am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP);
+ am_writeword (dev, hdr_addr + 4, 0xf000);
+ am_writeword (dev, hdr_addr + 6, 0);
+ first_free_addr += 1600;
+ hdr_addr += 8;
+ }
+
+ write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
+ write_rreg (dev->base_addr, BASERXH, 0);
+ write_rreg (dev->base_addr, BASETXL, priv->txhdr);
+ write_rreg (dev->base_addr, BASERXH, 0);
+ write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+ write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
+ write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM);
+ write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
+}
+
+static void am79c961_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_priv *priv = netdev_priv(dev);
+ unsigned int lnkstat, carrier;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->chip_lock, flags);
+ lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+ carrier = netif_carrier_ok(dev);
+
+ if (lnkstat && !carrier) {
+ netif_carrier_on(dev);
+ printk("%s: link up\n", dev->name);
+ } else if (!lnkstat && carrier) {
+ netif_carrier_off(dev);
+ printk("%s: link down\n", dev->name);
+ }
+
+ mod_timer(&priv->timer, jiffies + msecs_to_jiffies(500));
+}
+
+/*
+ * Open/initialize the board.
+ */
+static int
+am79c961_open(struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev);
+ if (ret)
+ return ret;
+
+ am79c961_init_for_open(dev);
+
+ netif_carrier_off(dev);
+
+ priv->timer.expires = jiffies;
+ add_timer(&priv->timer);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * The inverse routine to am79c961_open().
+ */
+static int
+am79c961_close(struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ del_timer_sync(&priv->timer);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ spin_lock_irqsave(&priv->chip_lock, flags);
+ write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+ write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+
+ free_irq (dev->irq, dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear promiscuous/multicast mode filter for this adapter.
+ */
+static void am79c961_setmulticastlist (struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
+ int i, stopped;
+
+ spin_lock_irqsave(&priv->chip_lock, flags);
+
+ stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
+
+ if (!stopped) {
+ /*
+ * Put the chip into suspend mode
+ */
+ write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
+
+ /*
+ * Spin waiting for chip to report suspend mode
+ */
+ while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+ nop();
+ spin_lock_irqsave(&priv->chip_lock, flags);
+ }
+ }
+
+ /*
+ * Update the multicast hash table
+ */
+ for (i = 0; i < ARRAY_SIZE(multi_hash); i++)
+ write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
+
+ /*
+ * Write the mode register
+ */
+ write_rreg(dev->base_addr, MODE, mode);
+
+ if (!stopped) {
+ /*
+ * Put the chip back into running mode
+ */
+ write_rreg(dev->base_addr, CTRL1, 0);
+ }
+
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+}
+
+static void am79c961_timeout(struct net_device *dev)
+{
+ printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n",
+ dev->name);
+
+ /*
+ * ought to do some setup of the tx side here
+ */
+
+ netif_wake_queue(dev);
+}
+
+/*
+ * Transmit a packet
+ */
+static int
+am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+ unsigned int hdraddr, bufaddr;
+ unsigned int head;
+ unsigned long flags;
+
+ head = priv->txhead;
+ hdraddr = priv->txhdr + (head << 3);
+ bufaddr = priv->txbuffer[head];
+ head += 1;
+ if (head >= TX_BUFFERS)
+ head = 0;
+
+ am_writebuffer (dev, bufaddr, skb->data, skb->len);
+ am_writeword (dev, hdraddr + 4, -skb->len);
+ am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+ priv->txhead = head;
+
+ spin_lock_irqsave(&priv->chip_lock, flags);
+ write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
+ spin_unlock_irqrestore(&priv->chip_lock, flags);
+
+ /*
+ * If the next packet is owned by the ethernet device,
+ * then the tx ring is full and we can't add another
+ * packet.
+ */
+ if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
+ netif_stop_queue(dev);
+
+ dev_consume_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+static void
+am79c961_rx(struct net_device *dev, struct dev_priv *priv)
+{
+ do {
+ struct sk_buff *skb;
+ u_int hdraddr;
+ u_int pktaddr;
+ u_int status;
+ int len;
+
+ hdraddr = priv->rxhdr + (priv->rxtail << 3);
+ pktaddr = priv->rxbuffer[priv->rxtail];
+
+ status = am_readword (dev, hdraddr + 2);
+ if (status & RMD_OWN) /* do we own it? */
+ break;
+
+ priv->rxtail ++;
+ if (priv->rxtail >= RX_BUFFERS)
+ priv->rxtail = 0;
+
+ if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) {
+ am_writeword (dev, hdraddr + 2, RMD_OWN);
+ dev->stats.rx_errors++;
+ if (status & RMD_ERR) {
+ if (status & RMD_FRAM)
+ dev->stats.rx_frame_errors++;
+ if (status & RMD_CRC)
+ dev->stats.rx_crc_errors++;
+ } else if (status & RMD_STP)
+ dev->stats.rx_length_errors++;
+ continue;
+ }
+
+ len = am_readword(dev, hdraddr + 6);
+ skb = netdev_alloc_skb(dev, len + 2);
+
+ if (skb) {
+ skb_reserve(skb, 2);
+
+ am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
+ am_writeword(dev, hdraddr + 2, RMD_OWN);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_bytes += len;
+ dev->stats.rx_packets++;
+ } else {
+ am_writeword (dev, hdraddr + 2, RMD_OWN);
+ dev->stats.rx_dropped++;
+ break;
+ }
+ } while (1);
+}
+
+/*
+ * Update stats for the transmitted packet
+ */
+static void
+am79c961_tx(struct net_device *dev, struct dev_priv *priv)
+{
+ do {
+ short len;
+ u_int hdraddr;
+ u_int status;
+
+ hdraddr = priv->txhdr + (priv->txtail << 3);
+ status = am_readword (dev, hdraddr + 2);
+ if (status & TMD_OWN)
+ break;
+
+ priv->txtail ++;
+ if (priv->txtail >= TX_BUFFERS)
+ priv->txtail = 0;
+
+ if (status & TMD_ERR) {
+ u_int status2;
+
+ dev->stats.tx_errors++;
+
+ status2 = am_readword (dev, hdraddr + 6);
+
+ /*
+ * Clear the error byte
+ */
+ am_writeword (dev, hdraddr + 6, 0);
+
+ if (status2 & TST_RTRY)
+ dev->stats.collisions += 16;
+ if (status2 & TST_LCOL)
+ dev->stats.tx_window_errors++;
+ if (status2 & TST_LCAR)
+ dev->stats.tx_carrier_errors++;
+ if (status2 & TST_UFLO)
+ dev->stats.tx_fifo_errors++;
+ continue;
+ }
+ dev->stats.tx_packets++;
+ len = am_readword (dev, hdraddr + 4);
+ dev->stats.tx_bytes += -len;
+ } while (priv->txtail != priv->txhead);
+
+ netif_wake_queue(dev);
+}
+
+static irqreturn_t
+am79c961_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct dev_priv *priv = netdev_priv(dev);
+ u_int status, n = 100;
+ int handled = 0;
+
+ do {
+ status = read_rreg(dev->base_addr, CSR0);
+ write_rreg(dev->base_addr, CSR0, status &
+ (CSR0_IENA|CSR0_TINT|CSR0_RINT|
+ CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL));
+
+ if (status & CSR0_RINT) {
+ handled = 1;
+ am79c961_rx(dev, priv);
+ }
+ if (status & CSR0_TINT) {
+ handled = 1;
+ am79c961_tx(dev, priv);
+ }
+ if (status & CSR0_MISS) {
+ handled = 1;
+ dev->stats.rx_dropped++;
+ }
+ if (status & CSR0_CERR) {
+ handled = 1;
+ mod_timer(&priv->timer, jiffies);
+ }
+ } while (--n && status & (CSR0_RINT | CSR0_TINT));
+
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void am79c961_poll_controller(struct net_device *dev)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ am79c961_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+}
+#endif
+
+/*
+ * Initialise the chip. Note that we always expect
+ * to be entered with interrupts enabled.
+ */
+static int
+am79c961_hw_init(struct net_device *dev)
+{
+ struct dev_priv *priv = netdev_priv(dev);
+
+ spin_lock_irq(&priv->chip_lock);
+ write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+ write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+ spin_unlock_irq(&priv->chip_lock);
+
+ am79c961_ramtest(dev, 0x66);
+ am79c961_ramtest(dev, 0x99);
+
+ return 0;
+}
+
+static void __init am79c961_banner(void)
+{
+ static unsigned version_printed;
+
+ if (net_debug && version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+}
+static const struct net_device_ops am79c961_netdev_ops = {
+ .ndo_open = am79c961_open,
+ .ndo_stop = am79c961_close,
+ .ndo_start_xmit = am79c961_sendpacket,
+ .ndo_set_rx_mode = am79c961_setmulticastlist,
+ .ndo_tx_timeout = am79c961_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = am79c961_poll_controller,
+#endif
+};
+
+static int am79c961_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct net_device *dev;
+ struct dev_priv *priv;
+ int i, ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -ENODEV;
+
+ dev = alloc_etherdev(sizeof(struct dev_priv));
+ ret = -ENOMEM;
+ if (!dev)
+ goto out;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ priv = netdev_priv(dev);
+
+ /*
+ * Fixed address and IRQ lines here.
+ * The PNP initialisation should have been
+ * done by the ether bootp loader.
+ */
+ dev->base_addr = res->start;
+ ret = platform_get_irq(pdev, 0);
+
+ if (ret < 0) {
+ ret = -ENODEV;
+ goto nodev;
+ }
+ dev->irq = ret;
+
+ ret = -ENODEV;
+ if (!request_region(dev->base_addr, 0x18, dev->name))
+ goto nodev;
+
+ /*
+ * Reset the device.
+ */
+ inb(dev->base_addr + NET_RESET);
+ udelay(5);
+
+ /*
+ * Check the manufacturer part of the
+ * ether address.
+ */
+ if (inb(dev->base_addr) != 0x08 ||
+ inb(dev->base_addr + 2) != 0x00 ||
+ inb(dev->base_addr + 4) != 0x2b)
+ goto release;
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff;
+
+ am79c961_banner();
+
+ spin_lock_init(&priv->chip_lock);
+ init_timer(&priv->timer);
+ priv->timer.data = (unsigned long)dev;
+ priv->timer.function = am79c961_timer;
+
+ if (am79c961_hw_init(dev))
+ goto release;
+
+ dev->netdev_ops = &am79c961_netdev_ops;
+
+ ret = register_netdev(dev);
+ if (ret == 0) {
+ printk(KERN_INFO "%s: ether address %pM\n",
+ dev->name, dev->dev_addr);
+ return 0;
+ }
+
+release:
+ release_region(dev->base_addr, 0x18);
+nodev:
+ free_netdev(dev);
+out:
+ return ret;
+}
+
+static struct platform_driver am79c961_driver = {
+ .probe = am79c961_probe,
+ .driver = {
+ .name = "am79c961",
+ },
+};
+
+static int __init am79c961_init(void)
+{
+ return platform_driver_register(&am79c961_driver);
+}
+
+__initcall(am79c961_init);
diff --git a/drivers/net/ethernet/amd/am79c961a.h b/drivers/net/ethernet/amd/am79c961a.h
new file mode 100644
index 00000000000..9f384b79507
--- /dev/null
+++ b/drivers/net/ethernet/amd/am79c961a.h
@@ -0,0 +1,145 @@
+/*
+ * linux/drivers/net/ethernet/amd/am79c961a.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_am79c961a_H
+#define _LINUX_am79c961a_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX 2
+#define DEBUG_RX 4
+#define DEBUG_INT 8
+#define DEBUG_IC 16
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+
+#define NET_UID 0
+#define NET_RDP 0x10
+#define NET_RAP 0x12
+#define NET_RESET 0x14
+#define NET_IDP 0x16
+
+/*
+ * RAP registers
+ */
+#define CSR0 0
+#define CSR0_INIT 0x0001
+#define CSR0_STRT 0x0002
+#define CSR0_STOP 0x0004
+#define CSR0_TDMD 0x0008
+#define CSR0_TXON 0x0010
+#define CSR0_RXON 0x0020
+#define CSR0_IENA 0x0040
+#define CSR0_INTR 0x0080
+#define CSR0_IDON 0x0100
+#define CSR0_TINT 0x0200
+#define CSR0_RINT 0x0400
+#define CSR0_MERR 0x0800
+#define CSR0_MISS 0x1000
+#define CSR0_CERR 0x2000
+#define CSR0_BABL 0x4000
+#define CSR0_ERR 0x8000
+
+#define CSR3 3
+#define CSR3_EMBA 0x0008
+#define CSR3_DXMT2PD 0x0010
+#define CSR3_LAPPEN 0x0020
+#define CSR3_DXSUFLO 0x0040
+#define CSR3_IDONM 0x0100
+#define CSR3_TINTM 0x0200
+#define CSR3_RINTM 0x0400
+#define CSR3_MERRM 0x0800
+#define CSR3_MISSM 0x1000
+#define CSR3_BABLM 0x4000
+#define CSR3_MASKALL 0x5F00
+
+#define CSR4 4
+#define CSR4_JABM 0x0001
+#define CSR4_JAB 0x0002
+#define CSR4_TXSTRTM 0x0004
+#define CSR4_TXSTRT 0x0008
+#define CSR4_RCVCCOM 0x0010
+#define CSR4_RCVCCO 0x0020
+#define CSR4_MFCOM 0x0100
+#define CSR4_MFCO 0x0200
+#define CSR4_ASTRP_RCV 0x0400
+#define CSR4_APAD_XMIT 0x0800
+
+#define CTRL1 5
+#define CTRL1_SPND 0x0001
+
+#define LADRL 8
+#define LADRM1 9
+#define LADRM2 10
+#define LADRH 11
+#define PADRL 12
+#define PADRM 13
+#define PADRH 14
+
+#define MODE 15
+#define MODE_DISRX 0x0001
+#define MODE_DISTX 0x0002
+#define MODE_LOOP 0x0004
+#define MODE_DTCRC 0x0008
+#define MODE_COLL 0x0010
+#define MODE_DRETRY 0x0020
+#define MODE_INTLOOP 0x0040
+#define MODE_PORT_AUI 0x0000
+#define MODE_PORT_10BT 0x0080
+#define MODE_DRXPA 0x2000
+#define MODE_DRXBA 0x4000
+#define MODE_PROMISC 0x8000
+
+#define BASERXL 24
+#define BASERXH 25
+#define BASETXL 30
+#define BASETXH 31
+
+#define POLLINT 47
+
+#define SIZERXR 76
+#define SIZETXR 78
+
+#define CSR_MFC 112
+
+#define RMD_ENP 0x0100
+#define RMD_STP 0x0200
+#define RMD_CRC 0x0800
+#define RMD_FRAM 0x2000
+#define RMD_ERR 0x4000
+#define RMD_OWN 0x8000
+
+#define TMD_ENP 0x0100
+#define TMD_STP 0x0200
+#define TMD_MORE 0x1000
+#define TMD_ERR 0x4000
+#define TMD_OWN 0x8000
+
+#define TST_RTRY 0x0400
+#define TST_LCAR 0x0800
+#define TST_LCOL 0x1000
+#define TST_UFLO 0x4000
+#define TST_BUFF 0x8000
+
+#define ISALED0 0x0004
+#define ISALED0_LNKST 0x8000
+
+struct dev_priv {
+ unsigned long rxbuffer[RX_BUFFERS];
+ unsigned long txbuffer[TX_BUFFERS];
+ unsigned char txhead;
+ unsigned char txtail;
+ unsigned char rxhead;
+ unsigned char rxtail;
+ unsigned long rxhdr;
+ unsigned long txhdr;
+ spinlock_t chip_lock;
+ struct timer_list timer;
+};
+
+#endif
diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c
new file mode 100644
index 00000000000..068dc7cad5f
--- /dev/null
+++ b/drivers/net/ethernet/amd/amd8111e.c
@@ -0,0 +1,1982 @@
+
+/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2004 Advanced Micro Devices
+ *
+ *
+ * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ]
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c]
+ * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ]
+ * Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.[ pcnet32.c ]
+ * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ]
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+Module Name:
+
+ amd8111e.c
+
+Abstract:
+
+ AMD8111 based 10/100 Ethernet Controller Driver.
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+ 3.0.0
+ Initial Revision.
+ 3.0.1
+ 1. Dynamic interrupt coalescing.
+ 2. Removed prev_stats.
+ 3. MII support.
+ 4. Dynamic IPG support
+ 3.0.2 05/29/2003
+ 1. Bug fix: Fixed failure to send jumbo packets larger than 4k.
+ 2. Bug fix: Fixed VLAN support failure.
+ 3. Bug fix: Fixed receive interrupt coalescing bug.
+ 4. Dynamic IPG support is disabled by default.
+ 3.0.3 06/05/2003
+ 1. Bug fix: Fixed failure to close the interface if SMP is enabled.
+ 3.0.4 12/09/2003
+ 1. Added set_mac_address routine for bonding driver support.
+ 2. Tested the driver for bonding support
+ 3. Bug fix: Fixed mismach in actual receive buffer lenth and lenth
+ indicated to the h/w.
+ 4. Modified amd8111e_rx() routine to receive all the received packets
+ in the first interrupt.
+ 5. Bug fix: Corrected rx_errors reported in get_stats() function.
+ 3.0.5 03/22/2004
+ 1. Added NAPI support
+
+*/
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/ctype.h>
+#include <linux/crc32.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define AMD8111E_VLAN_TAG_USED 1
+#else
+#define AMD8111E_VLAN_TAG_USED 0
+#endif
+
+#include "amd8111e.h"
+#define MODULE_NAME "amd8111e"
+#define MODULE_VERS "3.0.7"
+MODULE_AUTHOR("Advanced Micro Devices, Inc.");
+MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version "MODULE_VERS);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, amd8111e_pci_tbl);
+module_param_array(speed_duplex, int, NULL, 0);
+MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotiate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex");
+module_param_array(coalesce, bool, NULL, 0);
+MODULE_PARM_DESC(coalesce, "Enable or Disable interrupt coalescing, 1: Enable, 0: Disable");
+module_param_array(dynamic_ipg, bool, NULL, 0);
+MODULE_PARM_DESC(dynamic_ipg, "Enable or Disable dynamic IPG, 1: Enable, 0: Disable");
+
+static DEFINE_PCI_DEVICE_TABLE(amd8111e_pci_tbl) = {
+
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD8111E_7462,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { 0, }
+
+};
+/*
+This function will read the PHY registers.
+*/
+static int amd8111e_read_phy(struct amd8111e_priv* lp, int phy_id, int reg, u32* val)
+{
+ void __iomem *mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int repeat= REPEAT_CNT;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl( mmio + PHY_ACCESS );
+
+ writel( PHY_RD_CMD | ((phy_id & 0x1f) << 21) |
+ ((reg & 0x1f) << 16), mmio +PHY_ACCESS);
+ do{
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write data */
+ } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+ if(reg_val & PHY_RD_ERR)
+ goto err_phy_read;
+
+ *val = reg_val & 0xffff;
+ return 0;
+err_phy_read:
+ *val = 0;
+ return -EINVAL;
+
+}
+
+/*
+This function will write into PHY registers.
+*/
+static int amd8111e_write_phy(struct amd8111e_priv* lp,int phy_id, int reg, u32 val)
+{
+ unsigned int repeat = REPEAT_CNT;
+ void __iomem *mmio = lp->mmio;
+ unsigned int reg_val;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl( mmio + PHY_ACCESS );
+
+ writel( PHY_WR_CMD | ((phy_id & 0x1f) << 21) |
+ ((reg & 0x1f) << 16)|val, mmio + PHY_ACCESS);
+
+ do{
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write the data */
+ } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+
+ if(reg_val & PHY_RD_ERR)
+ goto err_phy_write;
+
+ return 0;
+
+err_phy_write:
+ return -EINVAL;
+
+}
+/*
+This is the mii register read function provided to the mii interface.
+*/
+static int amd8111e_mdio_read(struct net_device * dev, int phy_id, int reg_num)
+{
+ struct amd8111e_priv* lp = netdev_priv(dev);
+ unsigned int reg_val;
+
+ amd8111e_read_phy(lp,phy_id,reg_num,&reg_val);
+ return reg_val;
+
+}
+
+/*
+This is the mii register write function provided to the mii interface.
+*/
+static void amd8111e_mdio_write(struct net_device * dev, int phy_id, int reg_num, int val)
+{
+ struct amd8111e_priv* lp = netdev_priv(dev);
+
+ amd8111e_write_phy(lp, phy_id, reg_num, val);
+}
+
+/*
+This function will set PHY speed. During initialization sets the original speed to 100 full.
+*/
+static void amd8111e_set_ext_phy(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ u32 bmcr,advert,tmp;
+
+ /* Determine mii register values to set the speed */
+ advert = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_ADVERTISE);
+ tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ switch (lp->ext_phy_option){
+
+ default:
+ case SPEED_AUTONEG: /* advertise all values */
+ tmp |= ( ADVERTISE_10HALF|ADVERTISE_10FULL|
+ ADVERTISE_100HALF|ADVERTISE_100FULL) ;
+ break;
+ case SPEED10_HALF:
+ tmp |= ADVERTISE_10HALF;
+ break;
+ case SPEED10_FULL:
+ tmp |= ADVERTISE_10FULL;
+ break;
+ case SPEED100_HALF:
+ tmp |= ADVERTISE_100HALF;
+ break;
+ case SPEED100_FULL:
+ tmp |= ADVERTISE_100FULL;
+ break;
+ }
+
+ if(advert != tmp)
+ amd8111e_mdio_write(dev, lp->ext_phy_addr, MII_ADVERTISE, tmp);
+ /* Restart auto negotiation */
+ bmcr = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_BMCR);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ amd8111e_mdio_write(dev, lp->ext_phy_addr, MII_BMCR, bmcr);
+
+}
+
+/*
+This function will unmap skb->data space and will free
+all transmit and receive skbuffs.
+*/
+static int amd8111e_free_skbs(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ struct sk_buff* rx_skbuff;
+ int i;
+
+ /* Freeing transmit skbs */
+ for(i = 0; i < NUM_TX_BUFFERS; i++){
+ if(lp->tx_skbuff[i]){
+ pci_unmap_single(lp->pci_dev,lp->tx_dma_addr[i], lp->tx_skbuff[i]->len,PCI_DMA_TODEVICE);
+ dev_kfree_skb (lp->tx_skbuff[i]);
+ lp->tx_skbuff[i] = NULL;
+ lp->tx_dma_addr[i] = 0;
+ }
+ }
+ /* Freeing previously allocated receive buffers */
+ for (i = 0; i < NUM_RX_BUFFERS; i++){
+ rx_skbuff = lp->rx_skbuff[i];
+ if(rx_skbuff != NULL){
+ pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[i],
+ lp->rx_buff_len - 2,PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(lp->rx_skbuff[i]);
+ lp->rx_skbuff[i] = NULL;
+ lp->rx_dma_addr[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+This will set the receive buffer length corresponding to the mtu size of networkinterface.
+*/
+static inline void amd8111e_set_rx_buff_len(struct net_device* dev)
+{
+ struct amd8111e_priv* lp = netdev_priv(dev);
+ unsigned int mtu = dev->mtu;
+
+ if (mtu > ETH_DATA_LEN){
+ /* MTU + ethernet header + FCS
+ + optional VLAN tag + skb reserve space 2 */
+
+ lp->rx_buff_len = mtu + ETH_HLEN + 10;
+ lp->options |= OPTION_JUMBO_ENABLE;
+ } else{
+ lp->rx_buff_len = PKT_BUFF_SZ;
+ lp->options &= ~OPTION_JUMBO_ENABLE;
+ }
+}
+
+/*
+This function will free all the previously allocated buffers, determine new receive buffer length and will allocate new receive buffers. This function also allocates and initializes both the transmitter and receive hardware descriptors.
+ */
+static int amd8111e_init_ring(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int i;
+
+ lp->rx_idx = lp->tx_idx = 0;
+ lp->tx_complete_idx = 0;
+ lp->tx_ring_idx = 0;
+
+
+ if(lp->opened)
+ /* Free previously allocated transmit and receive skbs */
+ amd8111e_free_skbs(dev);
+
+ else{
+ /* allocate the tx and rx descriptors */
+ if((lp->tx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,
+ &lp->tx_ring_dma_addr)) == NULL)
+
+ goto err_no_mem;
+
+ if((lp->rx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,
+ &lp->rx_ring_dma_addr)) == NULL)
+
+ goto err_free_tx_ring;
+
+ }
+ /* Set new receive buff size */
+ amd8111e_set_rx_buff_len(dev);
+
+ /* Allocating receive skbs */
+ for (i = 0; i < NUM_RX_BUFFERS; i++) {
+
+ lp->rx_skbuff[i] = netdev_alloc_skb(dev, lp->rx_buff_len);
+ if (!lp->rx_skbuff[i]) {
+ /* Release previos allocated skbs */
+ for(--i; i >= 0 ;i--)
+ dev_kfree_skb(lp->rx_skbuff[i]);
+ goto err_free_rx_ring;
+ }
+ skb_reserve(lp->rx_skbuff[i],2);
+ }
+ /* Initilaizing receive descriptors */
+ for (i = 0; i < NUM_RX_BUFFERS; i++) {
+ lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev,
+ lp->rx_skbuff[i]->data,lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
+
+ lp->rx_ring[i].buff_phy_addr = cpu_to_le32(lp->rx_dma_addr[i]);
+ lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len-2);
+ wmb();
+ lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT);
+ }
+
+ /* Initializing transmit descriptors */
+ for (i = 0; i < NUM_TX_RING_DR; i++) {
+ lp->tx_ring[i].buff_phy_addr = 0;
+ lp->tx_ring[i].tx_flags = 0;
+ lp->tx_ring[i].buff_count = 0;
+ }
+
+ return 0;
+
+err_free_rx_ring:
+
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,lp->rx_ring,
+ lp->rx_ring_dma_addr);
+
+err_free_tx_ring:
+
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,lp->tx_ring,
+ lp->tx_ring_dma_addr);
+
+err_no_mem:
+ return -ENOMEM;
+}
+/* This function will set the interrupt coalescing according to the input arguments */
+static int amd8111e_set_coalesce(struct net_device * dev, enum coal_mode cmod)
+{
+ unsigned int timeout;
+ unsigned int event_count;
+
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ void __iomem *mmio = lp->mmio;
+ struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf;
+
+
+ switch(cmod)
+ {
+ case RX_INTR_COAL :
+ timeout = coal_conf->rx_timeout;
+ event_count = coal_conf->rx_event_count;
+ if( timeout > MAX_TIMEOUT ||
+ event_count > MAX_EVENT_COUNT )
+ return -EINVAL;
+
+ timeout = timeout * DELAY_TIMER_CONV;
+ writel(VAL0|STINTEN, mmio+INTEN0);
+ writel((u32)DLY_INT_A_R0|( event_count<< 16 )|timeout,
+ mmio+DLY_INT_A);
+ break;
+
+ case TX_INTR_COAL :
+ timeout = coal_conf->tx_timeout;
+ event_count = coal_conf->tx_event_count;
+ if( timeout > MAX_TIMEOUT ||
+ event_count > MAX_EVENT_COUNT )
+ return -EINVAL;
+
+
+ timeout = timeout * DELAY_TIMER_CONV;
+ writel(VAL0|STINTEN,mmio+INTEN0);
+ writel((u32)DLY_INT_B_T0|( event_count<< 16 )|timeout,
+ mmio+DLY_INT_B);
+ break;
+
+ case DISABLE_COAL:
+ writel(0,mmio+STVAL);
+ writel(STINTEN, mmio+INTEN0);
+ writel(0, mmio +DLY_INT_B);
+ writel(0, mmio+DLY_INT_A);
+ break;
+ case ENABLE_COAL:
+ /* Start the timer */
+ writel((u32)SOFT_TIMER_FREQ, mmio+STVAL); /* 0.5 sec */
+ writel(VAL0|STINTEN, mmio+INTEN0);
+ break;
+ default:
+ break;
+
+ }
+ return 0;
+
+}
+
+/*
+This function initializes the device registers and starts the device.
+*/
+static int amd8111e_restart(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ void __iomem *mmio = lp->mmio;
+ int i,reg_val;
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ if(amd8111e_init_ring(dev))
+ return -ENOMEM;
+
+ /* enable the port manager and set auto negotiation always */
+ writel((u32) VAL1|EN_PMGR, mmio + CMD3 );
+ writel((u32)XPHYANE|XPHYRST , mmio + CTRL2);
+
+ amd8111e_set_ext_phy(dev);
+
+ /* set control registers */
+ reg_val = readl(mmio + CTRL1);
+ reg_val &= ~XMTSP_MASK;
+ writel( reg_val| XMTSP_128 | CACHE_ALIGN, mmio + CTRL1 );
+
+ /* enable interrupt */
+ writel( APINT5EN | APINT4EN | APINT3EN | APINT2EN | APINT1EN |
+ APINT0EN | MIIPDTINTEN | MCCIINTEN | MCCINTEN | MREINTEN |
+ SPNDINTEN | MPINTEN | SINTEN | STINTEN, mmio + INTEN0);
+
+ writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0);
+
+ /* initialize tx and rx ring base addresses */
+ writel((u32)lp->tx_ring_dma_addr,mmio + XMT_RING_BASE_ADDR0);
+ writel((u32)lp->rx_ring_dma_addr,mmio+ RCV_RING_BASE_ADDR0);
+
+ writew((u32)NUM_TX_RING_DR, mmio + XMT_RING_LEN0);
+ writew((u16)NUM_RX_RING_DR, mmio + RCV_RING_LEN0);
+
+ /* set default IPG to 96 */
+ writew((u32)DEFAULT_IPG,mmio+IPG);
+ writew((u32)(DEFAULT_IPG-IFS1_DELTA), mmio + IFS1);
+
+ if(lp->options & OPTION_JUMBO_ENABLE){
+ writel((u32)VAL2|JUMBO, mmio + CMD3);
+ /* Reset REX_UFLO */
+ writel( REX_UFLO, mmio + CMD2);
+ /* Should not set REX_UFLO for jumbo frames */
+ writel( VAL0 | APAD_XMT|REX_RTRY , mmio + CMD2);
+ }else{
+ writel( VAL0 | APAD_XMT | REX_RTRY|REX_UFLO, mmio + CMD2);
+ writel((u32)JUMBO, mmio + CMD3);
+ }
+
+#if AMD8111E_VLAN_TAG_USED
+ writel((u32) VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3);
+#endif
+ writel( VAL0 | APAD_XMT | REX_RTRY, mmio + CMD2 );
+
+ /* Setting the MAC address to the device */
+ for (i = 0; i < ETH_ALEN; i++)
+ writeb( dev->dev_addr[i], mmio + PADR + i );
+
+ /* Enable interrupt coalesce */
+ if(lp->options & OPTION_INTR_COAL_ENABLE){
+ printk(KERN_INFO "%s: Interrupt Coalescing Enabled.\n",
+ dev->name);
+ amd8111e_set_coalesce(dev,ENABLE_COAL);
+ }
+
+ /* set RUN bit to start the chip */
+ writel(VAL2 | RDMD0, mmio + CMD0);
+ writel(VAL0 | INTREN | RUN, mmio + CMD0);
+
+ /* To avoid PCI posting bug */
+ readl(mmio+CMD0);
+ return 0;
+}
+/*
+This function clears necessary the device registers.
+*/
+static void amd8111e_init_hw_default( struct amd8111e_priv* lp)
+{
+ unsigned int reg_val;
+ unsigned int logic_filter[2] ={0,};
+ void __iomem *mmio = lp->mmio;
+
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */
+ writew( 0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0);
+
+ /* Clear RCV_RING_BASE_ADDR */
+ writel(0, mmio + RCV_RING_BASE_ADDR0);
+
+ /* Clear XMT_RING_BASE_ADDR */
+ writel(0, mmio + XMT_RING_BASE_ADDR0);
+ writel(0, mmio + XMT_RING_BASE_ADDR1);
+ writel(0, mmio + XMT_RING_BASE_ADDR2);
+ writel(0, mmio + XMT_RING_BASE_ADDR3);
+
+ /* Clear CMD0 */
+ writel(CMD0_CLEAR,mmio + CMD0);
+
+ /* Clear CMD2 */
+ writel(CMD2_CLEAR, mmio +CMD2);
+
+ /* Clear CMD7 */
+ writel(CMD7_CLEAR , mmio + CMD7);
+
+ /* Clear DLY_INT_A and DLY_INT_B */
+ writel(0x0, mmio + DLY_INT_A);
+ writel(0x0, mmio + DLY_INT_B);
+
+ /* Clear FLOW_CONTROL */
+ writel(0x0, mmio + FLOW_CONTROL);
+
+ /* Clear INT0 write 1 to clear register */
+ reg_val = readl(mmio + INT0);
+ writel(reg_val, mmio + INT0);
+
+ /* Clear STVAL */
+ writel(0x0, mmio + STVAL);
+
+ /* Clear INTEN0 */
+ writel( INTEN0_CLEAR, mmio + INTEN0);
+
+ /* Clear LADRF */
+ writel(0x0 , mmio + LADRF);
+
+ /* Set SRAM_SIZE & SRAM_BOUNDARY registers */
+ writel( 0x80010,mmio + SRAM_SIZE);
+
+ /* Clear RCV_RING0_LEN */
+ writel(0x0, mmio + RCV_RING_LEN0);
+
+ /* Clear XMT_RING0/1/2/3_LEN */
+ writel(0x0, mmio + XMT_RING_LEN0);
+ writel(0x0, mmio + XMT_RING_LEN1);
+ writel(0x0, mmio + XMT_RING_LEN2);
+ writel(0x0, mmio + XMT_RING_LEN3);
+
+ /* Clear XMT_RING_LIMIT */
+ writel(0x0, mmio + XMT_RING_LIMIT);
+
+ /* Clear MIB */
+ writew(MIB_CLEAR, mmio + MIB_ADDR);
+
+ /* Clear LARF */
+ amd8111e_writeq(*(u64*)logic_filter,mmio+LADRF);
+
+ /* SRAM_SIZE register */
+ reg_val = readl(mmio + SRAM_SIZE);
+
+ if(lp->options & OPTION_JUMBO_ENABLE)
+ writel( VAL2|JUMBO, mmio + CMD3);
+#if AMD8111E_VLAN_TAG_USED
+ writel(VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3 );
+#endif
+ /* Set default value to CTRL1 Register */
+ writel(CTRL1_DEFAULT, mmio + CTRL1);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD2);
+
+}
+
+/*
+This function disables the interrupt and clears all the pending
+interrupts in INT0
+ */
+static void amd8111e_disable_interrupt(struct amd8111e_priv* lp)
+{
+ u32 intr0;
+
+ /* Disable interrupt */
+ writel(INTREN, lp->mmio + CMD0);
+
+ /* Clear INT0 */
+ intr0 = readl(lp->mmio + INT0);
+ writel(intr0, lp->mmio + INT0);
+
+ /* To avoid PCI posting bug */
+ readl(lp->mmio + INT0);
+
+}
+
+/*
+This function stops the chip.
+*/
+static void amd8111e_stop_chip(struct amd8111e_priv* lp)
+{
+ writel(RUN, lp->mmio + CMD0);
+
+ /* To avoid PCI posting bug */
+ readl(lp->mmio + CMD0);
+}
+
+/*
+This function frees the transmiter and receiver descriptor rings.
+*/
+static void amd8111e_free_ring(struct amd8111e_priv* lp)
+{
+ /* Free transmit and receive descriptor rings */
+ if(lp->rx_ring){
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,
+ lp->rx_ring, lp->rx_ring_dma_addr);
+ lp->rx_ring = NULL;
+ }
+
+ if(lp->tx_ring){
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,
+ lp->tx_ring, lp->tx_ring_dma_addr);
+
+ lp->tx_ring = NULL;
+ }
+
+}
+
+/*
+This function will free all the transmit skbs that are actually transmitted by the device. It will check the ownership of the skb before freeing the skb.
+*/
+static int amd8111e_tx(struct net_device *dev)
+{
+ struct amd8111e_priv* lp = netdev_priv(dev);
+ int tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
+ int status;
+ /* Complete all the transmit packet */
+ while (lp->tx_complete_idx != lp->tx_idx){
+ tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
+ status = le16_to_cpu(lp->tx_ring[tx_index].tx_flags);
+
+ if(status & OWN_BIT)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[tx_index].buff_phy_addr = 0;
+
+ /* We must free the original skb */
+ if (lp->tx_skbuff[tx_index]) {
+ pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[tx_index],
+ lp->tx_skbuff[tx_index]->len,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq (lp->tx_skbuff[tx_index]);
+ lp->tx_skbuff[tx_index] = NULL;
+ lp->tx_dma_addr[tx_index] = 0;
+ }
+ lp->tx_complete_idx++;
+ /*COAL update tx coalescing parameters */
+ lp->coal_conf.tx_packets++;
+ lp->coal_conf.tx_bytes +=
+ le16_to_cpu(lp->tx_ring[tx_index].buff_count);
+
+ if (netif_queue_stopped(dev) &&
+ lp->tx_complete_idx > lp->tx_idx - NUM_TX_BUFFERS +2){
+ /* The ring is no longer full, clear tbusy. */
+ /* lp->tx_full = 0; */
+ netif_wake_queue (dev);
+ }
+ }
+ return 0;
+}
+
+/* This function handles the driver receive operation in polling mode */
+static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct amd8111e_priv *lp = container_of(napi, struct amd8111e_priv, napi);
+ struct net_device *dev = lp->amd8111e_net_dev;
+ int rx_index = lp->rx_idx & RX_RING_DR_MOD_MASK;
+ void __iomem *mmio = lp->mmio;
+ struct sk_buff *skb,*new_skb;
+ int min_pkt_len, status;
+ unsigned int intr0;
+ int num_rx_pkt = 0;
+ short pkt_len;
+#if AMD8111E_VLAN_TAG_USED
+ short vtag;
+#endif
+ int rx_pkt_limit = budget;
+ unsigned long flags;
+
+ if (rx_pkt_limit <= 0)
+ goto rx_not_empty;
+
+ do{
+ /* process receive packets until we use the quota*/
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while(1) {
+ status = le16_to_cpu(lp->rx_ring[rx_index].rx_flags);
+ if (status & OWN_BIT)
+ break;
+
+ /*
+ * There is a tricky error noted by John Murphy,
+ * <murf@perftech.com> to Russ Nelson: Even with
+ * full-sized * buffers it's possible for a
+ * jabber packet to use two buffers, with only
+ * the last correctly noting the error.
+ */
+
+ if(status & ERR_BIT) {
+ /* reseting flags */
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
+ goto err_next_pkt;
+ }
+ /* check for STP and ENP */
+ if(!((status & STP_BIT) && (status & ENP_BIT))){
+ /* reseting flags */
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
+ goto err_next_pkt;
+ }
+ pkt_len = le16_to_cpu(lp->rx_ring[rx_index].msg_count) - 4;
+
+#if AMD8111E_VLAN_TAG_USED
+ vtag = status & TT_MASK;
+ /*MAC will strip vlan tag*/
+ if (vtag != 0)
+ min_pkt_len =MIN_PKT_LEN - 4;
+ else
+#endif
+ min_pkt_len =MIN_PKT_LEN;
+
+ if (pkt_len < min_pkt_len) {
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
+ lp->drv_rx_errors++;
+ goto err_next_pkt;
+ }
+ if(--rx_pkt_limit < 0)
+ goto rx_not_empty;
+ new_skb = netdev_alloc_skb(dev, lp->rx_buff_len);
+ if (!new_skb) {
+ /* if allocation fail,
+ ignore that pkt and go to next one */
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
+ lp->drv_rx_errors++;
+ goto err_next_pkt;
+ }
+
+ skb_reserve(new_skb, 2);
+ skb = lp->rx_skbuff[rx_index];
+ pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
+ lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
+ skb_put(skb, pkt_len);
+ lp->rx_skbuff[rx_index] = new_skb;
+ lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
+ new_skb->data,
+ lp->rx_buff_len-2,
+ PCI_DMA_FROMDEVICE);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+#if AMD8111E_VLAN_TAG_USED
+ if (vtag == TT_VLAN_TAGGED){
+ u16 vlan_tag = le16_to_cpu(lp->rx_ring[rx_index].tag_ctrl_info);
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
+ }
+#endif
+ netif_receive_skb(skb);
+ /*COAL update rx coalescing parameters*/
+ lp->coal_conf.rx_packets++;
+ lp->coal_conf.rx_bytes += pkt_len;
+ num_rx_pkt++;
+
+ err_next_pkt:
+ lp->rx_ring[rx_index].buff_phy_addr
+ = cpu_to_le32(lp->rx_dma_addr[rx_index]);
+ lp->rx_ring[rx_index].buff_count =
+ cpu_to_le16(lp->rx_buff_len-2);
+ wmb();
+ lp->rx_ring[rx_index].rx_flags |= cpu_to_le16(OWN_BIT);
+ rx_index = (++lp->rx_idx) & RX_RING_DR_MOD_MASK;
+ }
+ /* Check the interrupt status register for more packets in the
+ mean time. Process them since we have not used up our quota.*/
+
+ intr0 = readl(mmio + INT0);
+ /*Ack receive packets */
+ writel(intr0 & RINT0,mmio + INT0);
+
+ } while(intr0 & RINT0);
+
+ if (rx_pkt_limit > 0) {
+ /* Receive descriptor is empty now */
+ spin_lock_irqsave(&lp->lock, flags);
+ __napi_complete(napi);
+ writel(VAL0|RINTEN0, mmio + INTEN0);
+ writel(VAL2 | RDMD0, mmio + CMD0);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+rx_not_empty:
+ return num_rx_pkt;
+}
+
+/*
+This function will indicate the link status to the kernel.
+*/
+static int amd8111e_link_change(struct net_device* dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int status0,speed;
+
+ /* read the link change */
+ status0 = readl(lp->mmio + STAT0);
+
+ if(status0 & LINK_STATS){
+ if(status0 & AUTONEG_COMPLETE)
+ lp->link_config.autoneg = AUTONEG_ENABLE;
+ else
+ lp->link_config.autoneg = AUTONEG_DISABLE;
+
+ if(status0 & FULL_DPLX)
+ lp->link_config.duplex = DUPLEX_FULL;
+ else
+ lp->link_config.duplex = DUPLEX_HALF;
+ speed = (status0 & SPEED_MASK) >> 7;
+ if(speed == PHY_SPEED_10)
+ lp->link_config.speed = SPEED_10;
+ else if(speed == PHY_SPEED_100)
+ lp->link_config.speed = SPEED_100;
+
+ printk(KERN_INFO "%s: Link is Up. Speed is %s Mbps %s Duplex\n", dev->name,
+ (lp->link_config.speed == SPEED_100) ? "100": "10",
+ (lp->link_config.duplex == DUPLEX_FULL)? "Full": "Half");
+ netif_carrier_on(dev);
+ }
+ else{
+ lp->link_config.speed = SPEED_INVALID;
+ lp->link_config.duplex = DUPLEX_INVALID;
+ lp->link_config.autoneg = AUTONEG_INVALID;
+ printk(KERN_INFO "%s: Link is Down.\n",dev->name);
+ netif_carrier_off(dev);
+ }
+
+ return 0;
+}
+/*
+This function reads the mib counters.
+*/
+static int amd8111e_read_mib(void __iomem *mmio, u8 MIB_COUNTER)
+{
+ unsigned int status;
+ unsigned int data;
+ unsigned int repeat = REPEAT_CNT;
+
+ writew( MIB_RD_CMD | MIB_COUNTER, mmio + MIB_ADDR);
+ do {
+ status = readw(mmio + MIB_ADDR);
+ udelay(2); /* controller takes MAX 2 us to get mib data */
+ }
+ while (--repeat && (status & MIB_CMD_ACTIVE));
+
+ data = readl(mmio + MIB_DATA);
+ return data;
+}
+
+/*
+ * This function reads the mib registers and returns the hardware statistics.
+ * It updates previous internal driver statistics with new values.
+ */
+static struct net_device_stats *amd8111e_get_stats(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ void __iomem *mmio = lp->mmio;
+ unsigned long flags;
+ struct net_device_stats *new_stats = &dev->stats;
+
+ if (!lp->opened)
+ return new_stats;
+ spin_lock_irqsave (&lp->lock, flags);
+
+ /* stats.rx_packets */
+ new_stats->rx_packets = amd8111e_read_mib(mmio, rcv_broadcast_pkts)+
+ amd8111e_read_mib(mmio, rcv_multicast_pkts)+
+ amd8111e_read_mib(mmio, rcv_unicast_pkts);
+
+ /* stats.tx_packets */
+ new_stats->tx_packets = amd8111e_read_mib(mmio, xmt_packets);
+
+ /*stats.rx_bytes */
+ new_stats->rx_bytes = amd8111e_read_mib(mmio, rcv_octets);
+
+ /* stats.tx_bytes */
+ new_stats->tx_bytes = amd8111e_read_mib(mmio, xmt_octets);
+
+ /* stats.rx_errors */
+ /* hw errors + errors driver reported */
+ new_stats->rx_errors = amd8111e_read_mib(mmio, rcv_undersize_pkts)+
+ amd8111e_read_mib(mmio, rcv_fragments)+
+ amd8111e_read_mib(mmio, rcv_jabbers)+
+ amd8111e_read_mib(mmio, rcv_alignment_errors)+
+ amd8111e_read_mib(mmio, rcv_fcs_errors)+
+ amd8111e_read_mib(mmio, rcv_miss_pkts)+
+ lp->drv_rx_errors;
+
+ /* stats.tx_errors */
+ new_stats->tx_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts);
+
+ /* stats.rx_dropped*/
+ new_stats->rx_dropped = amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+ /* stats.tx_dropped*/
+ new_stats->tx_dropped = amd8111e_read_mib(mmio, xmt_underrun_pkts);
+
+ /* stats.multicast*/
+ new_stats->multicast = amd8111e_read_mib(mmio, rcv_multicast_pkts);
+
+ /* stats.collisions*/
+ new_stats->collisions = amd8111e_read_mib(mmio, xmt_collisions);
+
+ /* stats.rx_length_errors*/
+ new_stats->rx_length_errors =
+ amd8111e_read_mib(mmio, rcv_undersize_pkts)+
+ amd8111e_read_mib(mmio, rcv_oversize_pkts);
+
+ /* stats.rx_over_errors*/
+ new_stats->rx_over_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+ /* stats.rx_crc_errors*/
+ new_stats->rx_crc_errors = amd8111e_read_mib(mmio, rcv_fcs_errors);
+
+ /* stats.rx_frame_errors*/
+ new_stats->rx_frame_errors =
+ amd8111e_read_mib(mmio, rcv_alignment_errors);
+
+ /* stats.rx_fifo_errors */
+ new_stats->rx_fifo_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+ /* stats.rx_missed_errors */
+ new_stats->rx_missed_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+ /* stats.tx_aborted_errors*/
+ new_stats->tx_aborted_errors =
+ amd8111e_read_mib(mmio, xmt_excessive_collision);
+
+ /* stats.tx_carrier_errors*/
+ new_stats->tx_carrier_errors =
+ amd8111e_read_mib(mmio, xmt_loss_carrier);
+
+ /* stats.tx_fifo_errors*/
+ new_stats->tx_fifo_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts);
+
+ /* stats.tx_window_errors*/
+ new_stats->tx_window_errors =
+ amd8111e_read_mib(mmio, xmt_late_collision);
+
+ /* Reset the mibs for collecting new statistics */
+ /* writew(MIB_CLEAR, mmio + MIB_ADDR);*/
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ return new_stats;
+}
+/* This function recalculate the interrupt coalescing mode on every interrupt
+according to the datarate and the packet rate.
+*/
+static int amd8111e_calc_coalesce(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf;
+ int tx_pkt_rate;
+ int rx_pkt_rate;
+ int tx_data_rate;
+ int rx_data_rate;
+ int rx_pkt_size;
+ int tx_pkt_size;
+
+ tx_pkt_rate = coal_conf->tx_packets - coal_conf->tx_prev_packets;
+ coal_conf->tx_prev_packets = coal_conf->tx_packets;
+
+ tx_data_rate = coal_conf->tx_bytes - coal_conf->tx_prev_bytes;
+ coal_conf->tx_prev_bytes = coal_conf->tx_bytes;
+
+ rx_pkt_rate = coal_conf->rx_packets - coal_conf->rx_prev_packets;
+ coal_conf->rx_prev_packets = coal_conf->rx_packets;
+
+ rx_data_rate = coal_conf->rx_bytes - coal_conf->rx_prev_bytes;
+ coal_conf->rx_prev_bytes = coal_conf->rx_bytes;
+
+ if(rx_pkt_rate < 800){
+ if(coal_conf->rx_coal_type != NO_COALESCE){
+
+ coal_conf->rx_timeout = 0x0;
+ coal_conf->rx_event_count = 0;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = NO_COALESCE;
+ }
+ }
+ else{
+
+ rx_pkt_size = rx_data_rate/rx_pkt_rate;
+ if (rx_pkt_size < 128){
+ if(coal_conf->rx_coal_type != NO_COALESCE){
+
+ coal_conf->rx_timeout = 0;
+ coal_conf->rx_event_count = 0;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = NO_COALESCE;
+ }
+
+ }
+ else if ( (rx_pkt_size >= 128) && (rx_pkt_size < 512) ){
+
+ if(coal_conf->rx_coal_type != LOW_COALESCE){
+ coal_conf->rx_timeout = 1;
+ coal_conf->rx_event_count = 4;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = LOW_COALESCE;
+ }
+ }
+ else if ((rx_pkt_size >= 512) && (rx_pkt_size < 1024)){
+
+ if(coal_conf->rx_coal_type != MEDIUM_COALESCE){
+ coal_conf->rx_timeout = 1;
+ coal_conf->rx_event_count = 4;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = MEDIUM_COALESCE;
+ }
+
+ }
+ else if(rx_pkt_size >= 1024){
+ if(coal_conf->rx_coal_type != HIGH_COALESCE){
+ coal_conf->rx_timeout = 2;
+ coal_conf->rx_event_count = 3;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = HIGH_COALESCE;
+ }
+ }
+ }
+ /* NOW FOR TX INTR COALESC */
+ if(tx_pkt_rate < 800){
+ if(coal_conf->tx_coal_type != NO_COALESCE){
+
+ coal_conf->tx_timeout = 0x0;
+ coal_conf->tx_event_count = 0;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = NO_COALESCE;
+ }
+ }
+ else{
+
+ tx_pkt_size = tx_data_rate/tx_pkt_rate;
+ if (tx_pkt_size < 128){
+
+ if(coal_conf->tx_coal_type != NO_COALESCE){
+
+ coal_conf->tx_timeout = 0;
+ coal_conf->tx_event_count = 0;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = NO_COALESCE;
+ }
+
+ }
+ else if ( (tx_pkt_size >= 128) && (tx_pkt_size < 512) ){
+
+ if(coal_conf->tx_coal_type != LOW_COALESCE){
+ coal_conf->tx_timeout = 1;
+ coal_conf->tx_event_count = 2;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = LOW_COALESCE;
+
+ }
+ }
+ else if ((tx_pkt_size >= 512) && (tx_pkt_size < 1024)){
+
+ if(coal_conf->tx_coal_type != MEDIUM_COALESCE){
+ coal_conf->tx_timeout = 2;
+ coal_conf->tx_event_count = 5;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = MEDIUM_COALESCE;
+ }
+
+ }
+ else if(tx_pkt_size >= 1024){
+ if (tx_pkt_size >= 1024){
+ if(coal_conf->tx_coal_type != HIGH_COALESCE){
+ coal_conf->tx_timeout = 4;
+ coal_conf->tx_event_count = 8;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = HIGH_COALESCE;
+ }
+ }
+ }
+ }
+ return 0;
+
+}
+/*
+This is device interrupt function. It handles transmit, receive,link change and hardware timer interrupts.
+*/
+static irqreturn_t amd8111e_interrupt(int irq, void *dev_id)
+{
+
+ struct net_device * dev = (struct net_device *) dev_id;
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ void __iomem *mmio = lp->mmio;
+ unsigned int intr0, intren0;
+ unsigned int handled = 1;
+
+ if(unlikely(dev == NULL))
+ return IRQ_NONE;
+
+ spin_lock(&lp->lock);
+
+ /* disabling interrupt */
+ writel(INTREN, mmio + CMD0);
+
+ /* Read interrupt status */
+ intr0 = readl(mmio + INT0);
+ intren0 = readl(mmio + INTEN0);
+
+ /* Process all the INT event until INTR bit is clear. */
+
+ if (!(intr0 & INTR)){
+ handled = 0;
+ goto err_no_interrupt;
+ }
+
+ /* Current driver processes 4 interrupts : RINT,TINT,LCINT,STINT */
+ writel(intr0, mmio + INT0);
+
+ /* Check if Receive Interrupt has occurred. */
+ if (intr0 & RINT0) {
+ if (napi_schedule_prep(&lp->napi)) {
+ /* Disable receive interupts */
+ writel(RINTEN0, mmio + INTEN0);
+ /* Schedule a polling routine */
+ __napi_schedule(&lp->napi);
+ } else if (intren0 & RINTEN0) {
+ printk("************Driver bug! interrupt while in poll\n");
+ /* Fix by disable receive interrupts */
+ writel(RINTEN0, mmio + INTEN0);
+ }
+ }
+
+ /* Check if Transmit Interrupt has occurred. */
+ if (intr0 & TINT0)
+ amd8111e_tx(dev);
+
+ /* Check if Link Change Interrupt has occurred. */
+ if (intr0 & LCINT)
+ amd8111e_link_change(dev);
+
+ /* Check if Hardware Timer Interrupt has occurred. */
+ if (intr0 & STINT)
+ amd8111e_calc_coalesce(dev);
+
+err_no_interrupt:
+ writel( VAL0 | INTREN,mmio + CMD0);
+
+ spin_unlock(&lp->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void amd8111e_poll(struct net_device *dev)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ amd8111e_interrupt(0, dev);
+ local_irq_restore(flags);
+}
+#endif
+
+
+/*
+This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down.
+*/
+static int amd8111e_close(struct net_device * dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ netif_stop_queue(dev);
+
+ napi_disable(&lp->napi);
+
+ spin_lock_irq(&lp->lock);
+
+ amd8111e_disable_interrupt(lp);
+ amd8111e_stop_chip(lp);
+
+ /* Free transmit and receive skbs */
+ amd8111e_free_skbs(lp->amd8111e_net_dev);
+
+ netif_carrier_off(lp->amd8111e_net_dev);
+
+ /* Delete ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ del_timer_sync(&lp->ipg_data.ipg_timer);
+
+ spin_unlock_irq(&lp->lock);
+ free_irq(dev->irq, dev);
+ amd8111e_free_ring(lp);
+
+ /* Update the statistics before closing */
+ amd8111e_get_stats(dev);
+ lp->opened = 0;
+ return 0;
+}
+/* This function opens new interface.It requests irq for the device, initializes the device,buffers and descriptors, and starts the device.
+*/
+static int amd8111e_open(struct net_device * dev )
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+
+ if(dev->irq ==0 || request_irq(dev->irq, amd8111e_interrupt, IRQF_SHARED,
+ dev->name, dev))
+ return -EAGAIN;
+
+ napi_enable(&lp->napi);
+
+ spin_lock_irq(&lp->lock);
+
+ amd8111e_init_hw_default(lp);
+
+ if(amd8111e_restart(dev)){
+ spin_unlock_irq(&lp->lock);
+ napi_disable(&lp->napi);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ return -ENOMEM;
+ }
+ /* Start ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE){
+ add_timer(&lp->ipg_data.ipg_timer);
+ printk(KERN_INFO "%s: Dynamic IPG Enabled.\n",dev->name);
+ }
+
+ lp->opened = 1;
+
+ spin_unlock_irq(&lp->lock);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+/*
+This function checks if there is any transmit descriptors available to queue more packet.
+*/
+static int amd8111e_tx_queue_avail(struct amd8111e_priv* lp )
+{
+ int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK;
+ if (lp->tx_skbuff[tx_index])
+ return -1;
+ else
+ return 0;
+
+}
+/*
+This function will queue the transmit packets to the descriptors and will trigger the send operation. It also initializes the transmit descriptors with buffer physical address, byte count, ownership to hardware etc.
+*/
+
+static netdev_tx_t amd8111e_start_xmit(struct sk_buff *skb,
+ struct net_device * dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int tx_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ tx_index = lp->tx_idx & TX_RING_DR_MOD_MASK;
+
+ lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len);
+
+ lp->tx_skbuff[tx_index] = skb;
+ lp->tx_ring[tx_index].tx_flags = 0;
+
+#if AMD8111E_VLAN_TAG_USED
+ if (vlan_tx_tag_present(skb)) {
+ lp->tx_ring[tx_index].tag_ctrl_cmd |=
+ cpu_to_le16(TCC_VLAN_INSERT);
+ lp->tx_ring[tx_index].tag_ctrl_info =
+ cpu_to_le16(vlan_tx_tag_get(skb));
+
+ }
+#endif
+ lp->tx_dma_addr[tx_index] =
+ pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
+ lp->tx_ring[tx_index].buff_phy_addr =
+ cpu_to_le32(lp->tx_dma_addr[tx_index]);
+
+ /* Set FCS and LTINT bits */
+ wmb();
+ lp->tx_ring[tx_index].tx_flags |=
+ cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT);
+
+ lp->tx_idx++;
+
+ /* Trigger an immediate send poll. */
+ writel( VAL1 | TDMD0, lp->mmio + CMD0);
+ writel( VAL2 | RDMD0,lp->mmio + CMD0);
+
+ if(amd8111e_tx_queue_avail(lp) < 0){
+ netif_stop_queue(dev);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return NETDEV_TX_OK;
+}
+/*
+This function returns all the memory mapped registers of the device.
+*/
+static void amd8111e_read_regs(struct amd8111e_priv *lp, u32 *buf)
+{
+ void __iomem *mmio = lp->mmio;
+ /* Read only necessary registers */
+ buf[0] = readl(mmio + XMT_RING_BASE_ADDR0);
+ buf[1] = readl(mmio + XMT_RING_LEN0);
+ buf[2] = readl(mmio + RCV_RING_BASE_ADDR0);
+ buf[3] = readl(mmio + RCV_RING_LEN0);
+ buf[4] = readl(mmio + CMD0);
+ buf[5] = readl(mmio + CMD2);
+ buf[6] = readl(mmio + CMD3);
+ buf[7] = readl(mmio + CMD7);
+ buf[8] = readl(mmio + INT0);
+ buf[9] = readl(mmio + INTEN0);
+ buf[10] = readl(mmio + LADRF);
+ buf[11] = readl(mmio + LADRF+4);
+ buf[12] = readl(mmio + STAT0);
+}
+
+
+/*
+This function sets promiscuos mode, all-multi mode or the multicast address
+list to the device.
+*/
+static void amd8111e_set_multicast_list(struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ u32 mc_filter[2] ;
+ int bit_num;
+
+ if(dev->flags & IFF_PROMISC){
+ writel( VAL2 | PROM, lp->mmio + CMD2);
+ return;
+ }
+ else
+ writel( PROM, lp->mmio + CMD2);
+ if (dev->flags & IFF_ALLMULTI ||
+ netdev_mc_count(dev) > MAX_FILTER_SIZE) {
+ /* get all multicast packet */
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ lp->options |= OPTION_MULTICAST_ENABLE;
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
+ return;
+ }
+ if (netdev_mc_empty(dev)) {
+ /* get only own packets */
+ mc_filter[1] = mc_filter[0] = 0;
+ lp->options &= ~OPTION_MULTICAST_ENABLE;
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
+ /* disable promiscuous mode */
+ writel(PROM, lp->mmio + CMD2);
+ return;
+ }
+ /* load all the multicast addresses in the logic filter */
+ lp->options |= OPTION_MULTICAST_ENABLE;
+ mc_filter[1] = mc_filter[0] = 0;
+ netdev_for_each_mc_addr(ha, dev) {
+ bit_num = (ether_crc_le(ETH_ALEN, ha->addr) >> 26) & 0x3f;
+ mc_filter[bit_num >> 5] |= 1 << (bit_num & 31);
+ }
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio+ LADRF);
+
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD2);
+
+}
+
+static void amd8111e_get_drvinfo(struct net_device* dev, struct ethtool_drvinfo *info)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ struct pci_dev *pci_dev = lp->pci_dev;
+ strlcpy(info->driver, MODULE_NAME, sizeof(info->driver));
+ strlcpy(info->version, MODULE_VERS, sizeof(info->version));
+ snprintf(info->fw_version, sizeof(info->fw_version),
+ "%u", chip_version);
+ strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info));
+}
+
+static int amd8111e_get_regs_len(struct net_device *dev)
+{
+ return AMD8111E_REG_DUMP_LEN;
+}
+
+static void amd8111e_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ regs->version = 0;
+ amd8111e_read_regs(lp, buf);
+}
+
+static int amd8111e_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ spin_lock_irq(&lp->lock);
+ mii_ethtool_gset(&lp->mii_if, ecmd);
+ spin_unlock_irq(&lp->lock);
+ return 0;
+}
+
+static int amd8111e_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int res;
+ spin_lock_irq(&lp->lock);
+ res = mii_ethtool_sset(&lp->mii_if, ecmd);
+ spin_unlock_irq(&lp->lock);
+ return res;
+}
+
+static int amd8111e_nway_reset(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ return mii_nway_restart(&lp->mii_if);
+}
+
+static u32 amd8111e_get_link(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ return mii_link_ok(&lp->mii_if);
+}
+
+static void amd8111e_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ wol_info->supported = WAKE_MAGIC|WAKE_PHY;
+ if (lp->options & OPTION_WOL_ENABLE)
+ wol_info->wolopts = WAKE_MAGIC;
+}
+
+static int amd8111e_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ if (wol_info->wolopts & ~(WAKE_MAGIC|WAKE_PHY))
+ return -EINVAL;
+ spin_lock_irq(&lp->lock);
+ if (wol_info->wolopts & WAKE_MAGIC)
+ lp->options |=
+ (OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE);
+ else if(wol_info->wolopts & WAKE_PHY)
+ lp->options |=
+ (OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE);
+ else
+ lp->options &= ~OPTION_WOL_ENABLE;
+ spin_unlock_irq(&lp->lock);
+ return 0;
+}
+
+static const struct ethtool_ops ops = {
+ .get_drvinfo = amd8111e_get_drvinfo,
+ .get_regs_len = amd8111e_get_regs_len,
+ .get_regs = amd8111e_get_regs,
+ .get_settings = amd8111e_get_settings,
+ .set_settings = amd8111e_set_settings,
+ .nway_reset = amd8111e_nway_reset,
+ .get_link = amd8111e_get_link,
+ .get_wol = amd8111e_get_wol,
+ .set_wol = amd8111e_set_wol,
+};
+
+/*
+This function handles all the ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application.
+*/
+
+static int amd8111e_ioctl(struct net_device * dev , struct ifreq *ifr, int cmd)
+{
+ struct mii_ioctl_data *data = if_mii(ifr);
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int err;
+ u32 mii_regval;
+
+ switch(cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = lp->ext_phy_addr;
+
+ /* fallthru */
+ case SIOCGMIIREG:
+
+ spin_lock_irq(&lp->lock);
+ err = amd8111e_read_phy(lp, data->phy_id,
+ data->reg_num & PHY_REG_ADDR_MASK, &mii_regval);
+ spin_unlock_irq(&lp->lock);
+
+ data->val_out = mii_regval;
+ return err;
+
+ case SIOCSMIIREG:
+
+ spin_lock_irq(&lp->lock);
+ err = amd8111e_write_phy(lp, data->phy_id,
+ data->reg_num & PHY_REG_ADDR_MASK, data->val_in);
+ spin_unlock_irq(&lp->lock);
+
+ return err;
+
+ default:
+ /* do nothing */
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+static int amd8111e_set_mac_address(struct net_device *dev, void *p)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int i;
+ struct sockaddr *addr = p;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ spin_lock_irq(&lp->lock);
+ /* Setting the MAC address to the device */
+ for (i = 0; i < ETH_ALEN; i++)
+ writeb( dev->dev_addr[i], lp->mmio + PADR + i );
+
+ spin_unlock_irq(&lp->lock);
+
+ return 0;
+}
+
+/*
+This function changes the mtu of the device. It restarts the device to initialize the descriptor with new receive buffers.
+*/
+static int amd8111e_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int err;
+
+ if ((new_mtu < AMD8111E_MIN_MTU) || (new_mtu > AMD8111E_MAX_MTU))
+ return -EINVAL;
+
+ if (!netif_running(dev)) {
+ /* new_mtu will be used
+ when device starts netxt time */
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ spin_lock_irq(&lp->lock);
+
+ /* stop the chip */
+ writel(RUN, lp->mmio + CMD0);
+
+ dev->mtu = new_mtu;
+
+ err = amd8111e_restart(dev);
+ spin_unlock_irq(&lp->lock);
+ if(!err)
+ netif_start_queue(dev);
+ return err;
+}
+
+static int amd8111e_enable_magicpkt(struct amd8111e_priv* lp)
+{
+ writel( VAL1|MPPLBA, lp->mmio + CMD3);
+ writel( VAL0|MPEN_SW, lp->mmio + CMD7);
+
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD7);
+ return 0;
+}
+
+static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
+{
+
+ /* Adapter is already stoped/suspended/interrupt-disabled */
+ writel(VAL0|LCMODE_SW,lp->mmio + CMD7);
+
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD7);
+ return 0;
+}
+
+/*
+ * This function is called when a packet transmission fails to complete
+ * within a reasonable period, on the assumption that an interrupt have
+ * failed or the interface is locked up. This function will reinitialize
+ * the hardware.
+ */
+static void amd8111e_tx_timeout(struct net_device *dev)
+{
+ struct amd8111e_priv* lp = netdev_priv(dev);
+ int err;
+
+ printk(KERN_ERR "%s: transmit timed out, resetting\n",
+ dev->name);
+ spin_lock_irq(&lp->lock);
+ err = amd8111e_restart(dev);
+ spin_unlock_irq(&lp->lock);
+ if(!err)
+ netif_wake_queue(dev);
+}
+static int amd8111e_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pci_dev);
+ struct amd8111e_priv *lp = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ /* disable the interrupt */
+ spin_lock_irq(&lp->lock);
+ amd8111e_disable_interrupt(lp);
+ spin_unlock_irq(&lp->lock);
+
+ netif_device_detach(dev);
+
+ /* stop chip */
+ spin_lock_irq(&lp->lock);
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ del_timer_sync(&lp->ipg_data.ipg_timer);
+ amd8111e_stop_chip(lp);
+ spin_unlock_irq(&lp->lock);
+
+ if(lp->options & OPTION_WOL_ENABLE){
+ /* enable wol */
+ if(lp->options & OPTION_WAKE_MAGIC_ENABLE)
+ amd8111e_enable_magicpkt(lp);
+ if(lp->options & OPTION_WAKE_PHY_ENABLE)
+ amd8111e_enable_link_change(lp);
+
+ pci_enable_wake(pci_dev, PCI_D3hot, 1);
+ pci_enable_wake(pci_dev, PCI_D3cold, 1);
+
+ }
+ else{
+ pci_enable_wake(pci_dev, PCI_D3hot, 0);
+ pci_enable_wake(pci_dev, PCI_D3cold, 0);
+ }
+
+ pci_save_state(pci_dev);
+ pci_set_power_state(pci_dev, PCI_D3hot);
+
+ return 0;
+}
+static int amd8111e_resume(struct pci_dev *pci_dev)
+{
+ struct net_device *dev = pci_get_drvdata(pci_dev);
+ struct amd8111e_priv *lp = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ pci_set_power_state(pci_dev, PCI_D0);
+ pci_restore_state(pci_dev);
+
+ pci_enable_wake(pci_dev, PCI_D3hot, 0);
+ pci_enable_wake(pci_dev, PCI_D3cold, 0); /* D3 cold */
+
+ netif_device_attach(dev);
+
+ spin_lock_irq(&lp->lock);
+ amd8111e_restart(dev);
+ /* Restart ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ mod_timer(&lp->ipg_data.ipg_timer,
+ jiffies + IPG_CONVERGE_JIFFIES);
+ spin_unlock_irq(&lp->lock);
+
+ return 0;
+}
+
+
+static void amd8111e_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ if (dev) {
+ unregister_netdev(dev);
+ iounmap(((struct amd8111e_priv *)netdev_priv(dev))->mmio);
+ free_netdev(dev);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ }
+}
+static void amd8111e_config_ipg(struct net_device* dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ struct ipg_info* ipg_data = &lp->ipg_data;
+ void __iomem *mmio = lp->mmio;
+ unsigned int prev_col_cnt = ipg_data->col_cnt;
+ unsigned int total_col_cnt;
+ unsigned int tmp_ipg;
+
+ if(lp->link_config.duplex == DUPLEX_FULL){
+ ipg_data->ipg = DEFAULT_IPG;
+ return;
+ }
+
+ if(ipg_data->ipg_state == SSTATE){
+
+ if(ipg_data->timer_tick == IPG_STABLE_TIME){
+
+ ipg_data->timer_tick = 0;
+ ipg_data->ipg = MIN_IPG - IPG_STEP;
+ ipg_data->current_ipg = MIN_IPG;
+ ipg_data->diff_col_cnt = 0xFFFFFFFF;
+ ipg_data->ipg_state = CSTATE;
+ }
+ else
+ ipg_data->timer_tick++;
+ }
+
+ if(ipg_data->ipg_state == CSTATE){
+
+ /* Get the current collision count */
+
+ total_col_cnt = ipg_data->col_cnt =
+ amd8111e_read_mib(mmio, xmt_collisions);
+
+ if ((total_col_cnt - prev_col_cnt) <
+ (ipg_data->diff_col_cnt)){
+
+ ipg_data->diff_col_cnt =
+ total_col_cnt - prev_col_cnt ;
+
+ ipg_data->ipg = ipg_data->current_ipg;
+ }
+
+ ipg_data->current_ipg += IPG_STEP;
+
+ if (ipg_data->current_ipg <= MAX_IPG)
+ tmp_ipg = ipg_data->current_ipg;
+ else{
+ tmp_ipg = ipg_data->ipg;
+ ipg_data->ipg_state = SSTATE;
+ }
+ writew((u32)tmp_ipg, mmio + IPG);
+ writew((u32)(tmp_ipg - IFS1_DELTA), mmio + IFS1);
+ }
+ mod_timer(&lp->ipg_data.ipg_timer, jiffies + IPG_CONVERGE_JIFFIES);
+ return;
+
+}
+
+static void amd8111e_probe_ext_phy(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = netdev_priv(dev);
+ int i;
+
+ for (i = 0x1e; i >= 0; i--) {
+ u32 id1, id2;
+
+ if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1))
+ continue;
+ if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2))
+ continue;
+ lp->ext_phy_id = (id1 << 16) | id2;
+ lp->ext_phy_addr = i;
+ return;
+ }
+ lp->ext_phy_id = 0;
+ lp->ext_phy_addr = 1;
+}
+
+static const struct net_device_ops amd8111e_netdev_ops = {
+ .ndo_open = amd8111e_open,
+ .ndo_stop = amd8111e_close,
+ .ndo_start_xmit = amd8111e_start_xmit,
+ .ndo_tx_timeout = amd8111e_tx_timeout,
+ .ndo_get_stats = amd8111e_get_stats,
+ .ndo_set_rx_mode = amd8111e_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = amd8111e_set_mac_address,
+ .ndo_do_ioctl = amd8111e_ioctl,
+ .ndo_change_mtu = amd8111e_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = amd8111e_poll,
+#endif
+};
+
+static int amd8111e_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err, i;
+ unsigned long reg_addr,reg_len;
+ struct amd8111e_priv* lp;
+ struct net_device* dev;
+
+ err = pci_enable_device(pdev);
+ if(err){
+ printk(KERN_ERR "amd8111e: Cannot enable new PCI device, "
+ "exiting.\n");
+ return err;
+ }
+
+ if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)){
+ printk(KERN_ERR "amd8111e: Cannot find PCI base address, "
+ "exiting.\n");
+ err = -ENODEV;
+ goto err_disable_pdev;
+ }
+
+ err = pci_request_regions(pdev, MODULE_NAME);
+ if(err){
+ printk(KERN_ERR "amd8111e: Cannot obtain PCI resources, "
+ "exiting.\n");
+ goto err_disable_pdev;
+ }
+
+ pci_set_master(pdev);
+
+ /* Find power-management capability. */
+ if (!pdev->pm_cap) {
+ printk(KERN_ERR "amd8111e: No Power Management capability, "
+ "exiting.\n");
+ err = -ENODEV;
+ goto err_free_reg;
+ }
+
+ /* Initialize DMA */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) < 0) {
+ printk(KERN_ERR "amd8111e: DMA not supported,"
+ "exiting.\n");
+ err = -ENODEV;
+ goto err_free_reg;
+ }
+
+ reg_addr = pci_resource_start(pdev, 0);
+ reg_len = pci_resource_len(pdev, 0);
+
+ dev = alloc_etherdev(sizeof(struct amd8111e_priv));
+ if (!dev) {
+ err = -ENOMEM;
+ goto err_free_reg;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+#if AMD8111E_VLAN_TAG_USED
+ dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX ;
+#endif
+
+ lp = netdev_priv(dev);
+ lp->pci_dev = pdev;
+ lp->amd8111e_net_dev = dev;
+ lp->pm_cap = pdev->pm_cap;
+
+ spin_lock_init(&lp->lock);
+
+ lp->mmio = ioremap(reg_addr, reg_len);
+ if (!lp->mmio) {
+ printk(KERN_ERR "amd8111e: Cannot map device registers, "
+ "exiting\n");
+ err = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ /* Initializing MAC address */
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = readb(lp->mmio + PADR + i);
+
+ /* Setting user defined parametrs */
+ lp->ext_phy_option = speed_duplex[card_idx];
+ if(coalesce[card_idx])
+ lp->options |= OPTION_INTR_COAL_ENABLE;
+ if(dynamic_ipg[card_idx++])
+ lp->options |= OPTION_DYN_IPG_ENABLE;
+
+
+ /* Initialize driver entry points */
+ dev->netdev_ops = &amd8111e_netdev_ops;
+ dev->ethtool_ops = &ops;
+ dev->irq =pdev->irq;
+ dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
+ netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
+
+#if AMD8111E_VLAN_TAG_USED
+ dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+#endif
+ /* Probe the external PHY */
+ amd8111e_probe_ext_phy(dev);
+
+ /* setting mii default values */
+ lp->mii_if.dev = dev;
+ lp->mii_if.mdio_read = amd8111e_mdio_read;
+ lp->mii_if.mdio_write = amd8111e_mdio_write;
+ lp->mii_if.phy_id = lp->ext_phy_addr;
+
+ /* Set receive buffer length and set jumbo option*/
+ amd8111e_set_rx_buff_len(dev);
+
+
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_ERR "amd8111e: Cannot register net device, "
+ "exiting.\n");
+ goto err_iounmap;
+ }
+
+ pci_set_drvdata(pdev, dev);
+
+ /* Initialize software ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE){
+ init_timer(&lp->ipg_data.ipg_timer);
+ lp->ipg_data.ipg_timer.data = (unsigned long) dev;
+ lp->ipg_data.ipg_timer.function = (void *)&amd8111e_config_ipg;
+ lp->ipg_data.ipg_timer.expires = jiffies +
+ IPG_CONVERGE_JIFFIES;
+ lp->ipg_data.ipg = DEFAULT_IPG;
+ lp->ipg_data.ipg_state = CSTATE;
+ }
+
+ /* display driver and device information */
+
+ chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
+ printk(KERN_INFO "%s: AMD-8111e Driver Version: %s\n",
+ dev->name,MODULE_VERS);
+ printk(KERN_INFO "%s: [ Rev %x ] PCI 10/100BaseT Ethernet %pM\n",
+ dev->name, chip_version, dev->dev_addr);
+ if (lp->ext_phy_id)
+ printk(KERN_INFO "%s: Found MII PHY ID 0x%08x at address 0x%02x\n",
+ dev->name, lp->ext_phy_id, lp->ext_phy_addr);
+ else
+ printk(KERN_INFO "%s: Couldn't detect MII PHY, assuming address 0x01\n",
+ dev->name);
+ return 0;
+err_iounmap:
+ iounmap(lp->mmio);
+
+err_free_dev:
+ free_netdev(dev);
+
+err_free_reg:
+ pci_release_regions(pdev);
+
+err_disable_pdev:
+ pci_disable_device(pdev);
+ return err;
+
+}
+
+static struct pci_driver amd8111e_driver = {
+ .name = MODULE_NAME,
+ .id_table = amd8111e_pci_tbl,
+ .probe = amd8111e_probe_one,
+ .remove = amd8111e_remove_one,
+ .suspend = amd8111e_suspend,
+ .resume = amd8111e_resume
+};
+
+module_pci_driver(amd8111e_driver);
diff --git a/drivers/net/ethernet/amd/amd8111e.h b/drivers/net/ethernet/amd/amd8111e.h
new file mode 100644
index 00000000000..a75092d584c
--- /dev/null
+++ b/drivers/net/ethernet/amd/amd8111e.h
@@ -0,0 +1,813 @@
+/*
+ * Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2003 Advanced Micro Devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+Module Name:
+
+ amd8111e.h
+
+Abstract:
+
+ AMD8111 based 10/100 Ethernet Controller driver definitions.
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+ 3.0.0
+ Initial Revision.
+ 3.0.1
+*/
+
+#ifndef _AMD811E_H
+#define _AMD811E_H
+
+/* Command style register access
+
+Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the value bit that specifies the value that will be written into the selected bits of register.
+
+eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered.
+
+*/
+
+/* Offset for Memory Mapped Registers. */
+/* 32 bit registers */
+
+#define ASF_STAT 0x00 /* ASF status register */
+#define CHIPID 0x04 /* Chip ID regsiter */
+#define MIB_DATA 0x10 /* MIB data register */
+#define MIB_ADDR 0x14 /* MIB address register */
+#define STAT0 0x30 /* Status0 register */
+#define INT0 0x38 /* Interrupt0 register */
+#define INTEN0 0x40 /* Interrupt0 enable register*/
+#define CMD0 0x48 /* Command0 register */
+#define CMD2 0x50 /* Command2 register */
+#define CMD3 0x54 /* Command3 resiter */
+#define CMD7 0x64 /* Command7 register */
+
+#define CTRL1 0x6C /* Control1 register */
+#define CTRL2 0x70 /* Control2 register */
+
+#define XMT_RING_LIMIT 0x7C /* Transmit ring limit register */
+
+#define AUTOPOLL0 0x88 /* Auto-poll0 register */
+#define AUTOPOLL1 0x8A /* Auto-poll1 register */
+#define AUTOPOLL2 0x8C /* Auto-poll2 register */
+#define AUTOPOLL3 0x8E /* Auto-poll3 register */
+#define AUTOPOLL4 0x90 /* Auto-poll4 register */
+#define AUTOPOLL5 0x92 /* Auto-poll5 register */
+
+#define AP_VALUE 0x98 /* Auto-poll value register */
+#define DLY_INT_A 0xA8 /* Group A delayed interrupt register */
+#define DLY_INT_B 0xAC /* Group B delayed interrupt register */
+
+#define FLOW_CONTROL 0xC8 /* Flow control register */
+#define PHY_ACCESS 0xD0 /* PHY access register */
+
+#define STVAL 0xD8 /* Software timer value register */
+
+#define XMT_RING_BASE_ADDR0 0x100 /* Transmit ring0 base addr register */
+#define XMT_RING_BASE_ADDR1 0x108 /* Transmit ring1 base addr register */
+#define XMT_RING_BASE_ADDR2 0x110 /* Transmit ring2 base addr register */
+#define XMT_RING_BASE_ADDR3 0x118 /* Transmit ring2 base addr register */
+
+#define RCV_RING_BASE_ADDR0 0x120 /* Transmit ring0 base addr register */
+
+#define PMAT0 0x190 /* OnNow pattern register0 */
+#define PMAT1 0x194 /* OnNow pattern register1 */
+
+/* 16bit registers */
+
+#define XMT_RING_LEN0 0x140 /* Transmit Ring0 length register */
+#define XMT_RING_LEN1 0x144 /* Transmit Ring1 length register */
+#define XMT_RING_LEN2 0x148 /* Transmit Ring2 length register */
+#define XMT_RING_LEN3 0x14C /* Transmit Ring3 length register */
+
+#define RCV_RING_LEN0 0x150 /* Receive Ring0 length register */
+
+#define SRAM_SIZE 0x178 /* SRAM size register */
+#define SRAM_BOUNDARY 0x17A /* SRAM boundary register */
+
+/* 48bit register */
+
+#define PADR 0x160 /* Physical address register */
+
+#define IFS1 0x18C /* Inter-frame spacing Part1 register */
+#define IFS 0x18D /* Inter-frame spacing register */
+#define IPG 0x18E /* Inter-frame gap register */
+/* 64bit register */
+
+#define LADRF 0x168 /* Logical address filter register */
+
+
+/* Register Bit Definitions */
+typedef enum {
+
+ ASF_INIT_DONE = (1 << 1),
+ ASF_INIT_PRESENT = (1 << 0),
+
+}STAT_ASF_BITS;
+
+typedef enum {
+
+ MIB_CMD_ACTIVE = (1 << 15 ),
+ MIB_RD_CMD = (1 << 13 ),
+ MIB_CLEAR = (1 << 12 ),
+ MIB_ADDRESS = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)|
+ (1 << 4) | (1 << 5),
+}MIB_ADDR_BITS;
+
+
+typedef enum {
+
+ PMAT_DET = (1 << 12),
+ MP_DET = (1 << 11),
+ LC_DET = (1 << 10),
+ SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7),
+ FULL_DPLX = (1 << 6),
+ LINK_STATS = (1 << 5),
+ AUTONEG_COMPLETE = (1 << 4),
+ MIIPD = (1 << 3),
+ RX_SUSPENDED = (1 << 2),
+ TX_SUSPENDED = (1 << 1),
+ RUNNING = (1 << 0),
+
+}STAT0_BITS;
+
+#define PHY_SPEED_10 0x2
+#define PHY_SPEED_100 0x3
+
+/* INT0 0x38, 32bit register */
+typedef enum {
+
+ INTR = (1 << 31),
+ PCSINT = (1 << 28),
+ LCINT = (1 << 27),
+ APINT5 = (1 << 26),
+ APINT4 = (1 << 25),
+ APINT3 = (1 << 24),
+ TINT_SUM = (1 << 23),
+ APINT2 = (1 << 22),
+ APINT1 = (1 << 21),
+ APINT0 = (1 << 20),
+ MIIPDTINT = (1 << 19),
+ MCCINT = (1 << 17),
+ MREINT = (1 << 16),
+ RINT_SUM = (1 << 15),
+ SPNDINT = (1 << 14),
+ MPINT = (1 << 13),
+ SINT = (1 << 12),
+ TINT3 = (1 << 11),
+ TINT2 = (1 << 10),
+ TINT1 = (1 << 9),
+ TINT0 = (1 << 8),
+ UINT = (1 << 7),
+ STINT = (1 << 4),
+ RINT0 = (1 << 0),
+
+}INT0_BITS;
+
+typedef enum {
+
+ VAL3 = (1 << 31), /* VAL bit for byte 3 */
+ VAL2 = (1 << 23), /* VAL bit for byte 2 */
+ VAL1 = (1 << 15), /* VAL bit for byte 1 */
+ VAL0 = (1 << 7), /* VAL bit for byte 0 */
+
+}VAL_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ LCINTEN = (1 << 27),
+ APINT5EN = (1 << 26),
+ APINT4EN = (1 << 25),
+ APINT3EN = (1 << 24),
+ /* VAL2 */
+ APINT2EN = (1 << 22),
+ APINT1EN = (1 << 21),
+ APINT0EN = (1 << 20),
+ MIIPDTINTEN = (1 << 19),
+ MCCIINTEN = (1 << 18),
+ MCCINTEN = (1 << 17),
+ MREINTEN = (1 << 16),
+ /* VAL1 */
+ SPNDINTEN = (1 << 14),
+ MPINTEN = (1 << 13),
+ TINTEN3 = (1 << 11),
+ SINTEN = (1 << 12),
+ TINTEN2 = (1 << 10),
+ TINTEN1 = (1 << 9),
+ TINTEN0 = (1 << 8),
+ /* VAL0 */
+ STINTEN = (1 << 4),
+ RINTEN0 = (1 << 0),
+
+ INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */
+
+}INTEN0_BITS;
+
+typedef enum {
+ /* VAL2 */
+ RDMD0 = (1 << 16),
+ /* VAL1 */
+ TDMD3 = (1 << 11),
+ TDMD2 = (1 << 10),
+ TDMD1 = (1 << 9),
+ TDMD0 = (1 << 8),
+ /* VAL0 */
+ UINTCMD = (1 << 6),
+ RX_FAST_SPND = (1 << 5),
+ TX_FAST_SPND = (1 << 4),
+ RX_SPND = (1 << 3),
+ TX_SPND = (1 << 2),
+ INTREN = (1 << 1),
+ RUN = (1 << 0),
+
+ CMD0_CLEAR = 0x000F0F7F, /* Command style register */
+
+}CMD0_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ CONDUIT_MODE = (1 << 29),
+ /* VAL2 */
+ RPA = (1 << 19),
+ DRCVPA = (1 << 18),
+ DRCVBC = (1 << 17),
+ PROM = (1 << 16),
+ /* VAL1 */
+ ASTRP_RCV = (1 << 13),
+ RCV_DROP0 = (1 << 12),
+ EMBA = (1 << 11),
+ DXMT2PD = (1 << 10),
+ LTINTEN = (1 << 9),
+ DXMTFCS = (1 << 8),
+ /* VAL0 */
+ APAD_XMT = (1 << 6),
+ DRTY = (1 << 5),
+ INLOOP = (1 << 4),
+ EXLOOP = (1 << 3),
+ REX_RTRY = (1 << 2),
+ REX_UFLO = (1 << 1),
+ REX_LCOL = (1 << 0),
+
+ CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */
+
+}CMD2_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ ASF_INIT_DONE_ALIAS = (1 << 29),
+ /* VAL2 */
+ JUMBO = (1 << 21),
+ VSIZE = (1 << 20),
+ VLONLY = (1 << 19),
+ VL_TAG_DEL = (1 << 18),
+ /* VAL1 */
+ EN_PMGR = (1 << 14),
+ INTLEVEL = (1 << 13),
+ FORCE_FULL_DUPLEX = (1 << 12),
+ FORCE_LINK_STATUS = (1 << 11),
+ APEP = (1 << 10),
+ MPPLBA = (1 << 9),
+ /* VAL0 */
+ RESET_PHY_PULSE = (1 << 2),
+ RESET_PHY = (1 << 1),
+ PHY_RST_POL = (1 << 0),
+
+}CMD3_BITS;
+
+
+typedef enum {
+
+ /* VAL0 */
+ PMAT_SAVE_MATCH = (1 << 4),
+ PMAT_MODE = (1 << 3),
+ MPEN_SW = (1 << 1),
+ LCMODE_SW = (1 << 0),
+
+ CMD7_CLEAR = 0x0000001B /* Command style register */
+
+}CMD7_BITS;
+
+
+typedef enum {
+
+ RESET_PHY_WIDTH = (0xF << 16) | (0xF<< 20), /* 0x00FF0000 */
+ XMTSP_MASK = (1 << 9) | (1 << 8), /* 9:8 */
+ XMTSP_128 = (1 << 9), /* 9 */
+ XMTSP_64 = (1 << 8),
+ CACHE_ALIGN = (1 << 4),
+ BURST_LIMIT_MASK = (0xF << 0 ),
+ CTRL1_DEFAULT = 0x00010111,
+
+}CTRL1_BITS;
+
+typedef enum {
+
+ FMDC_MASK = (1 << 9)|(1 << 8), /* 9:8 */
+ XPHYRST = (1 << 7),
+ XPHYANE = (1 << 6),
+ XPHYFD = (1 << 5),
+ XPHYSP = (1 << 4) | (1 << 3), /* 4:3 */
+ APDW_MASK = (1 << 2) | (1 << 1) | (1 << 0), /* 2:0 */
+
+}CTRL2_BITS;
+
+/* XMT_RING_LIMIT 0x7C, 32bit register */
+typedef enum {
+
+ XMT_RING2_LIMIT = (0xFF << 16), /* 23:16 */
+ XMT_RING1_LIMIT = (0xFF << 8), /* 15:8 */
+ XMT_RING0_LIMIT = (0xFF << 0), /* 7:0 */
+
+}XMT_RING_LIMIT_BITS;
+
+typedef enum {
+
+ AP_REG0_EN = (1 << 15),
+ AP_REG0_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PHY0_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL0_BITS;
+
+/* AUTOPOLL1 0x8A, 16bit register */
+typedef enum {
+
+ AP_REG1_EN = (1 << 15),
+ AP_REG1_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP1 = (1 << 6),
+ AP_PHY1_DFLT = (1 << 5),
+ AP_PHY1_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL1_BITS;
+
+
+typedef enum {
+
+ AP_REG2_EN = (1 << 15),
+ AP_REG2_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP2 = (1 << 6),
+ AP_PHY2_DFLT = (1 << 5),
+ AP_PHY2_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL2_BITS;
+
+typedef enum {
+
+ AP_REG3_EN = (1 << 15),
+ AP_REG3_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP3 = (1 << 6),
+ AP_PHY3_DFLT = (1 << 5),
+ AP_PHY3_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL3_BITS;
+
+
+typedef enum {
+
+ AP_REG4_EN = (1 << 15),
+ AP_REG4_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP4 = (1 << 6),
+ AP_PHY4_DFLT = (1 << 5),
+ AP_PHY4_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL4_BITS;
+
+
+typedef enum {
+
+ AP_REG5_EN = (1 << 15),
+ AP_REG5_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP5 = (1 << 6),
+ AP_PHY5_DFLT = (1 << 5),
+ AP_PHY5_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL5_BITS;
+
+
+
+
+/* AP_VALUE 0x98, 32bit ragister */
+typedef enum {
+
+ AP_VAL_ACTIVE = (1 << 31),
+ AP_VAL_RD_CMD = ( 1 << 29),
+ AP_ADDR = (1 << 18)|(1 << 17)|(1 << 16), /* 18:16 */
+ AP_VAL = (0xF << 0) | (0xF << 4) |( 0xF << 8) |
+ (0xF << 12), /* 15:0 */
+
+}AP_VALUE_BITS;
+
+typedef enum {
+
+ DLY_INT_A_R3 = (1 << 31),
+ DLY_INT_A_R2 = (1 << 30),
+ DLY_INT_A_R1 = (1 << 29),
+ DLY_INT_A_R0 = (1 << 28),
+ DLY_INT_A_T3 = (1 << 27),
+ DLY_INT_A_T2 = (1 << 26),
+ DLY_INT_A_T1 = (1 << 25),
+ DLY_INT_A_T0 = ( 1 << 24),
+ EVENT_COUNT_A = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_A = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+
+}DLY_INT_A_BITS;
+
+typedef enum {
+
+ DLY_INT_B_R3 = (1 << 31),
+ DLY_INT_B_R2 = (1 << 30),
+ DLY_INT_B_R1 = (1 << 29),
+ DLY_INT_B_R0 = (1 << 28),
+ DLY_INT_B_T3 = (1 << 27),
+ DLY_INT_B_T2 = (1 << 26),
+ DLY_INT_B_T1 = (1 << 25),
+ DLY_INT_B_T0 = ( 1 << 24),
+ EVENT_COUNT_B = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_B = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+}DLY_INT_B_BITS;
+
+
+/* FLOW_CONTROL 0xC8, 32bit register */
+typedef enum {
+
+ PAUSE_LEN_CHG = (1 << 30),
+ FTPE = (1 << 22),
+ FRPE = (1 << 21),
+ NAPA = (1 << 20),
+ NPA = (1 << 19),
+ FIXP = ( 1 << 18),
+ FCCMD = ( 1 << 16),
+ PAUSE_LEN = (0xF << 0) | (0xF << 4) |( 0xF << 8) | (0xF << 12), /* 15:0 */
+
+}FLOW_CONTROL_BITS;
+
+/* PHY_ ACCESS 0xD0, 32bit register */
+typedef enum {
+
+ PHY_CMD_ACTIVE = (1 << 31),
+ PHY_WR_CMD = (1 << 30),
+ PHY_RD_CMD = (1 << 29),
+ PHY_RD_ERR = (1 << 28),
+ PHY_PRE_SUP = (1 << 27),
+ PHY_ADDR = (1 << 21) | (1 << 22) | (1 << 23)|
+ (1 << 24) |(1 << 25),/* 25:21 */
+ PHY_REG_ADDR = (1 << 16) | (1 << 17) | (1 << 18)| (1 << 19) | (1 << 20),/* 20:16 */
+ PHY_DATA = (0xF << 0)|(0xF << 4) |(0xF << 8)|
+ (0xF << 12),/* 15:0 */
+
+}PHY_ACCESS_BITS;
+
+
+/* PMAT0 0x190, 32bit register */
+typedef enum {
+ PMR_ACTIVE = (1 << 31),
+ PMR_WR_CMD = (1 << 30),
+ PMR_RD_CMD = (1 << 29),
+ PMR_BANK = (1 <<28),
+ PMR_ADDR = (0xF << 16)|(1 << 20)|(1 << 21)|
+ (1 << 22),/* 22:16 */
+ PMR_B4 = (0xF << 0) | (0xF << 4),/* 15:0 */
+}PMAT0_BITS;
+
+
+/* PMAT1 0x194, 32bit register */
+typedef enum {
+ PMR_B3 = (0xF << 24) | (0xF <<28),/* 31:24 */
+ PMR_B2 = (0xF << 16) |(0xF << 20),/* 23:16 */
+ PMR_B1 = (0xF << 8) | (0xF <<12), /* 15:8 */
+ PMR_B0 = (0xF << 0)|(0xF << 4),/* 7:0 */
+}PMAT1_BITS;
+
+/************************************************************************/
+/* */
+/* MIB counter definitions */
+/* */
+/************************************************************************/
+
+#define rcv_miss_pkts 0x00
+#define rcv_octets 0x01
+#define rcv_broadcast_pkts 0x02
+#define rcv_multicast_pkts 0x03
+#define rcv_undersize_pkts 0x04
+#define rcv_oversize_pkts 0x05
+#define rcv_fragments 0x06
+#define rcv_jabbers 0x07
+#define rcv_unicast_pkts 0x08
+#define rcv_alignment_errors 0x09
+#define rcv_fcs_errors 0x0A
+#define rcv_good_octets 0x0B
+#define rcv_mac_ctrl 0x0C
+#define rcv_flow_ctrl 0x0D
+#define rcv_pkts_64_octets 0x0E
+#define rcv_pkts_65to127_octets 0x0F
+#define rcv_pkts_128to255_octets 0x10
+#define rcv_pkts_256to511_octets 0x11
+#define rcv_pkts_512to1023_octets 0x12
+#define rcv_pkts_1024to1518_octets 0x13
+#define rcv_unsupported_opcode 0x14
+#define rcv_symbol_errors 0x15
+#define rcv_drop_pkts_ring1 0x16
+#define rcv_drop_pkts_ring2 0x17
+#define rcv_drop_pkts_ring3 0x18
+#define rcv_drop_pkts_ring4 0x19
+#define rcv_jumbo_pkts 0x1A
+
+#define xmt_underrun_pkts 0x20
+#define xmt_octets 0x21
+#define xmt_packets 0x22
+#define xmt_broadcast_pkts 0x23
+#define xmt_multicast_pkts 0x24
+#define xmt_collisions 0x25
+#define xmt_unicast_pkts 0x26
+#define xmt_one_collision 0x27
+#define xmt_multiple_collision 0x28
+#define xmt_deferred_transmit 0x29
+#define xmt_late_collision 0x2A
+#define xmt_excessive_defer 0x2B
+#define xmt_loss_carrier 0x2C
+#define xmt_excessive_collision 0x2D
+#define xmt_back_pressure 0x2E
+#define xmt_flow_ctrl 0x2F
+#define xmt_pkts_64_octets 0x30
+#define xmt_pkts_65to127_octets 0x31
+#define xmt_pkts_128to255_octets 0x32
+#define xmt_pkts_256to511_octets 0x33
+#define xmt_pkts_512to1023_octets 0x34
+#define xmt_pkts_1024to1518_octet 0x35
+#define xmt_oversize_pkts 0x36
+#define xmt_jumbo_pkts 0x37
+
+
+/* Driver definitions */
+
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD8111E_7462 0x7462
+
+#define MAX_UNITS 8 /* Maximum number of devices possible */
+
+#define NUM_TX_BUFFERS 32 /* Number of transmit buffers */
+#define NUM_RX_BUFFERS 32 /* Number of receive buffers */
+
+#define TX_BUFF_MOD_MASK 31 /* (NUM_TX_BUFFERS -1) */
+#define RX_BUFF_MOD_MASK 31 /* (NUM_RX_BUFFERS -1) */
+
+#define NUM_TX_RING_DR 32
+#define NUM_RX_RING_DR 32
+
+#define TX_RING_DR_MOD_MASK 31 /* (NUM_TX_RING_DR -1) */
+#define RX_RING_DR_MOD_MASK 31 /* (NUM_RX_RING_DR -1) */
+
+#define MAX_FILTER_SIZE 64 /* Maximum multicast address */
+#define AMD8111E_MIN_MTU 60
+#define AMD8111E_MAX_MTU 9000
+
+#define PKT_BUFF_SZ 1536
+#define MIN_PKT_LEN 60
+
+#define AMD8111E_TX_TIMEOUT (3 * HZ)/* 3 sec */
+#define SOFT_TIMER_FREQ 0xBEBC /* 0.5 sec */
+#define DELAY_TIMER_CONV 50 /* msec to 10 usec conversion.
+ Only 500 usec resolution */
+#define OPTION_VLAN_ENABLE 0x0001
+#define OPTION_JUMBO_ENABLE 0x0002
+#define OPTION_MULTICAST_ENABLE 0x0004
+#define OPTION_WOL_ENABLE 0x0008
+#define OPTION_WAKE_MAGIC_ENABLE 0x0010
+#define OPTION_WAKE_PHY_ENABLE 0x0020
+#define OPTION_INTR_COAL_ENABLE 0x0040
+#define OPTION_DYN_IPG_ENABLE 0x0080
+
+#define PHY_REG_ADDR_MASK 0x1f
+
+/* ipg parameters */
+#define DEFAULT_IPG 0x60
+#define IFS1_DELTA 36
+#define IPG_CONVERGE_JIFFIES (HZ/2)
+#define IPG_STABLE_TIME 5
+#define MIN_IPG 96
+#define MAX_IPG 255
+#define IPG_STEP 16
+#define CSTATE 1
+#define SSTATE 2
+
+/* Assume contoller gets data 10 times the maximum processing time */
+#define REPEAT_CNT 10
+
+/* amd8111e decriptor flag definitions */
+typedef enum {
+
+ OWN_BIT = (1 << 15),
+ ADD_FCS_BIT = (1 << 13),
+ LTINT_BIT = (1 << 12),
+ STP_BIT = (1 << 9),
+ ENP_BIT = (1 << 8),
+ KILL_BIT = (1 << 6),
+ TCC_VLAN_INSERT = (1 << 1),
+ TCC_VLAN_REPLACE = (1 << 1) |( 1<< 0),
+
+}TX_FLAG_BITS;
+
+typedef enum {
+ ERR_BIT = (1 << 14),
+ FRAM_BIT = (1 << 13),
+ OFLO_BIT = (1 << 12),
+ CRC_BIT = (1 << 11),
+ PAM_BIT = (1 << 6),
+ LAFM_BIT = (1 << 5),
+ BAM_BIT = (1 << 4),
+ TT_VLAN_TAGGED = (1 << 3) |(1 << 2),/* 0x000 */
+ TT_PRTY_TAGGED = (1 << 3),/* 0x0008 */
+
+}RX_FLAG_BITS;
+
+#define RESET_RX_FLAGS 0x0000
+#define TT_MASK 0x000c
+#define TCC_MASK 0x0003
+
+/* driver ioctl parameters */
+#define AMD8111E_REG_DUMP_LEN 13*sizeof(u32)
+
+/* amd8111e desriptor format */
+
+struct amd8111e_tx_dr{
+
+ __le16 buff_count; /* Size of the buffer pointed by this descriptor */
+
+ __le16 tx_flags;
+
+ __le16 tag_ctrl_info;
+
+ __le16 tag_ctrl_cmd;
+
+ __le32 buff_phy_addr;
+
+ __le32 reserved;
+};
+
+struct amd8111e_rx_dr{
+
+ __le32 reserved;
+
+ __le16 msg_count; /* Received message len */
+
+ __le16 tag_ctrl_info;
+
+ __le16 buff_count; /* Len of the buffer pointed by descriptor. */
+
+ __le16 rx_flags;
+
+ __le32 buff_phy_addr;
+
+};
+struct amd8111e_link_config{
+
+#define SPEED_INVALID 0xffff
+#define DUPLEX_INVALID 0xff
+#define AUTONEG_INVALID 0xff
+
+ unsigned long orig_phy_option;
+ u16 speed;
+ u8 duplex;
+ u8 autoneg;
+ u8 reserved; /* 32bit alignment */
+};
+
+enum coal_type{
+
+ NO_COALESCE,
+ LOW_COALESCE,
+ MEDIUM_COALESCE,
+ HIGH_COALESCE,
+
+};
+
+enum coal_mode{
+ RX_INTR_COAL,
+ TX_INTR_COAL,
+ DISABLE_COAL,
+ ENABLE_COAL,
+
+};
+#define MAX_TIMEOUT 40
+#define MAX_EVENT_COUNT 31
+struct amd8111e_coalesce_conf{
+
+ unsigned int rx_timeout;
+ unsigned int rx_event_count;
+ unsigned long rx_packets;
+ unsigned long rx_prev_packets;
+ unsigned long rx_bytes;
+ unsigned long rx_prev_bytes;
+ unsigned int rx_coal_type;
+
+ unsigned int tx_timeout;
+ unsigned int tx_event_count;
+ unsigned long tx_packets;
+ unsigned long tx_prev_packets;
+ unsigned long tx_bytes;
+ unsigned long tx_prev_bytes;
+ unsigned int tx_coal_type;
+
+};
+struct ipg_info{
+
+ unsigned int ipg_state;
+ unsigned int ipg;
+ unsigned int current_ipg;
+ unsigned int col_cnt;
+ unsigned int diff_col_cnt;
+ unsigned int timer_tick;
+ unsigned int prev_ipg;
+ struct timer_list ipg_timer;
+};
+
+struct amd8111e_priv{
+
+ struct amd8111e_tx_dr* tx_ring;
+ struct amd8111e_rx_dr* rx_ring;
+ dma_addr_t tx_ring_dma_addr; /* tx descriptor ring base address */
+ dma_addr_t rx_ring_dma_addr; /* rx descriptor ring base address */
+ const char *name;
+ struct pci_dev *pci_dev; /* Ptr to the associated pci_dev */
+ struct net_device* amd8111e_net_dev; /* ptr to associated net_device */
+ /* Transmit and receive skbs */
+ struct sk_buff *tx_skbuff[NUM_TX_BUFFERS];
+ struct sk_buff *rx_skbuff[NUM_RX_BUFFERS];
+ /* Transmit and receive dma mapped addr */
+ dma_addr_t tx_dma_addr[NUM_TX_BUFFERS];
+ dma_addr_t rx_dma_addr[NUM_RX_BUFFERS];
+ /* Reg memory mapped address */
+ void __iomem *mmio;
+
+ struct napi_struct napi;
+
+ spinlock_t lock; /* Guard lock */
+ unsigned long rx_idx, tx_idx; /* The next free ring entry */
+ unsigned long tx_complete_idx;
+ unsigned long tx_ring_complete_idx;
+ unsigned long tx_ring_idx;
+ unsigned int rx_buff_len; /* Buffer length of rx buffers */
+ int options; /* Options enabled/disabled for the device */
+
+ unsigned long ext_phy_option;
+ int ext_phy_addr;
+ u32 ext_phy_id;
+
+ struct amd8111e_link_config link_config;
+ int pm_cap;
+
+ struct net_device *next;
+ int mii;
+ struct mii_if_info mii_if;
+ char opened;
+ unsigned int drv_rx_errors;
+ struct amd8111e_coalesce_conf coal_conf;
+
+ struct ipg_info ipg_data;
+
+};
+
+/* kernel provided writeq does not write 64 bits into the amd8111e device register instead writes only higher 32bits data into lower 32bits of the register.
+BUG? */
+#define amd8111e_writeq(_UlData,_memMap) \
+ writel(*(u32*)(&_UlData), _memMap); \
+ writel(*(u32*)((u8*)(&_UlData)+4), _memMap+4)
+
+/* maps the external speed options to internal value */
+typedef enum {
+ SPEED_AUTONEG,
+ SPEED10_HALF,
+ SPEED10_FULL,
+ SPEED100_HALF,
+ SPEED100_FULL,
+}EXT_PHY_OPTION;
+
+static int card_idx;
+static int speed_duplex[MAX_UNITS] = { 0, };
+static bool coalesce[MAX_UNITS] = { [ 0 ... MAX_UNITS-1] = true };
+static bool dynamic_ipg[MAX_UNITS] = { [ 0 ... MAX_UNITS-1] = false };
+static unsigned int chip_version;
+
+#endif /* _AMD8111E_H */
+
diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c
new file mode 100644
index 00000000000..968b7bfac8f
--- /dev/null
+++ b/drivers/net/ethernet/amd/ariadne.c
@@ -0,0 +1,792 @@
+/*
+ * Amiga Linux/m68k Ariadne Ethernet Driver
+ *
+ * © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org)
+ * Peter De Schrijver (p2@mind.be)
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * lance.c: An AMD LANCE ethernet driver for linux.
+ * Written 1993-94 by Donald Becker.
+ *
+ * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
+ * Advanced Micro Devices
+ * Publication #16907, Rev. B, Amendment/0, May 1994
+ *
+ * MC68230: Parallel Interface/Timer (PI/T)
+ * Motorola Semiconductors, December, 1983
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The Ariadne is a Zorro-II board made by Village Tronic. It contains:
+ *
+ * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
+ * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
+ *
+ * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+/*#define DEBUG*/
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/zorro.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+#include "ariadne.h"
+
+#ifdef ARIADNE_DEBUG
+int ariadne_debug = ARIADNE_DEBUG;
+#else
+int ariadne_debug = 1;
+#endif
+
+/* Macros to Fix Endianness problems */
+
+/* Swap the Bytes in a WORD */
+#define swapw(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00))
+/* Get the Low BYTE in a WORD */
+#define lowb(x) (x & 0xff)
+/* Get the Swapped High WORD in a LONG */
+#define swhighw(x) ((((x) >> 8) & 0xff00) | (((x) >> 24) & 0x00ff))
+/* Get the Swapped Low WORD in a LONG */
+#define swloww(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff))
+
+/* Transmit/Receive Ring Definitions */
+
+#define TX_RING_SIZE 5
+#define RX_RING_SIZE 16
+
+#define PKT_BUF_SIZE 1520
+
+/* Private Device Data */
+
+struct ariadne_private {
+ volatile struct TDRE *tx_ring[TX_RING_SIZE];
+ volatile struct RDRE *rx_ring[RX_RING_SIZE];
+ volatile u_short *tx_buff[TX_RING_SIZE];
+ volatile u_short *rx_buff[RX_RING_SIZE];
+ int cur_tx, cur_rx; /* The next free ring entry */
+ int dirty_tx; /* The ring entries to be free()ed */
+ char tx_full;
+};
+
+/* Structure Created in the Ariadne's RAM Buffer */
+
+struct lancedata {
+ struct TDRE tx_ring[TX_RING_SIZE];
+ struct RDRE rx_ring[RX_RING_SIZE];
+ u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
+ u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
+};
+
+static void memcpyw(volatile u_short *dest, u_short *src, int len)
+{
+ while (len >= 2) {
+ *(dest++) = *(src++);
+ len -= 2;
+ }
+ if (len == 1)
+ *dest = (*(u_char *)src) << 8;
+}
+
+static void ariadne_init_ring(struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start;
+ int i;
+
+ netif_stop_queue(dev);
+
+ priv->tx_full = 0;
+ priv->cur_rx = priv->cur_tx = 0;
+ priv->dirty_tx = 0;
+
+ /* Set up TX Ring */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ volatile struct TDRE *t = &lancedata->tx_ring[i];
+ t->TMD0 = swloww(ARIADNE_RAM +
+ offsetof(struct lancedata, tx_buff[i]));
+ t->TMD1 = swhighw(ARIADNE_RAM +
+ offsetof(struct lancedata, tx_buff[i])) |
+ TF_STP | TF_ENP;
+ t->TMD2 = swapw((u_short)-PKT_BUF_SIZE);
+ t->TMD3 = 0;
+ priv->tx_ring[i] = &lancedata->tx_ring[i];
+ priv->tx_buff[i] = lancedata->tx_buff[i];
+ netdev_dbg(dev, "TX Entry %2d at %p, Buf at %p\n",
+ i, &lancedata->tx_ring[i], lancedata->tx_buff[i]);
+ }
+
+ /* Set up RX Ring */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ volatile struct RDRE *r = &lancedata->rx_ring[i];
+ r->RMD0 = swloww(ARIADNE_RAM +
+ offsetof(struct lancedata, rx_buff[i]));
+ r->RMD1 = swhighw(ARIADNE_RAM +
+ offsetof(struct lancedata, rx_buff[i])) |
+ RF_OWN;
+ r->RMD2 = swapw((u_short)-PKT_BUF_SIZE);
+ r->RMD3 = 0x0000;
+ priv->rx_ring[i] = &lancedata->rx_ring[i];
+ priv->rx_buff[i] = lancedata->rx_buff[i];
+ netdev_dbg(dev, "RX Entry %2d at %p, Buf at %p\n",
+ i, &lancedata->rx_ring[i], lancedata->rx_buff[i]);
+ }
+}
+
+static int ariadne_rx(struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ int entry = priv->cur_rx % RX_RING_SIZE;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up */
+ while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) {
+ int status = lowb(priv->rx_ring[entry]->RMD1);
+
+ if (status != (RF_STP | RF_ENP)) { /* There was an error */
+ /* There is a tricky error noted by
+ * John Murphy <murf@perftech.com> to Russ Nelson:
+ * Even with full-sized buffers it's possible for a
+ * jabber packet to use two buffers, with only the
+ * last correctly noting the error
+ */
+ /* Only count a general error at the end of a packet */
+ if (status & RF_ENP)
+ dev->stats.rx_errors++;
+ if (status & RF_FRAM)
+ dev->stats.rx_frame_errors++;
+ if (status & RF_OFLO)
+ dev->stats.rx_over_errors++;
+ if (status & RF_CRC)
+ dev->stats.rx_crc_errors++;
+ if (status & RF_BUFF)
+ dev->stats.rx_fifo_errors++;
+ priv->rx_ring[entry]->RMD1 &= 0xff00 | RF_STP | RF_ENP;
+ } else {
+ /* Malloc up new buffer, compatible with net-3 */
+ short pkt_len = swapw(priv->rx_ring[entry]->RMD3);
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (skb == NULL) {
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN)
+ break;
+
+ if (i > RX_RING_SIZE - 2) {
+ dev->stats.rx_dropped++;
+ priv->rx_ring[entry]->RMD1 |= RF_OWN;
+ priv->cur_rx++;
+ }
+ break;
+ }
+
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, pkt_len); /* Make room */
+ skb_copy_to_linear_data(skb,
+ (const void *)priv->rx_buff[entry],
+ pkt_len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netdev_dbg(dev, "RX pkt type 0x%04x from %pM to %pM data %p len %u\n",
+ ((u_short *)skb->data)[6],
+ skb->data + 6, skb->data,
+ skb->data, skb->len);
+
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+
+ priv->rx_ring[entry]->RMD1 |= RF_OWN;
+ entry = (++priv->cur_rx) % RX_RING_SIZE;
+ }
+
+ priv->cur_rx = priv->cur_rx % RX_RING_SIZE;
+
+ /* We should check that at least two ring entries are free.
+ * If not, we should free one and mark stats->rx_dropped++
+ */
+
+ return 0;
+}
+
+static irqreturn_t ariadne_interrupt(int irq, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ struct ariadne_private *priv;
+ int csr0, boguscnt;
+ int handled = 0;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+
+ if (!(lance->RDP & INTR)) /* Check if any interrupt has been */
+ return IRQ_NONE; /* generated by the board */
+
+ priv = netdev_priv(dev);
+
+ boguscnt = 10;
+ while ((csr0 = lance->RDP) & (ERR | RINT | TINT) && --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP */
+ lance->RDP = csr0 & ~(INEA | TDMD | STOP | STRT | INIT);
+
+#ifdef DEBUG
+ if (ariadne_debug > 5) {
+ netdev_dbg(dev, "interrupt csr0=%#02x new csr=%#02x [",
+ csr0, lance->RDP);
+ if (csr0 & INTR)
+ pr_cont(" INTR");
+ if (csr0 & INEA)
+ pr_cont(" INEA");
+ if (csr0 & RXON)
+ pr_cont(" RXON");
+ if (csr0 & TXON)
+ pr_cont(" TXON");
+ if (csr0 & TDMD)
+ pr_cont(" TDMD");
+ if (csr0 & STOP)
+ pr_cont(" STOP");
+ if (csr0 & STRT)
+ pr_cont(" STRT");
+ if (csr0 & INIT)
+ pr_cont(" INIT");
+ if (csr0 & ERR)
+ pr_cont(" ERR");
+ if (csr0 & BABL)
+ pr_cont(" BABL");
+ if (csr0 & CERR)
+ pr_cont(" CERR");
+ if (csr0 & MISS)
+ pr_cont(" MISS");
+ if (csr0 & MERR)
+ pr_cont(" MERR");
+ if (csr0 & RINT)
+ pr_cont(" RINT");
+ if (csr0 & TINT)
+ pr_cont(" TINT");
+ if (csr0 & IDON)
+ pr_cont(" IDON");
+ pr_cont(" ]\n");
+ }
+#endif
+
+ if (csr0 & RINT) { /* Rx interrupt */
+ handled = 1;
+ ariadne_rx(dev);
+ }
+
+ if (csr0 & TINT) { /* Tx-done interrupt */
+ int dirty_tx = priv->dirty_tx;
+
+ handled = 1;
+ while (dirty_tx < priv->cur_tx) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = lowb(priv->tx_ring[entry]->TMD1);
+
+ if (status & TF_OWN)
+ break; /* It still hasn't been Txed */
+
+ priv->tx_ring[entry]->TMD1 &= 0xff00;
+
+ if (status & TF_ERR) {
+ /* There was an major error, log it */
+ int err_status = priv->tx_ring[entry]->TMD3;
+ dev->stats.tx_errors++;
+ if (err_status & EF_RTRY)
+ dev->stats.tx_aborted_errors++;
+ if (err_status & EF_LCAR)
+ dev->stats.tx_carrier_errors++;
+ if (err_status & EF_LCOL)
+ dev->stats.tx_window_errors++;
+ if (err_status & EF_UFLO) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ dev->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ netdev_err(dev, "Tx FIFO error! Status %04x\n",
+ csr0);
+ /* Restart the chip */
+ lance->RDP = STRT;
+ }
+ } else {
+ if (status & (TF_MORE | TF_ONE))
+ dev->stats.collisions++;
+ dev->stats.tx_packets++;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ netdev_err(dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n",
+ dirty_tx, priv->cur_tx,
+ priv->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (priv->tx_full && netif_queue_stopped(dev) &&
+ dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full */
+ priv->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ priv->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors */
+ if (csr0 & BABL) {
+ handled = 1;
+ dev->stats.tx_errors++; /* Tx babble */
+ }
+ if (csr0 & MISS) {
+ handled = 1;
+ dev->stats.rx_errors++; /* Missed a Rx frame */
+ }
+ if (csr0 & MERR) {
+ handled = 1;
+ netdev_err(dev, "Bus master arbitration failure, status %04x\n",
+ csr0);
+ /* Restart the chip */
+ lance->RDP = STRT;
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | BABL | CERR | MISS | MERR | IDON;
+
+ if (ariadne_debug > 4)
+ netdev_dbg(dev, "exiting interrupt, csr%d=%#04x\n",
+ lance->RAP, lance->RDP);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int ariadne_open(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ u_short in;
+ u_long version;
+ int i;
+
+ /* Reset the LANCE */
+ in = lance->Reset;
+
+ /* Stop the LANCE */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP;
+
+ /* Check the LANCE version */
+ lance->RAP = CSR88; /* Chip ID */
+ version = swapw(lance->RDP);
+ lance->RAP = CSR89; /* Chip ID */
+ version |= swapw(lance->RDP) << 16;
+ if ((version & 0x00000fff) != 0x00000003) {
+ pr_warn("Couldn't find AMD Ethernet Chip\n");
+ return -EAGAIN;
+ }
+ if ((version & 0x0ffff000) != 0x00003000) {
+ pr_warn("Couldn't find Am79C960 (Wrong part number = %ld)\n",
+ (version & 0x0ffff000) >> 12);
+ return -EAGAIN;
+ }
+
+ netdev_dbg(dev, "Am79C960 (PCnet-ISA) Revision %ld\n",
+ (version & 0xf0000000) >> 28);
+
+ ariadne_init_ring(dev);
+
+ /* Miscellaneous Stuff */
+ lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR4; /* Test and Features Control */
+ lance->RDP = DPOLL | APAD_XMT | MFCOM | RCVCCOM | TXSTRTM | JABM;
+
+ /* Set the Multicast Table */
+ lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */
+ lance->RDP = 0x0000;
+ lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */
+ lance->RDP = 0x0000;
+
+ /* Set the Ethernet Hardware Address */
+ lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[0];
+ lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[1];
+ lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */
+ lance->RDP = ((u_short *)&dev->dev_addr[0])[2];
+
+ /* Set the Init Block Mode */
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = 0x0000;
+
+ /* Set the Transmit Descriptor Ring Pointer */
+ lance->RAP = CSR30; /* Base Address of Transmit Ring */
+ lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
+ lance->RAP = CSR31; /* Base Address of transmit Ring */
+ lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
+
+ /* Set the Receive Descriptor Ring Pointer */
+ lance->RAP = CSR24; /* Base Address of Receive Ring */
+ lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
+ lance->RAP = CSR25; /* Base Address of Receive Ring */
+ lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
+
+ /* Set the Number of RX and TX Ring Entries */
+ lance->RAP = CSR76; /* Receive Ring Length */
+ lance->RDP = swapw(((u_short)-RX_RING_SIZE));
+ lance->RAP = CSR78; /* Transmit Ring Length */
+ lance->RDP = swapw(((u_short)-TX_RING_SIZE));
+
+ /* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */
+ lance->RAP = ISACSR2; /* Miscellaneous Configuration */
+ lance->IDP = ASEL;
+
+ /* LED Control */
+ lance->RAP = ISACSR5; /* LED1 Status */
+ lance->IDP = PSE|XMTE;
+ lance->RAP = ISACSR6; /* LED2 Status */
+ lance->IDP = PSE|COLE;
+ lance->RAP = ISACSR7; /* LED3 Status */
+ lance->IDP = PSE|RCVE;
+
+ netif_start_queue(dev);
+
+ i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (i)
+ return i;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | STRT;
+
+ return 0;
+}
+
+static int ariadne_close(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ lance->RAP = CSR112; /* Missed Frame Count */
+ dev->stats.rx_missed_errors = swapw(lance->RDP);
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+
+ if (ariadne_debug > 1) {
+ netdev_dbg(dev, "Shutting down ethercard, status was %02x\n",
+ lance->RDP);
+ netdev_dbg(dev, "%lu packets missed\n",
+ dev->stats.rx_missed_errors);
+ }
+
+ /* We stop the LANCE here -- it occasionally polls memory if we don't */
+ lance->RDP = STOP;
+
+ free_irq(IRQ_AMIGA_PORTS, dev);
+
+ return 0;
+}
+
+static inline void ariadne_reset(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP;
+ ariadne_init_ring(dev);
+ lance->RDP = INEA | STRT;
+ netif_start_queue(dev);
+}
+
+static void ariadne_tx_timeout(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ netdev_err(dev, "transmit timed out, status %04x, resetting\n",
+ lance->RDP);
+ ariadne_reset(dev);
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t ariadne_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ariadne_private *priv = netdev_priv(dev);
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ int entry;
+ unsigned long flags;
+ int len = skb->len;
+
+#if 0
+ if (ariadne_debug > 3) {
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ netdev_dbg(dev, "%s: csr0 %04x\n", __func__, lance->RDP);
+ lance->RDP = 0x0000;
+ }
+#endif
+
+ /* FIXME: is the 79C960 new enough to do its own padding right ? */
+ if (skb->len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ len = ETH_ZLEN;
+ }
+
+ /* Fill in a Tx ring entry */
+
+ netdev_dbg(dev, "TX pkt type 0x%04x from %pM to %pM data %p len %u\n",
+ ((u_short *)skb->data)[6],
+ skb->data + 6, skb->data,
+ skb->data, skb->len);
+
+ local_irq_save(flags);
+
+ entry = priv->cur_tx % TX_RING_SIZE;
+
+ /* Caution: the write order is important here, set the base address with
+ the "ownership" bits last */
+
+ priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len);
+ priv->tx_ring[entry]->TMD3 = 0x0000;
+ memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_DEBUG, "tx_buff: ", DUMP_PREFIX_OFFSET, 16, 1,
+ (void *)priv->tx_buff[entry],
+ skb->len > 64 ? 64 : skb->len, true);
+#endif
+
+ priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1 & 0xff00)
+ | TF_OWN | TF_STP | TF_ENP;
+
+ dev_kfree_skb(skb);
+
+ priv->cur_tx++;
+ if ((priv->cur_tx >= TX_RING_SIZE) &&
+ (priv->dirty_tx >= TX_RING_SIZE)) {
+
+ netdev_dbg(dev, "*** Subtracting TX_RING_SIZE from cur_tx (%d) and dirty_tx (%d)\n",
+ priv->cur_tx, priv->dirty_tx);
+
+ priv->cur_tx -= TX_RING_SIZE;
+ priv->dirty_tx -= TX_RING_SIZE;
+ }
+ dev->stats.tx_bytes += len;
+
+ /* Trigger an immediate send poll */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | TDMD;
+
+ if (lowb(priv->tx_ring[(entry + 1) % TX_RING_SIZE]->TMD1) != 0) {
+ netif_stop_queue(dev);
+ priv->tx_full = 1;
+ }
+ local_irq_restore(flags);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *ariadne_get_stats(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+ short saved_addr;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ saved_addr = lance->RAP;
+ lance->RAP = CSR112; /* Missed Frame Count */
+ dev->stats.rx_missed_errors = swapw(lance->RDP);
+ lance->RAP = saved_addr;
+ local_irq_restore(flags);
+
+ return &dev->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * 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.
+ */
+static void set_multicast_list(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
+
+ if (!netif_running(dev))
+ return;
+
+ netif_stop_queue(dev);
+
+ /* We take the simple way out and always enable promiscuous mode */
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP; /* Temporarily stop the lance */
+ ariadne_init_ring(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = PROM; /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int num_addrs = netdev_mc_count(dev);
+ int i;
+ /* We don't use the multicast table,
+ * but rely on upper-layer filtering
+ */
+ memset(multicast_table, (num_addrs == 0) ? 0 : -1,
+ sizeof(multicast_table));
+ for (i = 0; i < 4; i++) {
+ lance->RAP = CSR8 + (i << 8);
+ /* Logical Address Filter */
+ lance->RDP = swapw(multicast_table[i]);
+ }
+ lance->RAP = CSR15; /* Mode Register */
+ lance->RDP = 0x0000; /* Unset promiscuous mode */
+ }
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = INEA | STRT | IDON;/* Resume normal operation */
+
+ netif_wake_queue(dev);
+}
+
+
+static void ariadne_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960));
+ release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE);
+ free_netdev(dev);
+}
+
+static struct zorro_device_id ariadne_zorro_tbl[] = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl);
+
+static const struct net_device_ops ariadne_netdev_ops = {
+ .ndo_open = ariadne_open,
+ .ndo_stop = ariadne_close,
+ .ndo_start_xmit = ariadne_start_xmit,
+ .ndo_tx_timeout = ariadne_tx_timeout,
+ .ndo_get_stats = ariadne_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int ariadne_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ unsigned long board = z->resource.start;
+ unsigned long base_addr = board + ARIADNE_LANCE;
+ unsigned long mem_start = board + ARIADNE_RAM;
+ struct resource *r1, *r2;
+ struct net_device *dev;
+ u32 serial;
+ int err;
+
+ r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960");
+ if (!r1)
+ return -EBUSY;
+ r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM");
+ if (!r2) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ return -EBUSY;
+ }
+
+ dev = alloc_etherdev(sizeof(struct ariadne_private));
+ if (dev == NULL) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ release_mem_region(mem_start, ARIADNE_RAM_SIZE);
+ return -ENOMEM;
+ }
+
+ r1->name = dev->name;
+ r2->name = dev->name;
+
+ serial = be32_to_cpu(z->rom.er_SerialNumber);
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x60;
+ dev->dev_addr[2] = 0x30;
+ dev->dev_addr[3] = (serial >> 16) & 0xff;
+ dev->dev_addr[4] = (serial >> 8) & 0xff;
+ dev->dev_addr[5] = serial & 0xff;
+ dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr);
+ dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start);
+ dev->mem_end = dev->mem_start + ARIADNE_RAM_SIZE;
+
+ dev->netdev_ops = &ariadne_netdev_ops;
+ dev->watchdog_timeo = 5 * HZ;
+
+ err = register_netdev(dev);
+ if (err) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ release_mem_region(mem_start, ARIADNE_RAM_SIZE);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+
+ netdev_info(dev, "Ariadne at 0x%08lx, Ethernet Address %pM\n",
+ board, dev->dev_addr);
+
+ return 0;
+}
+
+static struct zorro_driver ariadne_driver = {
+ .name = "ariadne",
+ .id_table = ariadne_zorro_tbl,
+ .probe = ariadne_init_one,
+ .remove = ariadne_remove_one,
+};
+
+static int __init ariadne_init_module(void)
+{
+ return zorro_register_driver(&ariadne_driver);
+}
+
+static void __exit ariadne_cleanup_module(void)
+{
+ zorro_unregister_driver(&ariadne_driver);
+}
+
+module_init(ariadne_init_module);
+module_exit(ariadne_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/amd/ariadne.h b/drivers/net/ethernet/amd/ariadne.h
new file mode 100644
index 00000000000..727be5cdd1e
--- /dev/null
+++ b/drivers/net/ethernet/amd/ariadne.h
@@ -0,0 +1,415 @@
+/*
+ * Amiga Linux/m68k Ariadne Ethernet Driver
+ *
+ * © Copyright 1995 by Geert Uytterhoeven (geert@linux-m68k.org)
+ * Peter De Schrijver
+ * (Peter.DeSchrijver@linux.cc.kuleuven.ac.be)
+ *
+ * ----------------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * lance.c: An AMD LANCE ethernet driver for linux.
+ * Written 1993-94 by Donald Becker.
+ *
+ * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
+ * Advanced Micro Devices
+ * Publication #16907, Rev. B, Amendment/0, May 1994
+ *
+ * MC68230: Parallel Interface/Timer (PI/T)
+ * Motorola Semiconductors, December, 1983
+ *
+ * ----------------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------------
+ *
+ * The Ariadne is a Zorro-II board made by Village Tronic. It contains:
+ *
+ * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
+ * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
+ *
+ * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
+ */
+
+
+ /*
+ * Am79C960 PCnet-ISA
+ */
+
+struct Am79C960 {
+ volatile u_short AddressPROM[8];
+ /* IEEE Address PROM (Unused in the Ariadne) */
+ volatile u_short RDP; /* Register Data Port */
+ volatile u_short RAP; /* Register Address Port */
+ volatile u_short Reset; /* Reset Chip on Read Access */
+ volatile u_short IDP; /* ISACSR Data Port */
+};
+
+
+ /*
+ * Am79C960 Control and Status Registers
+ *
+ * These values are already swap()ed!!
+ *
+ * Only registers marked with a `-' are intended for network software
+ * access
+ */
+
+#define CSR0 0x0000 /* - PCnet-ISA Controller Status */
+#define CSR1 0x0100 /* - IADR[15:0] */
+#define CSR2 0x0200 /* - IADR[23:16] */
+#define CSR3 0x0300 /* - Interrupt Masks and Deferral Control */
+#define CSR4 0x0400 /* - Test and Features Control */
+#define CSR6 0x0600 /* RCV/XMT Descriptor Table Length */
+#define CSR8 0x0800 /* - Logical Address Filter, LADRF[15:0] */
+#define CSR9 0x0900 /* - Logical Address Filter, LADRF[31:16] */
+#define CSR10 0x0a00 /* - Logical Address Filter, LADRF[47:32] */
+#define CSR11 0x0b00 /* - Logical Address Filter, LADRF[63:48] */
+#define CSR12 0x0c00 /* - Physical Address Register, PADR[15:0] */
+#define CSR13 0x0d00 /* - Physical Address Register, PADR[31:16] */
+#define CSR14 0x0e00 /* - Physical Address Register, PADR[47:32] */
+#define CSR15 0x0f00 /* - Mode Register */
+#define CSR16 0x1000 /* Initialization Block Address Lower */
+#define CSR17 0x1100 /* Initialization Block Address Upper */
+#define CSR18 0x1200 /* Current Receive Buffer Address */
+#define CSR19 0x1300 /* Current Receive Buffer Address */
+#define CSR20 0x1400 /* Current Transmit Buffer Address */
+#define CSR21 0x1500 /* Current Transmit Buffer Address */
+#define CSR22 0x1600 /* Next Receive Buffer Address */
+#define CSR23 0x1700 /* Next Receive Buffer Address */
+#define CSR24 0x1800 /* - Base Address of Receive Ring */
+#define CSR25 0x1900 /* - Base Address of Receive Ring */
+#define CSR26 0x1a00 /* Next Receive Descriptor Address */
+#define CSR27 0x1b00 /* Next Receive Descriptor Address */
+#define CSR28 0x1c00 /* Current Receive Descriptor Address */
+#define CSR29 0x1d00 /* Current Receive Descriptor Address */
+#define CSR30 0x1e00 /* - Base Address of Transmit Ring */
+#define CSR31 0x1f00 /* - Base Address of transmit Ring */
+#define CSR32 0x2000 /* Next Transmit Descriptor Address */
+#define CSR33 0x2100 /* Next Transmit Descriptor Address */
+#define CSR34 0x2200 /* Current Transmit Descriptor Address */
+#define CSR35 0x2300 /* Current Transmit Descriptor Address */
+#define CSR36 0x2400 /* Next Next Receive Descriptor Address */
+#define CSR37 0x2500 /* Next Next Receive Descriptor Address */
+#define CSR38 0x2600 /* Next Next Transmit Descriptor Address */
+#define CSR39 0x2700 /* Next Next Transmit Descriptor Address */
+#define CSR40 0x2800 /* Current Receive Status and Byte Count */
+#define CSR41 0x2900 /* Current Receive Status and Byte Count */
+#define CSR42 0x2a00 /* Current Transmit Status and Byte Count */
+#define CSR43 0x2b00 /* Current Transmit Status and Byte Count */
+#define CSR44 0x2c00 /* Next Receive Status and Byte Count */
+#define CSR45 0x2d00 /* Next Receive Status and Byte Count */
+#define CSR46 0x2e00 /* Poll Time Counter */
+#define CSR47 0x2f00 /* Polling Interval */
+#define CSR48 0x3000 /* Temporary Storage */
+#define CSR49 0x3100 /* Temporary Storage */
+#define CSR50 0x3200 /* Temporary Storage */
+#define CSR51 0x3300 /* Temporary Storage */
+#define CSR52 0x3400 /* Temporary Storage */
+#define CSR53 0x3500 /* Temporary Storage */
+#define CSR54 0x3600 /* Temporary Storage */
+#define CSR55 0x3700 /* Temporary Storage */
+#define CSR56 0x3800 /* Temporary Storage */
+#define CSR57 0x3900 /* Temporary Storage */
+#define CSR58 0x3a00 /* Temporary Storage */
+#define CSR59 0x3b00 /* Temporary Storage */
+#define CSR60 0x3c00 /* Previous Transmit Descriptor Address */
+#define CSR61 0x3d00 /* Previous Transmit Descriptor Address */
+#define CSR62 0x3e00 /* Previous Transmit Status and Byte Count */
+#define CSR63 0x3f00 /* Previous Transmit Status and Byte Count */
+#define CSR64 0x4000 /* Next Transmit Buffer Address */
+#define CSR65 0x4100 /* Next Transmit Buffer Address */
+#define CSR66 0x4200 /* Next Transmit Status and Byte Count */
+#define CSR67 0x4300 /* Next Transmit Status and Byte Count */
+#define CSR68 0x4400 /* Transmit Status Temporary Storage */
+#define CSR69 0x4500 /* Transmit Status Temporary Storage */
+#define CSR70 0x4600 /* Temporary Storage */
+#define CSR71 0x4700 /* Temporary Storage */
+#define CSR72 0x4800 /* Receive Ring Counter */
+#define CSR74 0x4a00 /* Transmit Ring Counter */
+#define CSR76 0x4c00 /* - Receive Ring Length */
+#define CSR78 0x4e00 /* - Transmit Ring Length */
+#define CSR80 0x5000 /* - Burst and FIFO Threshold Control */
+#define CSR82 0x5200 /* - Bus Activity Timer */
+#define CSR84 0x5400 /* DMA Address */
+#define CSR85 0x5500 /* DMA Address */
+#define CSR86 0x5600 /* Buffer Byte Counter */
+#define CSR88 0x5800 /* - Chip ID */
+#define CSR89 0x5900 /* - Chip ID */
+#define CSR92 0x5c00 /* Ring Length Conversion */
+#define CSR94 0x5e00 /* Transmit Time Domain Reflectometry Count */
+#define CSR96 0x6000 /* Bus Interface Scratch Register 0 */
+#define CSR97 0x6100 /* Bus Interface Scratch Register 0 */
+#define CSR98 0x6200 /* Bus Interface Scratch Register 1 */
+#define CSR99 0x6300 /* Bus Interface Scratch Register 1 */
+#define CSR104 0x6800 /* SWAP */
+#define CSR105 0x6900 /* SWAP */
+#define CSR108 0x6c00 /* Buffer Management Scratch */
+#define CSR109 0x6d00 /* Buffer Management Scratch */
+#define CSR112 0x7000 /* - Missed Frame Count */
+#define CSR114 0x7200 /* - Receive Collision Count */
+#define CSR124 0x7c00 /* - Buffer Management Unit Test */
+
+
+ /*
+ * Am79C960 ISA Control and Status Registers
+ *
+ * These values are already swap()ed!!
+ */
+
+#define ISACSR0 0x0000 /* Master Mode Read Active */
+#define ISACSR1 0x0100 /* Master Mode Write Active */
+#define ISACSR2 0x0200 /* Miscellaneous Configuration */
+#define ISACSR4 0x0400 /* LED0 Status (Link Integrity) */
+#define ISACSR5 0x0500 /* LED1 Status */
+#define ISACSR6 0x0600 /* LED2 Status */
+#define ISACSR7 0x0700 /* LED3 Status */
+
+
+ /*
+ * Bit definitions for CSR0 (PCnet-ISA Controller Status)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define ERR 0x0080 /* Error */
+#define BABL 0x0040 /* Babble: Transmitted too many bits */
+#define CERR 0x0020 /* No Heartbeat (10BASE-T) */
+#define MISS 0x0010 /* Missed Frame */
+#define MERR 0x0008 /* Memory Error */
+#define RINT 0x0004 /* Receive Interrupt */
+#define TINT 0x0002 /* Transmit Interrupt */
+#define IDON 0x0001 /* Initialization Done */
+#define INTR 0x8000 /* Interrupt Flag */
+#define INEA 0x4000 /* Interrupt Enable */
+#define RXON 0x2000 /* Receive On */
+#define TXON 0x1000 /* Transmit On */
+#define TDMD 0x0800 /* Transmit Demand */
+#define STOP 0x0400 /* Stop */
+#define STRT 0x0200 /* Start */
+#define INIT 0x0100 /* Initialize */
+
+
+ /*
+ * Bit definitions for CSR3 (Interrupt Masks and Deferral Control)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define BABLM 0x0040 /* Babble Mask */
+#define MISSM 0x0010 /* Missed Frame Mask */
+#define MERRM 0x0008 /* Memory Error Mask */
+#define RINTM 0x0004 /* Receive Interrupt Mask */
+#define TINTM 0x0002 /* Transmit Interrupt Mask */
+#define IDONM 0x0001 /* Initialization Done Mask */
+#define DXMT2PD 0x1000 /* Disable Transmit Two Part Deferral */
+#define EMBA 0x0800 /* Enable Modified Back-off Algorithm */
+
+
+ /*
+ * Bit definitions for CSR4 (Test and Features Control)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define ENTST 0x0080 /* Enable Test Mode */
+#define DMAPLUS 0x0040 /* Disable Burst Transaction Counter */
+#define TIMER 0x0020 /* Timer Enable Register */
+#define DPOLL 0x0010 /* Disable Transmit Polling */
+#define APAD_XMT 0x0008 /* Auto Pad Transmit */
+#define ASTRP_RCV 0x0004 /* Auto Pad Stripping */
+#define MFCO 0x0002 /* Missed Frame Counter Overflow Interrupt */
+#define MFCOM 0x0001 /* Missed Frame Counter Overflow Mask */
+#define RCVCCO 0x2000 /* Receive Collision Counter Overflow Interrupt */
+#define RCVCCOM 0x1000 /* Receive Collision Counter Overflow Mask */
+#define TXSTRT 0x0800 /* Transmit Start Status */
+#define TXSTRTM 0x0400 /* Transmit Start Mask */
+#define JAB 0x0200 /* Jabber Error */
+#define JABM 0x0100 /* Jabber Error Mask */
+
+
+ /*
+ * Bit definitions for CSR15 (Mode Register)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define PROM 0x0080 /* Promiscuous Mode */
+#define DRCVBC 0x0040 /* Disable Receive Broadcast */
+#define DRCVPA 0x0020 /* Disable Receive Physical Address */
+#define DLNKTST 0x0010 /* Disable Link Status */
+#define DAPC 0x0008 /* Disable Automatic Polarity Correction */
+#define MENDECL 0x0004 /* MENDEC Loopback Mode */
+#define LRTTSEL 0x0002 /* Low Receive Threshold/Transmit Mode Select */
+#define PORTSEL1 0x0001 /* Port Select Bits */
+#define PORTSEL2 0x8000 /* Port Select Bits */
+#define INTL 0x4000 /* Internal Loopback */
+#define DRTY 0x2000 /* Disable Retry */
+#define FCOLL 0x1000 /* Force Collision */
+#define DXMTFCS 0x0800 /* Disable Transmit CRC */
+#define LOOP 0x0400 /* Loopback Enable */
+#define DTX 0x0200 /* Disable Transmitter */
+#define DRX 0x0100 /* Disable Receiver */
+
+
+ /*
+ * Bit definitions for ISACSR2 (Miscellaneous Configuration)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define ASEL 0x0200 /* Media Interface Port Auto Select */
+
+
+ /*
+ * Bit definitions for ISACSR5-7 (LED1-3 Status)
+ *
+ * These values are already swap()ed!!
+ */
+
+#define LEDOUT 0x0080 /* Current LED Status */
+#define PSE 0x8000 /* Pulse Stretcher Enable */
+#define XMTE 0x1000 /* Enable Transmit Status Signal */
+#define RVPOLE 0x0800 /* Enable Receive Polarity Signal */
+#define RCVE 0x0400 /* Enable Receive Status Signal */
+#define JABE 0x0200 /* Enable Jabber Signal */
+#define COLE 0x0100 /* Enable Collision Signal */
+
+
+ /*
+ * Receive Descriptor Ring Entry
+ */
+
+struct RDRE {
+ volatile u_short RMD0; /* LADR[15:0] */
+ volatile u_short RMD1; /* HADR[23:16] | Receive Flags */
+ volatile u_short RMD2; /* Buffer Byte Count (two's complement) */
+ volatile u_short RMD3; /* Message Byte Count */
+};
+
+
+ /*
+ * Transmit Descriptor Ring Entry
+ */
+
+struct TDRE {
+ volatile u_short TMD0; /* LADR[15:0] */
+ volatile u_short TMD1; /* HADR[23:16] | Transmit Flags */
+ volatile u_short TMD2; /* Buffer Byte Count (two's complement) */
+ volatile u_short TMD3; /* Error Flags */
+};
+
+
+ /*
+ * Receive Flags
+ */
+
+#define RF_OWN 0x0080 /* PCnet-ISA controller owns the descriptor */
+#define RF_ERR 0x0040 /* Error */
+#define RF_FRAM 0x0020 /* Framing Error */
+#define RF_OFLO 0x0010 /* Overflow Error */
+#define RF_CRC 0x0008 /* CRC Error */
+#define RF_BUFF 0x0004 /* Buffer Error */
+#define RF_STP 0x0002 /* Start of Packet */
+#define RF_ENP 0x0001 /* End of Packet */
+
+
+ /*
+ * Transmit Flags
+ */
+
+#define TF_OWN 0x0080 /* PCnet-ISA controller owns the descriptor */
+#define TF_ERR 0x0040 /* Error */
+#define TF_ADD_FCS 0x0020 /* Controls FCS Generation */
+#define TF_MORE 0x0010 /* More than one retry needed */
+#define TF_ONE 0x0008 /* One retry needed */
+#define TF_DEF 0x0004 /* Deferred */
+#define TF_STP 0x0002 /* Start of Packet */
+#define TF_ENP 0x0001 /* End of Packet */
+
+
+ /*
+ * Error Flags
+ */
+
+#define EF_BUFF 0x0080 /* Buffer Error */
+#define EF_UFLO 0x0040 /* Underflow Error */
+#define EF_LCOL 0x0010 /* Late Collision */
+#define EF_LCAR 0x0008 /* Loss of Carrier */
+#define EF_RTRY 0x0004 /* Retry Error */
+#define EF_TDR 0xff03 /* Time Domain Reflectometry */
+
+
+
+ /*
+ * MC68230 Parallel Interface/Timer
+ */
+
+struct MC68230 {
+ volatile u_char PGCR; /* Port General Control Register */
+ u_char Pad1[1];
+ volatile u_char PSRR; /* Port Service Request Register */
+ u_char Pad2[1];
+ volatile u_char PADDR; /* Port A Data Direction Register */
+ u_char Pad3[1];
+ volatile u_char PBDDR; /* Port B Data Direction Register */
+ u_char Pad4[1];
+ volatile u_char PCDDR; /* Port C Data Direction Register */
+ u_char Pad5[1];
+ volatile u_char PIVR; /* Port Interrupt Vector Register */
+ u_char Pad6[1];
+ volatile u_char PACR; /* Port A Control Register */
+ u_char Pad7[1];
+ volatile u_char PBCR; /* Port B Control Register */
+ u_char Pad8[1];
+ volatile u_char PADR; /* Port A Data Register */
+ u_char Pad9[1];
+ volatile u_char PBDR; /* Port B Data Register */
+ u_char Pad10[1];
+ volatile u_char PAAR; /* Port A Alternate Register */
+ u_char Pad11[1];
+ volatile u_char PBAR; /* Port B Alternate Register */
+ u_char Pad12[1];
+ volatile u_char PCDR; /* Port C Data Register */
+ u_char Pad13[1];
+ volatile u_char PSR; /* Port Status Register */
+ u_char Pad14[5];
+ volatile u_char TCR; /* Timer Control Register */
+ u_char Pad15[1];
+ volatile u_char TIVR; /* Timer Interrupt Vector Register */
+ u_char Pad16[3];
+ volatile u_char CPRH; /* Counter Preload Register (High) */
+ u_char Pad17[1];
+ volatile u_char CPRM; /* Counter Preload Register (Mid) */
+ u_char Pad18[1];
+ volatile u_char CPRL; /* Counter Preload Register (Low) */
+ u_char Pad19[3];
+ volatile u_char CNTRH; /* Count Register (High) */
+ u_char Pad20[1];
+ volatile u_char CNTRM; /* Count Register (Mid) */
+ u_char Pad21[1];
+ volatile u_char CNTRL; /* Count Register (Low) */
+ u_char Pad22[1];
+ volatile u_char TSR; /* Timer Status Register */
+ u_char Pad23[11];
+};
+
+
+ /*
+ * Ariadne Expansion Board Structure
+ */
+
+#define ARIADNE_LANCE 0x360
+
+#define ARIADNE_PIT 0x1000
+
+#define ARIADNE_BOOTPROM 0x4000 /* I guess it's here :-) */
+#define ARIADNE_BOOTPROM_SIZE 0x4000
+
+#define ARIADNE_RAM 0x8000 /* Always access WORDs!! */
+#define ARIADNE_RAM_SIZE 0x8000
+
diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c
new file mode 100644
index 00000000000..e07ce5ff2d4
--- /dev/null
+++ b/drivers/net/ethernet/amd/atarilance.c
@@ -0,0 +1,1169 @@
+/* atarilance.c: Ethernet driver for VME Lance cards on the Atari */
+/*
+ Written 1995/96 by Roman Hodek (Roman.Hodek@informatik.uni-erlangen.de)
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ This drivers was written with the following sources of reference:
+ - The driver for the Riebl Lance card by the TU Vienna.
+ - The modified TUW driver for PAM's VME cards
+ - The PC-Linux driver for Lance cards (but this is for bus master
+ cards, not the shared memory ones)
+ - The Amiga Ariadne driver
+
+ v1.0: (in 1.2.13pl4/0.9.13)
+ Initial version
+ v1.1: (in 1.2.13pl5)
+ more comments
+ deleted some debugging stuff
+ optimized register access (keep AREG pointing to CSR0)
+ following AMD, CSR0_STRT should be set only after IDON is detected
+ use memcpy() for data transfers, that also employs long word moves
+ better probe procedure for 24-bit systems
+ non-VME-RieblCards need extra delays in memcpy
+ must also do write test, since 0xfxe00000 may hit ROM
+ use 8/32 tx/rx buffers, which should give better NFS performance;
+ this is made possible by shifting the last packet buffer after the
+ RieblCard reserved area
+ v1.2: (in 1.2.13pl8)
+ again fixed probing for the Falcon; 0xfe01000 hits phys. 0x00010000
+ and thus RAM, in case of no Lance found all memory contents have to
+ be restored!
+ Now possible to compile as module.
+ v1.3: 03/30/96 Jes Sorensen, Roman (in 1.3)
+ Several little 1.3 adaptions
+ When the lance is stopped it jumps back into little-endian
+ mode. It is therefore necessary to put it back where it
+ belongs, in big endian mode, in order to make things work.
+ This might be the reason why multicast-mode didn't work
+ before, but I'm not able to test it as I only got an Amiga
+ (we had similar problems with the A2065 driver).
+
+*/
+
+static char version[] = "atarilance.c: v1.3 04/04/96 "
+ "Roman.Hodek@informatik.uni-erlangen.de\n";
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/io.h>
+
+/* Debug level:
+ * 0 = silent, print only serious errors
+ * 1 = normal, print error messages
+ * 2 = debug, print debug infos
+ * 3 = debug, print even more debug infos (packet data)
+ */
+
+#define LANCE_DEBUG 1
+
+#ifdef LANCE_DEBUG
+static int lance_debug = LANCE_DEBUG;
+#else
+static int lance_debug = 1;
+#endif
+module_param(lance_debug, int, 0);
+MODULE_PARM_DESC(lance_debug, "atarilance debug level (0-3)");
+MODULE_LICENSE("GPL");
+
+/* Print debug messages on probing? */
+#undef LANCE_DEBUG_PROBE
+
+#define DPRINTK(n,a) \
+ do { \
+ if (lance_debug >= n) \
+ printk a; \
+ } while( 0 )
+
+#ifdef LANCE_DEBUG_PROBE
+# define PROBE_PRINT(a) printk a
+#else
+# define PROBE_PRINT(a)
+#endif
+
+/* These define the number of Rx and Tx buffers as log2. (Only powers
+ * of two are valid)
+ * Much more rx buffers (32) are reserved than tx buffers (8), since receiving
+ * is more time critical then sending and packets may have to remain in the
+ * board's memory when main memory is low.
+ */
+
+#define TX_LOG_RING_SIZE 3
+#define RX_LOG_RING_SIZE 5
+
+/* These are the derived values */
+
+#define TX_RING_SIZE (1 << TX_LOG_RING_SIZE)
+#define TX_RING_LEN_BITS (TX_LOG_RING_SIZE << 5)
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+
+#define RX_RING_SIZE (1 << RX_LOG_RING_SIZE)
+#define RX_RING_LEN_BITS (RX_LOG_RING_SIZE << 5)
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+
+#define TX_TIMEOUT (HZ/5)
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ unsigned short base; /* Low word of base addr */
+ volatile unsigned char flag;
+ unsigned char base_hi; /* High word of base addr (unused) */
+ short buf_length; /* This length is 2s complement! */
+ volatile short msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ unsigned short base; /* Low word of base addr */
+ volatile unsigned char flag;
+ unsigned char base_hi; /* High word of base addr (unused) */
+ short length; /* Length is 2s complement! */
+ volatile short misc;
+};
+
+struct ringdesc {
+ unsigned short adr_lo; /* Low 16 bits of address */
+ unsigned char len; /* Length bits */
+ unsigned char adr_hi; /* High 8 bits of address (unused) */
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode */
+ unsigned char hwaddr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with length bits. */
+ struct ringdesc rx_ring;
+ struct ringdesc tx_ring;
+};
+
+/* The whole layout of the Lance shared memory */
+struct lance_memory {
+ struct lance_init_block init;
+ struct lance_tx_head tx_head[TX_RING_SIZE];
+ struct lance_rx_head rx_head[RX_RING_SIZE];
+ char packet_area[0]; /* packet data follow after the
+ * init block and the ring
+ * descriptors and are located
+ * at runtime */
+};
+
+/* RieblCard specifics:
+ * The original TOS driver for these cards reserves the area from offset
+ * 0xee70 to 0xeebb for storing configuration data. Of interest to us is the
+ * Ethernet address there, and the magic for verifying the data's validity.
+ * The reserved area isn't touch by packet buffers. Furthermore, offset 0xfffe
+ * is reserved for the interrupt vector number.
+ */
+#define RIEBL_RSVD_START 0xee70
+#define RIEBL_RSVD_END 0xeec0
+#define RIEBL_MAGIC 0x09051990
+#define RIEBL_MAGIC_ADDR ((unsigned long *)(((char *)MEM) + 0xee8a))
+#define RIEBL_HWADDR_ADDR ((unsigned char *)(((char *)MEM) + 0xee8e))
+#define RIEBL_IVEC_ADDR ((unsigned short *)(((char *)MEM) + 0xfffe))
+
+/* This is a default address for the old RieblCards without a battery
+ * that have no ethernet address at boot time. 00:00:36:04 is the
+ * prefix for Riebl cards, the 00:00 at the end is arbitrary.
+ */
+
+static unsigned char OldRieblDefHwaddr[6] = {
+ 0x00, 0x00, 0x36, 0x04, 0x00, 0x00
+};
+
+
+/* I/O registers of the Lance chip */
+
+struct lance_ioreg {
+/* base+0x0 */ volatile unsigned short data;
+/* base+0x2 */ volatile unsigned short addr;
+ unsigned char _dummy1[3];
+/* base+0x7 */ volatile unsigned char ivec;
+ unsigned char _dummy2[5];
+/* base+0xd */ volatile unsigned char eeprom;
+ unsigned char _dummy3;
+/* base+0xf */ volatile unsigned char mem;
+};
+
+/* Types of boards this driver supports */
+
+enum lance_type {
+ OLD_RIEBL, /* old Riebl card without battery */
+ NEW_RIEBL, /* new Riebl card with battery */
+ PAM_CARD /* PAM card with EEPROM */
+};
+
+static char *lance_names[] = {
+ "Riebl-Card (without battery)",
+ "Riebl-Card (with battery)",
+ "PAM intern card"
+};
+
+/* The driver's private device structure */
+
+struct lance_private {
+ enum lance_type cardtype;
+ struct lance_ioreg *iobase;
+ struct lance_memory *mem;
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_tx; /* Ring entries to be freed. */
+ /* copy function */
+ void *(*memcpy_f)( void *, const void *, size_t );
+/* This must be long for set_bit() */
+ long tx_full;
+ spinlock_t devlock;
+};
+
+/* I/O register access macros */
+
+#define MEM lp->mem
+#define DREG IO->data
+#define AREG IO->addr
+#define REGA(a) (*( AREG = (a), &DREG ))
+
+/* Definitions for packet buffer access: */
+#define PKT_BUF_SZ 1544
+/* Get the address of a packet buffer corresponding to a given buffer head */
+#define PKTBUF_ADDR(head) (((unsigned char *)(MEM)) + (head)->base)
+
+/* Possible memory/IO addresses for probing */
+
+static struct lance_addr {
+ unsigned long memaddr;
+ unsigned long ioaddr;
+ int slow_flag;
+} lance_addr_list[] = {
+ { 0xfe010000, 0xfe00fff0, 0 }, /* RieblCard VME in TT */
+ { 0xffc10000, 0xffc0fff0, 0 }, /* RieblCard VME in MegaSTE
+ (highest byte stripped) */
+ { 0xffe00000, 0xffff7000, 1 }, /* RieblCard in ST
+ (highest byte stripped) */
+ { 0xffd00000, 0xffff7000, 1 }, /* RieblCard in ST with hw modif. to
+ avoid conflict with ROM
+ (highest byte stripped) */
+ { 0xffcf0000, 0xffcffff0, 0 }, /* PAMCard VME in TT and MSTE
+ (highest byte stripped) */
+ { 0xfecf0000, 0xfecffff0, 0 }, /* Rhotron's PAMCard VME in TT and MSTE
+ (highest byte stripped) */
+};
+
+#define N_LANCE_ADDR ARRAY_SIZE(lance_addr_list)
+
+
+/* Definitions for the Lance */
+
+/* tx_head flags */
+#define TMD1_ENP 0x01 /* end of packet */
+#define TMD1_STP 0x02 /* start of packet */
+#define TMD1_DEF 0x04 /* deferred */
+#define TMD1_ONE 0x08 /* one retry needed */
+#define TMD1_MORE 0x10 /* more than one retry needed */
+#define TMD1_ERR 0x40 /* error summary */
+#define TMD1_OWN 0x80 /* ownership (set: chip owns) */
+
+#define TMD1_OWN_CHIP TMD1_OWN
+#define TMD1_OWN_HOST 0
+
+/* tx_head misc field */
+#define TMD3_TDR 0x03FF /* Time Domain Reflectometry counter */
+#define TMD3_RTRY 0x0400 /* failed after 16 retries */
+#define TMD3_LCAR 0x0800 /* carrier lost */
+#define TMD3_LCOL 0x1000 /* late collision */
+#define TMD3_UFLO 0x4000 /* underflow (late memory) */
+#define TMD3_BUFF 0x8000 /* buffering error (no ENP) */
+
+/* rx_head flags */
+#define RMD1_ENP 0x01 /* end of packet */
+#define RMD1_STP 0x02 /* start of packet */
+#define RMD1_BUFF 0x04 /* buffer error */
+#define RMD1_CRC 0x08 /* CRC error */
+#define RMD1_OFLO 0x10 /* overflow */
+#define RMD1_FRAM 0x20 /* framing error */
+#define RMD1_ERR 0x40 /* error summary */
+#define RMD1_OWN 0x80 /* ownership (set: ship owns) */
+
+#define RMD1_OWN_CHIP RMD1_OWN
+#define RMD1_OWN_HOST 0
+
+/* register names */
+#define CSR0 0 /* mode/status */
+#define CSR1 1 /* init block addr (low) */
+#define CSR2 2 /* init block addr (high) */
+#define CSR3 3 /* misc */
+#define CSR8 8 /* address filter */
+#define CSR15 15 /* promiscuous mode */
+
+/* CSR0 */
+/* (R=readable, W=writeable, S=set on write, C=clear on write) */
+#define CSR0_INIT 0x0001 /* initialize (RS) */
+#define CSR0_STRT 0x0002 /* start (RS) */
+#define CSR0_STOP 0x0004 /* stop (RS) */
+#define CSR0_TDMD 0x0008 /* transmit demand (RS) */
+#define CSR0_TXON 0x0010 /* transmitter on (R) */
+#define CSR0_RXON 0x0020 /* receiver on (R) */
+#define CSR0_INEA 0x0040 /* interrupt enable (RW) */
+#define CSR0_INTR 0x0080 /* interrupt active (R) */
+#define CSR0_IDON 0x0100 /* initialization done (RC) */
+#define CSR0_TINT 0x0200 /* transmitter interrupt (RC) */
+#define CSR0_RINT 0x0400 /* receiver interrupt (RC) */
+#define CSR0_MERR 0x0800 /* memory error (RC) */
+#define CSR0_MISS 0x1000 /* missed frame (RC) */
+#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) (RC) */
+#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits (RC) */
+#define CSR0_ERR 0x8000 /* error (RC) */
+
+/* CSR3 */
+#define CSR3_BCON 0x0001 /* byte control */
+#define CSR3_ACON 0x0002 /* ALE control */
+#define CSR3_BSWP 0x0004 /* byte swap (1=big endian) */
+
+
+
+/***************************** Prototypes *****************************/
+
+static unsigned long lance_probe1( struct net_device *dev, struct lance_addr
+ *init_rec );
+static int lance_open( struct net_device *dev );
+static void lance_init_ring( struct net_device *dev );
+static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev );
+static irqreturn_t lance_interrupt( int irq, void *dev_id );
+static int lance_rx( struct net_device *dev );
+static int lance_close( struct net_device *dev );
+static void set_multicast_list( struct net_device *dev );
+static int lance_set_mac_address( struct net_device *dev, void *addr );
+static void lance_tx_timeout (struct net_device *dev);
+
+/************************* End of Prototypes **************************/
+
+
+
+
+
+static void *slow_memcpy( void *dst, const void *src, size_t len )
+
+{ char *cto = dst;
+ const char *cfrom = src;
+
+ while( len-- ) {
+ *cto++ = *cfrom++;
+ MFPDELAY();
+ }
+ return dst;
+}
+
+
+struct net_device * __init atarilance_probe(int unit)
+{
+ int i;
+ static int found;
+ struct net_device *dev;
+ int err = -ENODEV;
+
+ if (!MACH_IS_ATARI || found)
+ /* Assume there's only one board possible... That seems true, since
+ * the Riebl/PAM board's address cannot be changed. */
+ return ERR_PTR(-ENODEV);
+
+ dev = alloc_etherdev(sizeof(struct lance_private));
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ for( i = 0; i < N_LANCE_ADDR; ++i ) {
+ if (lance_probe1( dev, &lance_addr_list[i] )) {
+ found = 1;
+ err = register_netdev(dev);
+ if (!err)
+ return dev;
+ free_irq(dev->irq, dev);
+ break;
+ }
+ }
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+
+/* Derived from hwreg_present() in atari/config.c: */
+
+static noinline int __init addr_accessible(volatile void *regp, int wordflag,
+ int writeflag)
+{
+ int ret;
+ unsigned long flags;
+ long *vbr, save_berr;
+
+ local_irq_save(flags);
+
+ __asm__ __volatile__ ( "movec %/vbr,%0" : "=r" (vbr) : );
+ save_berr = vbr[2];
+
+ __asm__ __volatile__
+ ( "movel %/sp,%/d1\n\t"
+ "movel #Lberr,%2@\n\t"
+ "moveq #0,%0\n\t"
+ "tstl %3\n\t"
+ "bne 1f\n\t"
+ "moveb %1@,%/d0\n\t"
+ "nop \n\t"
+ "bra 2f\n"
+"1: movew %1@,%/d0\n\t"
+ "nop \n"
+"2: tstl %4\n\t"
+ "beq 2f\n\t"
+ "tstl %3\n\t"
+ "bne 1f\n\t"
+ "clrb %1@\n\t"
+ "nop \n\t"
+ "moveb %/d0,%1@\n\t"
+ "nop \n\t"
+ "bra 2f\n"
+"1: clrw %1@\n\t"
+ "nop \n\t"
+ "movew %/d0,%1@\n\t"
+ "nop \n"
+"2: moveq #1,%0\n"
+"Lberr: movel %/d1,%/sp"
+ : "=&d" (ret)
+ : "a" (regp), "a" (&vbr[2]), "rm" (wordflag), "rm" (writeflag)
+ : "d0", "d1", "memory"
+ );
+
+ vbr[2] = save_berr;
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_stop = lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_set_mac_address = lance_set_mac_address,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static unsigned long __init lance_probe1( struct net_device *dev,
+ struct lance_addr *init_rec )
+{
+ volatile unsigned short *memaddr =
+ (volatile unsigned short *)init_rec->memaddr;
+ volatile unsigned short *ioaddr =
+ (volatile unsigned short *)init_rec->ioaddr;
+ struct lance_private *lp;
+ struct lance_ioreg *IO;
+ int i;
+ static int did_version;
+ unsigned short save1, save2;
+
+ PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n",
+ (long)memaddr, (long)ioaddr ));
+
+ /* Test whether memory readable and writable */
+ PROBE_PRINT(( "lance_probe1: testing memory to be accessible\n" ));
+ if (!addr_accessible( memaddr, 1, 1 )) goto probe_fail;
+
+ /* Written values should come back... */
+ PROBE_PRINT(( "lance_probe1: testing memory to be writable (1)\n" ));
+ save1 = *memaddr;
+ *memaddr = 0x0001;
+ if (*memaddr != 0x0001) goto probe_fail;
+ PROBE_PRINT(( "lance_probe1: testing memory to be writable (2)\n" ));
+ *memaddr = 0x0000;
+ if (*memaddr != 0x0000) goto probe_fail;
+ *memaddr = save1;
+
+ /* First port should be readable and writable */
+ PROBE_PRINT(( "lance_probe1: testing ioport to be accessible\n" ));
+ if (!addr_accessible( ioaddr, 1, 1 )) goto probe_fail;
+
+ /* and written values should be readable */
+ PROBE_PRINT(( "lance_probe1: testing ioport to be writeable\n" ));
+ save2 = ioaddr[1];
+ ioaddr[1] = 0x0001;
+ if (ioaddr[1] != 0x0001) goto probe_fail;
+
+ /* The CSR0_INIT bit should not be readable */
+ PROBE_PRINT(( "lance_probe1: testing CSR0 register function (1)\n" ));
+ save1 = ioaddr[0];
+ ioaddr[1] = CSR0;
+ ioaddr[0] = CSR0_INIT | CSR0_STOP;
+ if (ioaddr[0] != CSR0_STOP) {
+ ioaddr[0] = save1;
+ ioaddr[1] = save2;
+ goto probe_fail;
+ }
+ PROBE_PRINT(( "lance_probe1: testing CSR0 register function (2)\n" ));
+ ioaddr[0] = CSR0_STOP;
+ if (ioaddr[0] != CSR0_STOP) {
+ ioaddr[0] = save1;
+ ioaddr[1] = save2;
+ goto probe_fail;
+ }
+
+ /* Now ok... */
+ PROBE_PRINT(( "lance_probe1: Lance card detected\n" ));
+ goto probe_ok;
+
+ probe_fail:
+ return 0;
+
+ probe_ok:
+ lp = netdev_priv(dev);
+ MEM = (struct lance_memory *)memaddr;
+ IO = lp->iobase = (struct lance_ioreg *)ioaddr;
+ dev->base_addr = (unsigned long)ioaddr; /* informational only */
+ lp->memcpy_f = init_rec->slow_flag ? slow_memcpy : memcpy;
+
+ REGA( CSR0 ) = CSR0_STOP;
+
+ /* Now test for type: If the eeprom I/O port is readable, it is a
+ * PAM card */
+ if (addr_accessible( &(IO->eeprom), 0, 0 )) {
+ /* Switch back to Ram */
+ i = IO->mem;
+ lp->cardtype = PAM_CARD;
+ }
+ else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) {
+ lp->cardtype = NEW_RIEBL;
+ }
+ else
+ lp->cardtype = OLD_RIEBL;
+
+ if (lp->cardtype == PAM_CARD ||
+ memaddr == (unsigned short *)0xffe00000) {
+ /* PAMs card and Riebl on ST use level 5 autovector */
+ if (request_irq(IRQ_AUTO_5, lance_interrupt, IRQ_TYPE_PRIO,
+ "PAM,Riebl-ST Ethernet", dev)) {
+ printk( "Lance: request for irq %d failed\n", IRQ_AUTO_5 );
+ return 0;
+ }
+ dev->irq = IRQ_AUTO_5;
+ }
+ else {
+ /* For VME-RieblCards, request a free VME int */
+ unsigned int irq = atari_register_vme_int();
+ if (!irq) {
+ printk( "Lance: request for VME interrupt failed\n" );
+ return 0;
+ }
+ if (request_irq(irq, lance_interrupt, IRQ_TYPE_PRIO,
+ "Riebl-VME Ethernet", dev)) {
+ printk( "Lance: request for irq %u failed\n", irq );
+ return 0;
+ }
+ dev->irq = irq;
+ }
+
+ printk("%s: %s at io %#lx, mem %#lx, irq %d%s, hwaddr ",
+ dev->name, lance_names[lp->cardtype],
+ (unsigned long)ioaddr,
+ (unsigned long)memaddr,
+ dev->irq,
+ init_rec->slow_flag ? " (slow memcpy)" : "" );
+
+ /* Get the ethernet address */
+ switch( lp->cardtype ) {
+ case OLD_RIEBL:
+ /* No ethernet address! (Set some default address) */
+ memcpy(dev->dev_addr, OldRieblDefHwaddr, ETH_ALEN);
+ break;
+ case NEW_RIEBL:
+ lp->memcpy_f(dev->dev_addr, RIEBL_HWADDR_ADDR, ETH_ALEN);
+ break;
+ case PAM_CARD:
+ i = IO->eeprom;
+ for( i = 0; i < 6; ++i )
+ dev->dev_addr[i] =
+ ((((unsigned short *)MEM)[i*2] & 0x0f) << 4) |
+ ((((unsigned short *)MEM)[i*2+1] & 0x0f));
+ i = IO->mem;
+ break;
+ }
+ printk("%pM\n", dev->dev_addr);
+ if (lp->cardtype == OLD_RIEBL) {
+ printk( "%s: Warning: This is a default ethernet address!\n",
+ dev->name );
+ printk( " Use \"ifconfig hw ether ...\" to set the address.\n" );
+ }
+
+ spin_lock_init(&lp->devlock);
+
+ MEM->init.mode = 0x0000; /* Disable Rx and Tx. */
+ for( i = 0; i < 6; i++ )
+ MEM->init.hwaddr[i] = dev->dev_addr[i^1]; /* <- 16 bit swap! */
+ MEM->init.filter[0] = 0x00000000;
+ MEM->init.filter[1] = 0x00000000;
+ MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head );
+ MEM->init.rx_ring.adr_hi = 0;
+ MEM->init.rx_ring.len = RX_RING_LEN_BITS;
+ MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head );
+ MEM->init.tx_ring.adr_hi = 0;
+ MEM->init.tx_ring.len = TX_RING_LEN_BITS;
+
+ if (lp->cardtype == PAM_CARD)
+ IO->ivec = IRQ_SOURCE_TO_VECTOR(dev->irq);
+ else
+ *RIEBL_IVEC_ADDR = IRQ_SOURCE_TO_VECTOR(dev->irq);
+
+ if (did_version++ == 0)
+ DPRINTK( 1, ( version ));
+
+ dev->netdev_ops = &lance_netdev_ops;
+
+ /* XXX MSch */
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ return 1;
+}
+
+
+static int lance_open( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_ioreg *IO = lp->iobase;
+ int i;
+
+ DPRINTK( 2, ( "%s: lance_open()\n", dev->name ));
+
+ lance_init_ring(dev);
+ /* Re-initialize the LANCE, and start it when done. */
+
+ REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0);
+ REGA( CSR2 ) = 0;
+ REGA( CSR1 ) = 0;
+ REGA( CSR0 ) = CSR0_INIT;
+ /* From now on, AREG is kept to point to CSR0 */
+
+ i = 1000000;
+ while (--i > 0)
+ if (DREG & CSR0_IDON)
+ break;
+ if (i <= 0 || (DREG & CSR0_ERR)) {
+ DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n",
+ dev->name, i, DREG ));
+ DREG = CSR0_STOP;
+ return -EIO;
+ }
+ DREG = CSR0_IDON;
+ DREG = CSR0_STRT;
+ DREG = CSR0_INEA;
+
+ netif_start_queue (dev);
+
+ DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG ));
+
+ return 0;
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+
+static void lance_init_ring( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int i;
+ unsigned offset;
+
+ lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_tx = 0;
+
+ offset = offsetof( struct lance_memory, packet_area );
+
+/* If the packet buffer at offset 'o' would conflict with the reserved area
+ * of RieblCards, advance it */
+#define CHECK_OFFSET(o) \
+ do { \
+ if (lp->cardtype == OLD_RIEBL || lp->cardtype == NEW_RIEBL) { \
+ if (((o) < RIEBL_RSVD_START) ? (o)+PKT_BUF_SZ > RIEBL_RSVD_START \
+ : (o) < RIEBL_RSVD_END) \
+ (o) = RIEBL_RSVD_END; \
+ } \
+ } while(0)
+
+ for( i = 0; i < TX_RING_SIZE; i++ ) {
+ CHECK_OFFSET(offset);
+ MEM->tx_head[i].base = offset;
+ MEM->tx_head[i].flag = TMD1_OWN_HOST;
+ MEM->tx_head[i].base_hi = 0;
+ MEM->tx_head[i].length = 0;
+ MEM->tx_head[i].misc = 0;
+ offset += PKT_BUF_SZ;
+ }
+
+ for( i = 0; i < RX_RING_SIZE; i++ ) {
+ CHECK_OFFSET(offset);
+ MEM->rx_head[i].base = offset;
+ MEM->rx_head[i].flag = TMD1_OWN_CHIP;
+ MEM->rx_head[i].base_hi = 0;
+ MEM->rx_head[i].buf_length = -PKT_BUF_SZ;
+ MEM->rx_head[i].msg_length = 0;
+ offset += PKT_BUF_SZ;
+ }
+}
+
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+
+static void lance_tx_timeout (struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_ioreg *IO = lp->iobase;
+
+ AREG = CSR0;
+ DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n",
+ dev->name, DREG ));
+ DREG = CSR0_STOP;
+ /*
+ * Always set BSWP after a STOP as STOP puts it back into
+ * little endian mode.
+ */
+ REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0);
+ dev->stats.tx_errors++;
+#ifndef final_version
+ { int i;
+ DPRINTK( 2, ( "Ring data: dirty_tx %d cur_tx %d%s cur_rx %d\n",
+ lp->dirty_tx, lp->cur_tx,
+ lp->tx_full ? " (full)" : "",
+ lp->cur_rx ));
+ for( i = 0 ; i < RX_RING_SIZE; i++ )
+ DPRINTK( 2, ( "rx #%d: base=%04x blen=%04x mlen=%04x\n",
+ i, MEM->rx_head[i].base,
+ -MEM->rx_head[i].buf_length,
+ MEM->rx_head[i].msg_length ));
+ for( i = 0 ; i < TX_RING_SIZE; i++ )
+ DPRINTK( 2, ( "tx #%d: base=%04x len=%04x misc=%04x\n",
+ i, MEM->tx_head[i].base,
+ -MEM->tx_head[i].length,
+ MEM->tx_head[i].misc ));
+ }
+#endif
+ /* XXX MSch: maybe purge/reinit ring here */
+ /* lance_restart, essentially */
+ lance_init_ring(dev);
+ REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT;
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_ioreg *IO = lp->iobase;
+ int entry, len;
+ struct lance_tx_head *head;
+ unsigned long flags;
+
+ DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
+ dev->name, DREG ));
+
+
+ /* The old LANCE chips doesn't automatically pad buffers to min. size. */
+ len = skb->len;
+ if (len < ETH_ZLEN)
+ len = ETH_ZLEN;
+ /* PAM-Card has a bug: Can only send packets with even number of bytes! */
+ else if (lp->cardtype == PAM_CARD && (len & 1))
+ ++len;
+
+ if (len > skb->len) {
+ if (skb_padto(skb, len))
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue (dev);
+
+ /* Fill in a Tx ring entry */
+ if (lance_debug >= 3) {
+ printk( "%s: TX pkt type 0x%04x from %pM to %pM"
+ " data at 0x%08x len %d\n",
+ dev->name, ((u_short *)skb->data)[6],
+ &skb->data[6], skb->data,
+ (int)skb->data, (int)skb->len );
+ }
+
+ /* We're not prepared for the int until the last flags are set/reset. And
+ * the int may happen already after setting the OWN_CHIP... */
+ spin_lock_irqsave (&lp->devlock, flags);
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & TX_RING_MOD_MASK;
+ head = &(MEM->tx_head[entry]);
+
+ /* Caution: the write order is important here, set the "ownership" bits
+ * last.
+ */
+
+
+ head->length = -len;
+ head->misc = 0;
+ lp->memcpy_f( PKTBUF_ADDR(head), (void *)skb->data, skb->len );
+ head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
+ dev->stats.tx_bytes += skb->len;
+ dev_kfree_skb( skb );
+ lp->cur_tx++;
+ while( lp->cur_tx >= TX_RING_SIZE && lp->dirty_tx >= TX_RING_SIZE ) {
+ lp->cur_tx -= TX_RING_SIZE;
+ lp->dirty_tx -= TX_RING_SIZE;
+ }
+
+ /* Trigger an immediate send poll. */
+ DREG = CSR0_INEA | CSR0_TDMD;
+
+ if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) ==
+ TMD1_OWN_HOST)
+ netif_start_queue (dev);
+ else
+ lp->tx_full = 1;
+ spin_unlock_irqrestore (&lp->devlock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/* The LANCE interrupt handler. */
+
+static irqreturn_t lance_interrupt( int irq, void *dev_id )
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp;
+ struct lance_ioreg *IO;
+ int csr0, boguscnt = 10;
+ int handled = 0;
+
+ if (dev == NULL) {
+ DPRINTK( 1, ( "lance_interrupt(): interrupt for unknown device.\n" ));
+ return IRQ_NONE;
+ }
+
+ lp = netdev_priv(dev);
+ IO = lp->iobase;
+ spin_lock (&lp->devlock);
+
+ AREG = CSR0;
+
+ while( ((csr0 = DREG) & (CSR0_ERR | CSR0_TINT | CSR0_RINT)) &&
+ --boguscnt >= 0) {
+ handled = 1;
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ DREG = csr0 & ~(CSR0_INIT | CSR0_STRT | CSR0_STOP |
+ CSR0_TDMD | CSR0_INEA);
+
+ DPRINTK( 2, ( "%s: interrupt csr0=%04x new csr=%04x.\n",
+ dev->name, csr0, DREG ));
+
+ if (csr0 & CSR0_RINT) /* Rx interrupt */
+ lance_rx( dev );
+
+ if (csr0 & CSR0_TINT) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while( dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx & TX_RING_MOD_MASK;
+ int status = MEM->tx_head[entry].flag;
+
+ if (status & TMD1_OWN_CHIP)
+ break; /* It still hasn't been Txed */
+
+ MEM->tx_head[entry].flag = 0;
+
+ if (status & TMD1_ERR) {
+ /* There was an major error, log it. */
+ int err_status = MEM->tx_head[entry].misc;
+ dev->stats.tx_errors++;
+ if (err_status & TMD3_RTRY) dev->stats.tx_aborted_errors++;
+ if (err_status & TMD3_LCAR) dev->stats.tx_carrier_errors++;
+ if (err_status & TMD3_LCOL) dev->stats.tx_window_errors++;
+ if (err_status & TMD3_UFLO) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ dev->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ DPRINTK( 1, ( "%s: Tx FIFO error! Status %04x\n",
+ dev->name, csr0 ));
+ /* Restart the chip. */
+ DREG = CSR0_STRT;
+ }
+ } else {
+ if (status & (TMD1_MORE | TMD1_ONE | TMD1_DEF))
+ dev->stats.collisions++;
+ dev->stats.tx_packets++;
+ }
+
+ /* XXX MSch: free skb?? */
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ DPRINTK( 0, ( "out-of-sync dirty pointer,"
+ " %d vs. %d, full=%ld.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full ));
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && (netif_queue_stopped(dev)) &&
+ dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ netif_wake_queue (dev);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors. */
+ if (csr0 & CSR0_BABL) dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & CSR0_MISS) dev->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & CSR0_MERR) {
+ DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), "
+ "status %04x.\n", dev->name, csr0 ));
+ /* Restart the chip. */
+ DREG = CSR0_STRT;
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable. */
+ DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR |
+ CSR0_IDON | CSR0_INEA;
+
+ DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n",
+ dev->name, DREG ));
+
+ spin_unlock (&lp->devlock);
+ return IRQ_RETVAL(handled);
+}
+
+
+static int lance_rx( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int i;
+
+ DPRINTK( 2, ( "%s: rx int, flag=%04x\n", dev->name,
+ MEM->rx_head[entry].flag ));
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) {
+ struct lance_rx_head *head = &(MEM->rx_head[entry]);
+ int status = head->flag;
+
+ if (status != (RMD1_ENP|RMD1_STP)) { /* There was an error. */
+ /* There is a tricky error noted by John Murphy,
+ <murf@perftech.com> to Russ Nelson: Even with full-sized
+ buffers it's possible for a jabber packet to use two
+ buffers, with only the last correctly noting the error. */
+ if (status & RMD1_ENP) /* Only count a general error at the */
+ dev->stats.rx_errors++; /* end of a packet.*/
+ if (status & RMD1_FRAM) dev->stats.rx_frame_errors++;
+ if (status & RMD1_OFLO) dev->stats.rx_over_errors++;
+ if (status & RMD1_CRC) dev->stats.rx_crc_errors++;
+ if (status & RMD1_BUFF) dev->stats.rx_fifo_errors++;
+ head->flag &= (RMD1_ENP|RMD1_STP);
+ } else {
+ /* Malloc up new buffer, compatible with net-3. */
+ short pkt_len = head->msg_length & 0xfff;
+ struct sk_buff *skb;
+
+ if (pkt_len < 60) {
+ printk( "%s: Runt packet!\n", dev->name );
+ dev->stats.rx_errors++;
+ }
+ else {
+ skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (skb == NULL) {
+ for( i = 0; i < RX_RING_SIZE; i++ )
+ if (MEM->rx_head[(entry+i) & RX_RING_MOD_MASK].flag &
+ RMD1_OWN_CHIP)
+ break;
+
+ if (i > RX_RING_SIZE - 2) {
+ dev->stats.rx_dropped++;
+ head->flag |= RMD1_OWN_CHIP;
+ lp->cur_rx++;
+ }
+ break;
+ }
+
+ if (lance_debug >= 3) {
+ u_char *data = PKTBUF_ADDR(head);
+
+ printk(KERN_DEBUG "%s: RX pkt type 0x%04x from %pM to %pM "
+ "data %02x %02x %02x %02x %02x %02x %02x %02x "
+ "len %d\n",
+ dev->name, ((u_short *)data)[6],
+ &data[6], data,
+ data[15], data[16], data[17], data[18],
+ data[19], data[20], data[21], data[22],
+ pkt_len);
+ }
+
+ skb_reserve( skb, 2 ); /* 16 byte align */
+ skb_put( skb, pkt_len ); /* Make room */
+ lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len );
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ }
+
+ head->flag |= RMD1_OWN_CHIP;
+ entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ }
+ lp->cur_rx &= RX_RING_MOD_MASK;
+
+ /* From lance.c (Donald Becker): */
+ /* We should check that at least two ring entries are free. If not,
+ we should free one and mark stats->rx_dropped++. */
+
+ return 0;
+}
+
+
+static int lance_close( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_ioreg *IO = lp->iobase;
+
+ netif_stop_queue (dev);
+
+ AREG = CSR0;
+
+ DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, DREG ));
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ DREG = CSR0_STOP;
+
+ return 0;
+}
+
+
+/* Set or clear the multicast filter for this adaptor.
+ 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.
+ */
+
+static void set_multicast_list( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_ioreg *IO = lp->iobase;
+
+ if (netif_running(dev))
+ /* Only possible if board is already started */
+ return;
+
+ /* We take the simple way out and always enable promiscuous mode. */
+ DREG = CSR0_STOP; /* Temporarily stop the lance. */
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Log any net taps. */
+ DPRINTK( 2, ( "%s: Promiscuous mode enabled.\n", dev->name ));
+ REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int num_addrs = netdev_mc_count(dev);
+ int i;
+ /* We don't use the multicast table, but rely on upper-layer
+ * filtering. */
+ memset( multicast_table, (num_addrs == 0) ? 0 : -1,
+ sizeof(multicast_table) );
+ for( i = 0; i < 4; i++ )
+ REGA( CSR8+i ) = multicast_table[i];
+ REGA( CSR15 ) = 0; /* Unset promiscuous mode */
+ }
+
+ /*
+ * Always set BSWP after a STOP as STOP puts it back into
+ * little endian mode.
+ */
+ REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0);
+
+ /* Resume normal operation and reset AREG to CSR0 */
+ REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT;
+}
+
+
+/* This is needed for old RieblCards and possible for new RieblCards */
+
+static int lance_set_mac_address( struct net_device *dev, void *addr )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct sockaddr *saddr = addr;
+ int i;
+
+ if (lp->cardtype != OLD_RIEBL && lp->cardtype != NEW_RIEBL)
+ return -EOPNOTSUPP;
+
+ if (netif_running(dev)) {
+ /* Only possible while card isn't started */
+ DPRINTK( 1, ( "%s: hwaddr can be set only while card isn't open.\n",
+ dev->name ));
+ return -EIO;
+ }
+
+ memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len );
+ for( i = 0; i < 6; i++ )
+ MEM->init.hwaddr[i] = dev->dev_addr[i^1]; /* <- 16 bit swap! */
+ lp->memcpy_f( RIEBL_HWADDR_ADDR, dev->dev_addr, 6 );
+ /* set also the magic for future sessions */
+ *RIEBL_MAGIC_ADDR = RIEBL_MAGIC;
+
+ return 0;
+}
+
+
+#ifdef MODULE
+static struct net_device *atarilance_dev;
+
+static int __init atarilance_module_init(void)
+{
+ atarilance_dev = atarilance_probe(-1);
+ return PTR_ERR_OR_ZERO(atarilance_dev);
+}
+
+static void __exit atarilance_module_exit(void)
+{
+ unregister_netdev(atarilance_dev);
+ free_irq(atarilance_dev->irq, atarilance_dev);
+ free_netdev(atarilance_dev);
+}
+module_init(atarilance_module_init);
+module_exit(atarilance_module_exit);
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
new file mode 100644
index 00000000000..a78e4c13695
--- /dev/null
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -0,0 +1,1347 @@
+/*
+ *
+ * Alchemy Au1x00 ethernet driver
+ *
+ * Copyright 2001-2003, 2006 MontaVista Software Inc.
+ * Copyright 2002 TimeSys Corp.
+ * Added ethtool/mii-tool support,
+ * Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
+ * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de
+ * or riemer@riemer-nt.de: fixed the link beat detection with
+ * ioctls (SIOCGMIIPHY)
+ * Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org>
+ * converted to use linux-2.6.x's PHY framework
+ *
+ * Author: MontaVista Software, Inc.
+ * ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * ########################################################################
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/capability.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/cpu.h>
+#include <linux/io.h>
+
+#include <asm/mipsregs.h>
+#include <asm/irq.h>
+#include <asm/processor.h>
+
+#include <au1000.h>
+#include <au1xxx_eth.h>
+#include <prom.h>
+
+#include "au1000_eth.h"
+
+#ifdef AU1000_ETH_DEBUG
+static int au1000_debug = 5;
+#else
+static int au1000_debug = 3;
+#endif
+
+#define AU1000_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+#define DRV_NAME "au1000_eth"
+#define DRV_VERSION "1.7"
+#define DRV_AUTHOR "Pete Popov <ppopov@embeddedalley.com>"
+#define DRV_DESC "Au1xxx on-chip Ethernet driver"
+
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+/*
+ * Theory of operation
+ *
+ * The Au1000 MACs use a simple rx and tx descriptor ring scheme.
+ * There are four receive and four transmit descriptors. These
+ * descriptors are not in memory; rather, they are just a set of
+ * hardware registers.
+ *
+ * Since the Au1000 has a coherent data cache, the receive and
+ * transmit buffers are allocated from the KSEG0 segment. The
+ * hardware registers, however, are still mapped at KSEG1 to
+ * make sure there's no out-of-order writes, and that all writes
+ * complete immediately.
+ */
+
+/*
+ * board-specific configurations
+ *
+ * PHY detection algorithm
+ *
+ * If phy_static_config is undefined, the PHY setup is
+ * autodetected:
+ *
+ * mii_probe() first searches the current MAC's MII bus for a PHY,
+ * selecting the first (or last, if phy_search_highest_addr is
+ * defined) PHY address not already claimed by another netdev.
+ *
+ * If nothing was found that way when searching for the 2nd ethernet
+ * controller's PHY and phy1_search_mac0 is defined, then
+ * the first MII bus is searched as well for an unclaimed PHY; this is
+ * needed in case of a dual-PHY accessible only through the MAC0's MII
+ * bus.
+ *
+ * Finally, if no PHY is found, then the corresponding ethernet
+ * controller is not registered to the network subsystem.
+ */
+
+/* autodetection defaults: phy1_search_mac0 */
+
+/* static PHY setup
+ *
+ * most boards PHY setup should be detectable properly with the
+ * autodetection algorithm in mii_probe(), but in some cases (e.g. if
+ * you have a switch attached, or want to use the PHY's interrupt
+ * notification capabilities) you can provide a static PHY
+ * configuration here
+ *
+ * IRQs may only be set, if a PHY address was configured
+ * If a PHY address is given, also a bus id is required to be set
+ *
+ * ps: make sure the used irqs are configured properly in the board
+ * specific irq-map
+ */
+
+static void au1000_enable_mac(struct net_device *dev, int force_reset)
+{
+ unsigned long flags;
+ struct au1000_private *aup = netdev_priv(dev);
+
+ spin_lock_irqsave(&aup->lock, flags);
+
+ if (force_reset || (!aup->mac_enabled)) {
+ writel(MAC_EN_CLOCK_ENABLE, aup->enable);
+ au_sync_delay(2);
+ writel((MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
+ | MAC_EN_CLOCK_ENABLE), aup->enable);
+ au_sync_delay(2);
+
+ aup->mac_enabled = 1;
+ }
+
+ spin_unlock_irqrestore(&aup->lock, flags);
+}
+
+/*
+ * MII operations
+ */
+static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ u32 *const mii_control_reg = &aup->mac->mii_control;
+ u32 *const mii_data_reg = &aup->mac->mii_data;
+ u32 timedout = 20;
+ u32 mii_control;
+
+ while (readl(mii_control_reg) & MAC_MII_BUSY) {
+ mdelay(1);
+ if (--timedout == 0) {
+ netdev_err(dev, "read_MII busy timeout!!\n");
+ return -1;
+ }
+ }
+
+ mii_control = MAC_SET_MII_SELECT_REG(reg) |
+ MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;
+
+ writel(mii_control, mii_control_reg);
+
+ timedout = 20;
+ while (readl(mii_control_reg) & MAC_MII_BUSY) {
+ mdelay(1);
+ if (--timedout == 0) {
+ netdev_err(dev, "mdio_read busy timeout!!\n");
+ return -1;
+ }
+ }
+ return readl(mii_data_reg);
+}
+
+static void au1000_mdio_write(struct net_device *dev, int phy_addr,
+ int reg, u16 value)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ u32 *const mii_control_reg = &aup->mac->mii_control;
+ u32 *const mii_data_reg = &aup->mac->mii_data;
+ u32 timedout = 20;
+ u32 mii_control;
+
+ while (readl(mii_control_reg) & MAC_MII_BUSY) {
+ mdelay(1);
+ if (--timedout == 0) {
+ netdev_err(dev, "mdio_write busy timeout!!\n");
+ return;
+ }
+ }
+
+ mii_control = MAC_SET_MII_SELECT_REG(reg) |
+ MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_WRITE;
+
+ writel(value, mii_data_reg);
+ writel(mii_control, mii_control_reg);
+}
+
+static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ /* WARNING: bus->phy_map[phy_addr].attached_dev == dev does
+ * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus)
+ */
+ struct net_device *const dev = bus->priv;
+
+ /* make sure the MAC associated with this
+ * mii_bus is enabled
+ */
+ au1000_enable_mac(dev, 0);
+
+ return au1000_mdio_read(dev, phy_addr, regnum);
+}
+
+static int au1000_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 value)
+{
+ struct net_device *const dev = bus->priv;
+
+ /* make sure the MAC associated with this
+ * mii_bus is enabled
+ */
+ au1000_enable_mac(dev, 0);
+
+ au1000_mdio_write(dev, phy_addr, regnum, value);
+ return 0;
+}
+
+static int au1000_mdiobus_reset(struct mii_bus *bus)
+{
+ struct net_device *const dev = bus->priv;
+
+ /* make sure the MAC associated with this
+ * mii_bus is enabled
+ */
+ au1000_enable_mac(dev, 0);
+
+ return 0;
+}
+
+static void au1000_hard_stop(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ u32 reg;
+
+ netif_dbg(aup, drv, dev, "hard stop\n");
+
+ reg = readl(&aup->mac->control);
+ reg &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);
+ writel(reg, &aup->mac->control);
+ au_sync_delay(10);
+}
+
+static void au1000_enable_rx_tx(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ u32 reg;
+
+ netif_dbg(aup, hw, dev, "enable_rx_tx\n");
+
+ reg = readl(&aup->mac->control);
+ reg |= (MAC_RX_ENABLE | MAC_TX_ENABLE);
+ writel(reg, &aup->mac->control);
+ au_sync_delay(10);
+}
+
+static void
+au1000_adjust_link(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ struct phy_device *phydev = aup->phy_dev;
+ unsigned long flags;
+ u32 reg;
+
+ int status_change = 0;
+
+ BUG_ON(!aup->phy_dev);
+
+ spin_lock_irqsave(&aup->lock, flags);
+
+ if (phydev->link && (aup->old_speed != phydev->speed)) {
+ /* speed changed */
+
+ switch (phydev->speed) {
+ case SPEED_10:
+ case SPEED_100:
+ break;
+ default:
+ netdev_warn(dev, "Speed (%d) is not 10/100 ???\n",
+ phydev->speed);
+ break;
+ }
+
+ aup->old_speed = phydev->speed;
+
+ status_change = 1;
+ }
+
+ if (phydev->link && (aup->old_duplex != phydev->duplex)) {
+ /* duplex mode changed */
+
+ /* switching duplex mode requires to disable rx and tx! */
+ au1000_hard_stop(dev);
+
+ reg = readl(&aup->mac->control);
+ if (DUPLEX_FULL == phydev->duplex) {
+ reg |= MAC_FULL_DUPLEX;
+ reg &= ~MAC_DISABLE_RX_OWN;
+ } else {
+ reg &= ~MAC_FULL_DUPLEX;
+ reg |= MAC_DISABLE_RX_OWN;
+ }
+ writel(reg, &aup->mac->control);
+ au_sync_delay(1);
+
+ au1000_enable_rx_tx(dev);
+ aup->old_duplex = phydev->duplex;
+
+ status_change = 1;
+ }
+
+ if (phydev->link != aup->old_link) {
+ /* link state changed */
+
+ if (!phydev->link) {
+ /* link went down */
+ aup->old_speed = 0;
+ aup->old_duplex = -1;
+ }
+
+ aup->old_link = phydev->link;
+ status_change = 1;
+ }
+
+ spin_unlock_irqrestore(&aup->lock, flags);
+
+ if (status_change) {
+ if (phydev->link)
+ netdev_info(dev, "link up (%d/%s)\n",
+ phydev->speed,
+ DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
+ else
+ netdev_info(dev, "link down\n");
+ }
+}
+
+static int au1000_mii_probe(struct net_device *dev)
+{
+ struct au1000_private *const aup = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int phy_addr;
+
+ if (aup->phy_static_config) {
+ BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
+
+ if (aup->phy_addr)
+ phydev = aup->mii_bus->phy_map[aup->phy_addr];
+ else
+ netdev_info(dev, "using PHY-less setup\n");
+ return 0;
+ }
+
+ /* find the first (lowest address) PHY
+ * on the current MAC's MII bus
+ */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
+ if (aup->mii_bus->phy_map[phy_addr]) {
+ phydev = aup->mii_bus->phy_map[phy_addr];
+ if (!aup->phy_search_highest_addr)
+ /* break out with first one found */
+ break;
+ }
+
+ if (aup->phy1_search_mac0) {
+ /* try harder to find a PHY */
+ if (!phydev && (aup->mac_id == 1)) {
+ /* no PHY found, maybe we have a dual PHY? */
+ dev_info(&dev->dev, ": no PHY found on MAC1, "
+ "let's see if it's attached to MAC0...\n");
+
+ /* find the first (lowest address) non-attached
+ * PHY on the MAC0 MII bus
+ */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ struct phy_device *const tmp_phydev =
+ aup->mii_bus->phy_map[phy_addr];
+
+ if (aup->mac_id == 1)
+ break;
+
+ /* no PHY here... */
+ if (!tmp_phydev)
+ continue;
+
+ /* already claimed by MAC0 */
+ if (tmp_phydev->attached_dev)
+ continue;
+
+ phydev = tmp_phydev;
+ break; /* found it */
+ }
+ }
+ }
+
+ if (!phydev) {
+ netdev_err(dev, "no PHY found\n");
+ return -1;
+ }
+
+ /* now we are supposed to have a proper phydev, to attach to... */
+ BUG_ON(phydev->attached_dev);
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev),
+ &au1000_adjust_link, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ /* mask with MAC supported features */
+ phydev->supported &= (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg
+ /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */
+ | SUPPORTED_MII
+ | SUPPORTED_TP);
+
+ phydev->advertising = phydev->supported;
+
+ aup->old_link = 0;
+ aup->old_speed = 0;
+ aup->old_duplex = -1;
+ aup->phy_dev = phydev;
+
+ netdev_info(dev, "attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s, irq=%d)\n",
+ phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+ return 0;
+}
+
+
+/*
+ * Buffer allocation/deallocation routines. The buffer descriptor returned
+ * has the virtual and dma address of a buffer suitable for
+ * both, receive and transmit operations.
+ */
+static struct db_dest *au1000_GetFreeDB(struct au1000_private *aup)
+{
+ struct db_dest *pDB;
+ pDB = aup->pDBfree;
+
+ if (pDB)
+ aup->pDBfree = pDB->pnext;
+
+ return pDB;
+}
+
+void au1000_ReleaseDB(struct au1000_private *aup, struct db_dest *pDB)
+{
+ struct db_dest *pDBfree = aup->pDBfree;
+ if (pDBfree)
+ pDBfree->pnext = pDB;
+ aup->pDBfree = pDB;
+}
+
+static void au1000_reset_mac_unlocked(struct net_device *dev)
+{
+ struct au1000_private *const aup = netdev_priv(dev);
+ int i;
+
+ au1000_hard_stop(dev);
+
+ writel(MAC_EN_CLOCK_ENABLE, aup->enable);
+ au_sync_delay(2);
+ writel(0, aup->enable);
+ au_sync_delay(2);
+
+ aup->tx_full = 0;
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ /* reset control bits */
+ aup->rx_dma_ring[i]->buff_stat &= ~0xf;
+ }
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ /* reset control bits */
+ aup->tx_dma_ring[i]->buff_stat &= ~0xf;
+ }
+
+ aup->mac_enabled = 0;
+
+}
+
+static void au1000_reset_mac(struct net_device *dev)
+{
+ struct au1000_private *const aup = netdev_priv(dev);
+ unsigned long flags;
+
+ netif_dbg(aup, hw, dev, "reset mac, aup %x\n",
+ (unsigned)aup);
+
+ spin_lock_irqsave(&aup->lock, flags);
+
+ au1000_reset_mac_unlocked(dev);
+
+ spin_unlock_irqrestore(&aup->lock, flags);
+}
+
+/*
+ * Setup the receive and transmit "rings". These pointers are the addresses
+ * of the rx and tx MAC DMA registers so they are fixed by the hardware --
+ * these are not descriptors sitting in memory.
+ */
+static void
+au1000_setup_hw_rings(struct au1000_private *aup, void __iomem *tx_base)
+{
+ int i;
+
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ aup->rx_dma_ring[i] = (struct rx_dma *)
+ (tx_base + 0x100 + sizeof(struct rx_dma) * i);
+ }
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ aup->tx_dma_ring[i] = (struct tx_dma *)
+ (tx_base + sizeof(struct tx_dma) * i);
+ }
+}
+
+/*
+ * ethtool operations
+ */
+
+static int au1000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+
+ if (aup->phy_dev)
+ return phy_ethtool_gset(aup->phy_dev, cmd);
+
+ return -EINVAL;
+}
+
+static int au1000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (aup->phy_dev)
+ return phy_ethtool_sset(aup->phy_dev, cmd);
+
+ return -EINVAL;
+}
+
+static void
+au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME,
+ aup->mac_id);
+ info->regdump_len = 0;
+}
+
+static void au1000_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ aup->msg_enable = value;
+}
+
+static u32 au1000_get_msglevel(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ return aup->msg_enable;
+}
+
+static const struct ethtool_ops au1000_ethtool_ops = {
+ .get_settings = au1000_get_settings,
+ .set_settings = au1000_set_settings,
+ .get_drvinfo = au1000_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = au1000_get_msglevel,
+ .set_msglevel = au1000_set_msglevel,
+};
+
+
+/*
+ * Initialize the interface.
+ *
+ * When the device powers up, the clocks are disabled and the
+ * mac is in reset state. When the interface is closed, we
+ * do the same -- reset the device and disable the clocks to
+ * conserve power. Thus, whenever au1000_init() is called,
+ * the device should already be in reset state.
+ */
+static int au1000_init(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ unsigned long flags;
+ int i;
+ u32 control;
+
+ netif_dbg(aup, hw, dev, "au1000_init\n");
+
+ /* bring the device out of reset */
+ au1000_enable_mac(dev, 1);
+
+ spin_lock_irqsave(&aup->lock, flags);
+
+ writel(0, &aup->mac->control);
+ aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2;
+ aup->tx_tail = aup->tx_head;
+ aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2;
+
+ writel(dev->dev_addr[5]<<8 | dev->dev_addr[4],
+ &aup->mac->mac_addr_high);
+ writel(dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
+ dev->dev_addr[1]<<8 | dev->dev_addr[0],
+ &aup->mac->mac_addr_low);
+
+
+ for (i = 0; i < NUM_RX_DMA; i++)
+ aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
+
+ au_sync();
+
+ control = MAC_RX_ENABLE | MAC_TX_ENABLE;
+#ifndef CONFIG_CPU_LITTLE_ENDIAN
+ control |= MAC_BIG_ENDIAN;
+#endif
+ if (aup->phy_dev) {
+ if (aup->phy_dev->link && (DUPLEX_FULL == aup->phy_dev->duplex))
+ control |= MAC_FULL_DUPLEX;
+ else
+ control |= MAC_DISABLE_RX_OWN;
+ } else { /* PHY-less op, assume full-duplex */
+ control |= MAC_FULL_DUPLEX;
+ }
+
+ writel(control, &aup->mac->control);
+ writel(0x8100, &aup->mac->vlan1_tag); /* activate vlan support */
+ au_sync();
+
+ spin_unlock_irqrestore(&aup->lock, flags);
+ return 0;
+}
+
+static inline void au1000_update_rx_stats(struct net_device *dev, u32 status)
+{
+ struct net_device_stats *ps = &dev->stats;
+
+ ps->rx_packets++;
+ if (status & RX_MCAST_FRAME)
+ ps->multicast++;
+
+ if (status & RX_ERROR) {
+ ps->rx_errors++;
+ if (status & RX_MISSED_FRAME)
+ ps->rx_missed_errors++;
+ if (status & (RX_OVERLEN | RX_RUNT | RX_LEN_ERROR))
+ ps->rx_length_errors++;
+ if (status & RX_CRC_ERROR)
+ ps->rx_crc_errors++;
+ if (status & RX_COLL)
+ ps->collisions++;
+ } else
+ ps->rx_bytes += status & RX_FRAME_LEN_MASK;
+
+}
+
+/*
+ * Au1000 receive routine.
+ */
+static int au1000_rx(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct rx_dma *prxd;
+ u32 buff_stat, status;
+ struct db_dest *pDB;
+ u32 frmlen;
+
+ netif_dbg(aup, rx_status, dev, "au1000_rx head %d\n", aup->rx_head);
+
+ prxd = aup->rx_dma_ring[aup->rx_head];
+ buff_stat = prxd->buff_stat;
+ while (buff_stat & RX_T_DONE) {
+ status = prxd->status;
+ pDB = aup->rx_db_inuse[aup->rx_head];
+ au1000_update_rx_stats(dev, status);
+ if (!(status & RX_ERROR)) {
+
+ /* good frame */
+ frmlen = (status & RX_FRAME_LEN_MASK);
+ frmlen -= 4; /* Remove FCS */
+ skb = netdev_alloc_skb(dev, frmlen + 2);
+ if (skb == NULL) {
+ dev->stats.rx_dropped++;
+ continue;
+ }
+ skb_reserve(skb, 2); /* 16 byte IP header align */
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)pDB->vaddr, frmlen);
+ skb_put(skb, frmlen);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb); /* pass the packet to upper layers */
+ } else {
+ if (au1000_debug > 4) {
+ pr_err("rx_error(s):");
+ if (status & RX_MISSED_FRAME)
+ pr_cont(" miss");
+ if (status & RX_WDOG_TIMER)
+ pr_cont(" wdog");
+ if (status & RX_RUNT)
+ pr_cont(" runt");
+ if (status & RX_OVERLEN)
+ pr_cont(" overlen");
+ if (status & RX_COLL)
+ pr_cont(" coll");
+ if (status & RX_MII_ERROR)
+ pr_cont(" mii error");
+ if (status & RX_CRC_ERROR)
+ pr_cont(" crc error");
+ if (status & RX_LEN_ERROR)
+ pr_cont(" len error");
+ if (status & RX_U_CNTRL_FRAME)
+ pr_cont(" u control frame");
+ pr_cont("\n");
+ }
+ }
+ prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE);
+ aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1);
+ au_sync();
+
+ /* next descriptor */
+ prxd = aup->rx_dma_ring[aup->rx_head];
+ buff_stat = prxd->buff_stat;
+ }
+ return 0;
+}
+
+static void au1000_update_tx_stats(struct net_device *dev, u32 status)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ struct net_device_stats *ps = &dev->stats;
+
+ if (status & TX_FRAME_ABORTED) {
+ if (!aup->phy_dev || (DUPLEX_FULL == aup->phy_dev->duplex)) {
+ if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) {
+ /* any other tx errors are only valid
+ * in half duplex mode
+ */
+ ps->tx_errors++;
+ ps->tx_aborted_errors++;
+ }
+ } else {
+ ps->tx_errors++;
+ ps->tx_aborted_errors++;
+ if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER))
+ ps->tx_carrier_errors++;
+ }
+ }
+}
+
+/*
+ * Called from the interrupt service routine to acknowledge
+ * the TX DONE bits. This is a must if the irq is setup as
+ * edge triggered.
+ */
+static void au1000_tx_ack(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ struct tx_dma *ptxd;
+
+ ptxd = aup->tx_dma_ring[aup->tx_tail];
+
+ while (ptxd->buff_stat & TX_T_DONE) {
+ au1000_update_tx_stats(dev, ptxd->status);
+ ptxd->buff_stat &= ~TX_T_DONE;
+ ptxd->len = 0;
+ au_sync();
+
+ aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1);
+ ptxd = aup->tx_dma_ring[aup->tx_tail];
+
+ if (aup->tx_full) {
+ aup->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+/*
+ * Au1000 interrupt service routine.
+ */
+static irqreturn_t au1000_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+
+ /* Handle RX interrupts first to minimize chance of overrun */
+
+ au1000_rx(dev);
+ au1000_tx_ack(dev);
+ return IRQ_RETVAL(1);
+}
+
+static int au1000_open(struct net_device *dev)
+{
+ int retval;
+ struct au1000_private *aup = netdev_priv(dev);
+
+ netif_dbg(aup, drv, dev, "open: dev=%p\n", dev);
+
+ retval = request_irq(dev->irq, au1000_interrupt, 0,
+ dev->name, dev);
+ if (retval) {
+ netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
+ return retval;
+ }
+
+ retval = au1000_init(dev);
+ if (retval) {
+ netdev_err(dev, "error in au1000_init\n");
+ free_irq(dev->irq, dev);
+ return retval;
+ }
+
+ if (aup->phy_dev) {
+ /* cause the PHY state machine to schedule a link state check */
+ aup->phy_dev->state = PHY_CHANGELINK;
+ phy_start(aup->phy_dev);
+ }
+
+ netif_start_queue(dev);
+
+ netif_dbg(aup, drv, dev, "open: Initialization done.\n");
+
+ return 0;
+}
+
+static int au1000_close(struct net_device *dev)
+{
+ unsigned long flags;
+ struct au1000_private *const aup = netdev_priv(dev);
+
+ netif_dbg(aup, drv, dev, "close: dev=%p\n", dev);
+
+ if (aup->phy_dev)
+ phy_stop(aup->phy_dev);
+
+ spin_lock_irqsave(&aup->lock, flags);
+
+ au1000_reset_mac_unlocked(dev);
+
+ /* stop the device */
+ netif_stop_queue(dev);
+
+ /* disable the interrupt */
+ free_irq(dev->irq, dev);
+ spin_unlock_irqrestore(&aup->lock, flags);
+
+ return 0;
+}
+
+/*
+ * Au1000 transmit routine.
+ */
+static netdev_tx_t au1000_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ struct net_device_stats *ps = &dev->stats;
+ struct tx_dma *ptxd;
+ u32 buff_stat;
+ struct db_dest *pDB;
+ int i;
+
+ netif_dbg(aup, tx_queued, dev, "tx: aup %x len=%d, data=%p, head %d\n",
+ (unsigned)aup, skb->len,
+ skb->data, aup->tx_head);
+
+ ptxd = aup->tx_dma_ring[aup->tx_head];
+ buff_stat = ptxd->buff_stat;
+ if (buff_stat & TX_DMA_ENABLE) {
+ /* We've wrapped around and the transmitter is still busy */
+ netif_stop_queue(dev);
+ aup->tx_full = 1;
+ return NETDEV_TX_BUSY;
+ } else if (buff_stat & TX_T_DONE) {
+ au1000_update_tx_stats(dev, ptxd->status);
+ ptxd->len = 0;
+ }
+
+ if (aup->tx_full) {
+ aup->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ pDB = aup->tx_db_inuse[aup->tx_head];
+ skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
+ if (skb->len < ETH_ZLEN) {
+ for (i = skb->len; i < ETH_ZLEN; i++)
+ ((char *)pDB->vaddr)[i] = 0;
+
+ ptxd->len = ETH_ZLEN;
+ } else
+ ptxd->len = skb->len;
+
+ ps->tx_packets++;
+ ps->tx_bytes += ptxd->len;
+
+ ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE;
+ au_sync();
+ dev_kfree_skb(skb);
+ aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1);
+ return NETDEV_TX_OK;
+}
+
+/*
+ * The Tx ring has been full longer than the watchdog timeout
+ * value. The transmitter must be hung?
+ */
+static void au1000_tx_timeout(struct net_device *dev)
+{
+ netdev_err(dev, "au1000_tx_timeout: dev=%p\n", dev);
+ au1000_reset_mac(dev);
+ au1000_init(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+static void au1000_multicast_list(struct net_device *dev)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+ u32 reg;
+
+ netif_dbg(aup, drv, dev, "%s: flags=%x\n", __func__, dev->flags);
+ reg = readl(&aup->mac->control);
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ reg |= MAC_PROMISCUOUS;
+ } else if ((dev->flags & IFF_ALLMULTI) ||
+ netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) {
+ reg |= MAC_PASS_ALL_MULTI;
+ reg &= ~MAC_PROMISCUOUS;
+ netdev_info(dev, "Pass all multicast\n");
+ } else {
+ struct netdev_hw_addr *ha;
+ u32 mc_filter[2]; /* Multicast hash filter */
+
+ mc_filter[1] = mc_filter[0] = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ set_bit(ether_crc(ETH_ALEN, ha->addr)>>26,
+ (long *)mc_filter);
+ writel(mc_filter[1], &aup->mac->multi_hash_high);
+ writel(mc_filter[0], &aup->mac->multi_hash_low);
+ reg &= ~MAC_PROMISCUOUS;
+ reg |= MAC_HASH_MODE;
+ }
+ writel(reg, &aup->mac->control);
+}
+
+static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct au1000_private *aup = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!aup->phy_dev)
+ return -EINVAL; /* PHY not controllable */
+
+ return phy_mii_ioctl(aup->phy_dev, rq, cmd);
+}
+
+static const struct net_device_ops au1000_netdev_ops = {
+ .ndo_open = au1000_open,
+ .ndo_stop = au1000_close,
+ .ndo_start_xmit = au1000_tx,
+ .ndo_set_rx_mode = au1000_multicast_list,
+ .ndo_do_ioctl = au1000_ioctl,
+ .ndo_tx_timeout = au1000_tx_timeout,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int au1000_probe(struct platform_device *pdev)
+{
+ static unsigned version_printed;
+ struct au1000_private *aup = NULL;
+ struct au1000_eth_platform_data *pd;
+ struct net_device *dev = NULL;
+ struct db_dest *pDB, *pDBfree;
+ int irq, i, err = 0;
+ struct resource *base, *macen, *macdma;
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!base) {
+ dev_err(&pdev->dev, "failed to retrieve base register\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!macen) {
+ dev_err(&pdev->dev, "failed to retrieve MAC Enable register\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to retrieve IRQ\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!macdma) {
+ dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (!request_mem_region(base->start, resource_size(base),
+ pdev->name)) {
+ dev_err(&pdev->dev, "failed to request memory region for base registers\n");
+ err = -ENXIO;
+ goto out;
+ }
+
+ if (!request_mem_region(macen->start, resource_size(macen),
+ pdev->name)) {
+ dev_err(&pdev->dev, "failed to request memory region for MAC enable register\n");
+ err = -ENXIO;
+ goto err_request;
+ }
+
+ if (!request_mem_region(macdma->start, resource_size(macdma),
+ pdev->name)) {
+ dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
+ err = -ENXIO;
+ goto err_macdma;
+ }
+
+ dev = alloc_etherdev(sizeof(struct au1000_private));
+ if (!dev) {
+ err = -ENOMEM;
+ goto err_alloc;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+ aup = netdev_priv(dev);
+
+ spin_lock_init(&aup->lock);
+ aup->msg_enable = (au1000_debug < 4 ?
+ AU1000_DEF_MSG_ENABLE : au1000_debug);
+
+ /* Allocate the data buffers
+ * Snooping works fine with eth on all au1xxx
+ */
+ aup->vaddr = (u32)dma_alloc_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ &aup->dma_addr, 0);
+ if (!aup->vaddr) {
+ dev_err(&pdev->dev, "failed to allocate data buffers\n");
+ err = -ENOMEM;
+ goto err_vaddr;
+ }
+
+ /* aup->mac is the base address of the MAC's registers */
+ aup->mac = (struct mac_reg *)
+ ioremap_nocache(base->start, resource_size(base));
+ if (!aup->mac) {
+ dev_err(&pdev->dev, "failed to ioremap MAC registers\n");
+ err = -ENXIO;
+ goto err_remap1;
+ }
+
+ /* Setup some variables for quick register address access */
+ aup->enable = (u32 *)ioremap_nocache(macen->start,
+ resource_size(macen));
+ if (!aup->enable) {
+ dev_err(&pdev->dev, "failed to ioremap MAC enable register\n");
+ err = -ENXIO;
+ goto err_remap2;
+ }
+ aup->mac_id = pdev->id;
+
+ aup->macdma = ioremap_nocache(macdma->start, resource_size(macdma));
+ if (!aup->macdma) {
+ dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
+ err = -ENXIO;
+ goto err_remap3;
+ }
+
+ au1000_setup_hw_rings(aup, aup->macdma);
+
+ writel(0, aup->enable);
+ aup->mac_enabled = 0;
+
+ pd = dev_get_platdata(&pdev->dev);
+ if (!pd) {
+ dev_info(&pdev->dev, "no platform_data passed,"
+ " PHY search on MAC0\n");
+ aup->phy1_search_mac0 = 1;
+ } else {
+ if (is_valid_ether_addr(pd->mac)) {
+ memcpy(dev->dev_addr, pd->mac, ETH_ALEN);
+ } else {
+ /* Set a random MAC since no valid provided by platform_data. */
+ eth_hw_addr_random(dev);
+ }
+
+ aup->phy_static_config = pd->phy_static_config;
+ aup->phy_search_highest_addr = pd->phy_search_highest_addr;
+ aup->phy1_search_mac0 = pd->phy1_search_mac0;
+ aup->phy_addr = pd->phy_addr;
+ aup->phy_busid = pd->phy_busid;
+ aup->phy_irq = pd->phy_irq;
+ }
+
+ if (aup->phy_busid && aup->phy_busid > 0) {
+ dev_err(&pdev->dev, "MAC0-associated PHY attached 2nd MACs MII bus not supported yet\n");
+ err = -ENODEV;
+ goto err_mdiobus_alloc;
+ }
+
+ aup->mii_bus = mdiobus_alloc();
+ if (aup->mii_bus == NULL) {
+ dev_err(&pdev->dev, "failed to allocate mdiobus structure\n");
+ err = -ENOMEM;
+ goto err_mdiobus_alloc;
+ }
+
+ aup->mii_bus->priv = dev;
+ aup->mii_bus->read = au1000_mdiobus_read;
+ aup->mii_bus->write = au1000_mdiobus_write;
+ aup->mii_bus->reset = au1000_mdiobus_reset;
+ aup->mii_bus->name = "au1000_eth_mii";
+ snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ pdev->name, aup->mac_id);
+ aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+ if (aup->mii_bus->irq == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ aup->mii_bus->irq[i] = PHY_POLL;
+ /* if known, set corresponding PHY IRQs */
+ if (aup->phy_static_config)
+ if (aup->phy_irq && aup->phy_busid == aup->mac_id)
+ aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;
+
+ err = mdiobus_register(aup->mii_bus);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register MDIO bus\n");
+ goto err_mdiobus_reg;
+ }
+
+ err = au1000_mii_probe(dev);
+ if (err != 0)
+ goto err_out;
+
+ pDBfree = NULL;
+ /* setup the data buffer descriptors and attach a buffer to each one */
+ pDB = aup->db;
+ for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
+ pDB->pnext = pDBfree;
+ pDBfree = pDB;
+ pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
+ pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
+ pDB++;
+ }
+ aup->pDBfree = pDBfree;
+
+ err = -ENODEV;
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ pDB = au1000_GetFreeDB(aup);
+ if (!pDB)
+ goto err_out;
+
+ aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
+ aup->rx_db_inuse[i] = pDB;
+ }
+
+ err = -ENODEV;
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ pDB = au1000_GetFreeDB(aup);
+ if (!pDB)
+ goto err_out;
+
+ aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
+ aup->tx_dma_ring[i]->len = 0;
+ aup->tx_db_inuse[i] = pDB;
+ }
+
+ dev->base_addr = base->start;
+ dev->irq = irq;
+ dev->netdev_ops = &au1000_netdev_ops;
+ dev->ethtool_ops = &au1000_ethtool_ops;
+ dev->watchdog_timeo = ETH_TX_TIMEOUT;
+
+ /*
+ * The boot code uses the ethernet controller, so reset it to start
+ * fresh. au1000_init() expects that the device is in reset state.
+ */
+ au1000_reset_mac(dev);
+
+ err = register_netdev(dev);
+ if (err) {
+ netdev_err(dev, "Cannot register net device, aborting.\n");
+ goto err_out;
+ }
+
+ netdev_info(dev, "Au1xx0 Ethernet found at 0x%lx, irq %d\n",
+ (unsigned long)base->start, irq);
+ if (version_printed++ == 0)
+ pr_info("%s version %s %s\n",
+ DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+
+ return 0;
+
+err_out:
+ if (aup->mii_bus != NULL)
+ mdiobus_unregister(aup->mii_bus);
+
+ /* here we should have a valid dev plus aup-> register addresses
+ * so we can reset the mac properly.
+ */
+ au1000_reset_mac(dev);
+
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ if (aup->rx_db_inuse[i])
+ au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
+ }
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ if (aup->tx_db_inuse[i])
+ au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
+ }
+err_mdiobus_reg:
+ mdiobus_free(aup->mii_bus);
+err_mdiobus_alloc:
+ iounmap(aup->macdma);
+err_remap3:
+ iounmap(aup->enable);
+err_remap2:
+ iounmap(aup->mac);
+err_remap1:
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
+err_vaddr:
+ free_netdev(dev);
+err_alloc:
+ release_mem_region(macdma->start, resource_size(macdma));
+err_macdma:
+ release_mem_region(macen->start, resource_size(macen));
+err_request:
+ release_mem_region(base->start, resource_size(base));
+out:
+ return err;
+}
+
+static int au1000_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct au1000_private *aup = netdev_priv(dev);
+ int i;
+ struct resource *base, *macen;
+
+ unregister_netdev(dev);
+ mdiobus_unregister(aup->mii_bus);
+ mdiobus_free(aup->mii_bus);
+
+ for (i = 0; i < NUM_RX_DMA; i++)
+ if (aup->rx_db_inuse[i])
+ au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
+
+ for (i = 0; i < NUM_TX_DMA; i++)
+ if (aup->tx_db_inuse[i])
+ au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
+
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
+
+ iounmap(aup->macdma);
+ iounmap(aup->mac);
+ iounmap(aup->enable);
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ release_mem_region(base->start, resource_size(base));
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(base->start, resource_size(base));
+
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(macen->start, resource_size(macen));
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static struct platform_driver au1000_eth_driver = {
+ .probe = au1000_probe,
+ .remove = au1000_remove,
+ .driver = {
+ .name = "au1000-eth",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(au1000_eth_driver);
+
+MODULE_ALIAS("platform:au1000-eth");
diff --git a/drivers/net/ethernet/amd/au1000_eth.h b/drivers/net/ethernet/amd/au1000_eth.h
new file mode 100644
index 00000000000..ca53024f017
--- /dev/null
+++ b/drivers/net/ethernet/amd/au1000_eth.h
@@ -0,0 +1,133 @@
+/*
+ *
+ * Alchemy Au1x00 ethernet driver include file
+ *
+ * Author: Pete Popov <ppopov@mvista.com>
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * ########################################################################
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * ########################################################################
+ *
+ *
+ */
+
+
+#define MAC_IOSIZE 0x10000
+#define NUM_RX_DMA 4 /* Au1x00 has 4 rx hardware descriptors */
+#define NUM_TX_DMA 4 /* Au1x00 has 4 tx hardware descriptors */
+
+#define NUM_RX_BUFFS 4
+#define NUM_TX_BUFFS 4
+#define MAX_BUF_SIZE 2048
+
+#define ETH_TX_TIMEOUT (HZ/4)
+#define MAC_MIN_PKT_SIZE 64
+
+#define MULTICAST_FILTER_LIMIT 64
+
+/*
+ * Data Buffer Descriptor. Data buffers must be aligned on 32 byte
+ * boundary for both, receive and transmit.
+ */
+struct db_dest {
+ struct db_dest *pnext;
+ u32 *vaddr;
+ dma_addr_t dma_addr;
+};
+
+/*
+ * The transmit and receive descriptors are memory
+ * mapped registers.
+ */
+struct tx_dma {
+ u32 status;
+ u32 buff_stat;
+ u32 len;
+ u32 pad;
+};
+
+struct rx_dma {
+ u32 status;
+ u32 buff_stat;
+ u32 pad[2];
+};
+
+
+/*
+ * MAC control registers, memory mapped.
+ */
+struct mac_reg {
+ u32 control;
+ u32 mac_addr_high;
+ u32 mac_addr_low;
+ u32 multi_hash_high;
+ u32 multi_hash_low;
+ u32 mii_control;
+ u32 mii_data;
+ u32 flow_control;
+ u32 vlan1_tag;
+ u32 vlan2_tag;
+};
+
+
+struct au1000_private {
+ struct db_dest *pDBfree;
+ struct db_dest db[NUM_RX_BUFFS+NUM_TX_BUFFS];
+ struct rx_dma *rx_dma_ring[NUM_RX_DMA];
+ struct tx_dma *tx_dma_ring[NUM_TX_DMA];
+ struct db_dest *rx_db_inuse[NUM_RX_DMA];
+ struct db_dest *tx_db_inuse[NUM_TX_DMA];
+ u32 rx_head;
+ u32 tx_head;
+ u32 tx_tail;
+ u32 tx_full;
+
+ int mac_id;
+
+ int mac_enabled; /* whether MAC is currently enabled and running
+ * (req. for mdio)
+ */
+
+ int old_link; /* used by au1000_adjust_link */
+ int old_speed;
+ int old_duplex;
+
+ struct phy_device *phy_dev;
+ struct mii_bus *mii_bus;
+
+ /* PHY configuration */
+ int phy_static_config;
+ int phy_search_highest_addr;
+ int phy1_search_mac0;
+
+ int phy_addr;
+ int phy_busid;
+ int phy_irq;
+
+ /* These variables are just for quick access
+ * to certain regs addresses.
+ */
+ struct mac_reg *mac; /* mac registers */
+ u32 *enable; /* address of MAC Enable Register */
+ void __iomem *macdma; /* base of MAC DMA port */
+ u32 vaddr; /* virtual address of rx/tx buffers */
+ dma_addr_t dma_addr; /* dma address of rx/tx buffers */
+
+ spinlock_t lock; /* Serialise access to device */
+
+ u32 msg_enable;
+};
diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c
new file mode 100644
index 00000000000..57397295887
--- /dev/null
+++ b/drivers/net/ethernet/amd/declance.c
@@ -0,0 +1,1376 @@
+/*
+ * Lance ethernet driver for the MIPS processor based
+ * DECstation family
+ *
+ *
+ * adopted from sunlance.c by Richard van den Berg
+ *
+ * Copyright (C) 2002, 2003, 2005, 2006 Maciej W. Rozycki
+ *
+ * additional sources:
+ * - PMAD-AA TURBOchannel Ethernet Module Functional Specification,
+ * Revision 1.2
+ *
+ * History:
+ *
+ * v0.001: The kernel accepts the code and it shows the hardware address.
+ *
+ * v0.002: Removed most sparc stuff, left only some module and dma stuff.
+ *
+ * v0.003: Enhanced base address calculation from proposals by
+ * Harald Koerfgen and Thomas Riemer.
+ *
+ * v0.004: lance-regs is pointing at the right addresses, added prom
+ * check. First start of address mapping and DMA.
+ *
+ * v0.005: started to play around with LANCE-DMA. This driver will not
+ * work for non IOASIC lances. HK
+ *
+ * v0.006: added pointer arrays to lance_private and setup routine for
+ * them in dec_lance_init. HK
+ *
+ * v0.007: Big shit. The LANCE seems to use a different DMA mechanism to
+ * access the init block. This looks like one (short) word at a
+ * time, but the smallest amount the IOASIC can transfer is a
+ * (long) word. So we have a 2-2 padding here. Changed
+ * lance_init_block accordingly. The 16-16 padding for the buffers
+ * seems to be correct. HK
+ *
+ * v0.008: mods to make PMAX_LANCE work. 01/09/1999 triemer
+ *
+ * v0.009: Module support fixes, multiple interfaces support, various
+ * bits. macro
+ *
+ * v0.010: Fixes for the PMAD mapping of the LANCE buffer and for the
+ * PMAX requirement to only use halfword accesses to the
+ * buffer. macro
+ *
+ * v0.011: Converted the PMAD to the driver model. macro
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/tc.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/system.h>
+
+static char version[] =
+"declance.c: v0.011 by Linux MIPS DECstation task force\n";
+
+MODULE_AUTHOR("Linux MIPS DECstation task force");
+MODULE_DESCRIPTION("DEC LANCE (DECstation onboard, PMAD-xx) driver");
+MODULE_LICENSE("GPL");
+
+#define __unused __attribute__ ((unused))
+
+/*
+ * card types
+ */
+#define ASIC_LANCE 1
+#define PMAD_LANCE 2
+#define PMAX_LANCE 3
+
+
+#define LE_CSR0 0
+#define LE_CSR1 1
+#define LE_CSR2 2
+#define LE_CSR3 3
+
+#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */
+
+#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */
+#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */
+#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */
+#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */
+#define LE_C0_MERR 0x0800 /* ME: Memory error */
+#define LE_C0_RINT 0x0400 /* Received interrupt */
+#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */
+#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */
+#define LE_C0_INTR 0x0080 /* Interrupt or error */
+#define LE_C0_INEA 0x0040 /* Interrupt enable */
+#define LE_C0_RXON 0x0020 /* Receiver on */
+#define LE_C0_TXON 0x0010 /* Transmitter on */
+#define LE_C0_TDMD 0x0008 /* Transmitter demand */
+#define LE_C0_STOP 0x0004 /* Stop the card */
+#define LE_C0_STRT 0x0002 /* Start the card */
+#define LE_C0_INIT 0x0001 /* Init the card */
+
+#define LE_C3_BSWP 0x4 /* SWAP */
+#define LE_C3_ACON 0x2 /* ALE Control */
+#define LE_C3_BCON 0x1 /* Byte control */
+
+/* Receive message descriptor 1 */
+#define LE_R1_OWN 0x8000 /* Who owns the entry */
+#define LE_R1_ERR 0x4000 /* Error: if FRA, OFL, CRC or BUF is set */
+#define LE_R1_FRA 0x2000 /* FRA: Frame error */
+#define LE_R1_OFL 0x1000 /* OFL: Frame overflow */
+#define LE_R1_CRC 0x0800 /* CRC error */
+#define LE_R1_BUF 0x0400 /* BUF: Buffer error */
+#define LE_R1_SOP 0x0200 /* Start of packet */
+#define LE_R1_EOP 0x0100 /* End of packet */
+#define LE_R1_POK 0x0300 /* Packet is complete: SOP + EOP */
+
+/* Transmit message descriptor 1 */
+#define LE_T1_OWN 0x8000 /* Lance owns the packet */
+#define LE_T1_ERR 0x4000 /* Error summary */
+#define LE_T1_EMORE 0x1000 /* Error: more than one retry needed */
+#define LE_T1_EONE 0x0800 /* Error: one retry needed */
+#define LE_T1_EDEF 0x0400 /* Error: deferred */
+#define LE_T1_SOP 0x0200 /* Start of packet */
+#define LE_T1_EOP 0x0100 /* End of packet */
+#define LE_T1_POK 0x0300 /* Packet is complete: SOP + EOP */
+
+#define LE_T3_BUF 0x8000 /* Buffer error */
+#define LE_T3_UFL 0x4000 /* Error underflow */
+#define LE_T3_LCOL 0x1000 /* Error late collision */
+#define LE_T3_CLOS 0x0800 /* Error carrier loss */
+#define LE_T3_RTY 0x0400 /* Error retry */
+#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */
+
+/* Define: 2^4 Tx buffers and 2^4 Rx buffers */
+
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+
+#define PKT_BUF_SZ 1536
+#define RX_BUFF_SIZE PKT_BUF_SZ
+#define TX_BUFF_SIZE PKT_BUF_SZ
+
+#undef TEST_HITS
+#define ZERO 0
+
+/*
+ * The DS2100/3100 have a linear 64 kB buffer which supports halfword
+ * accesses only. Each halfword of the buffer is word-aligned in the
+ * CPU address space.
+ *
+ * The PMAD-AA has a 128 kB buffer on-board.
+ *
+ * The IOASIC LANCE devices use a shared memory region. This region
+ * as seen from the CPU is (max) 128 kB long and has to be on an 128 kB
+ * boundary. The LANCE sees this as a 64 kB long continuous memory
+ * region.
+ *
+ * The LANCE's DMA address is used as an index in this buffer and DMA
+ * takes place in bursts of eight 16-bit words which are packed into
+ * four 32-bit words by the IOASIC. This leads to a strange padding:
+ * 16 bytes of valid data followed by a 16 byte gap :-(.
+ */
+
+struct lance_rx_desc {
+ unsigned short rmd0; /* low address of packet */
+ unsigned short rmd1; /* high address of packet
+ and descriptor bits */
+ short length; /* 2s complement (negative!)
+ of buffer length */
+ unsigned short mblength; /* actual number of bytes received */
+};
+
+struct lance_tx_desc {
+ unsigned short tmd0; /* low address of packet */
+ unsigned short tmd1; /* high address of packet
+ and descriptor bits */
+ short length; /* 2s complement (negative!)
+ of buffer length */
+ unsigned short misc;
+};
+
+
+/* First part of the LANCE initialization block, described in databook. */
+struct lance_init_block {
+ unsigned short mode; /* pre-set mode (reg. 15) */
+
+ unsigned short phys_addr[3]; /* physical ethernet address */
+ unsigned short filter[4]; /* multicast filter */
+
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned short rx_ptr; /* receive descriptor addr */
+ unsigned short rx_len; /* receive len and high addr */
+ unsigned short tx_ptr; /* transmit descriptor addr */
+ unsigned short tx_len; /* transmit len and high addr */
+
+ short gap[4];
+
+ /* The buffer descriptors */
+ struct lance_rx_desc brx_ring[RX_RING_SIZE];
+ struct lance_tx_desc btx_ring[TX_RING_SIZE];
+};
+
+#define BUF_OFFSET_CPU sizeof(struct lance_init_block)
+#define BUF_OFFSET_LNC sizeof(struct lance_init_block)
+
+#define shift_off(off, type) \
+ (type == ASIC_LANCE || type == PMAX_LANCE ? off << 1 : off)
+
+#define lib_off(rt, type) \
+ shift_off(offsetof(struct lance_init_block, rt), type)
+
+#define lib_ptr(ib, rt, type) \
+ ((volatile u16 *)((u8 *)(ib) + lib_off(rt, type)))
+
+#define rds_off(rt, type) \
+ shift_off(offsetof(struct lance_rx_desc, rt), type)
+
+#define rds_ptr(rd, rt, type) \
+ ((volatile u16 *)((u8 *)(rd) + rds_off(rt, type)))
+
+#define tds_off(rt, type) \
+ shift_off(offsetof(struct lance_tx_desc, rt), type)
+
+#define tds_ptr(td, rt, type) \
+ ((volatile u16 *)((u8 *)(td) + tds_off(rt, type)))
+
+struct lance_private {
+ struct net_device *next;
+ int type;
+ int dma_irq;
+ volatile struct lance_regs *ll;
+
+ spinlock_t lock;
+
+ int rx_new, tx_new;
+ int rx_old, tx_old;
+
+ unsigned short busmaster_regval;
+
+ struct timer_list multicast_timer;
+
+ /* Pointers to the ring buffers as seen from the CPU */
+ char *rx_buf_ptr_cpu[RX_RING_SIZE];
+ char *tx_buf_ptr_cpu[TX_RING_SIZE];
+
+ /* Pointers to the ring buffers as seen from the LANCE */
+ uint rx_buf_ptr_lnc[RX_RING_SIZE];
+ uint tx_buf_ptr_lnc[TX_RING_SIZE];
+};
+
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
+ lp->tx_old - lp->tx_new-1)
+
+/* The lance control ports are at an absolute address, machine and tc-slot
+ * dependent.
+ * DECstations do only 32-bit access and the LANCE uses 16 bit addresses,
+ * so we have to give the structure an extra member making rap pointing
+ * at the right address
+ */
+struct lance_regs {
+ volatile unsigned short rdp; /* register data port */
+ unsigned short pad;
+ volatile unsigned short rap; /* register address port */
+};
+
+int dec_lance_debug = 2;
+
+static struct tc_driver dec_lance_tc_driver;
+static struct net_device *root_lance_dev;
+
+static inline void writereg(volatile unsigned short *regptr, short value)
+{
+ *regptr = value;
+ iob();
+}
+
+/* Load the CSR registers */
+static void load_csrs(struct lance_private *lp)
+{
+ volatile struct lance_regs *ll = lp->ll;
+ uint leptr;
+
+ /* The address space as seen from the LANCE
+ * begins at address 0. HK
+ */
+ leptr = 0;
+
+ writereg(&ll->rap, LE_CSR1);
+ writereg(&ll->rdp, (leptr & 0xFFFF));
+ writereg(&ll->rap, LE_CSR2);
+ writereg(&ll->rdp, leptr >> 16);
+ writereg(&ll->rap, LE_CSR3);
+ writereg(&ll->rdp, lp->busmaster_regval);
+
+ /* Point back to csr0 */
+ writereg(&ll->rap, LE_CSR0);
+}
+
+/*
+ * Our specialized copy routines
+ *
+ */
+static void cp_to_buf(const int type, void *to, const void *from, int len)
+{
+ unsigned short *tp;
+ const unsigned short *fp;
+ unsigned short clen;
+ unsigned char *rtp;
+ const unsigned char *rfp;
+
+ if (type == PMAD_LANCE) {
+ memcpy(to, from, len);
+ } else if (type == PMAX_LANCE) {
+ clen = len >> 1;
+ tp = to;
+ fp = from;
+
+ while (clen--) {
+ *tp++ = *fp++;
+ tp++;
+ }
+
+ clen = len & 1;
+ rtp = (unsigned char *)tp;
+ rfp = (const unsigned char *)fp;
+ while (clen--) {
+ *rtp++ = *rfp++;
+ }
+ } else {
+ /*
+ * copy 16 Byte chunks
+ */
+ clen = len >> 4;
+ tp = to;
+ fp = from;
+ while (clen--) {
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ tp += 8;
+ }
+
+ /*
+ * do the rest, if any.
+ */
+ clen = len & 15;
+ rtp = (unsigned char *)tp;
+ rfp = (const unsigned char *)fp;
+ while (clen--) {
+ *rtp++ = *rfp++;
+ }
+ }
+
+ iob();
+}
+
+static void cp_from_buf(const int type, void *to, const void *from, int len)
+{
+ unsigned short *tp;
+ const unsigned short *fp;
+ unsigned short clen;
+ unsigned char *rtp;
+ const unsigned char *rfp;
+
+ if (type == PMAD_LANCE) {
+ memcpy(to, from, len);
+ } else if (type == PMAX_LANCE) {
+ clen = len >> 1;
+ tp = to;
+ fp = from;
+ while (clen--) {
+ *tp++ = *fp++;
+ fp++;
+ }
+
+ clen = len & 1;
+
+ rtp = (unsigned char *)tp;
+ rfp = (const unsigned char *)fp;
+
+ while (clen--) {
+ *rtp++ = *rfp++;
+ }
+ } else {
+
+ /*
+ * copy 16 Byte chunks
+ */
+ clen = len >> 4;
+ tp = to;
+ fp = from;
+ while (clen--) {
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ *tp++ = *fp++;
+ fp += 8;
+ }
+
+ /*
+ * do the rest, if any.
+ */
+ clen = len & 15;
+ rtp = (unsigned char *)tp;
+ rfp = (const unsigned char *)fp;
+ while (clen--) {
+ *rtp++ = *rfp++;
+ }
+
+
+ }
+
+}
+
+/* Setup the Lance Rx and Tx rings */
+static void lance_init_ring(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ uint leptr;
+ int i;
+
+ /* Lock out other processes while setting up hardware */
+ netif_stop_queue(dev);
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ /* Copy the ethernet address to the lance init block.
+ * XXX bit 0 of the physical address registers has to be zero
+ */
+ *lib_ptr(ib, phys_addr[0], lp->type) = (dev->dev_addr[1] << 8) |
+ dev->dev_addr[0];
+ *lib_ptr(ib, phys_addr[1], lp->type) = (dev->dev_addr[3] << 8) |
+ dev->dev_addr[2];
+ *lib_ptr(ib, phys_addr[2], lp->type) = (dev->dev_addr[5] << 8) |
+ dev->dev_addr[4];
+ /* Setup the initialization block */
+
+ /* Setup rx descriptor pointer */
+ leptr = offsetof(struct lance_init_block, brx_ring);
+ *lib_ptr(ib, rx_len, lp->type) = (LANCE_LOG_RX_BUFFERS << 13) |
+ (leptr >> 16);
+ *lib_ptr(ib, rx_ptr, lp->type) = leptr;
+ if (ZERO)
+ printk("RX ptr: %8.8x(%8.8x)\n",
+ leptr, lib_off(brx_ring, lp->type));
+
+ /* Setup tx descriptor pointer */
+ leptr = offsetof(struct lance_init_block, btx_ring);
+ *lib_ptr(ib, tx_len, lp->type) = (LANCE_LOG_TX_BUFFERS << 13) |
+ (leptr >> 16);
+ *lib_ptr(ib, tx_ptr, lp->type) = leptr;
+ if (ZERO)
+ printk("TX ptr: %8.8x(%8.8x)\n",
+ leptr, lib_off(btx_ring, lp->type));
+
+ if (ZERO)
+ printk("TX rings:\n");
+
+ /* Setup the Tx ring entries */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ leptr = lp->tx_buf_ptr_lnc[i];
+ *lib_ptr(ib, btx_ring[i].tmd0, lp->type) = leptr;
+ *lib_ptr(ib, btx_ring[i].tmd1, lp->type) = (leptr >> 16) &
+ 0xff;
+ *lib_ptr(ib, btx_ring[i].length, lp->type) = 0xf000;
+ /* The ones required by tmd2 */
+ *lib_ptr(ib, btx_ring[i].misc, lp->type) = 0;
+ if (i < 3 && ZERO)
+ printk("%d: 0x%8.8x(0x%8.8x)\n",
+ i, leptr, (uint)lp->tx_buf_ptr_cpu[i]);
+ }
+
+ /* Setup the Rx ring entries */
+ if (ZERO)
+ printk("RX rings:\n");
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ leptr = lp->rx_buf_ptr_lnc[i];
+ *lib_ptr(ib, brx_ring[i].rmd0, lp->type) = leptr;
+ *lib_ptr(ib, brx_ring[i].rmd1, lp->type) = ((leptr >> 16) &
+ 0xff) |
+ LE_R1_OWN;
+ *lib_ptr(ib, brx_ring[i].length, lp->type) = -RX_BUFF_SIZE |
+ 0xf000;
+ *lib_ptr(ib, brx_ring[i].mblength, lp->type) = 0;
+ if (i < 3 && ZERO)
+ printk("%d: 0x%8.8x(0x%8.8x)\n",
+ i, leptr, (uint)lp->rx_buf_ptr_cpu[i]);
+ }
+ iob();
+}
+
+static int init_restart_lance(struct lance_private *lp)
+{
+ volatile struct lance_regs *ll = lp->ll;
+ int i;
+
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_INIT);
+
+ /* Wait for the lance to complete initialization */
+ for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) {
+ udelay(10);
+ }
+ if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
+ printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
+ i, ll->rdp);
+ return -1;
+ }
+ if ((ll->rdp & LE_C0_ERR)) {
+ printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
+ i, ll->rdp);
+ return -1;
+ }
+ writereg(&ll->rdp, LE_C0_IDON);
+ writereg(&ll->rdp, LE_C0_STRT);
+ writereg(&ll->rdp, LE_C0_INEA);
+
+ return 0;
+}
+
+static int lance_rx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ volatile u16 *rd;
+ unsigned short bits;
+ int entry, len;
+ struct sk_buff *skb;
+
+#ifdef TEST_HITS
+ {
+ int i;
+
+ printk("[");
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (i == lp->rx_new)
+ printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
+ lp->type) &
+ LE_R1_OWN ? "_" : "X");
+ else
+ printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
+ lp->type) &
+ LE_R1_OWN ? "." : "1");
+ }
+ printk("]");
+ }
+#endif
+
+ for (rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type);
+ !((bits = *rds_ptr(rd, rmd1, lp->type)) & LE_R1_OWN);
+ rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type)) {
+ entry = lp->rx_new;
+
+ /* We got an incomplete frame? */
+ if ((bits & LE_R1_POK) != LE_R1_POK) {
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ } else if (bits & LE_R1_ERR) {
+ /* Count only the end frame as a rx error,
+ * not the beginning
+ */
+ if (bits & LE_R1_BUF)
+ dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC)
+ dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL)
+ dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA)
+ dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP)
+ dev->stats.rx_errors++;
+ } else {
+ len = (*rds_ptr(rd, mblength, lp->type) & 0xfff) - 4;
+ skb = netdev_alloc_skb(dev, len + 2);
+
+ if (skb == 0) {
+ dev->stats.rx_dropped++;
+ *rds_ptr(rd, mblength, lp->type) = 0;
+ *rds_ptr(rd, rmd1, lp->type) =
+ ((lp->rx_buf_ptr_lnc[entry] >> 16) &
+ 0xff) | LE_R1_OWN;
+ lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
+ return 0;
+ }
+ dev->stats.rx_bytes += len;
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
+
+ cp_from_buf(lp->type, skb->data,
+ lp->rx_buf_ptr_cpu[entry], len);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+
+ /* Return the packet to the pool */
+ *rds_ptr(rd, mblength, lp->type) = 0;
+ *rds_ptr(rd, length, lp->type) = -RX_BUFF_SIZE | 0xf000;
+ *rds_ptr(rd, rmd1, lp->type) =
+ ((lp->rx_buf_ptr_lnc[entry] >> 16) & 0xff) | LE_R1_OWN;
+ lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
+ }
+ return 0;
+}
+
+static void lance_tx(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ volatile struct lance_regs *ll = lp->ll;
+ volatile u16 *td;
+ int i, j;
+ int status;
+
+ j = lp->tx_old;
+
+ spin_lock(&lp->lock);
+
+ for (i = j; i != lp->tx_new; i = j) {
+ td = lib_ptr(ib, btx_ring[i], lp->type);
+ /* If we hit a packet not owned by us, stop */
+ if (*tds_ptr(td, tmd1, lp->type) & LE_T1_OWN)
+ break;
+
+ if (*tds_ptr(td, tmd1, lp->type) & LE_T1_ERR) {
+ status = *tds_ptr(td, misc, lp->type);
+
+ dev->stats.tx_errors++;
+ if (status & LE_T3_RTY)
+ dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL)
+ dev->stats.tx_window_errors++;
+
+ if (status & LE_T3_CLOS) {
+ dev->stats.tx_carrier_errors++;
+ printk("%s: Carrier Lost\n", dev->name);
+ /* Stop the lance */
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ /* Buffer errors and underflows turn off the
+ * transmitter, restart the adapter.
+ */
+ if (status & (LE_T3_BUF | LE_T3_UFL)) {
+ dev->stats.tx_fifo_errors++;
+
+ printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+ dev->name);
+ /* Stop the lance */
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ } else if ((*tds_ptr(td, tmd1, lp->type) & LE_T1_POK) ==
+ LE_T1_POK) {
+ /*
+ * So we don't count the packet more than once.
+ */
+ *tds_ptr(td, tmd1, lp->type) &= ~(LE_T1_POK);
+
+ /* One collision before packet was sent. */
+ if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EONE)
+ dev->stats.collisions++;
+
+ /* More than one collision, be optimistic. */
+ if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EMORE)
+ dev->stats.collisions += 2;
+
+ dev->stats.tx_packets++;
+ }
+ j = (j + 1) & TX_RING_MOD_MASK;
+ }
+ lp->tx_old = j;
+out:
+ if (netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL > 0)
+ netif_wake_queue(dev);
+
+ spin_unlock(&lp->lock);
+}
+
+static irqreturn_t lance_dma_merr_int(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+
+ printk(KERN_ERR "%s: DMA error\n", dev->name);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int csr0;
+
+ writereg(&ll->rap, LE_CSR0);
+ csr0 = ll->rdp;
+
+ /* Acknowledge all the interrupt sources ASAP */
+ writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT));
+
+ if ((csr0 & LE_C0_ERR)) {
+ /* Clear the error condition */
+ writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
+ LE_C0_CERR | LE_C0_MERR);
+ }
+ if (csr0 & LE_C0_RINT)
+ lance_rx(dev);
+
+ if (csr0 & LE_C0_TINT)
+ lance_tx(dev);
+
+ if (csr0 & LE_C0_BABL)
+ dev->stats.tx_errors++;
+
+ if (csr0 & LE_C0_MISS)
+ dev->stats.rx_errors++;
+
+ if (csr0 & LE_C0_MERR) {
+ printk("%s: Memory error, status %04x\n", dev->name, csr0);
+
+ writereg(&ll->rdp, LE_C0_STOP);
+
+ lance_init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ netif_wake_queue(dev);
+ }
+
+ writereg(&ll->rdp, LE_C0_INEA);
+ writereg(&ll->rdp, LE_C0_INEA);
+ return IRQ_HANDLED;
+}
+
+static int lance_open(struct net_device *dev)
+{
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int status = 0;
+
+ /* Stop the Lance */
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+
+ /* Set mode and clear multicast filter only at device open,
+ * so that lance_init_ring() called at any error will not
+ * forget multicast filters.
+ *
+ * BTW it is common bug in all lance drivers! --ANK
+ */
+ *lib_ptr(ib, mode, lp->type) = 0;
+ *lib_ptr(ib, filter[0], lp->type) = 0;
+ *lib_ptr(ib, filter[1], lp->type) = 0;
+ *lib_ptr(ib, filter[2], lp->type) = 0;
+ *lib_ptr(ib, filter[3], lp->type) = 0;
+
+ lance_init_ring(dev);
+ load_csrs(lp);
+
+ netif_start_queue(dev);
+
+ /* Associate IRQ with lance_interrupt */
+ if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) {
+ printk("%s: Can't get IRQ %d\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ if (lp->dma_irq >= 0) {
+ unsigned long flags;
+
+ if (request_irq(lp->dma_irq, lance_dma_merr_int, IRQF_ONESHOT,
+ "lance error", dev)) {
+ free_irq(dev->irq, dev);
+ printk("%s: Can't get DMA IRQ %d\n", dev->name,
+ lp->dma_irq);
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&ioasic_ssr_lock, flags);
+
+ fast_mb();
+ /* Enable I/O ASIC LANCE DMA. */
+ ioasic_write(IO_REG_SSR,
+ ioasic_read(IO_REG_SSR) | IO_SSR_LANCE_DMA_EN);
+
+ fast_mb();
+ spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
+ }
+
+ status = init_restart_lance(lp);
+ return status;
+}
+
+static int lance_close(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->multicast_timer);
+
+ /* Stop the card */
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+
+ if (lp->dma_irq >= 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioasic_ssr_lock, flags);
+
+ fast_mb();
+ /* Disable I/O ASIC LANCE DMA. */
+ ioasic_write(IO_REG_SSR,
+ ioasic_read(IO_REG_SSR) & ~IO_SSR_LANCE_DMA_EN);
+
+ fast_iob();
+ spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
+
+ free_irq(lp->dma_irq, dev);
+ }
+ free_irq(dev->irq, dev);
+ return 0;
+}
+
+static inline int lance_reset(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ int status;
+
+ /* Stop the lance */
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+
+ lance_init_ring(dev);
+ load_csrs(lp);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ status = init_restart_lance(lp);
+ return status;
+}
+
+static void lance_tx_timeout(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+
+ printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
+ dev->name, ll->rdp);
+ lance_reset(dev);
+ netif_wake_queue(dev);
+}
+
+static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile struct lance_regs *ll = lp->ll;
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ unsigned long flags;
+ int entry, len;
+
+ len = skb->len;
+
+ if (len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ len = ETH_ZLEN;
+ }
+
+ dev->stats.tx_bytes += len;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ entry = lp->tx_new;
+ *lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len);
+ *lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0;
+
+ cp_to_buf(lp->type, lp->tx_buf_ptr_cpu[entry], skb->data, len);
+
+ /* Now, give the packet to the lance */
+ *lib_ptr(ib, btx_ring[entry].tmd1, lp->type) =
+ ((lp->tx_buf_ptr_lnc[entry] >> 16) & 0xff) |
+ (LE_T1_POK | LE_T1_OWN);
+ lp->tx_new = (entry + 1) & TX_RING_MOD_MASK;
+
+ if (TX_BUFFS_AVAIL <= 0)
+ netif_stop_queue(dev);
+
+ /* Kick the lance: transmit now */
+ writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static void lance_load_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI) {
+ *lib_ptr(ib, filter[0], lp->type) = 0xffff;
+ *lib_ptr(ib, filter[1], lp->type) = 0xffff;
+ *lib_ptr(ib, filter[2], lp->type) = 0xffff;
+ *lib_ptr(ib, filter[3], lp->type) = 0xffff;
+ return;
+ }
+ /* clear the multicast filter */
+ *lib_ptr(ib, filter[0], lp->type) = 0;
+ *lib_ptr(ib, filter[1], lp->type) = 0;
+ *lib_ptr(ib, filter[2], lp->type) = 0;
+ *lib_ptr(ib, filter[3], lp->type) = 0;
+
+ /* Add addresses */
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc_le(ETH_ALEN, ha->addr);
+ crc = crc >> 26;
+ *lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf);
+ }
+}
+
+static void lance_set_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ volatile u16 *ib = (volatile u16 *)dev->mem_start;
+ volatile struct lance_regs *ll = lp->ll;
+
+ if (!netif_running(dev))
+ return;
+
+ if (lp->tx_old != lp->tx_new) {
+ mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100);
+ netif_wake_queue(dev);
+ return;
+ }
+
+ netif_stop_queue(dev);
+
+ writereg(&ll->rap, LE_CSR0);
+ writereg(&ll->rdp, LE_C0_STOP);
+
+ lance_init_ring(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ *lib_ptr(ib, mode, lp->type) |= LE_MO_PROM;
+ } else {
+ *lib_ptr(ib, mode, lp->type) &= ~LE_MO_PROM;
+ lance_load_multicast(dev);
+ }
+ load_csrs(lp);
+ init_restart_lance(lp);
+ netif_wake_queue(dev);
+}
+
+static void lance_set_multicast_retry(unsigned long _opaque)
+{
+ struct net_device *dev = (struct net_device *) _opaque;
+
+ lance_set_multicast(dev);
+}
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_stop = lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_set_rx_mode = lance_set_multicast,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int dec_lance_probe(struct device *bdev, const int type)
+{
+ static unsigned version_printed;
+ static const char fmt[] = "declance%d";
+ char name[10];
+ struct net_device *dev;
+ struct lance_private *lp;
+ volatile struct lance_regs *ll;
+ resource_size_t start = 0, len = 0;
+ int i, ret;
+ unsigned long esar_base;
+ unsigned char *esar;
+
+ if (dec_lance_debug && version_printed++ == 0)
+ printk(version);
+
+ if (bdev)
+ snprintf(name, sizeof(name), "%s", dev_name(bdev));
+ else {
+ i = 0;
+ dev = root_lance_dev;
+ while (dev) {
+ i++;
+ lp = netdev_priv(dev);
+ dev = lp->next;
+ }
+ snprintf(name, sizeof(name), fmt, i);
+ }
+
+ dev = alloc_etherdev(sizeof(struct lance_private));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ /*
+ * alloc_etherdev ensures the data structures used by the LANCE
+ * are aligned.
+ */
+ lp = netdev_priv(dev);
+ spin_lock_init(&lp->lock);
+
+ lp->type = type;
+ switch (type) {
+ case ASIC_LANCE:
+ dev->base_addr = CKSEG1ADDR(dec_kn_slot_base + IOASIC_LANCE);
+
+ /* buffer space for the on-board LANCE shared memory */
+ /*
+ * FIXME: ugly hack!
+ */
+ dev->mem_start = CKSEG1ADDR(0x00020000);
+ dev->mem_end = dev->mem_start + 0x00020000;
+ dev->irq = dec_interrupt[DEC_IRQ_LANCE];
+ esar_base = CKSEG1ADDR(dec_kn_slot_base + IOASIC_ESAR);
+
+ /* Workaround crash with booting KN04 2.1k from Disk */
+ memset((void *)dev->mem_start, 0,
+ dev->mem_end - dev->mem_start);
+
+ /*
+ * setup the pointer arrays, this sucks [tm] :-(
+ */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
+ 2 * i * RX_BUFF_SIZE);
+ lp->rx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
+ 2 * RX_RING_SIZE * RX_BUFF_SIZE +
+ 2 * i * TX_BUFF_SIZE);
+ lp->tx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC +
+ RX_RING_SIZE * RX_BUFF_SIZE +
+ i * TX_BUFF_SIZE);
+ }
+
+ /* Setup I/O ASIC LANCE DMA. */
+ lp->dma_irq = dec_interrupt[DEC_IRQ_LANCE_MERR];
+ ioasic_write(IO_REG_LANCE_DMA_P,
+ CPHYSADDR(dev->mem_start) << 3);
+
+ break;
+#ifdef CONFIG_TC
+ case PMAD_LANCE:
+ dev_set_drvdata(bdev, dev);
+
+ start = to_tc_dev(bdev)->resource.start;
+ len = to_tc_dev(bdev)->resource.end - start + 1;
+ if (!request_mem_region(start, len, dev_name(bdev))) {
+ printk(KERN_ERR
+ "%s: Unable to reserve MMIO resource\n",
+ dev_name(bdev));
+ ret = -EBUSY;
+ goto err_out_dev;
+ }
+
+ dev->mem_start = CKSEG1ADDR(start);
+ dev->mem_end = dev->mem_start + 0x100000;
+ dev->base_addr = dev->mem_start + 0x100000;
+ dev->irq = to_tc_dev(bdev)->interrupt;
+ esar_base = dev->mem_start + 0x1c0002;
+ lp->dma_irq = -1;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + BUF_OFFSET_CPU +
+ i * RX_BUFF_SIZE);
+ lp->rx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + BUF_OFFSET_CPU +
+ RX_RING_SIZE * RX_BUFF_SIZE +
+ i * TX_BUFF_SIZE);
+ lp->tx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC +
+ RX_RING_SIZE * RX_BUFF_SIZE +
+ i * TX_BUFF_SIZE);
+ }
+
+ break;
+#endif
+ case PMAX_LANCE:
+ dev->irq = dec_interrupt[DEC_IRQ_LANCE];
+ dev->base_addr = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE);
+ dev->mem_start = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE_MEM);
+ dev->mem_end = dev->mem_start + KN01_SLOT_SIZE;
+ esar_base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_ESAR + 1);
+ lp->dma_irq = -1;
+
+ /*
+ * setup the pointer arrays, this sucks [tm] :-(
+ */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
+ 2 * i * RX_BUFF_SIZE);
+ lp->rx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_buf_ptr_cpu[i] =
+ (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
+ 2 * RX_RING_SIZE * RX_BUFF_SIZE +
+ 2 * i * TX_BUFF_SIZE);
+ lp->tx_buf_ptr_lnc[i] =
+ (BUF_OFFSET_LNC +
+ RX_RING_SIZE * RX_BUFF_SIZE +
+ i * TX_BUFF_SIZE);
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "%s: declance_init called with unknown type\n",
+ name);
+ ret = -ENODEV;
+ goto err_out_dev;
+ }
+
+ ll = (struct lance_regs *) dev->base_addr;
+ esar = (unsigned char *) esar_base;
+
+ /* prom checks */
+ /* First, check for test pattern */
+ if (esar[0x60] != 0xff && esar[0x64] != 0x00 &&
+ esar[0x68] != 0x55 && esar[0x6c] != 0xaa) {
+ printk(KERN_ERR
+ "%s: Ethernet station address prom not found!\n",
+ name);
+ ret = -ENODEV;
+ goto err_out_resource;
+ }
+ /* Check the prom contents */
+ for (i = 0; i < 8; i++) {
+ if (esar[i * 4] != esar[0x3c - i * 4] &&
+ esar[i * 4] != esar[0x40 + i * 4] &&
+ esar[0x3c - i * 4] != esar[0x40 + i * 4]) {
+ printk(KERN_ERR "%s: Something is wrong with the "
+ "ethernet station address prom!\n", name);
+ ret = -ENODEV;
+ goto err_out_resource;
+ }
+ }
+
+ /* Copy the ethernet address to the device structure, later to the
+ * lance initialization block so the lance gets it every time it's
+ * (re)initialized.
+ */
+ switch (type) {
+ case ASIC_LANCE:
+ printk("%s: IOASIC onboard LANCE", name);
+ break;
+ case PMAD_LANCE:
+ printk("%s: PMAD-AA", name);
+ break;
+ case PMAX_LANCE:
+ printk("%s: PMAX onboard LANCE", name);
+ break;
+ }
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = esar[i * 4];
+
+ printk(", addr = %pM, irq = %d\n", dev->dev_addr, dev->irq);
+
+ dev->netdev_ops = &lance_netdev_ops;
+ dev->watchdog_timeo = 5*HZ;
+
+ /* lp->ll is the location of the registers for lance card */
+ lp->ll = ll;
+
+ /* busmaster_regval (CSR3) should be zero according to the PMAD-AA
+ * specification.
+ */
+ lp->busmaster_regval = 0;
+
+ dev->dma = 0;
+
+ /* We cannot sleep if the chip is busy during a
+ * multicast list update event, because such events
+ * can occur from interrupts (ex. IPv6). So we
+ * use a timer to try again later when necessary. -DaveM
+ */
+ init_timer(&lp->multicast_timer);
+ lp->multicast_timer.data = (unsigned long) dev;
+ lp->multicast_timer.function = lance_set_multicast_retry;
+
+ ret = register_netdev(dev);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: Unable to register netdev, aborting.\n", name);
+ goto err_out_resource;
+ }
+
+ if (!bdev) {
+ lp->next = root_lance_dev;
+ root_lance_dev = dev;
+ }
+
+ printk("%s: registered as %s.\n", name, dev->name);
+ return 0;
+
+err_out_resource:
+ if (bdev)
+ release_mem_region(start, len);
+
+err_out_dev:
+ free_netdev(dev);
+
+err_out:
+ return ret;
+}
+
+static void __exit dec_lance_remove(struct device *bdev)
+{
+ struct net_device *dev = dev_get_drvdata(bdev);
+ resource_size_t start, len;
+
+ unregister_netdev(dev);
+ start = to_tc_dev(bdev)->resource.start;
+ len = to_tc_dev(bdev)->resource.end - start + 1;
+ release_mem_region(start, len);
+ free_netdev(dev);
+}
+
+/* Find all the lance cards on the system and initialize them */
+static int __init dec_lance_platform_probe(void)
+{
+ int count = 0;
+
+ if (dec_interrupt[DEC_IRQ_LANCE] >= 0) {
+ if (dec_interrupt[DEC_IRQ_LANCE_MERR] >= 0) {
+ if (dec_lance_probe(NULL, ASIC_LANCE) >= 0)
+ count++;
+ } else if (!TURBOCHANNEL) {
+ if (dec_lance_probe(NULL, PMAX_LANCE) >= 0)
+ count++;
+ }
+ }
+
+ return (count > 0) ? 0 : -ENODEV;
+}
+
+static void __exit dec_lance_platform_remove(void)
+{
+ while (root_lance_dev) {
+ struct net_device *dev = root_lance_dev;
+ struct lance_private *lp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ root_lance_dev = lp->next;
+ free_netdev(dev);
+ }
+}
+
+#ifdef CONFIG_TC
+static int dec_lance_tc_probe(struct device *dev);
+static int __exit dec_lance_tc_remove(struct device *dev);
+
+static const struct tc_device_id dec_lance_tc_table[] = {
+ { "DEC ", "PMAD-AA " },
+ { }
+};
+MODULE_DEVICE_TABLE(tc, dec_lance_tc_table);
+
+static struct tc_driver dec_lance_tc_driver = {
+ .id_table = dec_lance_tc_table,
+ .driver = {
+ .name = "declance",
+ .bus = &tc_bus_type,
+ .probe = dec_lance_tc_probe,
+ .remove = __exit_p(dec_lance_tc_remove),
+ },
+};
+
+static int dec_lance_tc_probe(struct device *dev)
+{
+ int status = dec_lance_probe(dev, PMAD_LANCE);
+ if (!status)
+ get_device(dev);
+ return status;
+}
+
+static int __exit dec_lance_tc_remove(struct device *dev)
+{
+ put_device(dev);
+ dec_lance_remove(dev);
+ return 0;
+}
+#endif
+
+static int __init dec_lance_init(void)
+{
+ int status;
+
+ status = tc_register_driver(&dec_lance_tc_driver);
+ if (!status)
+ dec_lance_platform_probe();
+ return status;
+}
+
+static void __exit dec_lance_exit(void)
+{
+ dec_lance_platform_remove();
+ tc_unregister_driver(&dec_lance_tc_driver);
+}
+
+
+module_init(dec_lance_init);
+module_exit(dec_lance_exit);
diff --git a/drivers/net/ethernet/amd/hplance.c b/drivers/net/ethernet/amd/hplance.c
new file mode 100644
index 00000000000..6c9de117ffc
--- /dev/null
+++ b/drivers/net/ethernet/amd/hplance.c
@@ -0,0 +1,232 @@
+/* hplance.c : the Linux/hp300/lance ethernet driver
+ *
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * Based on the Sun Lance driver and the NetBSD HP Lance driver
+ * Uses the generic 7990.c LANCE code.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+/* Used for the temporal inet entries and routing */
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <linux/dio.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+#include "hplance.h"
+
+/* We have 16392 bytes of RAM for the init block and buffers. This places
+ * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
+ */
+#define LANCE_LOG_TX_BUFFERS 1
+#define LANCE_LOG_RX_BUFFERS 3
+
+#include "7990.h" /* use generic LANCE code */
+
+/* Our private data structure */
+struct hplance_private {
+ struct lance_private lance;
+};
+
+/* function prototypes... This is easy because all the grot is in the
+ * generic LANCE support. All we have to support is probing for boards,
+ * plus board-specific init, open and close actions.
+ * Oh, and we need to tell the generic code how to read and write LANCE registers...
+ */
+static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent);
+static void hplance_init(struct net_device *dev, struct dio_dev *d);
+static void hplance_remove_one(struct dio_dev *d);
+static void hplance_writerap(void *priv, unsigned short value);
+static void hplance_writerdp(void *priv, unsigned short value);
+static unsigned short hplance_readrdp(void *priv);
+static int hplance_open(struct net_device *dev);
+static int hplance_close(struct net_device *dev);
+
+static struct dio_device_id hplance_dio_tbl[] = {
+ { DIO_ID_LAN },
+ { 0 }
+};
+
+static struct dio_driver hplance_driver = {
+ .name = "hplance",
+ .id_table = hplance_dio_tbl,
+ .probe = hplance_init_one,
+ .remove = hplance_remove_one,
+};
+
+static const struct net_device_ops hplance_netdev_ops = {
+ .ndo_open = hplance_open,
+ .ndo_stop = hplance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_set_rx_mode = lance_set_multicast,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = lance_poll,
+#endif
+};
+
+/* Find all the HP Lance boards and initialise them... */
+static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent)
+{
+ struct net_device *dev;
+ int err = -ENOMEM;
+
+ dev = alloc_etherdev(sizeof(struct hplance_private));
+ if (!dev)
+ goto out;
+
+ err = -EBUSY;
+ if (!request_mem_region(dio_resource_start(d),
+ dio_resource_len(d), d->name))
+ goto out_free_netdev;
+
+ hplance_init(dev, d);
+ err = register_netdev(dev);
+ if (err)
+ goto out_release_mem_region;
+
+ dio_set_drvdata(d, dev);
+
+ printk(KERN_INFO "%s: %s; select code %d, addr %pM, irq %d\n",
+ dev->name, d->name, d->scode, dev->dev_addr, d->ipl);
+
+ return 0;
+
+ out_release_mem_region:
+ release_mem_region(dio_resource_start(d), dio_resource_len(d));
+ out_free_netdev:
+ free_netdev(dev);
+ out:
+ return err;
+}
+
+static void hplance_remove_one(struct dio_dev *d)
+{
+ struct net_device *dev = dio_get_drvdata(d);
+
+ unregister_netdev(dev);
+ release_mem_region(dio_resource_start(d), dio_resource_len(d));
+ free_netdev(dev);
+}
+
+/* Initialise a single lance board at the given DIO device */
+static void hplance_init(struct net_device *dev, struct dio_dev *d)
+{
+ unsigned long va = (d->resource.start + DIO_VIRADDRBASE);
+ struct hplance_private *lp;
+ int i;
+
+ /* reset the board */
+ out_8(va + DIO_IDOFF, 0xff);
+ udelay(100); /* ariba! ariba! udelay! udelay! */
+
+ /* Fill the dev fields */
+ dev->base_addr = va;
+ dev->netdev_ops = &hplance_netdev_ops;
+ dev->dma = 0;
+
+ for (i = 0; i < 6; i++) {
+ /* The NVRAM holds our ethernet address, one nibble per byte,
+ * at bytes NVRAMOFF+1,3,5,7,9...
+ */
+ dev->dev_addr[i] = ((in_8(va + HPLANCE_NVRAMOFF + i*4 + 1) & 0xF) << 4)
+ | (in_8(va + HPLANCE_NVRAMOFF + i*4 + 3) & 0xF);
+ }
+
+ lp = netdev_priv(dev);
+ lp->lance.name = d->name;
+ lp->lance.base = va;
+ lp->lance.init_block = (struct lance_init_block *)(va + HPLANCE_MEMOFF); /* CPU addr */
+ lp->lance.lance_init_block = NULL; /* LANCE addr of same RAM */
+ lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */
+ lp->lance.irq = d->ipl;
+ lp->lance.writerap = hplance_writerap;
+ lp->lance.writerdp = hplance_writerdp;
+ lp->lance.readrdp = hplance_readrdp;
+ lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
+ lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
+ lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
+ lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
+}
+
+/* This is disgusting. We have to check the DIO status register for ack every
+ * time we read or write the LANCE registers.
+ */
+static void hplance_writerap(void *priv, unsigned short value)
+{
+ struct lance_private *lp = (struct lance_private *)priv;
+ do {
+ out_be16(lp->base + HPLANCE_REGOFF + LANCE_RAP, value);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+}
+
+static void hplance_writerdp(void *priv, unsigned short value)
+{
+ struct lance_private *lp = (struct lance_private *)priv;
+ do {
+ out_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP, value);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+}
+
+static unsigned short hplance_readrdp(void *priv)
+{
+ struct lance_private *lp = (struct lance_private *)priv;
+ __u16 value;
+ do {
+ value = in_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP);
+ } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
+ return value;
+}
+
+static int hplance_open(struct net_device *dev)
+{
+ int status;
+ struct lance_private *lp = netdev_priv(dev);
+
+ status = lance_open(dev); /* call generic lance open code */
+ if (status)
+ return status;
+ /* enable interrupts at board level. */
+ out_8(lp->base + HPLANCE_STATUS, LE_IE);
+
+ return 0;
+}
+
+static int hplance_close(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ out_8(lp->base + HPLANCE_STATUS, 0); /* disable interrupts at boardlevel */
+ lance_close(dev);
+ return 0;
+}
+
+static int __init hplance_init_module(void)
+{
+ return dio_register_driver(&hplance_driver);
+}
+
+static void __exit hplance_cleanup_module(void)
+{
+ dio_unregister_driver(&hplance_driver);
+}
+
+module_init(hplance_init_module);
+module_exit(hplance_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/amd/hplance.h b/drivers/net/ethernet/amd/hplance.h
new file mode 100644
index 00000000000..04aee9e0376
--- /dev/null
+++ b/drivers/net/ethernet/amd/hplance.h
@@ -0,0 +1,26 @@
+/* Random defines and structures for the HP Lance driver.
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * Based on the Sun Lance driver and the NetBSD HP Lance driver
+ */
+
+/* Registers */
+#define HPLANCE_ID 0x01 /* DIO register: ID byte */
+#define HPLANCE_STATUS 0x03 /* DIO register: interrupt enable/status */
+
+/* Control and status bits for the status register */
+#define LE_IE 0x80 /* interrupt enable */
+#define LE_IR 0x40 /* interrupt requested */
+#define LE_LOCK 0x08 /* lock status register */
+#define LE_ACK 0x04 /* ack of lock */
+#define LE_JAB 0x02 /* loss of tx clock (???) */
+/* We can also extract the IPL from the status register with the standard
+ * DIO_IPL(hplance) macro, or using dio_scodetoipl()
+ */
+
+/* These are the offsets for the DIO regs (hplance_reg), lance_ioreg,
+ * memory and NVRAM:
+ */
+#define HPLANCE_IDOFF 0 /* board baseaddr */
+#define HPLANCE_REGOFF 0x4000 /* lance registers */
+#define HPLANCE_MEMOFF 0x8000 /* struct lance_init_block */
+#define HPLANCE_NVRAMOFF 0xC008 /* etheraddress as one *nibble* per byte */
diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c
new file mode 100644
index 00000000000..256f590f6bb
--- /dev/null
+++ b/drivers/net/ethernet/amd/lance.c
@@ -0,0 +1,1312 @@
+/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
+/*
+ Written/copyright 1993-1998 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
+ with most other LANCE-based bus-master (NE2100/NE2500) ethercards.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Andrey V. Savochkin:
+ - alignment problem with 1.3.* kernel and some minor changes.
+ Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
+ - added support for Linux/Alpha, but removed most of it, because
+ it worked only for the PCI chip.
+ - added hook for the 32bit lance driver
+ - added PCnetPCI II (79C970A) to chip table
+ Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
+ - hopefully fix above so Linux/Alpha can use ISA cards too.
+ 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
+ v1.12 10/27/97 Module support -djb
+ v1.14 2/3/98 Module support modified, made PCI support optional -djb
+ v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed
+ before unregister_netdev() which caused NULL pointer
+ reference later in the chain (in rtnetlink_fill_ifinfo())
+ -- Mika Kuoppala <miku@iki.fi>
+
+ Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
+ the 2.1 version of the old driver - Alan Cox
+
+ Get rid of check_region, check kmalloc return in lance_probe1
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
+
+ Reworked detection, added support for Racal InterLan EtherBlaster cards
+ Vesselin Kostadinov <vesok at yahoo dot com > - 22/4/2004
+*/
+
+static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
+static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
+static int __init do_lance_probe(struct net_device *dev);
+
+
+static struct card {
+ char id_offset14;
+ char id_offset15;
+} cards[] = {
+ { //"normal"
+ .id_offset14 = 0x57,
+ .id_offset15 = 0x57,
+ },
+ { //NI6510EB
+ .id_offset14 = 0x52,
+ .id_offset15 = 0x44,
+ },
+ { //Racal InterLan EtherBlaster
+ .id_offset14 = 0x52,
+ .id_offset15 = 0x49,
+ },
+};
+#define NUM_CARDS 3
+
+#ifdef LANCE_DEBUG
+static int lance_debug = LANCE_DEBUG;
+#else
+static int lance_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the AMD 79C960, the "PCnet-ISA
+single-chip ethernet controller for ISA". This chip is used in a wide
+variety of boards from vendors such as Allied Telesis, HP, Kingston,
+and Boca. This driver is also intended to work with older AMD 7990
+designs, such as the NE1500 and NE2100, and newer 79C961. For convenience,
+I use the name LANCE to refer to all of the AMD chips, even though it properly
+refers only to the original 7990.
+
+II. Board-specific settings
+
+The driver is designed to work the boards that use the faster
+bus-master mode, rather than in shared memory mode. (Only older designs
+have on-board buffer memory needed to support the slower shared memory mode.)
+
+Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
+channel. This driver probes the likely base addresses:
+{0x300, 0x320, 0x340, 0x360}.
+After the board is found it generates a DMA-timeout interrupt and uses
+autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
+of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
+probed for by enabling each free DMA channel in turn and checking if
+initialization succeeds.
+
+The HP-J2405A board is an exception: with this board it is easy to read the
+EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
+_know_ the base address -- that field is for writing the EEPROM.)
+
+III. Driver operation
+
+IIIa. Ring buffers
+The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes
+the base and length of the data buffer, along with status bits. The length
+of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
+the buffer length (rather than being directly the buffer length) for
+implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to
+ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries
+needlessly uses extra space and reduces the chance that an upper layer will
+be able to reorder queued Tx packets based on priority. Decreasing the number
+of entries makes it more difficult to achieve back-to-back packet transmission
+and increases the chance that Rx ring will overflow. (Consider the worst case
+of receiving back-to-back minimum-sized packets.)
+
+The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
+statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
+avoid the administrative overhead. For the Rx side this avoids dynamically
+allocating full-sized buffers "just in case", at the expense of a
+memory-to-memory data copy for each packet received. For most systems this
+is a good tradeoff: the Rx buffer will always be in low memory, the copy
+is inexpensive, and it primes the cache for later packet processing. For Tx
+the buffers are only used when needed as low-memory bounce buffers.
+
+IIIB. 16M memory limitations.
+For the ISA bus master mode all structures used directly by the LANCE,
+the initialization block, Rx and Tx rings, and data buffers, must be
+accessible from the ISA bus, i.e. in the lower 16M of real memory.
+This is a problem for current Linux kernels on >16M machines. The network
+devices are initialized after memory initialization, and the kernel doles out
+memory from the top of memory downward. The current solution is to have a
+special network initialization routine that's called before memory
+initialization; this will eventually be generalized for all network devices.
+As mentioned before, low-memory "bounce-buffers" are used when needed.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+*/
+
+/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
+ That translates to 4 and 4 (16 == 2^^4).
+ This is a compile-time option for efficiency.
+ */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+
+#define PKT_BUF_SZ 1544
+
+/* Offsets from base I/O address. */
+#define LANCE_DATA 0x10
+#define LANCE_ADDR 0x12
+#define LANCE_RESET 0x14
+#define LANCE_BUS_IF 0x16
+#define LANCE_TOTAL_SIZE 0x18
+
+#define TX_TIMEOUT (HZ/5)
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ s32 base;
+ s16 buf_length; /* This length is 2s complement (negative)! */
+ s16 msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ s32 base;
+ s16 length; /* Length is 2s complement (negative)! */
+ s16 misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+ u16 mode; /* Pre-set mode (reg. 15) */
+ u8 phys_addr[6]; /* Physical ethernet address */
+ u32 filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with extra bits. */
+ u32 rx_ring; /* Tx and Rx ring base pointers */
+ u32 tx_ring;
+};
+
+struct lance_private {
+ /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */
+ struct lance_rx_head rx_ring[RX_RING_SIZE];
+ struct lance_tx_head tx_ring[TX_RING_SIZE];
+ struct lance_init_block init_block;
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
+ /* Tx low-memory "bounce buffer" address. */
+ char (*tx_bounce_buffs)[PKT_BUF_SZ];
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ int dma;
+ unsigned char chip_version; /* See lance_chip_type. */
+ spinlock_t devlock;
+};
+
+#define LANCE_MUST_PAD 0x00000001
+#define LANCE_ENABLE_AUTOSELECT 0x00000002
+#define LANCE_MUST_REINIT_RING 0x00000004
+#define LANCE_MUST_UNRESET 0x00000008
+#define LANCE_HAS_MISSED_FRAME 0x00000010
+
+/* A mapping from the chip ID number to the part number and features.
+ These are from the datasheets -- in real life the '970 version
+ reportedly has the same ID as the '965. */
+static struct lance_chip_type {
+ int id_number;
+ const char *name;
+ int flags;
+} chip_table[] = {
+ {0x0000, "LANCE 7990", /* Ancient lance chip. */
+ LANCE_MUST_PAD + LANCE_MUST_UNRESET},
+ {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
+ it the PCnet32. */
+ {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x0, "PCnet (unknown)",
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+};
+
+enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6};
+
+
+/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
+ Assume yes until we know the memory size. */
+static unsigned char lance_need_isa_bounce_buffers = 1;
+
+static int lance_open(struct net_device *dev);
+static void lance_init_ring(struct net_device *dev, gfp_t mode);
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static int lance_rx(struct net_device *dev);
+static irqreturn_t lance_interrupt(int irq, void *dev_id);
+static int lance_close(struct net_device *dev);
+static struct net_device_stats *lance_get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void lance_tx_timeout (struct net_device *dev);
+
+
+
+#ifdef MODULE
+#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */
+
+static struct net_device *dev_lance[MAX_CARDS];
+static int io[MAX_CARDS];
+static int dma[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(dma, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param(lance_debug, int, 0);
+MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required");
+MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)");
+MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)");
+MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)");
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) /* only complain once */
+ break;
+ printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+ return -EPERM;
+ }
+ dev = alloc_etherdev(0);
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->dma = dma[this_dev];
+ if (do_lance_probe(dev) == 0) {
+ dev_lance[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ break;
+ }
+ if (found != 0)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+ if (dev->dma != 4)
+ free_dma(dev->dma);
+ release_region(dev->base_addr, LANCE_TOTAL_SIZE);
+ kfree(lp->tx_bounce_buffs);
+ kfree((void*)lp->rx_buffs);
+ kfree(lp);
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
+ struct net_device *dev = dev_lance[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
+
+
+/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
+ board probes now that kmalloc() can allocate ISA DMA-able regions.
+ This also allows the LANCE driver to be used as a module.
+ */
+static int __init do_lance_probe(struct net_device *dev)
+{
+ unsigned int *port;
+ int result;
+
+ if (high_memory <= phys_to_virt(16*1024*1024))
+ lance_need_isa_bounce_buffers = 0;
+
+ for (port = lance_portlist; *port; port++) {
+ int ioaddr = *port;
+ struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE,
+ "lance-probe");
+
+ if (r) {
+ /* Detect the card with minimal I/O reads */
+ char offset14 = inb(ioaddr + 14);
+ int card;
+ for (card = 0; card < NUM_CARDS; ++card)
+ if (cards[card].id_offset14 == offset14)
+ break;
+ if (card < NUM_CARDS) {/*yes, the first byte matches*/
+ char offset15 = inb(ioaddr + 15);
+ for (card = 0; card < NUM_CARDS; ++card)
+ if ((cards[card].id_offset14 == offset14) &&
+ (cards[card].id_offset15 == offset15))
+ break;
+ }
+ if (card < NUM_CARDS) { /*Signature OK*/
+ result = lance_probe1(dev, ioaddr, 0, 0);
+ if (!result) {
+ struct lance_private *lp = dev->ml_priv;
+ int ver = lp->chip_version;
+
+ r->name = chip_table[ver].name;
+ return 0;
+ }
+ }
+ release_region(ioaddr, LANCE_TOTAL_SIZE);
+ }
+ }
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init lance_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(0);
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_lance_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_stop = lance_close,
+ .ndo_get_stats = lance_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
+{
+ struct lance_private *lp;
+ unsigned long dma_channels; /* Mark spuriously-busy DMA channels */
+ int i, reset_val, lance_version;
+ const char *chipname;
+ /* Flags for specific chips or boards. */
+ unsigned char hpJ2405A = 0; /* HP ISA adaptor */
+ int hp_builtin = 0; /* HP on-board ethernet. */
+ static int did_version; /* Already printed version info. */
+ unsigned long flags;
+ int err = -ENOMEM;
+ void __iomem *bios;
+
+ /* First we look for special cases.
+ Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
+ There are two HP versions, check the BIOS for the configuration port.
+ This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
+ */
+ bios = ioremap(0xf00f0, 0x14);
+ if (!bios)
+ return -ENOMEM;
+ if (readw(bios + 0x12) == 0x5048) {
+ static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
+ int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99;
+ /* We can have boards other than the built-in! Verify this is on-board. */
+ if ((inb(hp_port) & 0xc0) == 0x80 &&
+ ioaddr_table[inb(hp_port) & 3] == ioaddr)
+ hp_builtin = hp_port;
+ }
+ iounmap(bios);
+ /* We also recognize the HP Vectra on-board here, but check below. */
+ hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 &&
+ inb(ioaddr+2) == 0x09);
+
+ /* Reset the LANCE. */
+ reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
+
+ /* The Un-Reset needed is only needed for the real NE2100, and will
+ confuse the HP board. */
+ if (!hpJ2405A)
+ outw(reset_val, ioaddr+LANCE_RESET);
+
+ outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
+ if (inw(ioaddr+LANCE_DATA) != 0x0004)
+ return -ENODEV;
+
+ /* Get the version of the chip. */
+ outw(88, ioaddr+LANCE_ADDR);
+ if (inw(ioaddr+LANCE_ADDR) != 88) {
+ lance_version = 0;
+ } else { /* Good, it's a newer chip. */
+ int chip_version = inw(ioaddr+LANCE_DATA);
+ outw(89, ioaddr+LANCE_ADDR);
+ chip_version |= inw(ioaddr+LANCE_DATA) << 16;
+ if (lance_debug > 2)
+ printk(" LANCE chip version is %#x.\n", chip_version);
+ if ((chip_version & 0xfff) != 0x003)
+ return -ENODEV;
+ chip_version = (chip_version >> 12) & 0xffff;
+ for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
+ if (chip_table[lance_version].id_number == chip_version)
+ break;
+ }
+ }
+
+ /* We can't allocate private data from alloc_etherdev() because it must
+ a ISA DMA-able region. */
+ chipname = chip_table[lance_version].name;
+ printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr);
+
+ /* There is a 16 byte station address PROM at the base address.
+ The first six bytes are the station address. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + i);
+ printk("%pM", dev->dev_addr);
+
+ dev->base_addr = ioaddr;
+ /* Make certain the data structures used by the LANCE are aligned and DMAble. */
+
+ lp = kzalloc(sizeof(*lp), GFP_DMA | GFP_KERNEL);
+ if(lp==NULL)
+ return -ENODEV;
+ if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
+ dev->ml_priv = lp;
+ lp->name = chipname;
+ lp->rx_buffs = (unsigned long)kmalloc(PKT_BUF_SZ*RX_RING_SIZE,
+ GFP_DMA | GFP_KERNEL);
+ if (!lp->rx_buffs)
+ goto out_lp;
+ if (lance_need_isa_bounce_buffers) {
+ lp->tx_bounce_buffs = kmalloc(PKT_BUF_SZ*TX_RING_SIZE,
+ GFP_DMA | GFP_KERNEL);
+ if (!lp->tx_bounce_buffs)
+ goto out_rx;
+ } else
+ lp->tx_bounce_buffs = NULL;
+
+ lp->chip_version = lance_version;
+ spin_lock_init(&lp->devlock);
+
+ lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
+
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+
+ if (irq) { /* Set iff PCI card. */
+ dev->dma = 4; /* Native bus-master, no DMA channel needed. */
+ dev->irq = irq;
+ } else if (hp_builtin) {
+ static const char dma_tbl[4] = {3, 5, 6, 0};
+ static const char irq_tbl[4] = {3, 4, 5, 9};
+ unsigned char port_val = inb(hp_builtin);
+ dev->dma = dma_tbl[(port_val >> 4) & 3];
+ dev->irq = irq_tbl[(port_val >> 2) & 3];
+ printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (hpJ2405A) {
+ static const char dma_tbl[4] = {3, 5, 6, 7};
+ static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
+ short reset_val = inw(ioaddr+LANCE_RESET);
+ dev->dma = dma_tbl[(reset_val >> 2) & 3];
+ dev->irq = irq_tbl[(reset_val >> 4) & 7];
+ printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */
+ short bus_info;
+ outw(8, ioaddr+LANCE_ADDR);
+ bus_info = inw(ioaddr+LANCE_BUS_IF);
+ dev->dma = bus_info & 0x07;
+ dev->irq = (bus_info >> 4) & 0x0F;
+ } else {
+ /* The DMA channel may be passed in PARAM1. */
+ if (dev->mem_start & 0x07)
+ dev->dma = dev->mem_start & 0x07;
+ }
+
+ if (dev->dma == 0) {
+ /* Read the DMA channel status register, so that we can avoid
+ stuck DMA channels in the DMA detection below. */
+ dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
+ (inb(DMA2_STAT_REG) & 0xf0);
+ }
+ err = -ENODEV;
+ if (dev->irq >= 2)
+ printk(" assigned IRQ %d", dev->irq);
+ else if (lance_version != 0) { /* 7990 boards need DMA detection first. */
+ unsigned long irq_mask;
+
+ /* To auto-IRQ we enable the initialization-done and DMA error
+ interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ boards will work. */
+ irq_mask = probe_irq_on();
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(0x0041, ioaddr+LANCE_DATA);
+
+ mdelay(20);
+ dev->irq = probe_irq_off(irq_mask);
+ if (dev->irq)
+ printk(", probed IRQ %d", dev->irq);
+ else {
+ printk(", failed to detect IRQ line.\n");
+ goto out_tx;
+ }
+
+ /* Check for the initialization done bit, 0x0100, which means
+ that we don't need a DMA channel. */
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ dev->dma = 4;
+ }
+
+ if (dev->dma == 4) {
+ printk(", no DMA needed.\n");
+ } else if (dev->dma) {
+ if (request_dma(dev->dma, chipname)) {
+ printk("DMA %d allocation failed.\n", dev->dma);
+ goto out_tx;
+ } else
+ printk(", assigned DMA %d.\n", dev->dma);
+ } else { /* OK, we have to auto-DMA. */
+ for (i = 0; i < 4; i++) {
+ static const char dmas[] = { 5, 6, 7, 3 };
+ int dma = dmas[i];
+ int boguscnt;
+
+ /* Don't enable a permanently busy DMA channel, or the machine
+ will hang. */
+ if (test_bit(dma, &dma_channels))
+ continue;
+ outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
+ if (request_dma(dma, chipname))
+ continue;
+
+ flags=claim_dma_lock();
+ set_dma_mode(dma, DMA_MODE_CASCADE);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ /* Trigger an initialization. */
+ outw(0x0001, ioaddr+LANCE_DATA);
+ for (boguscnt = 100; boguscnt > 0; --boguscnt)
+ if (inw(ioaddr+LANCE_DATA) & 0x0900)
+ break;
+ if (inw(ioaddr+LANCE_DATA) & 0x0100) {
+ dev->dma = dma;
+ printk(", DMA %d.\n", dev->dma);
+ break;
+ } else {
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ release_dma_lock(flags);
+ free_dma(dma);
+ }
+ }
+ if (i == 4) { /* Failure: bail. */
+ printk("DMA detection failed.\n");
+ goto out_tx;
+ }
+ }
+
+ if (lance_version == 0 && dev->irq == 0) {
+ /* We may auto-IRQ now that we have a DMA channel. */
+ /* Trigger an initialization just for the interrupt. */
+ unsigned long irq_mask;
+
+ irq_mask = probe_irq_on();
+ outw(0x0041, ioaddr+LANCE_DATA);
+
+ mdelay(40);
+ dev->irq = probe_irq_off(irq_mask);
+ if (dev->irq == 0) {
+ printk(" Failed to detect the 7990 IRQ line.\n");
+ goto out_dma;
+ }
+ printk(" Auto-IRQ detected IRQ%d.\n", dev->irq);
+ }
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* Turn on auto-select of media (10baseT or BNC) so that the user
+ can watch the LEDs even if the board isn't opened. */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ /* Don't touch 10base2 power bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ /* The LANCE-specific entries in the device structure. */
+ dev->netdev_ops = &lance_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ err = register_netdev(dev);
+ if (err)
+ goto out_dma;
+ return 0;
+out_dma:
+ if (dev->dma != 4)
+ free_dma(dev->dma);
+out_tx:
+ kfree(lp->tx_bounce_buffs);
+out_rx:
+ kfree((void*)lp->rx_buffs);
+out_lp:
+ kfree(lp);
+ return err;
+}
+
+
+static int
+lance_open(struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ if (dev->irq == 0 ||
+ request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) {
+ return -EAGAIN;
+ }
+
+ /* We used to allocate DMA here, but that was silly.
+ DMA lines can't be shared! We now permanently allocate them. */
+
+ /* Reset the LANCE */
+ inw(ioaddr+LANCE_RESET);
+
+ /* The DMA controller is used as a no-operation slave, "cascade mode". */
+ if (dev->dma != 4) {
+ unsigned long flags=claim_dma_lock();
+ enable_dma(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_CASCADE);
+ release_dma_lock(flags);
+ }
+
+ /* Un-Reset the LANCE, needed only for the NE2100. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
+ outw(0, ioaddr+LANCE_RESET);
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ /* Only touch autoselect bit. */
+ outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 1)
+ printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
+ dev->name, dev->irq, dev->dma,
+ (u32) isa_virt_to_bus(lp->tx_ring),
+ (u32) isa_virt_to_bus(lp->rx_ring),
+ (u32) isa_virt_to_bus(&lp->init_block));
+
+ lance_init_ring(dev, GFP_KERNEL);
+ /* Re-initialize the LANCE, and start it when done. */
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
+
+ outw(0x0004, ioaddr+LANCE_ADDR);
+ outw(0x0915, ioaddr+LANCE_DATA);
+
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0001, ioaddr+LANCE_DATA);
+
+ netif_start_queue (dev);
+
+ i = 0;
+ while (i++ < 100)
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ outw(0x0042, ioaddr+LANCE_DATA);
+
+ if (lance_debug > 2)
+ printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
+ dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
+
+ return 0; /* Always succeed */
+}
+
+/* The LANCE has been halted for one reason or another (busmaster memory
+ arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ etc.). Modern LANCE variants always reload their ring-buffer
+ configuration when restarted, so we must reinitialize our ring
+ context before restarting. As part of this reinitialization,
+ find all packets still on the Tx ring and pretend that they had been
+ sent (in effect, drop the packets on the floor) - the higher-level
+ protocols will time out and retransmit. It'd be better to shuffle
+ these skbs to a temp list and then actually re-Tx them after
+ restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
+*/
+
+static void
+lance_purge_ring(struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+ int i;
+
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = NULL;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb_any(skb);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i]) {
+ dev_kfree_skb_any(lp->tx_skbuff[i]);
+ lp->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+static void
+lance_init_ring(struct net_device *dev, gfp_t gfp)
+{
+ struct lance_private *lp = dev->ml_priv;
+ int i;
+
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+ void *rx_buff;
+
+ skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
+ lp->rx_skbuff[i] = skb;
+ if (skb)
+ rx_buff = skb->data;
+ else
+ rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
+ if (rx_buff == NULL)
+ lp->rx_ring[i].base = 0;
+ else
+ lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000;
+ lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+ }
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_skbuff[i] = NULL;
+ lp->tx_ring[i].base = 0;
+ }
+
+ lp->init_block.mode = 0x0000;
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
+}
+
+static void
+lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
+{
+ struct lance_private *lp = dev->ml_priv;
+
+ if (must_reinit ||
+ (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
+ lance_purge_ring(dev);
+ lance_init_ring(dev, GFP_ATOMIC);
+ }
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(csr0_bits, dev->base_addr + LANCE_DATA);
+}
+
+
+static void lance_tx_timeout (struct net_device *dev)
+{
+ struct lance_private *lp = (struct lance_private *) dev->ml_priv;
+ int ioaddr = dev->base_addr;
+
+ outw (0, ioaddr + LANCE_ADDR);
+ printk ("%s: transmit timed out, status %4.4x, resetting.\n",
+ dev->name, inw (ioaddr + LANCE_DATA));
+ outw (0x0004, ioaddr + LANCE_DATA);
+ dev->stats.tx_errors++;
+#ifndef final_version
+ if (lance_debug > 3) {
+ int i;
+ printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+ lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "",
+ lp->cur_rx);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+ lp->rx_ring[i].msg_length);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->tx_ring[i].base, -lp->tx_ring[i].length,
+ lp->tx_ring[i].misc);
+ printk ("\n");
+ }
+#endif
+ lance_restart (dev, 0x0043, 1);
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue (dev);
+}
+
+
+static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->devlock, flags);
+
+ if (lance_debug > 3) {
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
+ inw(ioaddr+LANCE_DATA));
+ outw(0x0000, ioaddr+LANCE_DATA);
+ }
+
+ /* Fill in a Tx ring entry */
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & TX_RING_MOD_MASK;
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* The old LANCE chips doesn't automatically pad buffers to min. size. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
+ if (skb->len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ goto out;
+ lp->tx_ring[entry].length = -ETH_ZLEN;
+ }
+ else
+ lp->tx_ring[entry].length = -skb->len;
+ } else
+ lp->tx_ring[entry].length = -skb->len;
+
+ lp->tx_ring[entry].misc = 0x0000;
+
+ dev->stats.tx_bytes += skb->len;
+
+ /* If any part of this buffer is >16M we must copy it to a low-memory
+ buffer. */
+ if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) {
+ if (lance_debug > 5)
+ printk("%s: bouncing a high-memory packet (%#x).\n",
+ dev->name, (u32)isa_virt_to_bus(skb->data));
+ skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
+ lp->tx_ring[entry].base =
+ ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
+ dev_kfree_skb(skb);
+ } else {
+ lp->tx_skbuff[entry] = skb;
+ lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
+ }
+ lp->cur_tx++;
+
+ /* Trigger an immediate send poll. */
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0048, ioaddr+LANCE_DATA);
+
+ if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE)
+ netif_stop_queue(dev);
+
+out:
+ spin_unlock_irqrestore(&lp->devlock, flags);
+ return NETDEV_TX_OK;
+}
+
+/* The LANCE interrupt handler. */
+static irqreturn_t lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp;
+ int csr0, ioaddr, boguscnt=10;
+ int must_restart;
+
+ ioaddr = dev->base_addr;
+ lp = dev->ml_priv;
+
+ spin_lock (&lp->devlock);
+
+ outw(0x00, dev->base_addr + LANCE_ADDR);
+ while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 &&
+ --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+
+ must_restart = 0;
+
+ if (lance_debug > 5)
+ printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
+ dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
+
+ if (csr0 & 0x0400) /* Rx interrupt */
+ lance_rx(dev);
+
+ if (csr0 & 0x0200) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx & TX_RING_MOD_MASK;
+ int status = lp->tx_ring[entry].base;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[entry].base = 0;
+
+ if (status & 0x40000000) {
+ /* There was an major error, log it. */
+ int err_status = lp->tx_ring[entry].misc;
+ dev->stats.tx_errors++;
+ if (err_status & 0x0400)
+ dev->stats.tx_aborted_errors++;
+ if (err_status & 0x0800)
+ dev->stats.tx_carrier_errors++;
+ if (err_status & 0x1000)
+ dev->stats.tx_window_errors++;
+ if (err_status & 0x4000) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ dev->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ printk("%s: Tx FIFO error! Status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+ } else {
+ if (status & 0x18000000)
+ dev->stats.collisions++;
+ dev->stats.tx_packets++;
+ }
+
+ /* We must free the original skb if it's not a data-only copy
+ in the bounce buffer. */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb_irq(lp->tx_skbuff[entry]);
+ lp->tx_skbuff[entry] = NULL;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n",
+ dirty_tx, lp->cur_tx,
+ netif_queue_stopped(dev) ? "yes" : "no");
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ /* if the ring is no longer full, accept more packets */
+ if (netif_queue_stopped(dev) &&
+ dirty_tx > lp->cur_tx - TX_RING_SIZE + 2)
+ netif_wake_queue (dev);
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors. */
+ if (csr0 & 0x4000)
+ dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & 0x1000)
+ dev->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & 0x0800) {
+ printk("%s: Bus master arbitration failure, status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x0004, dev->base_addr + LANCE_DATA);
+ lance_restart(dev, 0x0002, 0);
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable. */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x7940, dev->base_addr + LANCE_DATA);
+
+ if (lance_debug > 4)
+ printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+ dev->name, inw(ioaddr + LANCE_ADDR),
+ inw(dev->base_addr + LANCE_DATA));
+
+ spin_unlock (&lp->devlock);
+ return IRQ_HANDLED;
+}
+
+static int
+lance_rx(struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+ int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].base >= 0) {
+ int status = lp->rx_ring[entry].base >> 24;
+
+ if (status != 0x03) { /* There was an error. */
+ /* There is a tricky error noted by John Murphy,
+ <murf@perftech.com> to Russ Nelson: Even with full-sized
+ buffers it's possible for a jabber packet to use two
+ buffers, with only the last correctly noting the error. */
+ if (status & 0x01) /* Only count a general error at the */
+ dev->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20)
+ dev->stats.rx_frame_errors++;
+ if (status & 0x10)
+ dev->stats.rx_over_errors++;
+ if (status & 0x08)
+ dev->stats.rx_crc_errors++;
+ if (status & 0x04)
+ dev->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].base &= 0x03ffffff;
+ }
+ else
+ {
+ /* Malloc up new buffer, compatible with net3. */
+ short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
+ struct sk_buff *skb;
+
+ if(pkt_len<60)
+ {
+ printk("%s: Runt packet!\n",dev->name);
+ dev->stats.rx_errors++;
+ }
+ else
+ {
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL)
+ {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2)
+ {
+ dev->stats.rx_dropped++;
+ lp->rx_ring[entry].base |= 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)),
+ pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ }
+ /* The docs say that the buffer length isn't touched, but Andrew Boyd
+ of QNX reports that some revs of the 79C965 clear it. */
+ lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
+ lp->rx_ring[entry].base |= 0x80000000;
+ entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ }
+
+ /* We should check that at least two ring entries are free. If not,
+ we should free one and mark stats->rx_dropped++. */
+
+ return 0;
+}
+
+static int
+lance_close(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct lance_private *lp = dev->ml_priv;
+
+ netif_stop_queue (dev);
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ outw(112, ioaddr+LANCE_ADDR);
+ dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ }
+ outw(0, ioaddr+LANCE_ADDR);
+
+ if (lance_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ outw(0x0004, ioaddr+LANCE_DATA);
+
+ if (dev->dma != 4)
+ {
+ unsigned long flags=claim_dma_lock();
+ disable_dma(dev->dma);
+ release_dma_lock(flags);
+ }
+ free_irq(dev->irq, dev);
+
+ lance_purge_ring(dev);
+
+ return 0;
+}
+
+static struct net_device_stats *lance_get_stats(struct net_device *dev)
+{
+ struct lance_private *lp = dev->ml_priv;
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ short ioaddr = dev->base_addr;
+ short saved_addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->devlock, flags);
+ saved_addr = inw(ioaddr+LANCE_ADDR);
+ outw(112, ioaddr+LANCE_ADDR);
+ dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ outw(saved_addr, ioaddr+LANCE_ADDR);
+ spin_unlock_irqrestore(&lp->devlock, flags);
+ }
+
+ return &dev->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outw(0, ioaddr+LANCE_ADDR);
+ outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
+
+ if (dev->flags&IFF_PROMISC) {
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int i;
+ int num_addrs=netdev_mc_count(dev);
+ if(dev->flags&IFF_ALLMULTI)
+ num_addrs=1;
+ /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
+ memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
+ for (i = 0; i < 4; i++) {
+ outw(8 + i, ioaddr+LANCE_ADDR);
+ outw(multicast_table[i], ioaddr+LANCE_DATA);
+ }
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
+ }
+
+ lance_restart(dev, 0x0142, 0); /* Resume normal operation */
+
+}
+
diff --git a/drivers/net/ethernet/amd/mvme147.c b/drivers/net/ethernet/amd/mvme147.c
new file mode 100644
index 00000000000..0660ac5846b
--- /dev/null
+++ b/drivers/net/ethernet/amd/mvme147.c
@@ -0,0 +1,200 @@
+/* mvme147.c : the Linux/mvme147/lance ethernet driver
+ *
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * Based on the Sun Lance driver and the NetBSD HP Lance driver
+ * Uses the generic 7990.c LANCE code.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+/* Used for the temporal inet entries and routing */
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/mvme147hw.h>
+
+/* We have 32K of RAM for the init block and buffers. This places
+ * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
+ */
+#define LANCE_LOG_TX_BUFFERS 1
+#define LANCE_LOG_RX_BUFFERS 3
+
+#include "7990.h" /* use generic LANCE code */
+
+/* Our private data structure */
+struct m147lance_private {
+ struct lance_private lance;
+ unsigned long ram;
+};
+
+/* function prototypes... This is easy because all the grot is in the
+ * generic LANCE support. All we have to support is probing for boards,
+ * plus board-specific init, open and close actions.
+ * Oh, and we need to tell the generic code how to read and write LANCE registers...
+ */
+static int m147lance_open(struct net_device *dev);
+static int m147lance_close(struct net_device *dev);
+static void m147lance_writerap(struct lance_private *lp, unsigned short value);
+static void m147lance_writerdp(struct lance_private *lp, unsigned short value);
+static unsigned short m147lance_readrdp(struct lance_private *lp);
+
+typedef void (*writerap_t)(void *, unsigned short);
+typedef void (*writerdp_t)(void *, unsigned short);
+typedef unsigned short (*readrdp_t)(void *);
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = m147lance_open,
+ .ndo_stop = m147lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_set_rx_mode = lance_set_multicast,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+/* Initialise the one and only on-board 7990 */
+struct net_device * __init mvme147lance_probe(int unit)
+{
+ struct net_device *dev;
+ static int called;
+ static const char name[] = "MVME147 LANCE";
+ struct m147lance_private *lp;
+ u_long *addr;
+ u_long address;
+ int err;
+
+ if (!MACH_IS_MVME147 || called)
+ return ERR_PTR(-ENODEV);
+ called++;
+
+ dev = alloc_etherdev(sizeof(struct m147lance_private));
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0)
+ sprintf(dev->name, "eth%d", unit);
+
+ /* Fill the dev fields */
+ dev->base_addr = (unsigned long)MVME147_LANCE_BASE;
+ dev->netdev_ops = &lance_netdev_ops;
+ dev->dma = 0;
+
+ addr = (u_long *)ETHERNET_ADDRESS;
+ address = *addr;
+ dev->dev_addr[0] = 0x08;
+ dev->dev_addr[1] = 0x00;
+ dev->dev_addr[2] = 0x3e;
+ address = address >> 8;
+ dev->dev_addr[5] = address&0xff;
+ address = address >> 8;
+ dev->dev_addr[4] = address&0xff;
+ address = address >> 8;
+ dev->dev_addr[3] = address&0xff;
+
+ printk("%s: MVME147 at 0x%08lx, irq %d, Hardware Address %pM\n",
+ dev->name, dev->base_addr, MVME147_LANCE_IRQ,
+ dev->dev_addr);
+
+ lp = netdev_priv(dev);
+ lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 32K */
+ if (!lp->ram) {
+ printk("%s: No memory for LANCE buffers\n", dev->name);
+ free_netdev(dev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ lp->lance.name = name;
+ lp->lance.base = dev->base_addr;
+ lp->lance.init_block = (struct lance_init_block *)(lp->ram); /* CPU addr */
+ lp->lance.lance_init_block = (struct lance_init_block *)(lp->ram); /* LANCE addr of same RAM */
+ lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */
+ lp->lance.irq = MVME147_LANCE_IRQ;
+ lp->lance.writerap = (writerap_t)m147lance_writerap;
+ lp->lance.writerdp = (writerdp_t)m147lance_writerdp;
+ lp->lance.readrdp = (readrdp_t)m147lance_readrdp;
+ lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
+ lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
+ lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
+ lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
+
+ err = register_netdev(dev);
+ if (err) {
+ free_pages(lp->ram, 3);
+ free_netdev(dev);
+ return ERR_PTR(err);
+ }
+
+ return dev;
+}
+
+static void m147lance_writerap(struct lance_private *lp, unsigned short value)
+{
+ out_be16(lp->base + LANCE_RAP, value);
+}
+
+static void m147lance_writerdp(struct lance_private *lp, unsigned short value)
+{
+ out_be16(lp->base + LANCE_RDP, value);
+}
+
+static unsigned short m147lance_readrdp(struct lance_private *lp)
+{
+ return in_be16(lp->base + LANCE_RDP);
+}
+
+static int m147lance_open(struct net_device *dev)
+{
+ int status;
+
+ status = lance_open(dev); /* call generic lance open code */
+ if (status)
+ return status;
+ /* enable interrupts at board level. */
+ m147_pcc->lan_cntrl = 0; /* clear the interrupts (if any) */
+ m147_pcc->lan_cntrl = 0x08 | 0x04; /* Enable irq 4 */
+
+ return 0;
+}
+
+static int m147lance_close(struct net_device *dev)
+{
+ /* disable interrupts at boardlevel */
+ m147_pcc->lan_cntrl = 0x0; /* disable interrupts */
+ lance_close(dev);
+ return 0;
+}
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+
+static struct net_device *dev_mvme147_lance;
+int __init init_module(void)
+{
+ dev_mvme147_lance = mvme147lance_probe(-1);
+ return PTR_ERR_OR_ZERO(dev_mvme147_lance);
+}
+
+void __exit cleanup_module(void)
+{
+ struct m147lance_private *lp = netdev_priv(dev_mvme147_lance);
+ unregister_netdev(dev_mvme147_lance);
+ free_pages(lp->ram, 3);
+ free_netdev(dev_mvme147_lance);
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/amd/ni65.c b/drivers/net/ethernet/amd/ni65.c
new file mode 100644
index 00000000000..1cf33addd15
--- /dev/null
+++ b/drivers/net/ethernet/amd/ni65.c
@@ -0,0 +1,1252 @@
+/*
+ * ni6510 (am7990 'lance' chip) driver for Linux-net-3
+ * BETAcode v0.71 (96/09/29) for 2.0.0 (or later)
+ * copyrights (c) 1994,1995,1996 by M.Hipp
+ *
+ * This driver can handle the old ni6510 board and the newer ni6510
+ * EtherBlaster. (probably it also works with every full NE2100
+ * compatible card)
+ *
+ * driver probes: io: 0x360,0x300,0x320,0x340 / dma: 3,5,6,7
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same GNU General Public License that covers the Linux-kernel.
+ *
+ * comments/bugs/suggestions can be sent to:
+ * Michael Hipp
+ * email: hippm@informatik.uni-tuebingen.de
+ *
+ * sources:
+ * some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
+ * and from the original drivers by D.Becker
+ *
+ * known problems:
+ * - on some PCI boards (including my own) the card/board/ISA-bridge has
+ * problems with bus master DMA. This results in lotsa overruns.
+ * It may help to '#define RCV_PARANOIA_CHECK' or try to #undef
+ * the XMT and RCV_VIA_SKB option .. this reduces driver performance.
+ * Or just play with your BIOS options to optimize ISA-DMA access.
+ * Maybe you also wanna play with the LOW_PERFORAMCE and MID_PERFORMANCE
+ * defines -> please report me your experience then
+ * - Harald reported for ASUS SP3G mainboards, that you should use
+ * the 'optimal settings' from the user's manual on page 3-12!
+ *
+ * credits:
+ * thanx to Jason Sullivan for sending me a ni6510 card!
+ * lot of debug runs with ASUS SP3G Boards (Intel Saturn) by Harald Koenig
+ *
+ * simple performance test: (486DX-33/Ni6510-EB receives from 486DX4-100/Ni6510-EB)
+ * average: FTP -> 8384421 bytes received in 8.5 seconds
+ * (no RCV_VIA_SKB,no XMT_VIA_SKB,PARANOIA_CHECK,4 XMIT BUFS, 8 RCV_BUFFS)
+ * peak: FTP -> 8384421 bytes received in 7.5 seconds
+ * (RCV_VIA_SKB,XMT_VIA_SKB,no PARANOIA_CHECK,1(!) XMIT BUF, 16 RCV BUFFS)
+ */
+
+/*
+ * 99.Jun.8: added support for /proc/net/dev byte count for xosview (HK)
+ * 96.Sept.29: virt_to_bus stuff added for new memory modell
+ * 96.April.29: Added Harald Koenig's Patches (MH)
+ * 96.April.13: enhanced error handling .. more tests (MH)
+ * 96.April.5/6: a lot of performance tests. Got it stable now (hopefully) (MH)
+ * 96.April.1: (no joke ;) .. added EtherBlaster and Module support (MH)
+ * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66 (MH)
+ * hopefully no more 16MB limit
+ *
+ * 95.Nov.18: multicast tweaked (AC).
+ *
+ * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ *
+ * 94.July.16: fixed bugs in recv_skb and skb-alloc stuff (MH)
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ni65.h"
+
+/*
+ * the current setting allows an acceptable performance
+ * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in
+ * the header of this file
+ * 'invert' the defines for max. performance. This may cause DMA problems
+ * on some boards (e.g on my ASUS SP3G)
+ */
+#undef XMT_VIA_SKB
+#undef RCV_VIA_SKB
+#define RCV_PARANOIA_CHECK
+
+#define MID_PERFORMANCE
+
+#if defined( LOW_PERFORMANCE )
+ static int isa0=7,isa1=7,csr80=0x0c10;
+#elif defined( MID_PERFORMANCE )
+ static int isa0=5,isa1=5,csr80=0x2810;
+#else /* high performance */
+ static int isa0=4,isa1=4,csr80=0x0017;
+#endif
+
+/*
+ * a few card/vendor specific defines
+ */
+#define NI65_ID0 0x00
+#define NI65_ID1 0x55
+#define NI65_EB_ID0 0x52
+#define NI65_EB_ID1 0x44
+#define NE2100_ID0 0x57
+#define NE2100_ID1 0x57
+
+#define PORT p->cmdr_addr
+
+/*
+ * buffer configuration
+ */
+#if 1
+#define RMDNUM 16
+#define RMDNUMMASK 0x80000000
+#else
+#define RMDNUM 8
+#define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */
+#endif
+
+#if 0
+#define TMDNUM 1
+#define TMDNUMMASK 0x00000000
+#else
+#define TMDNUM 4
+#define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */
+#endif
+
+/* slightly oversized */
+#define R_BUF_SIZE 1544
+#define T_BUF_SIZE 1544
+
+/*
+ * lance register defines
+ */
+#define L_DATAREG 0x00
+#define L_ADDRREG 0x02
+#define L_RESET 0x04
+#define L_CONFIG 0x05
+#define L_BUSIF 0x06
+
+/*
+ * to access the lance/am7990-regs, you have to write
+ * reg-number into L_ADDRREG, then you can access it using L_DATAREG
+ */
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+#define INIT_RING_BEFORE_START 0x1
+#define FULL_RESET_ON_ERROR 0x2
+
+#if 0
+#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
+ outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
+ inw(PORT+L_DATAREG))
+#if 0
+#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+#else
+#define writedatareg(val) { writereg(val,CSR0); }
+#endif
+#else
+#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);outw(val,PORT+L_DATAREG);}
+#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_DATAREG))
+#define writedatareg(val) { writereg(val,CSR0); }
+#endif
+
+static unsigned char ni_vendor[] = { 0x02,0x07,0x01 };
+
+static struct card {
+ unsigned char id0,id1;
+ short id_offset;
+ short total_size;
+ short cmd_offset;
+ short addr_offset;
+ unsigned char *vendor_id;
+ char *cardname;
+ unsigned long config;
+} cards[] = {
+ {
+ .id0 = NI65_ID0,
+ .id1 = NI65_ID1,
+ .id_offset = 0x0e,
+ .total_size = 0x10,
+ .cmd_offset = 0x0,
+ .addr_offset = 0x8,
+ .vendor_id = ni_vendor,
+ .cardname = "ni6510",
+ .config = 0x1,
+ },
+ {
+ .id0 = NI65_EB_ID0,
+ .id1 = NI65_EB_ID1,
+ .id_offset = 0x0e,
+ .total_size = 0x18,
+ .cmd_offset = 0x10,
+ .addr_offset = 0x0,
+ .vendor_id = ni_vendor,
+ .cardname = "ni6510 EtherBlaster",
+ .config = 0x2,
+ },
+ {
+ .id0 = NE2100_ID0,
+ .id1 = NE2100_ID1,
+ .id_offset = 0x0e,
+ .total_size = 0x18,
+ .cmd_offset = 0x10,
+ .addr_offset = 0x0,
+ .vendor_id = NULL,
+ .cardname = "generic NE2100",
+ .config = 0x0,
+ },
+};
+#define NUM_CARDS 3
+
+struct priv
+{
+ struct rmd rmdhead[RMDNUM];
+ struct tmd tmdhead[TMDNUM];
+ struct init_block ib;
+ int rmdnum;
+ int tmdnum,tmdlast;
+#ifdef RCV_VIA_SKB
+ struct sk_buff *recv_skb[RMDNUM];
+#else
+ void *recvbounce[RMDNUM];
+#endif
+#ifdef XMT_VIA_SKB
+ struct sk_buff *tmd_skb[TMDNUM];
+#endif
+ void *tmdbounce[TMDNUM];
+ int tmdbouncenum;
+ int lock,xmit_queued;
+
+ void *self;
+ int cmdr_addr;
+ int cardno;
+ int features;
+ spinlock_t ring_lock;
+};
+
+static int ni65_probe1(struct net_device *dev,int);
+static irqreturn_t ni65_interrupt(int irq, void * dev_id);
+static void ni65_recv_intr(struct net_device *dev,int);
+static void ni65_xmit_intr(struct net_device *dev,int);
+static int ni65_open(struct net_device *dev);
+static int ni65_lance_reinit(struct net_device *dev);
+static void ni65_init_lance(struct priv *p,unsigned char*,int,int);
+static netdev_tx_t ni65_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
+static void ni65_timeout(struct net_device *dev);
+static int ni65_close(struct net_device *dev);
+static int ni65_alloc_buffer(struct net_device *dev);
+static void ni65_free_buffer(struct priv *p);
+static void set_multicast_list(struct net_device *dev);
+
+static int irqtab[] __initdata = { 9,12,15,5 }; /* irq config-translate */
+static int dmatab[] __initdata = { 0,3,5,6,7 }; /* dma config-translate and autodetect */
+
+static int debuglevel = 1;
+
+/*
+ * set 'performance' registers .. we must STOP lance for that
+ */
+static void ni65_set_performance(struct priv *p)
+{
+ writereg(CSR0_STOP | CSR0_CLRALL,CSR0); /* STOP */
+
+ if( !(cards[p->cardno].config & 0x02) )
+ return;
+
+ outw(80,PORT+L_ADDRREG);
+ if(inw(PORT+L_ADDRREG) != 80)
+ return;
+
+ writereg( (csr80 & 0x3fff) ,80); /* FIFO watermarks */
+ outw(0,PORT+L_ADDRREG);
+ outw((short)isa0,PORT+L_BUSIF); /* write ISA 0: DMA_R : isa0 * 50ns */
+ outw(1,PORT+L_ADDRREG);
+ outw((short)isa1,PORT+L_BUSIF); /* write ISA 1: DMA_W : isa1 * 50ns */
+
+ outw(CSR0,PORT+L_ADDRREG); /* switch back to CSR0 */
+}
+
+/*
+ * open interface (up)
+ */
+static int ni65_open(struct net_device *dev)
+{
+ struct priv *p = dev->ml_priv;
+ int irqval = request_irq(dev->irq, ni65_interrupt,0,
+ cards[p->cardno].cardname,dev);
+ if (irqval) {
+ printk(KERN_ERR "%s: unable to get IRQ %d (irqval=%d).\n",
+ dev->name,dev->irq, irqval);
+ return -EAGAIN;
+ }
+
+ if(ni65_lance_reinit(dev))
+ {
+ netif_start_queue(dev);
+ return 0;
+ }
+ else
+ {
+ free_irq(dev->irq,dev);
+ return -EAGAIN;
+ }
+}
+
+/*
+ * close interface (down)
+ */
+static int ni65_close(struct net_device *dev)
+{
+ struct priv *p = dev->ml_priv;
+
+ netif_stop_queue(dev);
+
+ outw(inw(PORT+L_RESET),PORT+L_RESET); /* that's the hard way */
+
+#ifdef XMT_VIA_SKB
+ {
+ int i;
+ for(i=0;i<TMDNUM;i++)
+ {
+ if(p->tmd_skb[i]) {
+ dev_kfree_skb(p->tmd_skb[i]);
+ p->tmd_skb[i] = NULL;
+ }
+ }
+ }
+#endif
+ free_irq(dev->irq,dev);
+ return 0;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ struct priv *p = dev->ml_priv;
+ disable_dma(dev->dma);
+ free_dma(dev->dma);
+ release_region(dev->base_addr, cards[p->cardno].total_size);
+ ni65_free_buffer(p);
+}
+
+/* set: io,irq,dma or set it when calling insmod */
+static int irq;
+static int io;
+static int dma;
+
+/*
+ * Probe The Card (not the lance-chip)
+ */
+struct net_device * __init ni65_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(0);
+ static const int ports[] = { 0x360, 0x300, 0x320, 0x340, 0 };
+ const int *port;
+ int err = 0;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ irq = dev->irq;
+ dma = dev->dma;
+ } else {
+ dev->base_addr = io;
+ }
+
+ if (dev->base_addr > 0x1ff) { /* Check a single specified location. */
+ err = ni65_probe1(dev, dev->base_addr);
+ } else if (dev->base_addr > 0) { /* Don't probe at all. */
+ err = -ENXIO;
+ } else {
+ for (port = ports; *port && ni65_probe1(dev, *port); port++)
+ ;
+ if (!*port)
+ err = -ENODEV;
+ }
+ if (err)
+ goto out;
+
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ return dev;
+out1:
+ cleanup_card(dev);
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static const struct net_device_ops ni65_netdev_ops = {
+ .ndo_open = ni65_open,
+ .ndo_stop = ni65_close,
+ .ndo_start_xmit = ni65_send_packet,
+ .ndo_tx_timeout = ni65_timeout,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*
+ * this is the real card probe ..
+ */
+static int __init ni65_probe1(struct net_device *dev,int ioaddr)
+{
+ int i,j;
+ struct priv *p;
+ unsigned long flags;
+
+ dev->irq = irq;
+ dev->dma = dma;
+
+ for(i=0;i<NUM_CARDS;i++) {
+ if(!request_region(ioaddr, cards[i].total_size, cards[i].cardname))
+ continue;
+ if(cards[i].id_offset >= 0) {
+ if(inb(ioaddr+cards[i].id_offset+0) != cards[i].id0 ||
+ inb(ioaddr+cards[i].id_offset+1) != cards[i].id1) {
+ release_region(ioaddr, cards[i].total_size);
+ continue;
+ }
+ }
+ if(cards[i].vendor_id) {
+ for(j=0;j<3;j++)
+ if(inb(ioaddr+cards[i].addr_offset+j) != cards[i].vendor_id[j]) {
+ release_region(ioaddr, cards[i].total_size);
+ continue;
+ }
+ }
+ break;
+ }
+ if(i == NUM_CARDS)
+ return -ENODEV;
+
+ for(j=0;j<6;j++)
+ dev->dev_addr[j] = inb(ioaddr+cards[i].addr_offset+j);
+
+ if( (j=ni65_alloc_buffer(dev)) < 0) {
+ release_region(ioaddr, cards[i].total_size);
+ return j;
+ }
+ p = dev->ml_priv;
+ p->cmdr_addr = ioaddr + cards[i].cmd_offset;
+ p->cardno = i;
+ spin_lock_init(&p->ring_lock);
+
+ printk(KERN_INFO "%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr);
+
+ outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */
+ if( (j=readreg(CSR0)) != 0x4) {
+ printk("failed.\n");
+ printk(KERN_ERR "%s: Can't RESET card: %04x\n", dev->name, j);
+ ni65_free_buffer(p);
+ release_region(ioaddr, cards[p->cardno].total_size);
+ return -EAGAIN;
+ }
+
+ outw(88,PORT+L_ADDRREG);
+ if(inw(PORT+L_ADDRREG) == 88) {
+ unsigned long v;
+ v = inw(PORT+L_DATAREG);
+ v <<= 16;
+ outw(89,PORT+L_ADDRREG);
+ v |= inw(PORT+L_DATAREG);
+ printk("Version %#08lx, ",v);
+ p->features = INIT_RING_BEFORE_START;
+ }
+ else {
+ printk("ancient LANCE, ");
+ p->features = 0x0;
+ }
+
+ if(test_bit(0,&cards[i].config)) {
+ dev->irq = irqtab[(inw(ioaddr+L_CONFIG)>>2)&3];
+ dev->dma = dmatab[inw(ioaddr+L_CONFIG)&3];
+ printk("IRQ %d (from card), DMA %d (from card).\n",dev->irq,dev->dma);
+ }
+ else {
+ if(dev->dma == 0) {
+ /* 'stuck test' from lance.c */
+ unsigned long dma_channels =
+ ((inb(DMA1_STAT_REG) >> 4) & 0x0f)
+ | (inb(DMA2_STAT_REG) & 0xf0);
+ for(i=1;i<5;i++) {
+ int dma = dmatab[i];
+ if(test_bit(dma,&dma_channels) || request_dma(dma,"ni6510"))
+ continue;
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ set_dma_mode(dma,DMA_MODE_CASCADE);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ ni65_init_lance(p,dev->dev_addr,0,0); /* trigger memory access */
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ free_dma(dma);
+ release_dma_lock(flags);
+
+ if(readreg(CSR0) & CSR0_IDON)
+ break;
+ }
+ if(i == 5) {
+ printk("failed.\n");
+ printk(KERN_ERR "%s: Can't detect DMA channel!\n", dev->name);
+ ni65_free_buffer(p);
+ release_region(ioaddr, cards[p->cardno].total_size);
+ return -EAGAIN;
+ }
+ dev->dma = dmatab[i];
+ printk("DMA %d (autodetected), ",dev->dma);
+ }
+ else
+ printk("DMA %d (assigned), ",dev->dma);
+
+ if(dev->irq < 2)
+ {
+ unsigned long irq_mask;
+
+ ni65_init_lance(p,dev->dev_addr,0,0);
+ irq_mask = probe_irq_on();
+ writereg(CSR0_INIT|CSR0_INEA,CSR0); /* trigger interrupt */
+ msleep(20);
+ dev->irq = probe_irq_off(irq_mask);
+ if(!dev->irq)
+ {
+ printk("Failed to detect IRQ line!\n");
+ ni65_free_buffer(p);
+ release_region(ioaddr, cards[p->cardno].total_size);
+ return -EAGAIN;
+ }
+ printk("IRQ %d (autodetected).\n",dev->irq);
+ }
+ else
+ printk("IRQ %d (assigned).\n",dev->irq);
+ }
+
+ if(request_dma(dev->dma, cards[p->cardno].cardname ) != 0)
+ {
+ printk(KERN_ERR "%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
+ ni65_free_buffer(p);
+ release_region(ioaddr, cards[p->cardno].total_size);
+ return -EAGAIN;
+ }
+
+ dev->base_addr = ioaddr;
+ dev->netdev_ops = &ni65_netdev_ops;
+ dev->watchdog_timeo = HZ/2;
+
+ return 0; /* everything is OK */
+}
+
+/*
+ * set lance register and trigger init
+ */
+static void ni65_init_lance(struct priv *p,unsigned char *daddr,int filter,int mode)
+{
+ int i;
+ u32 pib;
+
+ writereg(CSR0_CLRALL|CSR0_STOP,CSR0);
+
+ for(i=0;i<6;i++)
+ p->ib.eaddr[i] = daddr[i];
+
+ for(i=0;i<8;i++)
+ p->ib.filter[i] = filter;
+ p->ib.mode = mode;
+
+ p->ib.trp = (u32) isa_virt_to_bus(p->tmdhead) | TMDNUMMASK;
+ p->ib.rrp = (u32) isa_virt_to_bus(p->rmdhead) | RMDNUMMASK;
+ writereg(0,CSR3); /* busmaster/no word-swap */
+ pib = (u32) isa_virt_to_bus(&p->ib);
+ writereg(pib & 0xffff,CSR1);
+ writereg(pib >> 16,CSR2);
+
+ writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */
+
+ for(i=0;i<32;i++)
+ {
+ mdelay(4);
+ if(inw(PORT+L_DATAREG) & (CSR0_IDON | CSR0_MERR) )
+ break; /* init ok ? */
+ }
+}
+
+/*
+ * allocate memory area and check the 16MB border
+ */
+static void *ni65_alloc_mem(struct net_device *dev,char *what,int size,int type)
+{
+ struct sk_buff *skb=NULL;
+ unsigned char *ptr;
+ void *ret;
+
+ if(type) {
+ ret = skb = alloc_skb(2+16+size,GFP_KERNEL|GFP_DMA);
+ if(!skb) {
+ printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what);
+ return NULL;
+ }
+ skb_reserve(skb,2+16);
+ skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */
+ ptr = skb->data;
+ }
+ else {
+ ret = ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA);
+ if(!ret)
+ return NULL;
+ }
+ if( (u32) virt_to_phys(ptr+size) > 0x1000000) {
+ printk(KERN_WARNING "%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what);
+ if(type)
+ kfree_skb(skb);
+ else
+ kfree(ptr);
+ return NULL;
+ }
+ return ret;
+}
+
+/*
+ * allocate all memory structures .. send/recv buffers etc ...
+ */
+static int ni65_alloc_buffer(struct net_device *dev)
+{
+ unsigned char *ptr;
+ struct priv *p;
+ int i;
+
+ /*
+ * we need 8-aligned memory ..
+ */
+ ptr = ni65_alloc_mem(dev,"BUFFER",sizeof(struct priv)+8,0);
+ if(!ptr)
+ return -ENOMEM;
+
+ p = dev->ml_priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7);
+ memset((char *)p, 0, sizeof(struct priv));
+ p->self = ptr;
+
+ for(i=0;i<TMDNUM;i++)
+ {
+#ifdef XMT_VIA_SKB
+ p->tmd_skb[i] = NULL;
+#endif
+ p->tmdbounce[i] = ni65_alloc_mem(dev,"XMIT",T_BUF_SIZE,0);
+ if(!p->tmdbounce[i]) {
+ ni65_free_buffer(p);
+ return -ENOMEM;
+ }
+ }
+
+ for(i=0;i<RMDNUM;i++)
+ {
+#ifdef RCV_VIA_SKB
+ p->recv_skb[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,1);
+ if(!p->recv_skb[i]) {
+ ni65_free_buffer(p);
+ return -ENOMEM;
+ }
+#else
+ p->recvbounce[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,0);
+ if(!p->recvbounce[i]) {
+ ni65_free_buffer(p);
+ return -ENOMEM;
+ }
+#endif
+ }
+
+ return 0; /* everything is OK */
+}
+
+/*
+ * free buffers and private struct
+ */
+static void ni65_free_buffer(struct priv *p)
+{
+ int i;
+
+ if(!p)
+ return;
+
+ for(i=0;i<TMDNUM;i++) {
+ kfree(p->tmdbounce[i]);
+#ifdef XMT_VIA_SKB
+ if(p->tmd_skb[i])
+ dev_kfree_skb(p->tmd_skb[i]);
+#endif
+ }
+
+ for(i=0;i<RMDNUM;i++)
+ {
+#ifdef RCV_VIA_SKB
+ if(p->recv_skb[i])
+ dev_kfree_skb(p->recv_skb[i]);
+#else
+ kfree(p->recvbounce[i]);
+#endif
+ }
+ kfree(p->self);
+}
+
+
+/*
+ * stop and (re)start lance .. e.g after an error
+ */
+static void ni65_stop_start(struct net_device *dev,struct priv *p)
+{
+ int csr0 = CSR0_INEA;
+
+ writedatareg(CSR0_STOP);
+
+ if(debuglevel > 1)
+ printk(KERN_DEBUG "ni65_stop_start\n");
+
+ if(p->features & INIT_RING_BEFORE_START) {
+ int i;
+#ifdef XMT_VIA_SKB
+ struct sk_buff *skb_save[TMDNUM];
+#endif
+ unsigned long buffer[TMDNUM];
+ short blen[TMDNUM];
+
+ if(p->xmit_queued) {
+ while(1) {
+ if((p->tmdhead[p->tmdlast].u.s.status & XMIT_OWN))
+ break;
+ p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
+ if(p->tmdlast == p->tmdnum)
+ break;
+ }
+ }
+
+ for(i=0;i<TMDNUM;i++) {
+ struct tmd *tmdp = p->tmdhead + i;
+#ifdef XMT_VIA_SKB
+ skb_save[i] = p->tmd_skb[i];
+#endif
+ buffer[i] = (u32) isa_bus_to_virt(tmdp->u.buffer);
+ blen[i] = tmdp->blen;
+ tmdp->u.s.status = 0x0;
+ }
+
+ for(i=0;i<RMDNUM;i++) {
+ struct rmd *rmdp = p->rmdhead + i;
+ rmdp->u.s.status = RCV_OWN;
+ }
+ p->tmdnum = p->xmit_queued = 0;
+ writedatareg(CSR0_STRT | csr0);
+
+ for(i=0;i<TMDNUM;i++) {
+ int num = (i + p->tmdlast) & (TMDNUM-1);
+ p->tmdhead[i].u.buffer = (u32) isa_virt_to_bus((char *)buffer[num]); /* status is part of buffer field */
+ p->tmdhead[i].blen = blen[num];
+ if(p->tmdhead[i].u.s.status & XMIT_OWN) {
+ p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);
+ p->xmit_queued = 1;
+ writedatareg(CSR0_TDMD | CSR0_INEA | csr0);
+ }
+#ifdef XMT_VIA_SKB
+ p->tmd_skb[i] = skb_save[num];
+#endif
+ }
+ p->rmdnum = p->tmdlast = 0;
+ if(!p->lock)
+ if (p->tmdnum || !p->xmit_queued)
+ netif_wake_queue(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ }
+ else
+ writedatareg(CSR0_STRT | csr0);
+}
+
+/*
+ * init lance (write init-values .. init-buffers) (open-helper)
+ */
+static int ni65_lance_reinit(struct net_device *dev)
+{
+ int i;
+ struct priv *p = dev->ml_priv;
+ unsigned long flags;
+
+ p->lock = 0;
+ p->xmit_queued = 0;
+
+ flags=claim_dma_lock();
+ disable_dma(dev->dma); /* I've never worked with dma, but we do it like the packetdriver */
+ set_dma_mode(dev->dma,DMA_MODE_CASCADE);
+ enable_dma(dev->dma);
+ release_dma_lock(flags);
+
+ outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */
+ if( (i=readreg(CSR0) ) != 0x4)
+ {
+ printk(KERN_ERR "%s: can't RESET %s card: %04x\n",dev->name,
+ cards[p->cardno].cardname,(int) i);
+ flags=claim_dma_lock();
+ disable_dma(dev->dma);
+ release_dma_lock(flags);
+ return 0;
+ }
+
+ p->rmdnum = p->tmdnum = p->tmdlast = p->tmdbouncenum = 0;
+ for(i=0;i<TMDNUM;i++)
+ {
+ struct tmd *tmdp = p->tmdhead + i;
+#ifdef XMT_VIA_SKB
+ if(p->tmd_skb[i]) {
+ dev_kfree_skb(p->tmd_skb[i]);
+ p->tmd_skb[i] = NULL;
+ }
+#endif
+ tmdp->u.buffer = 0x0;
+ tmdp->u.s.status = XMIT_START | XMIT_END;
+ tmdp->blen = tmdp->status2 = 0;
+ }
+
+ for(i=0;i<RMDNUM;i++)
+ {
+ struct rmd *rmdp = p->rmdhead + i;
+#ifdef RCV_VIA_SKB
+ rmdp->u.buffer = (u32) isa_virt_to_bus(p->recv_skb[i]->data);
+#else
+ rmdp->u.buffer = (u32) isa_virt_to_bus(p->recvbounce[i]);
+#endif
+ rmdp->blen = -(R_BUF_SIZE-8);
+ rmdp->mlen = 0;
+ rmdp->u.s.status = RCV_OWN;
+ }
+
+ if(dev->flags & IFF_PROMISC)
+ ni65_init_lance(p,dev->dev_addr,0x00,M_PROM);
+ else if (netdev_mc_count(dev) || dev->flags & IFF_ALLMULTI)
+ ni65_init_lance(p,dev->dev_addr,0xff,0x0);
+ else
+ ni65_init_lance(p,dev->dev_addr,0x00,0x00);
+
+ /*
+ * ni65_set_lance_mem() sets L_ADDRREG to CSR0
+ * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED
+ */
+
+ if(inw(PORT+L_DATAREG) & CSR0_IDON) {
+ ni65_set_performance(p);
+ /* init OK: start lance , enable interrupts */
+ writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT);
+ return 1; /* ->OK */
+ }
+ printk(KERN_ERR "%s: can't init lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ flags=claim_dma_lock();
+ disable_dma(dev->dma);
+ release_dma_lock(flags);
+ return 0; /* ->Error */
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t ni65_interrupt(int irq, void * dev_id)
+{
+ int csr0 = 0;
+ struct net_device *dev = dev_id;
+ struct priv *p;
+ int bcnt = 32;
+
+ p = dev->ml_priv;
+
+ spin_lock(&p->ring_lock);
+
+ while(--bcnt) {
+ csr0 = inw(PORT+L_DATAREG);
+
+#if 0
+ writedatareg( (csr0 & CSR0_CLRALL) ); /* ack interrupts, disable int. */
+#else
+ writedatareg( (csr0 & CSR0_CLRALL) | CSR0_INEA ); /* ack interrupts, interrupts enabled */
+#endif
+
+ if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT)))
+ break;
+
+ if(csr0 & CSR0_RINT) /* RECV-int? */
+ ni65_recv_intr(dev,csr0);
+ if(csr0 & CSR0_TINT) /* XMIT-int? */
+ ni65_xmit_intr(dev,csr0);
+
+ if(csr0 & CSR0_ERR)
+ {
+ if(debuglevel > 1)
+ printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0);
+ if(csr0 & CSR0_BABL)
+ dev->stats.tx_errors++;
+ if(csr0 & CSR0_MISS) {
+ int i;
+ for(i=0;i<RMDNUM;i++)
+ printk("%02x ",p->rmdhead[i].u.s.status);
+ printk("\n");
+ dev->stats.rx_errors++;
+ }
+ if(csr0 & CSR0_MERR) {
+ if(debuglevel > 1)
+ printk(KERN_ERR "%s: Ooops .. memory error: %04x.\n",dev->name,csr0);
+ ni65_stop_start(dev,p);
+ }
+ }
+ }
+
+#ifdef RCV_PARANOIA_CHECK
+{
+ int j;
+ for(j=0;j<RMDNUM;j++)
+ {
+ int i, num2;
+ for(i=RMDNUM-1;i>0;i--) {
+ num2 = (p->rmdnum + i) & (RMDNUM-1);
+ if(!(p->rmdhead[num2].u.s.status & RCV_OWN))
+ break;
+ }
+
+ if(i) {
+ int k, num1;
+ for(k=0;k<RMDNUM;k++) {
+ num1 = (p->rmdnum + k) & (RMDNUM-1);
+ if(!(p->rmdhead[num1].u.s.status & RCV_OWN))
+ break;
+ }
+ if(!k)
+ break;
+
+ if(debuglevel > 0)
+ {
+ char buf[256],*buf1;
+ buf1 = buf;
+ for(k=0;k<RMDNUM;k++) {
+ sprintf(buf1,"%02x ",(p->rmdhead[k].u.s.status)); /* & RCV_OWN) ); */
+ buf1 += 3;
+ }
+ *buf1 = 0;
+ printk(KERN_ERR "%s: Ooops, receive ring corrupted %2d %2d | %s\n",dev->name,p->rmdnum,i,buf);
+ }
+
+ p->rmdnum = num1;
+ ni65_recv_intr(dev,csr0);
+ if((p->rmdhead[num2].u.s.status & RCV_OWN))
+ break; /* ok, we are 'in sync' again */
+ }
+ else
+ break;
+ }
+}
+#endif
+
+ if( (csr0 & (CSR0_RXON | CSR0_TXON)) != (CSR0_RXON | CSR0_TXON) ) {
+ printk(KERN_DEBUG "%s: RX or TX was offline -> restart\n",dev->name);
+ ni65_stop_start(dev,p);
+ }
+ else
+ writedatareg(CSR0_INEA);
+
+ spin_unlock(&p->ring_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * We have received an Xmit-Interrupt ..
+ * send a new packet if necessary
+ */
+static void ni65_xmit_intr(struct net_device *dev,int csr0)
+{
+ struct priv *p = dev->ml_priv;
+
+ while(p->xmit_queued)
+ {
+ struct tmd *tmdp = p->tmdhead + p->tmdlast;
+ int tmdstat = tmdp->u.s.status;
+
+ if(tmdstat & XMIT_OWN)
+ break;
+
+ if(tmdstat & XMIT_ERR)
+ {
+#if 0
+ if(tmdp->status2 & XMIT_TDRMASK && debuglevel > 3)
+ printk(KERN_ERR "%s: tdr-problems (e.g. no resistor)\n",dev->name);
+#endif
+ /* checking some errors */
+ if(tmdp->status2 & XMIT_RTRY)
+ dev->stats.tx_aborted_errors++;
+ if(tmdp->status2 & XMIT_LCAR)
+ dev->stats.tx_carrier_errors++;
+ if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
+ /* this stops the xmitter */
+ dev->stats.tx_fifo_errors++;
+ if(debuglevel > 0)
+ printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
+ if(p->features & INIT_RING_BEFORE_START) {
+ tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END; /* test: resend this frame */
+ ni65_stop_start(dev,p);
+ break; /* no more Xmit processing .. */
+ }
+ else
+ ni65_stop_start(dev,p);
+ }
+ if(debuglevel > 2)
+ printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
+ if(!(csr0 & CSR0_BABL)) /* don't count errors twice */
+ dev->stats.tx_errors++;
+ tmdp->status2 = 0;
+ }
+ else {
+ dev->stats.tx_bytes -= (short)(tmdp->blen);
+ dev->stats.tx_packets++;
+ }
+
+#ifdef XMT_VIA_SKB
+ if(p->tmd_skb[p->tmdlast]) {
+ dev_kfree_skb_irq(p->tmd_skb[p->tmdlast]);
+ p->tmd_skb[p->tmdlast] = NULL;
+ }
+#endif
+
+ p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
+ if(p->tmdlast == p->tmdnum)
+ p->xmit_queued = 0;
+ }
+ netif_wake_queue(dev);
+}
+
+/*
+ * We have received a packet
+ */
+static void ni65_recv_intr(struct net_device *dev,int csr0)
+{
+ struct rmd *rmdp;
+ int rmdstat,len;
+ int cnt=0;
+ struct priv *p = dev->ml_priv;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
+ {
+ cnt++;
+ if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */
+ {
+ if(!(rmdstat & RCV_ERR)) {
+ if(rmdstat & RCV_START)
+ {
+ dev->stats.rx_length_errors++;
+ printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
+ }
+ }
+ else {
+ if(debuglevel > 2)
+ printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
+ dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
+ if(rmdstat & RCV_FRAM)
+ dev->stats.rx_frame_errors++;
+ if(rmdstat & RCV_OFLO)
+ dev->stats.rx_over_errors++;
+ if(rmdstat & RCV_CRC)
+ dev->stats.rx_crc_errors++;
+ if(rmdstat & RCV_BUF_ERR)
+ dev->stats.rx_fifo_errors++;
+ }
+ if(!(csr0 & CSR0_MISS)) /* don't count errors twice */
+ dev->stats.rx_errors++;
+ }
+ else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
+ {
+#ifdef RCV_VIA_SKB
+ struct sk_buff *skb = alloc_skb(R_BUF_SIZE+2+16,GFP_ATOMIC);
+ if (skb)
+ skb_reserve(skb,16);
+#else
+ struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
+#endif
+ if(skb)
+ {
+ skb_reserve(skb,2);
+#ifdef RCV_VIA_SKB
+ if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
+ skb_put(skb,len);
+ skb_copy_to_linear_data(skb, (unsigned char *)(p->recv_skb[p->rmdnum]->data),len);
+ }
+ else {
+ struct sk_buff *skb1 = p->recv_skb[p->rmdnum];
+ skb_put(skb,R_BUF_SIZE);
+ p->recv_skb[p->rmdnum] = skb;
+ rmdp->u.buffer = (u32) isa_virt_to_bus(skb->data);
+ skb = skb1;
+ skb_trim(skb,len);
+ }
+#else
+ skb_put(skb,len);
+ skb_copy_to_linear_data(skb, (unsigned char *) p->recvbounce[p->rmdnum],len);
+#endif
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+ else
+ {
+ printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
+ dev->stats.rx_dropped++;
+ }
+ }
+ else {
+ printk(KERN_INFO "%s: received runt packet\n",dev->name);
+ dev->stats.rx_errors++;
+ }
+ rmdp->blen = -(R_BUF_SIZE-8);
+ rmdp->mlen = 0;
+ rmdp->u.s.status = RCV_OWN; /* change owner */
+ p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1);
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+}
+
+/*
+ * kick xmitter ..
+ */
+
+static void ni65_timeout(struct net_device *dev)
+{
+ int i;
+ struct priv *p = dev->ml_priv;
+
+ printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name);
+ for(i=0;i<TMDNUM;i++)
+ printk("%02x ",p->tmdhead[i].u.s.status);
+ printk("\n");
+ ni65_lance_reinit(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+/*
+ * Send a packet
+ */
+
+static netdev_tx_t ni65_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct priv *p = dev->ml_priv;
+
+ netif_stop_queue(dev);
+
+ if (test_and_set_bit(0, (void*)&p->lock)) {
+ printk(KERN_ERR "%s: Queue was locked.\n", dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ {
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ struct tmd *tmdp;
+ unsigned long flags;
+
+#ifdef XMT_VIA_SKB
+ if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
+#endif
+
+ skb_copy_from_linear_data(skb, p->tmdbounce[p->tmdbouncenum],
+ skb->len > T_BUF_SIZE ? T_BUF_SIZE :
+ skb->len);
+ if (len > skb->len)
+ memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len);
+ dev_kfree_skb (skb);
+
+ spin_lock_irqsave(&p->ring_lock, flags);
+ tmdp = p->tmdhead + p->tmdnum;
+ tmdp->u.buffer = (u32) isa_virt_to_bus(p->tmdbounce[p->tmdbouncenum]);
+ p->tmdbouncenum = (p->tmdbouncenum + 1) & (TMDNUM - 1);
+
+#ifdef XMT_VIA_SKB
+ }
+ else {
+ spin_lock_irqsave(&p->ring_lock, flags);
+
+ tmdp = p->tmdhead + p->tmdnum;
+ tmdp->u.buffer = (u32) isa_virt_to_bus(skb->data);
+ p->tmd_skb[p->tmdnum] = skb;
+ }
+#endif
+ tmdp->blen = -len;
+
+ tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
+ writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */
+
+ p->xmit_queued = 1;
+ p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);
+
+ if(p->tmdnum != p->tmdlast)
+ netif_wake_queue(dev);
+
+ p->lock = 0;
+
+ spin_unlock_irqrestore(&p->ring_lock, flags);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static void set_multicast_list(struct net_device *dev)
+{
+ if(!ni65_lance_reinit(dev))
+ printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name);
+ netif_wake_queue(dev);
+}
+
+#ifdef MODULE
+static struct net_device *dev_ni65;
+
+module_param(irq, int, 0);
+module_param(io, int, 0);
+module_param(dma, int, 0);
+MODULE_PARM_DESC(irq, "ni6510 IRQ number (ignored for some cards)");
+MODULE_PARM_DESC(io, "ni6510 I/O base address");
+MODULE_PARM_DESC(dma, "ni6510 ISA DMA channel (ignored for some cards)");
+
+int __init init_module(void)
+{
+ dev_ni65 = ni65_probe(-1);
+ return PTR_ERR_OR_ZERO(dev_ni65);
+}
+
+void __exit cleanup_module(void)
+{
+ unregister_netdev(dev_ni65);
+ cleanup_card(dev_ni65);
+ free_netdev(dev_ni65);
+}
+#endif /* MODULE */
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/amd/ni65.h b/drivers/net/ethernet/amd/ni65.h
new file mode 100644
index 00000000000..e6217e35edf
--- /dev/null
+++ b/drivers/net/ethernet/amd/ni65.h
@@ -0,0 +1,121 @@
+/* am7990 (lance) definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by
+ * same GNU General Public License that covers that work.
+ *
+ * Michael Hipp
+ * email: mhipp@student.uni-tuebingen.de
+ *
+ * sources: (mail me or ask archie if you need them)
+ * crynwr-packet-driver
+ */
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define M_PROM 0x8000 /* Promiscuous Mode */
+#define M_INTL 0x0040 /* Internal Loopback */
+#define M_DRTY 0x0020 /* Disable Retry */
+#define M_COLL 0x0010 /* Force Collision */
+#define M_DTCR 0x0008 /* Disable Transmit CRC) */
+#define M_LOOP 0x0004 /* Loopback */
+#define M_DTX 0x0002 /* Disable the Transmitter */
+#define M_DRX 0x0001 /* Disable the Receiver */
+
+
+/*
+ * Receive message descriptor bit definitions.
+ */
+
+#define RCV_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define RCV_ERR 0x40 /* Error Summary */
+#define RCV_FRAM 0x20 /* Framing Error */
+#define RCV_OFLO 0x10 /* Overflow Error */
+#define RCV_CRC 0x08 /* CRC Error */
+#define RCV_BUF_ERR 0x04 /* Buffer Error */
+#define RCV_START 0x02 /* Start of Packet */
+#define RCV_END 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor bit definitions.
+ */
+
+#define XMIT_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define XMIT_ERR 0x40 /* Error Summary */
+#define XMIT_RETRY 0x10 /* more the 1 retry needed to Xmit */
+#define XMIT_1_RETRY 0x08 /* one retry needed to Xmit */
+#define XMIT_DEF 0x04 /* Deferred */
+#define XMIT_START 0x02 /* Start of Packet */
+#define XMIT_END 0x01 /* End of Packet */
+
+/*
+ * transmit status (2) (valid if XMIT_ERR == 1)
+ */
+
+#define XMIT_TDRMASK 0x03ff /* time-domain-reflectometer-value */
+#define XMIT_RTRY 0x0400 /* Failed after 16 retransmissions */
+#define XMIT_LCAR 0x0800 /* Loss of Carrier */
+#define XMIT_LCOL 0x1000 /* Late collision */
+#define XMIT_RESERV 0x2000 /* Reserved */
+#define XMIT_UFLO 0x4000 /* Underflow (late memory) */
+#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */
+
+struct init_block {
+ unsigned short mode;
+ unsigned char eaddr[6];
+ unsigned char filter[8];
+ /* bit 29-31: number of rmd's (power of 2) */
+ u32 rrp; /* receive ring pointer (align 8) */
+ /* bit 29-31: number of tmd's (power of 2) */
+ u32 trp; /* transmit ring pointer (align 8) */
+};
+
+struct rmd { /* Receive Message Descriptor */
+ union {
+ volatile u32 buffer;
+ struct {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ volatile short blen;
+ volatile unsigned short mlen;
+};
+
+struct tmd {
+ union {
+ volatile u32 buffer;
+ struct {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ volatile unsigned short blen;
+ volatile unsigned short status2;
+};
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
new file mode 100644
index 00000000000..abf3b1581c8
--- /dev/null
+++ b/drivers/net/ethernet/amd/nmclan_cs.c
@@ -0,0 +1,1510 @@
+/* ----------------------------------------------------------------------------
+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 <asm/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;
+ struct net_device_stats linux_stats; /* Linux statistics counters */
+ 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);
+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_change_mtu = eth_change_mtu,
+ .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_exclusive_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)
+{
+ 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 */
+ dev->trans_start = jiffies; /* 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.
+ */
+
+ lp->linux_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);
+
+ 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) {
+ lp->linux_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) */
+
+ lp->linux_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. */
+ lp->linux_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. */
+
+ lp->linux_stats.rx_packets++;
+ lp->linux_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);
+ lp->linux_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 linux_stats. */
+
+ /* The MACE has no equivalent for linux_stats field which are commented
+ out. */
+
+ /* lp->linux_stats.multicast; */
+ lp->linux_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: */
+ lp->linux_stats.rx_length_errors =
+ lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
+ /* lp->linux_stats.rx_over_errors */
+ lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs;
+ lp->linux_stats.rx_frame_errors = lp->mace_stats.fram;
+ lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo;
+ lp->linux_stats.rx_missed_errors =
+ lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
+
+ /* detailed tx_errors */
+ lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry;
+ lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar;
+ /* LCAR usually results from bad cabling. */
+ lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo;
+ lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr;
+ /* lp->linux_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(&lp->linux_stats);
+ pr_mace_stats(&lp->mace_stats);
+
+ return &lp->linux_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);
diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c
new file mode 100644
index 00000000000..e7cc9174e36
--- /dev/null
+++ b/drivers/net/ethernet/amd/pcnet32.c
@@ -0,0 +1,2971 @@
+/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
+/*
+ * Copyright 1996-1999 Thomas Bogendoerfer
+ *
+ * Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This driver is for PCnet32 and PCnetPCI based ethercards
+ */
+/**************************************************************************
+ * 23 Oct, 2000.
+ * Fixed a few bugs, related to running the controller in 32bit mode.
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ *************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DRV_NAME "pcnet32"
+#define DRV_VERSION "1.35"
+#define DRV_RELDATE "21.Apr.2008"
+#define PFX DRV_NAME ": "
+
+static const char *const version =
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+/*
+ * PCI device identifiers for "new style" Linux PCI Device Drivers
+ */
+static DEFINE_PCI_DEVICE_TABLE(pcnet32_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME), },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE), },
+
+ /*
+ * Adapters that were sold with IBM's RS/6000 or pSeries hardware have
+ * the incorrect vendor id.
+ */
+ { PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE),
+ .class = (PCI_CLASS_NETWORK_ETHERNET << 8), .class_mask = 0xffff00, },
+
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, pcnet32_pci_tbl);
+
+static int cards_found;
+
+/*
+ * VLB I/O addresses
+ */
+static unsigned int pcnet32_portlist[] =
+ { 0x300, 0x320, 0x340, 0x360, 0 };
+
+static int pcnet32_debug;
+static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
+static int pcnet32vlb; /* check for VLB cards ? */
+
+static struct net_device *pcnet32_dev;
+
+static int max_interrupt_work = 2;
+static int rx_copybreak = 200;
+
+#define PCNET32_PORT_AUI 0x00
+#define PCNET32_PORT_10BT 0x01
+#define PCNET32_PORT_GPSI 0x02
+#define PCNET32_PORT_MII 0x03
+
+#define PCNET32_PORT_PORTSEL 0x03
+#define PCNET32_PORT_ASEL 0x04
+#define PCNET32_PORT_100 0x40
+#define PCNET32_PORT_FD 0x80
+
+#define PCNET32_DMA_MASK 0xffffffff
+
+#define PCNET32_WATCHDOG_TIMEOUT (jiffies + (2 * HZ))
+#define PCNET32_BLINK_TIMEOUT (jiffies + (HZ/4))
+
+/*
+ * table to translate option values from tulip
+ * to internal options
+ */
+static const unsigned char options_mapping[] = {
+ PCNET32_PORT_ASEL, /* 0 Auto-select */
+ PCNET32_PORT_AUI, /* 1 BNC/AUI */
+ PCNET32_PORT_AUI, /* 2 AUI/BNC */
+ PCNET32_PORT_ASEL, /* 3 not supported */
+ PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */
+ PCNET32_PORT_ASEL, /* 5 not supported */
+ PCNET32_PORT_ASEL, /* 6 not supported */
+ PCNET32_PORT_ASEL, /* 7 not supported */
+ PCNET32_PORT_ASEL, /* 8 not supported */
+ PCNET32_PORT_MII, /* 9 MII 10baseT */
+ PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */
+ PCNET32_PORT_MII, /* 11 MII (autosel) */
+ PCNET32_PORT_10BT, /* 12 10BaseT */
+ PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */
+ /* 14 MII 100BaseTx-FD */
+ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD,
+ PCNET32_PORT_ASEL /* 15 not supported */
+};
+
+static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {
+ "Loopback test (offline)"
+};
+
+#define PCNET32_TEST_LEN ARRAY_SIZE(pcnet32_gstrings_test)
+
+#define PCNET32_NUM_REGS 136
+
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS];
+static int full_duplex[MAX_UNITS];
+static int homepna[MAX_UNITS];
+
+/*
+ * Theory of Operation
+ *
+ * This driver uses the same software structure as the normal lance
+ * driver. So look for a verbose description in lance.c. The differences
+ * to the normal lance driver is the use of the 32bit mode of PCnet32
+ * and PCnetPCI chips. Because these chips are 32bit chips, there is no
+ * 16MB limitation and we don't need bounce buffers.
+ */
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
+ */
+#ifndef PCNET32_LOG_TX_BUFFERS
+#define PCNET32_LOG_TX_BUFFERS 4
+#define PCNET32_LOG_RX_BUFFERS 5
+#define PCNET32_LOG_MAX_TX_BUFFERS 9 /* 2^9 == 512 */
+#define PCNET32_LOG_MAX_RX_BUFFERS 9
+#endif
+
+#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))
+#define TX_MAX_RING_SIZE (1 << (PCNET32_LOG_MAX_TX_BUFFERS))
+
+#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS))
+#define RX_MAX_RING_SIZE (1 << (PCNET32_LOG_MAX_RX_BUFFERS))
+
+#define PKT_BUF_SKB 1544
+/* actual buffer length after being aligned */
+#define PKT_BUF_SIZE (PKT_BUF_SKB - NET_IP_ALIGN)
+/* chip wants twos complement of the (aligned) buffer length */
+#define NEG_BUF_SIZE (NET_IP_ALIGN - PKT_BUF_SKB)
+
+/* Offsets from base I/O address. */
+#define PCNET32_WIO_RDP 0x10
+#define PCNET32_WIO_RAP 0x12
+#define PCNET32_WIO_RESET 0x14
+#define PCNET32_WIO_BDP 0x16
+
+#define PCNET32_DWIO_RDP 0x10
+#define PCNET32_DWIO_RAP 0x14
+#define PCNET32_DWIO_RESET 0x18
+#define PCNET32_DWIO_BDP 0x1C
+
+#define PCNET32_TOTAL_SIZE 0x20
+
+#define CSR0 0
+#define CSR0_INIT 0x1
+#define CSR0_START 0x2
+#define CSR0_STOP 0x4
+#define CSR0_TXPOLL 0x8
+#define CSR0_INTEN 0x40
+#define CSR0_IDON 0x0100
+#define CSR0_NORMAL (CSR0_START | CSR0_INTEN)
+#define PCNET32_INIT_LOW 1
+#define PCNET32_INIT_HIGH 2
+#define CSR3 3
+#define CSR4 4
+#define CSR5 5
+#define CSR5_SUSPEND 0x0001
+#define CSR15 15
+#define PCNET32_MC_FILTER 8
+
+#define PCNET32_79C970A 0x2621
+
+/* The PCNET32 Rx and Tx ring descriptors. */
+struct pcnet32_rx_head {
+ __le32 base;
+ __le16 buf_length; /* two`s complement of length */
+ __le16 status;
+ __le32 msg_length;
+ __le32 reserved;
+};
+
+struct pcnet32_tx_head {
+ __le32 base;
+ __le16 length; /* two`s complement of length */
+ __le16 status;
+ __le32 misc;
+ __le32 reserved;
+};
+
+/* The PCNET32 32-Bit initialization block, described in databook. */
+struct pcnet32_init_block {
+ __le16 mode;
+ __le16 tlen_rlen;
+ u8 phys_addr[6];
+ __le16 reserved;
+ __le32 filter[2];
+ /* Receive and transmit ring base, along with extra bits. */
+ __le32 rx_ring;
+ __le32 tx_ring;
+};
+
+/* PCnet32 access functions */
+struct pcnet32_access {
+ u16 (*read_csr) (unsigned long, int);
+ void (*write_csr) (unsigned long, int, u16);
+ u16 (*read_bcr) (unsigned long, int);
+ void (*write_bcr) (unsigned long, int, u16);
+ u16 (*read_rap) (unsigned long);
+ void (*write_rap) (unsigned long, u16);
+ void (*reset) (unsigned long);
+};
+
+/*
+ * The first field of pcnet32_private is read by the ethernet device
+ * so the structure should be allocated using pci_alloc_consistent().
+ */
+struct pcnet32_private {
+ struct pcnet32_init_block *init_block;
+ /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
+ struct pcnet32_rx_head *rx_ring;
+ struct pcnet32_tx_head *tx_ring;
+ dma_addr_t init_dma_addr;/* DMA address of beginning of the init block,
+ returned by pci_alloc_consistent */
+ struct pci_dev *pci_dev;
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff **tx_skbuff;
+ struct sk_buff **rx_skbuff;
+ dma_addr_t *tx_dma_addr;
+ dma_addr_t *rx_dma_addr;
+ const struct pcnet32_access *a;
+ spinlock_t lock; /* Guard lock */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int rx_ring_size; /* current rx ring size */
+ unsigned int tx_ring_size; /* current tx ring size */
+ unsigned int rx_mod_mask; /* rx ring modular mask */
+ unsigned int tx_mod_mask; /* tx ring modular mask */
+ unsigned short rx_len_bits;
+ unsigned short tx_len_bits;
+ dma_addr_t rx_ring_dma_addr;
+ dma_addr_t tx_ring_dma_addr;
+ unsigned int dirty_rx, /* ring entries to be freed. */
+ dirty_tx;
+
+ struct net_device *dev;
+ struct napi_struct napi;
+ char tx_full;
+ char phycount; /* number of phys found */
+ int options;
+ unsigned int shared_irq:1, /* shared irq possible */
+ dxsuflo:1, /* disable transmit stop on uflo */
+ mii:1; /* mii port available */
+ struct net_device *next;
+ struct mii_if_info mii_if;
+ struct timer_list watchdog_timer;
+ u32 msg_enable; /* debug message level */
+
+ /* each bit indicates an available PHY */
+ u32 phymask;
+ unsigned short chip_version; /* which variant this is */
+
+ /* saved registers during ethtool blink */
+ u16 save_regs[4];
+};
+
+static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
+static int pcnet32_probe1(unsigned long, int, struct pci_dev *);
+static int pcnet32_open(struct net_device *);
+static int pcnet32_init_ring(struct net_device *);
+static netdev_tx_t pcnet32_start_xmit(struct sk_buff *,
+ struct net_device *);
+static void pcnet32_tx_timeout(struct net_device *dev);
+static irqreturn_t pcnet32_interrupt(int, void *);
+static int pcnet32_close(struct net_device *);
+static struct net_device_stats *pcnet32_get_stats(struct net_device *);
+static void pcnet32_load_multicast(struct net_device *dev);
+static void pcnet32_set_multicast_list(struct net_device *);
+static int pcnet32_ioctl(struct net_device *, struct ifreq *, int);
+static void pcnet32_watchdog(struct net_device *);
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num,
+ int val);
+static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits);
+static void pcnet32_ethtool_test(struct net_device *dev,
+ struct ethtool_test *eth_test, u64 * data);
+static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1);
+static int pcnet32_get_regs_len(struct net_device *dev);
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *ptr);
+static void pcnet32_purge_tx_ring(struct net_device *dev);
+static int pcnet32_alloc_ring(struct net_device *dev, const char *name);
+static void pcnet32_free_ring(struct net_device *dev);
+static void pcnet32_check_media(struct net_device *dev, int verbose);
+
+static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_RDP);
+}
+
+static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_RDP);
+}
+
+static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_BDP);
+}
+
+static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_BDP);
+}
+
+static u16 pcnet32_wio_read_rap(unsigned long addr)
+{
+ return inw(addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
+{
+ outw(val, addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_reset(unsigned long addr)
+{
+ inw(addr + PCNET32_WIO_RESET);
+}
+
+static int pcnet32_wio_check(unsigned long addr)
+{
+ outw(88, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_RAP) == 88;
+}
+
+static const struct pcnet32_access pcnet32_wio = {
+ .read_csr = pcnet32_wio_read_csr,
+ .write_csr = pcnet32_wio_write_csr,
+ .read_bcr = pcnet32_wio_read_bcr,
+ .write_bcr = pcnet32_wio_write_bcr,
+ .read_rap = pcnet32_wio_read_rap,
+ .write_rap = pcnet32_wio_write_rap,
+ .reset = pcnet32_wio_reset
+};
+
+static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return inl(addr + PCNET32_DWIO_RDP) & 0xffff;
+}
+
+static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_RDP);
+}
+
+static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return inl(addr + PCNET32_DWIO_BDP) & 0xffff;
+}
+
+static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_BDP);
+}
+
+static u16 pcnet32_dwio_read_rap(unsigned long addr)
+{
+ return inl(addr + PCNET32_DWIO_RAP) & 0xffff;
+}
+
+static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
+{
+ outl(val, addr + PCNET32_DWIO_RAP);
+}
+
+static void pcnet32_dwio_reset(unsigned long addr)
+{
+ inl(addr + PCNET32_DWIO_RESET);
+}
+
+static int pcnet32_dwio_check(unsigned long addr)
+{
+ outl(88, addr + PCNET32_DWIO_RAP);
+ return (inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88;
+}
+
+static const struct pcnet32_access pcnet32_dwio = {
+ .read_csr = pcnet32_dwio_read_csr,
+ .write_csr = pcnet32_dwio_write_csr,
+ .read_bcr = pcnet32_dwio_read_bcr,
+ .write_bcr = pcnet32_dwio_write_bcr,
+ .read_rap = pcnet32_dwio_read_rap,
+ .write_rap = pcnet32_dwio_write_rap,
+ .reset = pcnet32_dwio_reset
+};
+
+static void pcnet32_netif_stop(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ napi_disable(&lp->napi);
+ netif_tx_disable(dev);
+}
+
+static void pcnet32_netif_start(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ ulong ioaddr = dev->base_addr;
+ u16 val;
+
+ netif_wake_queue(dev);
+ val = lp->a->read_csr(ioaddr, CSR3);
+ val &= 0x00ff;
+ lp->a->write_csr(ioaddr, CSR3, val);
+ napi_enable(&lp->napi);
+}
+
+/*
+ * Allocate space for the new sized tx ring.
+ * Free old resources
+ * Save new resources.
+ * Any failure keeps old resources.
+ * Must be called with lp->lock held.
+ */
+static void pcnet32_realloc_tx_ring(struct net_device *dev,
+ struct pcnet32_private *lp,
+ unsigned int size)
+{
+ dma_addr_t new_ring_dma_addr;
+ dma_addr_t *new_dma_addr_list;
+ struct pcnet32_tx_head *new_tx_ring;
+ struct sk_buff **new_skb_list;
+
+ pcnet32_purge_tx_ring(dev);
+
+ new_tx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_tx_head) *
+ (1 << size),
+ &new_ring_dma_addr);
+ if (new_tx_ring == NULL) {
+ netif_err(lp, drv, dev, "Consistent memory allocation failed\n");
+ return;
+ }
+ memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size));
+
+ new_dma_addr_list = kcalloc(1 << size, sizeof(dma_addr_t),
+ GFP_ATOMIC);
+ if (!new_dma_addr_list)
+ goto free_new_tx_ring;
+
+ new_skb_list = kcalloc(1 << size, sizeof(struct sk_buff *),
+ GFP_ATOMIC);
+ if (!new_skb_list)
+ goto free_new_lists;
+
+ kfree(lp->tx_skbuff);
+ kfree(lp->tx_dma_addr);
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_tx_head) *
+ lp->tx_ring_size, lp->tx_ring,
+ lp->tx_ring_dma_addr);
+
+ lp->tx_ring_size = (1 << size);
+ lp->tx_mod_mask = lp->tx_ring_size - 1;
+ lp->tx_len_bits = (size << 12);
+ lp->tx_ring = new_tx_ring;
+ lp->tx_ring_dma_addr = new_ring_dma_addr;
+ lp->tx_dma_addr = new_dma_addr_list;
+ lp->tx_skbuff = new_skb_list;
+ return;
+
+free_new_lists:
+ kfree(new_dma_addr_list);
+free_new_tx_ring:
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_tx_head) *
+ (1 << size),
+ new_tx_ring,
+ new_ring_dma_addr);
+}
+
+/*
+ * Allocate space for the new sized rx ring.
+ * Re-use old receive buffers.
+ * alloc extra buffers
+ * free unneeded buffers
+ * free unneeded buffers
+ * Save new resources.
+ * Any failure keeps old resources.
+ * Must be called with lp->lock held.
+ */
+static void pcnet32_realloc_rx_ring(struct net_device *dev,
+ struct pcnet32_private *lp,
+ unsigned int size)
+{
+ dma_addr_t new_ring_dma_addr;
+ dma_addr_t *new_dma_addr_list;
+ struct pcnet32_rx_head *new_rx_ring;
+ struct sk_buff **new_skb_list;
+ int new, overlap;
+ unsigned int entries = 1 << size;
+
+ new_rx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_rx_head) *
+ entries,
+ &new_ring_dma_addr);
+ if (new_rx_ring == NULL) {
+ netif_err(lp, drv, dev, "Consistent memory allocation failed\n");
+ return;
+ }
+ memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * entries);
+
+ new_dma_addr_list = kcalloc(entries, sizeof(dma_addr_t), GFP_ATOMIC);
+ if (!new_dma_addr_list)
+ goto free_new_rx_ring;
+
+ new_skb_list = kcalloc(entries, sizeof(struct sk_buff *), GFP_ATOMIC);
+ if (!new_skb_list)
+ goto free_new_lists;
+
+ /* first copy the current receive buffers */
+ overlap = min(entries, lp->rx_ring_size);
+ for (new = 0; new < overlap; new++) {
+ new_rx_ring[new] = lp->rx_ring[new];
+ new_dma_addr_list[new] = lp->rx_dma_addr[new];
+ new_skb_list[new] = lp->rx_skbuff[new];
+ }
+ /* now allocate any new buffers needed */
+ for (; new < entries; new++) {
+ struct sk_buff *rx_skbuff;
+ new_skb_list[new] = netdev_alloc_skb(dev, PKT_BUF_SKB);
+ rx_skbuff = new_skb_list[new];
+ if (!rx_skbuff) {
+ /* keep the original lists and buffers */
+ netif_err(lp, drv, dev, "%s netdev_alloc_skb failed\n",
+ __func__);
+ goto free_all_new;
+ }
+ skb_reserve(rx_skbuff, NET_IP_ALIGN);
+
+ new_dma_addr_list[new] =
+ pci_map_single(lp->pci_dev, rx_skbuff->data,
+ PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev,
+ new_dma_addr_list[new])) {
+ netif_err(lp, drv, dev, "%s dma mapping failed\n",
+ __func__);
+ dev_kfree_skb(new_skb_list[new]);
+ goto free_all_new;
+ }
+ new_rx_ring[new].base = cpu_to_le32(new_dma_addr_list[new]);
+ new_rx_ring[new].buf_length = cpu_to_le16(NEG_BUF_SIZE);
+ new_rx_ring[new].status = cpu_to_le16(0x8000);
+ }
+ /* and free any unneeded buffers */
+ for (; new < lp->rx_ring_size; new++) {
+ if (lp->rx_skbuff[new]) {
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[new]))
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[new],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(lp->rx_skbuff[new]);
+ }
+ }
+
+ kfree(lp->rx_skbuff);
+ kfree(lp->rx_dma_addr);
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_rx_head) *
+ lp->rx_ring_size, lp->rx_ring,
+ lp->rx_ring_dma_addr);
+
+ lp->rx_ring_size = entries;
+ lp->rx_mod_mask = lp->rx_ring_size - 1;
+ lp->rx_len_bits = (size << 4);
+ lp->rx_ring = new_rx_ring;
+ lp->rx_ring_dma_addr = new_ring_dma_addr;
+ lp->rx_dma_addr = new_dma_addr_list;
+ lp->rx_skbuff = new_skb_list;
+ return;
+
+free_all_new:
+ while (--new >= lp->rx_ring_size) {
+ if (new_skb_list[new]) {
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ new_dma_addr_list[new]))
+ pci_unmap_single(lp->pci_dev,
+ new_dma_addr_list[new],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(new_skb_list[new]);
+ }
+ }
+ kfree(new_skb_list);
+free_new_lists:
+ kfree(new_dma_addr_list);
+free_new_rx_ring:
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_rx_head) * entries,
+ new_rx_ring,
+ new_ring_dma_addr);
+}
+
+static void pcnet32_purge_rx_ring(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int i;
+
+ /* free all allocated skbuffs */
+ for (i = 0; i < lp->rx_ring_size; i++) {
+ lp->rx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
+ if (lp->rx_skbuff[i]) {
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[i]))
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[i],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(lp->rx_skbuff[i]);
+ }
+ lp->rx_skbuff[i] = NULL;
+ lp->rx_dma_addr[i] = 0;
+ }
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void pcnet32_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ pcnet32_interrupt(0, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ mii_ethtool_gset(&lp->mii_if, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ r = 0;
+ }
+ return r;
+}
+
+static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ r = mii_ethtool_sset(&lp->mii_if, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return r;
+}
+
+static void pcnet32_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ if (lp->pci_dev)
+ strlcpy(info->bus_info, pci_name(lp->pci_dev),
+ sizeof(info->bus_info));
+ else
+ snprintf(info->bus_info, sizeof(info->bus_info),
+ "VLB 0x%lx", dev->base_addr);
+}
+
+static u32 pcnet32_get_link(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ if (lp->mii) {
+ r = mii_link_ok(&lp->mii_if);
+ } else if (lp->chip_version >= PCNET32_79C970A) {
+ ulong ioaddr = dev->base_addr; /* card base I/O address */
+ r = (lp->a->read_bcr(ioaddr, 4) != 0xc0);
+ } else { /* can not detect link on really old chips */
+ r = 1;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return r;
+}
+
+static u32 pcnet32_get_msglevel(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ return lp->msg_enable;
+}
+
+static void pcnet32_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ lp->msg_enable = value;
+}
+
+static int pcnet32_nway_reset(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ r = mii_nway_restart(&lp->mii_if);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return r;
+}
+
+static void pcnet32_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ ering->tx_max_pending = TX_MAX_RING_SIZE;
+ ering->tx_pending = lp->tx_ring_size;
+ ering->rx_max_pending = RX_MAX_RING_SIZE;
+ ering->rx_pending = lp->rx_ring_size;
+}
+
+static int pcnet32_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int size;
+ ulong ioaddr = dev->base_addr;
+ int i;
+
+ if (ering->rx_mini_pending || ering->rx_jumbo_pending)
+ return -EINVAL;
+
+ if (netif_running(dev))
+ pcnet32_netif_stop(dev);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
+
+ size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
+
+ /* set the minimum ring size to 4, to allow the loopback test to work
+ * unchanged.
+ */
+ for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) {
+ if (size <= (1 << i))
+ break;
+ }
+ if ((1 << i) != lp->tx_ring_size)
+ pcnet32_realloc_tx_ring(dev, lp, i);
+
+ size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);
+ for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) {
+ if (size <= (1 << i))
+ break;
+ }
+ if ((1 << i) != lp->rx_ring_size)
+ pcnet32_realloc_rx_ring(dev, lp, i);
+
+ lp->napi.weight = lp->rx_ring_size / 2;
+
+ if (netif_running(dev)) {
+ pcnet32_netif_start(dev);
+ pcnet32_restart(dev, CSR0_NORMAL);
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ netif_info(lp, drv, dev, "Ring Param Settings: RX: %d, TX: %d\n",
+ lp->rx_ring_size, lp->tx_ring_size);
+
+ return 0;
+}
+
+static void pcnet32_get_strings(struct net_device *dev, u32 stringset,
+ u8 *data)
+{
+ memcpy(data, pcnet32_gstrings_test, sizeof(pcnet32_gstrings_test));
+}
+
+static int pcnet32_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_TEST:
+ return PCNET32_TEST_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void pcnet32_ethtool_test(struct net_device *dev,
+ struct ethtool_test *test, u64 * data)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int rc;
+
+ if (test->flags == ETH_TEST_FL_OFFLINE) {
+ rc = pcnet32_loopback_test(dev, data);
+ if (rc) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "Loopback test failed\n");
+ test->flags |= ETH_TEST_FL_FAILED;
+ } else
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "Loopback test passed\n");
+ } else
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "No tests to run (specify 'Offline' on ethtool)\n");
+} /* end pcnet32_ethtool_test */
+
+static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ const struct pcnet32_access *a = lp->a; /* access to registers */
+ ulong ioaddr = dev->base_addr; /* card base I/O address */
+ struct sk_buff *skb; /* sk buff */
+ int x, i; /* counters */
+ int numbuffs = 4; /* number of TX/RX buffers and descs */
+ u16 status = 0x8300; /* TX ring status */
+ __le16 teststatus; /* test of ring status */
+ int rc; /* return code */
+ int size; /* size of packets */
+ unsigned char *packet; /* source packet data */
+ static const int data_len = 60; /* length of source packets */
+ unsigned long flags;
+ unsigned long ticks;
+
+ rc = 1; /* default to fail */
+
+ if (netif_running(dev))
+ pcnet32_netif_stop(dev);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */
+
+ numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));
+
+ /* Reset the PCNET32 */
+ lp->a->reset(ioaddr);
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+
+ /* switch pcnet32 to 32bit mode */
+ lp->a->write_bcr(ioaddr, 20, 2);
+
+ /* purge & init rings but don't actually restart */
+ pcnet32_restart(dev, 0x0000);
+
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
+
+ /* Initialize Transmit buffers. */
+ size = data_len + 15;
+ for (x = 0; x < numbuffs; x++) {
+ skb = netdev_alloc_skb(dev, size);
+ if (!skb) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "Cannot allocate skb at line: %d!\n",
+ __LINE__);
+ goto clean_up;
+ }
+ packet = skb->data;
+ skb_put(skb, size); /* create space for data */
+ lp->tx_skbuff[x] = skb;
+ lp->tx_ring[x].length = cpu_to_le16(-skb->len);
+ lp->tx_ring[x].misc = 0;
+
+ /* put DA and SA into the skb */
+ for (i = 0; i < 6; i++)
+ *packet++ = dev->dev_addr[i];
+ for (i = 0; i < 6; i++)
+ *packet++ = dev->dev_addr[i];
+ /* type */
+ *packet++ = 0x08;
+ *packet++ = 0x06;
+ /* packet number */
+ *packet++ = x;
+ /* fill packet with data */
+ for (i = 0; i < data_len; i++)
+ *packet++ = i;
+
+ lp->tx_dma_addr[x] =
+ pci_map_single(lp->pci_dev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, lp->tx_dma_addr[x])) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "DMA mapping error at line: %d!\n",
+ __LINE__);
+ goto clean_up;
+ }
+ lp->tx_ring[x].base = cpu_to_le32(lp->tx_dma_addr[x]);
+ wmb(); /* Make sure owner changes after all others are visible */
+ lp->tx_ring[x].status = cpu_to_le16(status);
+ }
+
+ x = a->read_bcr(ioaddr, 32); /* set internal loopback in BCR32 */
+ a->write_bcr(ioaddr, 32, x | 0x0002);
+
+ /* set int loopback in CSR15 */
+ x = a->read_csr(ioaddr, CSR15) & 0xfffc;
+ lp->a->write_csr(ioaddr, CSR15, x | 0x0044);
+
+ teststatus = cpu_to_le16(0x8000);
+ lp->a->write_csr(ioaddr, CSR0, CSR0_START); /* Set STRT bit */
+
+ /* Check status of descriptors */
+ for (x = 0; x < numbuffs; x++) {
+ ticks = 0;
+ rmb();
+ while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) {
+ spin_unlock_irqrestore(&lp->lock, flags);
+ msleep(1);
+ spin_lock_irqsave(&lp->lock, flags);
+ rmb();
+ ticks++;
+ }
+ if (ticks == 200) {
+ netif_err(lp, hw, dev, "Desc %d failed to reset!\n", x);
+ break;
+ }
+ }
+
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */
+ wmb();
+ if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
+ netdev_printk(KERN_DEBUG, dev, "RX loopback packets:\n");
+
+ for (x = 0; x < numbuffs; x++) {
+ netdev_printk(KERN_DEBUG, dev, "Packet %d: ", x);
+ skb = lp->rx_skbuff[x];
+ for (i = 0; i < size; i++)
+ pr_cont(" %02x", *(skb->data + i));
+ pr_cont("\n");
+ }
+ }
+
+ x = 0;
+ rc = 0;
+ while (x < numbuffs && !rc) {
+ skb = lp->rx_skbuff[x];
+ packet = lp->tx_skbuff[x]->data;
+ for (i = 0; i < size; i++) {
+ if (*(skb->data + i) != packet[i]) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "Error in compare! %2x - %02x %02x\n",
+ i, *(skb->data + i), packet[i]);
+ rc = 1;
+ break;
+ }
+ }
+ x++;
+ }
+
+clean_up:
+ *data1 = rc;
+ pcnet32_purge_tx_ring(dev);
+
+ x = a->read_csr(ioaddr, CSR15);
+ a->write_csr(ioaddr, CSR15, (x & ~0x0044)); /* reset bits 6 and 2 */
+
+ x = a->read_bcr(ioaddr, 32); /* reset internal loopback */
+ a->write_bcr(ioaddr, 32, (x & ~0x0002));
+
+ if (netif_running(dev)) {
+ pcnet32_netif_start(dev);
+ pcnet32_restart(dev, CSR0_NORMAL);
+ } else {
+ pcnet32_purge_rx_ring(dev);
+ lp->a->write_bcr(ioaddr, 20, 4); /* return to 16bit mode */
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return rc;
+} /* end pcnet32_loopback_test */
+
+static int pcnet32_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ const struct pcnet32_access *a = lp->a;
+ ulong ioaddr = dev->base_addr;
+ unsigned long flags;
+ int i;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ /* Save the current value of the bcrs */
+ spin_lock_irqsave(&lp->lock, flags);
+ for (i = 4; i < 8; i++)
+ lp->save_regs[i - 4] = a->read_bcr(ioaddr, i);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 2; /* cycle on/off twice per second */
+
+ case ETHTOOL_ID_ON:
+ case ETHTOOL_ID_OFF:
+ /* Blink the led */
+ spin_lock_irqsave(&lp->lock, flags);
+ for (i = 4; i < 8; i++)
+ a->write_bcr(ioaddr, i, a->read_bcr(ioaddr, i) ^ 0x4000);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
+ /* Restore the original value of the bcrs */
+ spin_lock_irqsave(&lp->lock, flags);
+ for (i = 4; i < 8; i++)
+ a->write_bcr(ioaddr, i, lp->save_regs[i - 4]);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return 0;
+}
+
+/*
+ * lp->lock must be held.
+ */
+static int pcnet32_suspend(struct net_device *dev, unsigned long *flags,
+ int can_sleep)
+{
+ int csr5;
+ struct pcnet32_private *lp = netdev_priv(dev);
+ const struct pcnet32_access *a = lp->a;
+ ulong ioaddr = dev->base_addr;
+ int ticks;
+
+ /* really old chips have to be stopped. */
+ if (lp->chip_version < PCNET32_79C970A)
+ return 0;
+
+ /* set SUSPEND (SPND) - CSR5 bit 0 */
+ csr5 = a->read_csr(ioaddr, CSR5);
+ a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND);
+
+ /* poll waiting for bit to be set */
+ ticks = 0;
+ while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) {
+ spin_unlock_irqrestore(&lp->lock, *flags);
+ if (can_sleep)
+ msleep(1);
+ else
+ mdelay(1);
+ spin_lock_irqsave(&lp->lock, *flags);
+ ticks++;
+ if (ticks > 200) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "Error getting into suspend!\n");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * process one receive descriptor entry
+ */
+
+static void pcnet32_rx_entry(struct net_device *dev,
+ struct pcnet32_private *lp,
+ struct pcnet32_rx_head *rxp,
+ int entry)
+{
+ int status = (short)le16_to_cpu(rxp->status) >> 8;
+ int rx_in_place = 0;
+ struct sk_buff *skb;
+ short pkt_len;
+
+ if (status != 0x03) { /* There was an error. */
+ /*
+ * There is a tricky error noted by John Murphy,
+ * <murf@perftech.com> to Russ Nelson: Even with full-sized
+ * buffers it's possible for a jabber packet to use two
+ * buffers, with only the last correctly noting the error.
+ */
+ if (status & 0x01) /* Only count a general error at the */
+ dev->stats.rx_errors++; /* end of a packet. */
+ if (status & 0x20)
+ dev->stats.rx_frame_errors++;
+ if (status & 0x10)
+ dev->stats.rx_over_errors++;
+ if (status & 0x08)
+ dev->stats.rx_crc_errors++;
+ if (status & 0x04)
+ dev->stats.rx_fifo_errors++;
+ return;
+ }
+
+ pkt_len = (le32_to_cpu(rxp->msg_length) & 0xfff) - 4;
+
+ /* Discard oversize frames. */
+ if (unlikely(pkt_len > PKT_BUF_SIZE)) {
+ netif_err(lp, drv, dev, "Impossible packet size %d!\n",
+ pkt_len);
+ dev->stats.rx_errors++;
+ return;
+ }
+ if (pkt_len < 60) {
+ netif_err(lp, rx_err, dev, "Runt packet!\n");
+ dev->stats.rx_errors++;
+ return;
+ }
+
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+ dma_addr_t new_dma_addr;
+
+ newskb = netdev_alloc_skb(dev, PKT_BUF_SKB);
+ /*
+ * map the new buffer, if mapping fails, drop the packet and
+ * reuse the old buffer
+ */
+ if (newskb) {
+ skb_reserve(newskb, NET_IP_ALIGN);
+ new_dma_addr = pci_map_single(lp->pci_dev,
+ newskb->data,
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, new_dma_addr)) {
+ netif_err(lp, rx_err, dev,
+ "DMA mapping error.\n");
+ dev_kfree_skb(newskb);
+ skb = NULL;
+ } else {
+ skb = lp->rx_skbuff[entry];
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ skb_put(skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ lp->rx_dma_addr[entry] = new_dma_addr;
+ rxp->base = cpu_to_le32(new_dma_addr);
+ rx_in_place = 1;
+ }
+ } else
+ skb = NULL;
+ } else
+ skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN);
+
+ if (skb == NULL) {
+ dev->stats.rx_dropped++;
+ return;
+ }
+ if (!rx_in_place) {
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pkt_len); /* Make room */
+ pci_dma_sync_single_for_cpu(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ pkt_len,
+ PCI_DMA_FROMDEVICE);
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)(lp->rx_skbuff[entry]->data),
+ pkt_len);
+ pci_dma_sync_single_for_device(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ pkt_len,
+ PCI_DMA_FROMDEVICE);
+ }
+ dev->stats.rx_bytes += skb->len;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_receive_skb(skb);
+ dev->stats.rx_packets++;
+}
+
+static int pcnet32_rx(struct net_device *dev, int budget)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int entry = lp->cur_rx & lp->rx_mod_mask;
+ struct pcnet32_rx_head *rxp = &lp->rx_ring[entry];
+ int npackets = 0;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (npackets < budget && (short)le16_to_cpu(rxp->status) >= 0) {
+ pcnet32_rx_entry(dev, lp, rxp, entry);
+ npackets += 1;
+ /*
+ * The docs say that the buffer length isn't touched, but Andrew
+ * Boyd of QNX reports that some revs of the 79C965 clear it.
+ */
+ rxp->buf_length = cpu_to_le16(NEG_BUF_SIZE);
+ wmb(); /* Make sure owner changes after others are visible */
+ rxp->status = cpu_to_le16(0x8000);
+ entry = (++lp->cur_rx) & lp->rx_mod_mask;
+ rxp = &lp->rx_ring[entry];
+ }
+
+ return npackets;
+}
+
+static int pcnet32_tx(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned int dirty_tx = lp->dirty_tx;
+ int delta;
+ int must_restart = 0;
+
+ while (dirty_tx != lp->cur_tx) {
+ int entry = dirty_tx & lp->tx_mod_mask;
+ int status = (short)le16_to_cpu(lp->tx_ring[entry].status);
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[entry].base = 0;
+
+ if (status & 0x4000) {
+ /* There was a major error, log it. */
+ int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
+ dev->stats.tx_errors++;
+ netif_err(lp, tx_err, dev,
+ "Tx error status=%04x err_status=%08x\n",
+ status, err_status);
+ if (err_status & 0x04000000)
+ dev->stats.tx_aborted_errors++;
+ if (err_status & 0x08000000)
+ dev->stats.tx_carrier_errors++;
+ if (err_status & 0x10000000)
+ dev->stats.tx_window_errors++;
+#ifndef DO_DXSUFLO
+ if (err_status & 0x40000000) {
+ dev->stats.tx_fifo_errors++;
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ /* Remove this verbosity later! */
+ netif_err(lp, tx_err, dev, "Tx FIFO error!\n");
+ must_restart = 1;
+ }
+#else
+ if (err_status & 0x40000000) {
+ dev->stats.tx_fifo_errors++;
+ if (!lp->dxsuflo) { /* If controller doesn't recover ... */
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ /* Remove this verbosity later! */
+ netif_err(lp, tx_err, dev, "Tx FIFO error!\n");
+ must_restart = 1;
+ }
+ }
+#endif
+ } else {
+ if (status & 0x1800)
+ dev->stats.collisions++;
+ dev->stats.tx_packets++;
+ }
+
+ /* We must free the original skb */
+ if (lp->tx_skbuff[entry]) {
+ pci_unmap_single(lp->pci_dev,
+ lp->tx_dma_addr[entry],
+ lp->tx_skbuff[entry]->
+ len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(lp->tx_skbuff[entry]);
+ lp->tx_skbuff[entry] = NULL;
+ lp->tx_dma_addr[entry] = 0;
+ }
+ dirty_tx++;
+ }
+
+ delta = (lp->cur_tx - dirty_tx) & (lp->tx_mod_mask + lp->tx_ring_size);
+ if (delta > lp->tx_ring_size) {
+ netif_err(lp, drv, dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += lp->tx_ring_size;
+ delta -= lp->tx_ring_size;
+ }
+
+ if (lp->tx_full &&
+ netif_queue_stopped(dev) &&
+ delta < lp->tx_ring_size - 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ lp->dirty_tx = dirty_tx;
+
+ return must_restart;
+}
+
+static int pcnet32_poll(struct napi_struct *napi, int budget)
+{
+ struct pcnet32_private *lp = container_of(napi, struct pcnet32_private, napi);
+ struct net_device *dev = lp->dev;
+ unsigned long ioaddr = dev->base_addr;
+ unsigned long flags;
+ int work_done;
+ u16 val;
+
+ work_done = pcnet32_rx(dev, budget);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ if (pcnet32_tx(dev)) {
+ /* reset the chip to clear the error condition, then restart */
+ lp->a->reset(ioaddr);
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+ pcnet32_restart(dev, CSR0_START);
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ if (work_done < budget) {
+ spin_lock_irqsave(&lp->lock, flags);
+
+ __napi_complete(napi);
+
+ /* clear interrupt masks */
+ val = lp->a->read_csr(ioaddr, CSR3);
+ val &= 0x00ff;
+ lp->a->write_csr(ioaddr, CSR3, val);
+
+ /* Set interrupt enable. */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return work_done;
+}
+
+#define PCNET32_REGS_PER_PHY 32
+#define PCNET32_MAX_PHYS 32
+static int pcnet32_get_regs_len(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int j = lp->phycount * PCNET32_REGS_PER_PHY;
+
+ return (PCNET32_NUM_REGS + j) * sizeof(u16);
+}
+
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *ptr)
+{
+ int i, csr0;
+ u16 *buff = ptr;
+ struct pcnet32_private *lp = netdev_priv(dev);
+ const struct pcnet32_access *a = lp->a;
+ ulong ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ csr0 = a->read_csr(ioaddr, CSR0);
+ if (!(csr0 & CSR0_STOP)) /* If not stopped */
+ pcnet32_suspend(dev, &flags, 1);
+
+ /* read address PROM */
+ for (i = 0; i < 16; i += 2)
+ *buff++ = inw(ioaddr + i);
+
+ /* read control and status registers */
+ for (i = 0; i < 90; i++)
+ *buff++ = a->read_csr(ioaddr, i);
+
+ *buff++ = a->read_csr(ioaddr, 112);
+ *buff++ = a->read_csr(ioaddr, 114);
+
+ /* read bus configuration registers */
+ for (i = 0; i < 30; i++)
+ *buff++ = a->read_bcr(ioaddr, i);
+
+ *buff++ = 0; /* skip bcr30 so as not to hang 79C976 */
+
+ for (i = 31; i < 36; i++)
+ *buff++ = a->read_bcr(ioaddr, i);
+
+ /* read mii phy registers */
+ if (lp->mii) {
+ int j;
+ for (j = 0; j < PCNET32_MAX_PHYS; j++) {
+ if (lp->phymask & (1 << j)) {
+ for (i = 0; i < PCNET32_REGS_PER_PHY; i++) {
+ lp->a->write_bcr(ioaddr, 33,
+ (j << 5) | i);
+ *buff++ = lp->a->read_bcr(ioaddr, 34);
+ }
+ }
+ }
+ }
+
+ if (!(csr0 & CSR0_STOP)) { /* If not stopped */
+ int csr5;
+
+ /* clear SUSPEND (SPND) - CSR5 bit 0 */
+ csr5 = a->read_csr(ioaddr, CSR5);
+ a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static const struct ethtool_ops pcnet32_ethtool_ops = {
+ .get_settings = pcnet32_get_settings,
+ .set_settings = pcnet32_set_settings,
+ .get_drvinfo = pcnet32_get_drvinfo,
+ .get_msglevel = pcnet32_get_msglevel,
+ .set_msglevel = pcnet32_set_msglevel,
+ .nway_reset = pcnet32_nway_reset,
+ .get_link = pcnet32_get_link,
+ .get_ringparam = pcnet32_get_ringparam,
+ .set_ringparam = pcnet32_set_ringparam,
+ .get_strings = pcnet32_get_strings,
+ .self_test = pcnet32_ethtool_test,
+ .set_phys_id = pcnet32_set_phys_id,
+ .get_regs_len = pcnet32_get_regs_len,
+ .get_regs = pcnet32_get_regs,
+ .get_sset_count = pcnet32_get_sset_count,
+};
+
+/* only probes for non-PCI devices, the rest are handled by
+ * pci_register_driver via pcnet32_probe_pci */
+
+static void pcnet32_probe_vlbus(unsigned int *pcnet32_portlist)
+{
+ unsigned int *port, ioaddr;
+
+ /* search for PCnet32 VLB cards at known addresses */
+ for (port = pcnet32_portlist; (ioaddr = *port); port++) {
+ if (request_region
+ (ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) {
+ /* check if there is really a pcnet chip on that ioaddr */
+ if ((inb(ioaddr + 14) == 0x57) &&
+ (inb(ioaddr + 15) == 0x57)) {
+ pcnet32_probe1(ioaddr, 0, NULL);
+ } else {
+ release_region(ioaddr, PCNET32_TOTAL_SIZE);
+ }
+ }
+ }
+}
+
+static int
+pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ unsigned long ioaddr;
+ int err;
+
+ err = pci_enable_device(pdev);
+ if (err < 0) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("failed to enable device -- err=%d\n", err);
+ return err;
+ }
+ pci_set_master(pdev);
+
+ ioaddr = pci_resource_start(pdev, 0);
+ if (!ioaddr) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("card has no PCI IO resources, aborting\n");
+ return -ENODEV;
+ }
+
+ if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("architecture does not support 32bit PCI busmaster DMA\n");
+ return -ENODEV;
+ }
+ if (!request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_pci")) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("io address range already allocated\n");
+ return -EBUSY;
+ }
+
+ err = pcnet32_probe1(ioaddr, 1, pdev);
+ if (err < 0)
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static const struct net_device_ops pcnet32_netdev_ops = {
+ .ndo_open = pcnet32_open,
+ .ndo_stop = pcnet32_close,
+ .ndo_start_xmit = pcnet32_start_xmit,
+ .ndo_tx_timeout = pcnet32_tx_timeout,
+ .ndo_get_stats = pcnet32_get_stats,
+ .ndo_set_rx_mode = pcnet32_set_multicast_list,
+ .ndo_do_ioctl = pcnet32_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = pcnet32_poll_controller,
+#endif
+};
+
+/* pcnet32_probe1
+ * Called from both pcnet32_probe_vlbus and pcnet_probe_pci.
+ * pdev will be NULL when called from pcnet32_probe_vlbus.
+ */
+static int
+pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
+{
+ struct pcnet32_private *lp;
+ int i, media;
+ int fdx, mii, fset, dxsuflo;
+ int chip_version;
+ char *chipname;
+ struct net_device *dev;
+ const struct pcnet32_access *a = NULL;
+ u8 promaddr[ETH_ALEN];
+ int ret = -ENODEV;
+
+ /* reset the chip */
+ pcnet32_wio_reset(ioaddr);
+
+ /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
+ if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) {
+ a = &pcnet32_wio;
+ } else {
+ pcnet32_dwio_reset(ioaddr);
+ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 &&
+ pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
+ } else {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("No access methods\n");
+ goto err_release_region;
+ }
+ }
+
+ chip_version =
+ a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);
+ if ((pcnet32_debug & NETIF_MSG_PROBE) && (pcnet32_debug & NETIF_MSG_HW))
+ pr_info(" PCnet chip version is %#x\n", chip_version);
+ if ((chip_version & 0xfff) != 0x003) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_info("Unsupported chip version\n");
+ goto err_release_region;
+ }
+
+ /* initialize variables */
+ fdx = mii = fset = dxsuflo = 0;
+ chip_version = (chip_version >> 12) & 0xffff;
+
+ switch (chip_version) {
+ case 0x2420:
+ chipname = "PCnet/PCI 79C970"; /* PCI */
+ break;
+ case 0x2430:
+ if (shared)
+ chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+ else
+ chipname = "PCnet/32 79C965"; /* 486/VL bus */
+ break;
+ case 0x2621:
+ chipname = "PCnet/PCI II 79C970A"; /* PCI */
+ fdx = 1;
+ break;
+ case 0x2623:
+ chipname = "PCnet/FAST 79C971"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ break;
+ case 0x2624:
+ chipname = "PCnet/FAST+ 79C972"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ break;
+ case 0x2625:
+ chipname = "PCnet/FAST III 79C973"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ case 0x2626:
+ chipname = "PCnet/Home 79C978"; /* PCI */
+ fdx = 1;
+ /*
+ * This is based on specs published at www.amd.com. This section
+ * assumes that a card with a 79C978 wants to go into standard
+ * ethernet mode. The 79C978 can also go into 1Mb HomePNA mode,
+ * and the module option homepna=1 can select this instead.
+ */
+ media = a->read_bcr(ioaddr, 49);
+ media &= ~3; /* default to 10Mb ethernet */
+ if (cards_found < MAX_UNITS && homepna[cards_found])
+ media |= 1; /* switch to home wiring mode */
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ printk(KERN_DEBUG PFX "media set to %sMbit mode\n",
+ (media & 1) ? "1" : "10");
+ a->write_bcr(ioaddr, 49, media);
+ break;
+ case 0x2627:
+ chipname = "PCnet/FAST III 79C975"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ case 0x2628:
+ chipname = "PCnet/PRO 79C976";
+ fdx = 1;
+ mii = 1;
+ break;
+ default:
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_info("PCnet version %#x, no PCnet32 chip\n",
+ chip_version);
+ goto err_release_region;
+ }
+
+ /*
+ * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
+ * starting until the packet is loaded. Strike one for reliability, lose
+ * one for latency - although on PCI this isn't a big loss. Older chips
+ * have FIFO's smaller than a packet, so you can't do this.
+ * Turn on BCR18:BurstRdEn and BCR18:BurstWrEn.
+ */
+
+ if (fset) {
+ a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860));
+ a->write_csr(ioaddr, 80,
+ (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
+ dxsuflo = 1;
+ }
+
+ dev = alloc_etherdev(sizeof(*lp));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_release_region;
+ }
+
+ if (pdev)
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_info("%s at %#3lx,", chipname, ioaddr);
+
+ /* In most chips, after a chip reset, the ethernet address is read from the
+ * station address PROM at the base address and programmed into the
+ * "Physical Address Registers" CSR12-14.
+ * As a precautionary measure, we read the PROM values and complain if
+ * they disagree with the CSRs. If they miscompare, and the PROM addr
+ * is valid, then the PROM addr is used.
+ */
+ for (i = 0; i < 3; i++) {
+ unsigned int val;
+ val = a->read_csr(ioaddr, i + 12) & 0x0ffff;
+ /* There may be endianness issues here. */
+ dev->dev_addr[2 * i] = val & 0x0ff;
+ dev->dev_addr[2 * i + 1] = (val >> 8) & 0x0ff;
+ }
+
+ /* read PROM address and compare with CSR address */
+ for (i = 0; i < ETH_ALEN; i++)
+ promaddr[i] = inb(ioaddr + i);
+
+ if (!ether_addr_equal(promaddr, dev->dev_addr) ||
+ !is_valid_ether_addr(dev->dev_addr)) {
+ if (is_valid_ether_addr(promaddr)) {
+ if (pcnet32_debug & NETIF_MSG_PROBE) {
+ pr_cont(" warning: CSR address invalid,\n");
+ pr_info(" using instead PROM address of");
+ }
+ memcpy(dev->dev_addr, promaddr, ETH_ALEN);
+ }
+ }
+
+ /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */
+ if (!is_valid_ether_addr(dev->dev_addr))
+ memset(dev->dev_addr, 0, ETH_ALEN);
+
+ if (pcnet32_debug & NETIF_MSG_PROBE) {
+ pr_cont(" %pM", dev->dev_addr);
+
+ /* Version 0x2623 and 0x2624 */
+ if (((chip_version + 1) & 0xfffe) == 0x2624) {
+ i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */
+ pr_info(" tx_start_pt(0x%04x):", i);
+ switch (i >> 10) {
+ case 0:
+ pr_cont(" 20 bytes,");
+ break;
+ case 1:
+ pr_cont(" 64 bytes,");
+ break;
+ case 2:
+ pr_cont(" 128 bytes,");
+ break;
+ case 3:
+ pr_cont("~220 bytes,");
+ break;
+ }
+ i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */
+ pr_cont(" BCR18(%x):", i & 0xffff);
+ if (i & (1 << 5))
+ pr_cont("BurstWrEn ");
+ if (i & (1 << 6))
+ pr_cont("BurstRdEn ");
+ if (i & (1 << 7))
+ pr_cont("DWordIO ");
+ if (i & (1 << 11))
+ pr_cont("NoUFlow ");
+ i = a->read_bcr(ioaddr, 25);
+ pr_info(" SRAMSIZE=0x%04x,", i << 8);
+ i = a->read_bcr(ioaddr, 26);
+ pr_cont(" SRAM_BND=0x%04x,", i << 8);
+ i = a->read_bcr(ioaddr, 27);
+ if (i & (1 << 14))
+ pr_cont("LowLatRx");
+ }
+ }
+
+ dev->base_addr = ioaddr;
+ lp = netdev_priv(dev);
+ /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */
+ lp->init_block = pci_alloc_consistent(pdev, sizeof(*lp->init_block),
+ &lp->init_dma_addr);
+ if (!lp->init_block) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_err("Consistent memory allocation failed\n");
+ ret = -ENOMEM;
+ goto err_free_netdev;
+ }
+ lp->pci_dev = pdev;
+
+ lp->dev = dev;
+
+ spin_lock_init(&lp->lock);
+
+ lp->name = chipname;
+ lp->shared_irq = shared;
+ lp->tx_ring_size = TX_RING_SIZE; /* default tx ring size */
+ lp->rx_ring_size = RX_RING_SIZE; /* default rx ring size */
+ lp->tx_mod_mask = lp->tx_ring_size - 1;
+ lp->rx_mod_mask = lp->rx_ring_size - 1;
+ lp->tx_len_bits = (PCNET32_LOG_TX_BUFFERS << 12);
+ lp->rx_len_bits = (PCNET32_LOG_RX_BUFFERS << 4);
+ lp->mii_if.full_duplex = fdx;
+ lp->mii_if.phy_id_mask = 0x1f;
+ lp->mii_if.reg_num_mask = 0x1f;
+ lp->dxsuflo = dxsuflo;
+ lp->mii = mii;
+ lp->chip_version = chip_version;
+ lp->msg_enable = pcnet32_debug;
+ if ((cards_found >= MAX_UNITS) ||
+ (options[cards_found] >= sizeof(options_mapping)))
+ lp->options = PCNET32_PORT_ASEL;
+ else
+ lp->options = options_mapping[options[cards_found]];
+ lp->mii_if.dev = dev;
+ lp->mii_if.mdio_read = mdio_read;
+ lp->mii_if.mdio_write = mdio_write;
+
+ /* napi.weight is used in both the napi and non-napi cases */
+ lp->napi.weight = lp->rx_ring_size / 2;
+
+ netif_napi_add(dev, &lp->napi, pcnet32_poll, lp->rx_ring_size / 2);
+
+ if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
+ ((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
+ lp->options |= PCNET32_PORT_FD;
+
+ lp->a = a;
+
+ /* prior to register_netdev, dev->name is not yet correct */
+ if (pcnet32_alloc_ring(dev, pci_name(lp->pci_dev))) {
+ ret = -ENOMEM;
+ goto err_free_ring;
+ }
+ /* detect special T1/E1 WAN card by checking for MAC address */
+ if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 &&
+ dev->dev_addr[2] == 0x75)
+ lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
+
+ lp->init_block->mode = cpu_to_le16(0x0003); /* Disable Rx and Tx. */
+ lp->init_block->tlen_rlen =
+ cpu_to_le16(lp->tx_len_bits | lp->rx_len_bits);
+ for (i = 0; i < 6; i++)
+ lp->init_block->phys_addr[i] = dev->dev_addr[i];
+ lp->init_block->filter[0] = 0x00000000;
+ lp->init_block->filter[1] = 0x00000000;
+ lp->init_block->rx_ring = cpu_to_le32(lp->rx_ring_dma_addr);
+ lp->init_block->tx_ring = cpu_to_le32(lp->tx_ring_dma_addr);
+
+ /* switch pcnet32 to 32bit mode */
+ a->write_bcr(ioaddr, 20, 2);
+
+ a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
+ a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
+
+ if (pdev) { /* use the IRQ provided by PCI */
+ dev->irq = pdev->irq;
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_cont(" assigned IRQ %d\n", dev->irq);
+ } else {
+ unsigned long irq_mask = probe_irq_on();
+
+ /*
+ * To auto-IRQ we enable the initialization-done and DMA error
+ * interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ * boards will work.
+ */
+ /* Trigger an initialization just for the interrupt. */
+ a->write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_INIT);
+ mdelay(1);
+
+ dev->irq = probe_irq_off(irq_mask);
+ if (!dev->irq) {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_cont(", failed to detect IRQ line\n");
+ ret = -ENODEV;
+ goto err_free_ring;
+ }
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_cont(", probed IRQ %d\n", dev->irq);
+ }
+
+ /* Set the mii phy_id so that we can query the link state */
+ if (lp->mii) {
+ /* lp->phycount and lp->phymask are set to 0 by memset above */
+
+ lp->mii_if.phy_id = ((lp->a->read_bcr(ioaddr, 33)) >> 5) & 0x1f;
+ /* scan for PHYs */
+ for (i = 0; i < PCNET32_MAX_PHYS; i++) {
+ unsigned short id1, id2;
+
+ id1 = mdio_read(dev, i, MII_PHYSID1);
+ if (id1 == 0xffff)
+ continue;
+ id2 = mdio_read(dev, i, MII_PHYSID2);
+ if (id2 == 0xffff)
+ continue;
+ if (i == 31 && ((chip_version + 1) & 0xfffe) == 0x2624)
+ continue; /* 79C971 & 79C972 have phantom phy at id 31 */
+ lp->phycount++;
+ lp->phymask |= (1 << i);
+ lp->mii_if.phy_id = i;
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_info("Found PHY %04x:%04x at address %d\n",
+ id1, id2, i);
+ }
+ lp->a->write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);
+ if (lp->phycount > 1)
+ lp->options |= PCNET32_PORT_MII;
+ }
+
+ init_timer(&lp->watchdog_timer);
+ lp->watchdog_timer.data = (unsigned long)dev;
+ lp->watchdog_timer.function = (void *)&pcnet32_watchdog;
+
+ /* The PCNET32-specific entries in the device structure. */
+ dev->netdev_ops = &pcnet32_netdev_ops;
+ dev->ethtool_ops = &pcnet32_ethtool_ops;
+ dev->watchdog_timeo = (5 * HZ);
+
+ /* Fill in the generic fields of the device structure. */
+ if (register_netdev(dev))
+ goto err_free_ring;
+
+ if (pdev) {
+ pci_set_drvdata(pdev, dev);
+ } else {
+ lp->next = pcnet32_dev;
+ pcnet32_dev = dev;
+ }
+
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ pr_info("%s: registered as %s\n", dev->name, lp->name);
+ cards_found++;
+
+ /* enable LED writes */
+ a->write_bcr(ioaddr, 2, a->read_bcr(ioaddr, 2) | 0x1000);
+
+ return 0;
+
+err_free_ring:
+ pcnet32_free_ring(dev);
+ pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),
+ lp->init_block, lp->init_dma_addr);
+err_free_netdev:
+ free_netdev(dev);
+err_release_region:
+ release_region(ioaddr, PCNET32_TOTAL_SIZE);
+ return ret;
+}
+
+/* if any allocation fails, caller must also call pcnet32_free_ring */
+static int pcnet32_alloc_ring(struct net_device *dev, const char *name)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ lp->tx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_tx_head) *
+ lp->tx_ring_size,
+ &lp->tx_ring_dma_addr);
+ if (lp->tx_ring == NULL) {
+ netif_err(lp, drv, dev, "Consistent memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ lp->rx_ring = pci_alloc_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_rx_head) *
+ lp->rx_ring_size,
+ &lp->rx_ring_dma_addr);
+ if (lp->rx_ring == NULL) {
+ netif_err(lp, drv, dev, "Consistent memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ lp->tx_dma_addr = kcalloc(lp->tx_ring_size, sizeof(dma_addr_t),
+ GFP_ATOMIC);
+ if (!lp->tx_dma_addr)
+ return -ENOMEM;
+
+ lp->rx_dma_addr = kcalloc(lp->rx_ring_size, sizeof(dma_addr_t),
+ GFP_ATOMIC);
+ if (!lp->rx_dma_addr)
+ return -ENOMEM;
+
+ lp->tx_skbuff = kcalloc(lp->tx_ring_size, sizeof(struct sk_buff *),
+ GFP_ATOMIC);
+ if (!lp->tx_skbuff)
+ return -ENOMEM;
+
+ lp->rx_skbuff = kcalloc(lp->rx_ring_size, sizeof(struct sk_buff *),
+ GFP_ATOMIC);
+ if (!lp->rx_skbuff)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void pcnet32_free_ring(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ kfree(lp->tx_skbuff);
+ lp->tx_skbuff = NULL;
+
+ kfree(lp->rx_skbuff);
+ lp->rx_skbuff = NULL;
+
+ kfree(lp->tx_dma_addr);
+ lp->tx_dma_addr = NULL;
+
+ kfree(lp->rx_dma_addr);
+ lp->rx_dma_addr = NULL;
+
+ if (lp->tx_ring) {
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_tx_head) *
+ lp->tx_ring_size, lp->tx_ring,
+ lp->tx_ring_dma_addr);
+ lp->tx_ring = NULL;
+ }
+
+ if (lp->rx_ring) {
+ pci_free_consistent(lp->pci_dev,
+ sizeof(struct pcnet32_rx_head) *
+ lp->rx_ring_size, lp->rx_ring,
+ lp->rx_ring_dma_addr);
+ lp->rx_ring = NULL;
+ }
+}
+
+static int pcnet32_open(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ struct pci_dev *pdev = lp->pci_dev;
+ unsigned long ioaddr = dev->base_addr;
+ u16 val;
+ int i;
+ int rc;
+ unsigned long flags;
+
+ if (request_irq(dev->irq, pcnet32_interrupt,
+ lp->shared_irq ? IRQF_SHARED : 0, dev->name,
+ (void *)dev)) {
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+ /* Check for a valid station address */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ rc = -EINVAL;
+ goto err_free_irq;
+ }
+
+ /* Reset the PCNET32 */
+ lp->a->reset(ioaddr);
+
+ /* switch pcnet32 to 32bit mode */
+ lp->a->write_bcr(ioaddr, 20, 2);
+
+ netif_printk(lp, ifup, KERN_DEBUG, dev,
+ "%s() irq %d tx/rx rings %#x/%#x init %#x\n",
+ __func__, dev->irq, (u32) (lp->tx_ring_dma_addr),
+ (u32) (lp->rx_ring_dma_addr),
+ (u32) (lp->init_dma_addr));
+
+ /* set/reset autoselect bit */
+ val = lp->a->read_bcr(ioaddr, 2) & ~2;
+ if (lp->options & PCNET32_PORT_ASEL)
+ val |= 2;
+ lp->a->write_bcr(ioaddr, 2, val);
+
+ /* handle full duplex setting */
+ if (lp->mii_if.full_duplex) {
+ val = lp->a->read_bcr(ioaddr, 9) & ~3;
+ if (lp->options & PCNET32_PORT_FD) {
+ val |= 1;
+ if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI))
+ val |= 2;
+ } else if (lp->options & PCNET32_PORT_ASEL) {
+ /* workaround of xSeries250, turn on for 79C975 only */
+ if (lp->chip_version == 0x2627)
+ val |= 3;
+ }
+ lp->a->write_bcr(ioaddr, 9, val);
+ }
+
+ /* set/reset GPSI bit in test register */
+ val = lp->a->read_csr(ioaddr, 124) & ~0x10;
+ if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
+ val |= 0x10;
+ lp->a->write_csr(ioaddr, 124, val);
+
+ /* Allied Telesyn AT 2700/2701 FX are 100Mbit only and do not negotiate */
+ if (pdev && pdev->subsystem_vendor == PCI_VENDOR_ID_AT &&
+ (pdev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FX ||
+ pdev->subsystem_device == PCI_SUBDEVICE_ID_AT_2701FX)) {
+ if (lp->options & PCNET32_PORT_ASEL) {
+ lp->options = PCNET32_PORT_FD | PCNET32_PORT_100;
+ netif_printk(lp, link, KERN_DEBUG, dev,
+ "Setting 100Mb-Full Duplex\n");
+ }
+ }
+ if (lp->phycount < 2) {
+ /*
+ * 24 Jun 2004 according AMD, in order to change the PHY,
+ * DANAS (or DISPM for 79C976) must be set; then select the speed,
+ * duplex, and/or enable auto negotiation, and clear DANAS
+ */
+ if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
+ lp->a->write_bcr(ioaddr, 32,
+ lp->a->read_bcr(ioaddr, 32) | 0x0080);
+ /* disable Auto Negotiation, set 10Mpbs, HD */
+ val = lp->a->read_bcr(ioaddr, 32) & ~0xb8;
+ if (lp->options & PCNET32_PORT_FD)
+ val |= 0x10;
+ if (lp->options & PCNET32_PORT_100)
+ val |= 0x08;
+ lp->a->write_bcr(ioaddr, 32, val);
+ } else {
+ if (lp->options & PCNET32_PORT_ASEL) {
+ lp->a->write_bcr(ioaddr, 32,
+ lp->a->read_bcr(ioaddr,
+ 32) | 0x0080);
+ /* enable auto negotiate, setup, disable fd */
+ val = lp->a->read_bcr(ioaddr, 32) & ~0x98;
+ val |= 0x20;
+ lp->a->write_bcr(ioaddr, 32, val);
+ }
+ }
+ } else {
+ int first_phy = -1;
+ u16 bmcr;
+ u32 bcr9;
+ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
+
+ /*
+ * There is really no good other way to handle multiple PHYs
+ * other than turning off all automatics
+ */
+ val = lp->a->read_bcr(ioaddr, 2);
+ lp->a->write_bcr(ioaddr, 2, val & ~2);
+ val = lp->a->read_bcr(ioaddr, 32);
+ lp->a->write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */
+
+ if (!(lp->options & PCNET32_PORT_ASEL)) {
+ /* setup ecmd */
+ ecmd.port = PORT_MII;
+ ecmd.transceiver = XCVR_INTERNAL;
+ ecmd.autoneg = AUTONEG_DISABLE;
+ ethtool_cmd_speed_set(&ecmd,
+ (lp->options & PCNET32_PORT_100) ?
+ SPEED_100 : SPEED_10);
+ bcr9 = lp->a->read_bcr(ioaddr, 9);
+
+ if (lp->options & PCNET32_PORT_FD) {
+ ecmd.duplex = DUPLEX_FULL;
+ bcr9 |= (1 << 0);
+ } else {
+ ecmd.duplex = DUPLEX_HALF;
+ bcr9 |= ~(1 << 0);
+ }
+ lp->a->write_bcr(ioaddr, 9, bcr9);
+ }
+
+ for (i = 0; i < PCNET32_MAX_PHYS; i++) {
+ if (lp->phymask & (1 << i)) {
+ /* isolate all but the first PHY */
+ bmcr = mdio_read(dev, i, MII_BMCR);
+ if (first_phy == -1) {
+ first_phy = i;
+ mdio_write(dev, i, MII_BMCR,
+ bmcr & ~BMCR_ISOLATE);
+ } else {
+ mdio_write(dev, i, MII_BMCR,
+ bmcr | BMCR_ISOLATE);
+ }
+ /* use mii_ethtool_sset to setup PHY */
+ lp->mii_if.phy_id = i;
+ ecmd.phy_address = i;
+ if (lp->options & PCNET32_PORT_ASEL) {
+ mii_ethtool_gset(&lp->mii_if, &ecmd);
+ ecmd.autoneg = AUTONEG_ENABLE;
+ }
+ mii_ethtool_sset(&lp->mii_if, &ecmd);
+ }
+ }
+ lp->mii_if.phy_id = first_phy;
+ netif_info(lp, link, dev, "Using PHY number %d\n", first_phy);
+ }
+
+#ifdef DO_DXSUFLO
+ if (lp->dxsuflo) { /* Disable transmit stop on underflow */
+ val = lp->a->read_csr(ioaddr, CSR3);
+ val |= 0x40;
+ lp->a->write_csr(ioaddr, CSR3, val);
+ }
+#endif
+
+ lp->init_block->mode =
+ cpu_to_le16((lp->options & PCNET32_PORT_PORTSEL) << 7);
+ pcnet32_load_multicast(dev);
+
+ if (pcnet32_init_ring(dev)) {
+ rc = -ENOMEM;
+ goto err_free_ring;
+ }
+
+ napi_enable(&lp->napi);
+
+ /* Re-initialize the PCNET32, and start it when done. */
+ lp->a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff));
+ lp->a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16));
+
+ lp->a->write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INIT);
+
+ netif_start_queue(dev);
+
+ if (lp->chip_version >= PCNET32_79C970A) {
+ /* Print the link status and start the watchdog */
+ pcnet32_check_media(dev, 1);
+ mod_timer(&lp->watchdog_timer, PCNET32_WATCHDOG_TIMEOUT);
+ }
+
+ i = 0;
+ while (i++ < 100)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_IDON)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_NORMAL);
+
+ netif_printk(lp, ifup, KERN_DEBUG, dev,
+ "pcnet32 open after %d ticks, init block %#x csr0 %4.4x\n",
+ i,
+ (u32) (lp->init_dma_addr),
+ lp->a->read_csr(ioaddr, CSR0));
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return 0; /* Always succeed */
+
+err_free_ring:
+ /* free any allocated skbuffs */
+ pcnet32_purge_rx_ring(dev);
+
+ /*
+ * Switch back to 16bit mode to avoid problems with dumb
+ * DOS packet driver after a warm reboot
+ */
+ lp->a->write_bcr(ioaddr, 20, 4);
+
+err_free_irq:
+ spin_unlock_irqrestore(&lp->lock, flags);
+ free_irq(dev->irq, dev);
+ return rc;
+}
+
+/*
+ * The LANCE has been halted for one reason or another (busmaster memory
+ * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ * etc.). Modern LANCE variants always reload their ring-buffer
+ * configuration when restarted, so we must reinitialize our ring
+ * context before restarting. As part of this reinitialization,
+ * find all packets still on the Tx ring and pretend that they had been
+ * sent (in effect, drop the packets on the floor) - the higher-level
+ * protocols will time out and retransmit. It'd be better to shuffle
+ * these skbs to a temp list and then actually re-Tx them after
+ * restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
+ */
+
+static void pcnet32_purge_tx_ring(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < lp->tx_ring_size; i++) {
+ lp->tx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
+ if (lp->tx_skbuff[i]) {
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->tx_dma_addr[i]))
+ pci_unmap_single(lp->pci_dev,
+ lp->tx_dma_addr[i],
+ lp->tx_skbuff[i]->len,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(lp->tx_skbuff[i]);
+ }
+ lp->tx_skbuff[i] = NULL;
+ lp->tx_dma_addr[i] = 0;
+ }
+}
+
+/* Initialize the PCNET32 Rx and Tx rings. */
+static int pcnet32_init_ring(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int i;
+
+ lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < lp->rx_ring_size; i++) {
+ struct sk_buff *rx_skbuff = lp->rx_skbuff[i];
+ if (rx_skbuff == NULL) {
+ lp->rx_skbuff[i] = netdev_alloc_skb(dev, PKT_BUF_SKB);
+ rx_skbuff = lp->rx_skbuff[i];
+ if (!rx_skbuff) {
+ /* there is not much we can do at this point */
+ netif_err(lp, drv, dev, "%s netdev_alloc_skb failed\n",
+ __func__);
+ return -1;
+ }
+ skb_reserve(rx_skbuff, NET_IP_ALIGN);
+ }
+
+ rmb();
+ if (lp->rx_dma_addr[i] == 0) {
+ lp->rx_dma_addr[i] =
+ pci_map_single(lp->pci_dev, rx_skbuff->data,
+ PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[i])) {
+ /* there is not much we can do at this point */
+ netif_err(lp, drv, dev,
+ "%s pci dma mapping error\n",
+ __func__);
+ return -1;
+ }
+ }
+ lp->rx_ring[i].base = cpu_to_le32(lp->rx_dma_addr[i]);
+ lp->rx_ring[i].buf_length = cpu_to_le16(NEG_BUF_SIZE);
+ wmb(); /* Make sure owner changes after all others are visible */
+ lp->rx_ring[i].status = cpu_to_le16(0x8000);
+ }
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ * the upper ownership bit. */
+ for (i = 0; i < lp->tx_ring_size; i++) {
+ lp->tx_ring[i].status = 0; /* CPU owns buffer */
+ wmb(); /* Make sure adapter sees owner change */
+ lp->tx_ring[i].base = 0;
+ lp->tx_dma_addr[i] = 0;
+ }
+
+ lp->init_block->tlen_rlen =
+ cpu_to_le16(lp->tx_len_bits | lp->rx_len_bits);
+ for (i = 0; i < 6; i++)
+ lp->init_block->phys_addr[i] = dev->dev_addr[i];
+ lp->init_block->rx_ring = cpu_to_le32(lp->rx_ring_dma_addr);
+ lp->init_block->tx_ring = cpu_to_le32(lp->tx_ring_dma_addr);
+ wmb(); /* Make sure all changes are visible */
+ return 0;
+}
+
+/* the pcnet32 has been issued a stop or reset. Wait for the stop bit
+ * then flush the pending transmit operations, re-initialize the ring,
+ * and tell the chip to initialize.
+ */
+static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int i;
+
+ /* wait for stop */
+ for (i = 0; i < 100; i++)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_STOP)
+ break;
+
+ if (i >= 100)
+ netif_err(lp, drv, dev, "%s timed out waiting for stop\n",
+ __func__);
+
+ pcnet32_purge_tx_ring(dev);
+ if (pcnet32_init_ring(dev))
+ return;
+
+ /* ReInit Ring */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INIT);
+ i = 0;
+ while (i++ < 1000)
+ if (lp->a->read_csr(ioaddr, CSR0) & CSR0_IDON)
+ break;
+
+ lp->a->write_csr(ioaddr, CSR0, csr0_bits);
+}
+
+static void pcnet32_tx_timeout(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr, flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ /* Transmitter timeout, serious problems. */
+ if (pcnet32_debug & NETIF_MSG_DRV)
+ pr_err("%s: transmit timed out, status %4.4x, resetting\n",
+ dev->name, lp->a->read_csr(ioaddr, CSR0));
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
+ dev->stats.tx_errors++;
+ if (netif_msg_tx_err(lp)) {
+ int i;
+ printk(KERN_DEBUG
+ " Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+ lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+ lp->cur_rx);
+ for (i = 0; i < lp->rx_ring_size; i++)
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ le32_to_cpu(lp->rx_ring[i].base),
+ (-le16_to_cpu(lp->rx_ring[i].buf_length)) &
+ 0xffff, le32_to_cpu(lp->rx_ring[i].msg_length),
+ le16_to_cpu(lp->rx_ring[i].status));
+ for (i = 0; i < lp->tx_ring_size; i++)
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ le32_to_cpu(lp->tx_ring[i].base),
+ (-le16_to_cpu(lp->tx_ring[i].length)) & 0xffff,
+ le32_to_cpu(lp->tx_ring[i].misc),
+ le16_to_cpu(lp->tx_ring[i].status));
+ printk("\n");
+ }
+ pcnet32_restart(dev, CSR0_NORMAL);
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static netdev_tx_t pcnet32_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ u16 status;
+ int entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ netif_printk(lp, tx_queued, KERN_DEBUG, dev,
+ "%s() called, csr0 %4.4x\n",
+ __func__, lp->a->read_csr(ioaddr, CSR0));
+
+ /* Default status -- will not enable Successful-TxDone
+ * interrupt when that option is available to us.
+ */
+ status = 0x8300;
+
+ /* Fill in a Tx ring entry */
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & lp->tx_mod_mask;
+
+ /* Caution: the write order is important here, set the status
+ * with the "ownership" bits last. */
+
+ lp->tx_ring[entry].length = cpu_to_le16(-skb->len);
+
+ lp->tx_ring[entry].misc = 0x00000000;
+
+ lp->tx_dma_addr[entry] =
+ pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, lp->tx_dma_addr[entry])) {
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ goto drop_packet;
+ }
+ lp->tx_skbuff[entry] = skb;
+ lp->tx_ring[entry].base = cpu_to_le32(lp->tx_dma_addr[entry]);
+ wmb(); /* Make sure owner changes after all others are visible */
+ lp->tx_ring[entry].status = cpu_to_le16(status);
+
+ lp->cur_tx++;
+ dev->stats.tx_bytes += skb->len;
+
+ /* Trigger an immediate send poll. */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN | CSR0_TXPOLL);
+
+ if (lp->tx_ring[(entry + 1) & lp->tx_mod_mask].base != 0) {
+ lp->tx_full = 1;
+ netif_stop_queue(dev);
+ }
+drop_packet:
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return NETDEV_TX_OK;
+}
+
+/* The PCNET32 interrupt handler. */
+static irqreturn_t
+pcnet32_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct pcnet32_private *lp;
+ unsigned long ioaddr;
+ u16 csr0;
+ int boguscnt = max_interrupt_work;
+
+ ioaddr = dev->base_addr;
+ lp = netdev_priv(dev);
+
+ spin_lock(&lp->lock);
+
+ csr0 = lp->a->read_csr(ioaddr, CSR0);
+ while ((csr0 & 0x8f00) && --boguscnt >= 0) {
+ if (csr0 == 0xffff)
+ break; /* PCMCIA remove happened */
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ lp->a->write_csr(ioaddr, CSR0, csr0 & ~0x004f);
+
+ netif_printk(lp, intr, KERN_DEBUG, dev,
+ "interrupt csr0=%#2.2x new csr=%#2.2x\n",
+ csr0, lp->a->read_csr(ioaddr, CSR0));
+
+ /* Log misc errors. */
+ if (csr0 & 0x4000)
+ dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & 0x1000) {
+ /*
+ * This happens when our receive ring is full. This
+ * shouldn't be a problem as we will see normal rx
+ * interrupts for the frames in the receive ring. But
+ * there are some PCI chipsets (I can reproduce this
+ * on SP3G with Intel saturn chipset) which have
+ * sometimes problems and will fill up the receive
+ * ring with error descriptors. In this situation we
+ * don't get a rx interrupt, but a missed frame
+ * interrupt sooner or later.
+ */
+ dev->stats.rx_errors++; /* Missed a Rx frame. */
+ }
+ if (csr0 & 0x0800) {
+ netif_err(lp, drv, dev, "Bus master arbitration failure, status %4.4x\n",
+ csr0);
+ /* unlike for the lance, there is no restart needed */
+ }
+ if (napi_schedule_prep(&lp->napi)) {
+ u16 val;
+ /* set interrupt masks */
+ val = lp->a->read_csr(ioaddr, CSR3);
+ val |= 0x5f00;
+ lp->a->write_csr(ioaddr, CSR3, val);
+
+ __napi_schedule(&lp->napi);
+ break;
+ }
+ csr0 = lp->a->read_csr(ioaddr, CSR0);
+ }
+
+ netif_printk(lp, intr, KERN_DEBUG, dev,
+ "exiting interrupt, csr0=%#4.4x\n",
+ lp->a->read_csr(ioaddr, CSR0));
+
+ spin_unlock(&lp->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int pcnet32_close(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ del_timer_sync(&lp->watchdog_timer);
+
+ netif_stop_queue(dev);
+ napi_disable(&lp->napi);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ dev->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
+
+ netif_printk(lp, ifdown, KERN_DEBUG, dev,
+ "Shutting down ethercard, status was %2.2x\n",
+ lp->a->read_csr(ioaddr, CSR0));
+
+ /* We stop the PCNET32 here -- it occasionally polls memory if we don't. */
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
+
+ /*
+ * Switch back to 16bit mode to avoid problems with dumb
+ * DOS packet driver after a warm reboot
+ */
+ lp->a->write_bcr(ioaddr, 20, 4);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ free_irq(dev->irq, dev);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ pcnet32_purge_rx_ring(dev);
+ pcnet32_purge_tx_ring(dev);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return 0;
+}
+
+static struct net_device_stats *pcnet32_get_stats(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ dev->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return &dev->stats;
+}
+
+/* taken from the sunlance driver, which it took from the depca driver */
+static void pcnet32_load_multicast(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ volatile struct pcnet32_init_block *ib = lp->init_block;
+ volatile __le16 *mcast_table = (__le16 *)ib->filter;
+ struct netdev_hw_addr *ha;
+ unsigned long ioaddr = dev->base_addr;
+ int i;
+ u32 crc;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI) {
+ ib->filter[0] = cpu_to_le32(~0U);
+ ib->filter[1] = cpu_to_le32(~0U);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);
+ return;
+ }
+ /* clear the multicast filter */
+ ib->filter[0] = 0;
+ ib->filter[1] = 0;
+
+ /* Add addresses */
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc_le(6, ha->addr);
+ crc = crc >> 26;
+ mcast_table[crc >> 4] |= cpu_to_le16(1 << (crc & 0xf));
+ }
+ for (i = 0; i < 4; i++)
+ lp->a->write_csr(ioaddr, PCNET32_MC_FILTER + i,
+ le16_to_cpu(mcast_table[i]));
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void pcnet32_set_multicast_list(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr, flags;
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int csr15, suspended;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ suspended = pcnet32_suspend(dev, &flags, 0);
+ csr15 = lp->a->read_csr(ioaddr, CSR15);
+ if (dev->flags & IFF_PROMISC) {
+ /* Log any net taps. */
+ netif_info(lp, hw, dev, "Promiscuous mode enabled\n");
+ lp->init_block->mode =
+ cpu_to_le16(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
+ 7);
+ lp->a->write_csr(ioaddr, CSR15, csr15 | 0x8000);
+ } else {
+ lp->init_block->mode =
+ cpu_to_le16((lp->options & PCNET32_PORT_PORTSEL) << 7);
+ lp->a->write_csr(ioaddr, CSR15, csr15 & 0x7fff);
+ pcnet32_load_multicast(dev);
+ }
+
+ if (suspended) {
+ int csr5;
+ /* clear SUSPEND (SPND) - CSR5 bit 0 */
+ csr5 = lp->a->read_csr(ioaddr, CSR5);
+ lp->a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
+ } else {
+ lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);
+ pcnet32_restart(dev, CSR0_NORMAL);
+ netif_wake_queue(dev);
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/* This routine assumes that the lp->lock is held */
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ u16 val_out;
+
+ if (!lp->mii)
+ return 0;
+
+ lp->a->write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ val_out = lp->a->read_bcr(ioaddr, 34);
+
+ return val_out;
+}
+
+/* This routine assumes that the lp->lock is held */
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+
+ if (!lp->mii)
+ return;
+
+ lp->a->write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ lp->a->write_bcr(ioaddr, 34, val);
+}
+
+static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int rc;
+ unsigned long flags;
+
+ /* SIOC[GS]MIIxxx ioctls */
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ rc = -EOPNOTSUPP;
+ }
+
+ return rc;
+}
+
+static int pcnet32_check_otherphy(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ struct mii_if_info mii = lp->mii_if;
+ u16 bmcr;
+ int i;
+
+ for (i = 0; i < PCNET32_MAX_PHYS; i++) {
+ if (i == lp->mii_if.phy_id)
+ continue; /* skip active phy */
+ if (lp->phymask & (1 << i)) {
+ mii.phy_id = i;
+ if (mii_link_ok(&mii)) {
+ /* found PHY with active link */
+ netif_info(lp, link, dev, "Using PHY number %d\n",
+ i);
+
+ /* isolate inactive phy */
+ bmcr =
+ mdio_read(dev, lp->mii_if.phy_id, MII_BMCR);
+ mdio_write(dev, lp->mii_if.phy_id, MII_BMCR,
+ bmcr | BMCR_ISOLATE);
+
+ /* de-isolate new phy */
+ bmcr = mdio_read(dev, i, MII_BMCR);
+ mdio_write(dev, i, MII_BMCR,
+ bmcr & ~BMCR_ISOLATE);
+
+ /* set new phy address */
+ lp->mii_if.phy_id = i;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Show the status of the media. Similar to mii_check_media however it
+ * correctly shows the link speed for all (tested) pcnet32 variants.
+ * Devices with no mii just report link state without speed.
+ *
+ * Caller is assumed to hold and release the lp->lock.
+ */
+
+static void pcnet32_check_media(struct net_device *dev, int verbose)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ int curr_link;
+ int prev_link = netif_carrier_ok(dev) ? 1 : 0;
+ u32 bcr9;
+
+ if (lp->mii) {
+ curr_link = mii_link_ok(&lp->mii_if);
+ } else {
+ ulong ioaddr = dev->base_addr; /* card base I/O address */
+ curr_link = (lp->a->read_bcr(ioaddr, 4) != 0xc0);
+ }
+ if (!curr_link) {
+ if (prev_link || verbose) {
+ netif_carrier_off(dev);
+ netif_info(lp, link, dev, "link down\n");
+ }
+ if (lp->phycount > 1) {
+ curr_link = pcnet32_check_otherphy(dev);
+ prev_link = 0;
+ }
+ } else if (verbose || !prev_link) {
+ netif_carrier_on(dev);
+ if (lp->mii) {
+ if (netif_msg_link(lp)) {
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_GSET };
+ mii_ethtool_gset(&lp->mii_if, &ecmd);
+ netdev_info(dev, "link up, %uMbps, %s-duplex\n",
+ ethtool_cmd_speed(&ecmd),
+ (ecmd.duplex == DUPLEX_FULL)
+ ? "full" : "half");
+ }
+ bcr9 = lp->a->read_bcr(dev->base_addr, 9);
+ if ((bcr9 & (1 << 0)) != lp->mii_if.full_duplex) {
+ if (lp->mii_if.full_duplex)
+ bcr9 |= (1 << 0);
+ else
+ bcr9 &= ~(1 << 0);
+ lp->a->write_bcr(dev->base_addr, 9, bcr9);
+ }
+ } else {
+ netif_info(lp, link, dev, "link up\n");
+ }
+ }
+}
+
+/*
+ * Check for loss of link and link establishment.
+ * Can not use mii_check_media because it does nothing if mode is forced.
+ */
+
+static void pcnet32_watchdog(struct net_device *dev)
+{
+ struct pcnet32_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Print the link status if it has changed */
+ spin_lock_irqsave(&lp->lock, flags);
+ pcnet32_check_media(dev, 0);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ mod_timer(&lp->watchdog_timer, round_jiffies(PCNET32_WATCHDOG_TIMEOUT));
+}
+
+static int pcnet32_pm_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ netif_device_detach(dev);
+ pcnet32_close(dev);
+ }
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int pcnet32_pm_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ if (netif_running(dev)) {
+ pcnet32_open(dev);
+ netif_device_attach(dev);
+ }
+ return 0;
+}
+
+static void pcnet32_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (dev) {
+ struct pcnet32_private *lp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ pcnet32_free_ring(dev);
+ release_region(dev->base_addr, PCNET32_TOTAL_SIZE);
+ pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),
+ lp->init_block, lp->init_dma_addr);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+ }
+}
+
+static struct pci_driver pcnet32_driver = {
+ .name = DRV_NAME,
+ .probe = pcnet32_probe_pci,
+ .remove = pcnet32_remove_one,
+ .id_table = pcnet32_pci_tbl,
+ .suspend = pcnet32_pm_suspend,
+ .resume = pcnet32_pm_resume,
+};
+
+/* An additional parameter that may be passed in... */
+static int debug = -1;
+static int tx_start_pt = -1;
+static int pcnet32_have_pci;
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, DRV_NAME " debug level");
+module_param(max_interrupt_work, int, 0);
+MODULE_PARM_DESC(max_interrupt_work,
+ DRV_NAME " maximum events handled per interrupt");
+module_param(rx_copybreak, int, 0);
+MODULE_PARM_DESC(rx_copybreak,
+ DRV_NAME " copy breakpoint for copy-only-tiny-frames");
+module_param(tx_start_pt, int, 0);
+MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)");
+module_param(pcnet32vlb, int, 0);
+MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)");
+module_param_array(options, int, NULL, 0);
+MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)");
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)");
+/* Module Parameter for HomePNA cards added by Patrick Simmons, 2004 */
+module_param_array(homepna, int, NULL, 0);
+MODULE_PARM_DESC(homepna,
+ DRV_NAME
+ " mode for 79C978 cards (1 for HomePNA, 0 for Ethernet, default Ethernet");
+
+MODULE_AUTHOR("Thomas Bogendoerfer");
+MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
+MODULE_LICENSE("GPL");
+
+#define PCNET32_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+static int __init pcnet32_init_module(void)
+{
+ pr_info("%s", version);
+
+ pcnet32_debug = netif_msg_init(debug, PCNET32_MSG_DEFAULT);
+
+ if ((tx_start_pt >= 0) && (tx_start_pt <= 3))
+ tx_start = tx_start_pt;
+
+ /* find the PCI devices */
+ if (!pci_register_driver(&pcnet32_driver))
+ pcnet32_have_pci = 1;
+
+ /* should we find any remaining VLbus devices ? */
+ if (pcnet32vlb)
+ pcnet32_probe_vlbus(pcnet32_portlist);
+
+ if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE))
+ pr_info("%d cards_found\n", cards_found);
+
+ return (pcnet32_have_pci + cards_found) ? 0 : -ENODEV;
+}
+
+static void __exit pcnet32_cleanup_module(void)
+{
+ struct net_device *next_dev;
+
+ while (pcnet32_dev) {
+ struct pcnet32_private *lp = netdev_priv(pcnet32_dev);
+ next_dev = lp->next;
+ unregister_netdev(pcnet32_dev);
+ pcnet32_free_ring(pcnet32_dev);
+ release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
+ pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),
+ lp->init_block, lp->init_dma_addr);
+ free_netdev(pcnet32_dev);
+ pcnet32_dev = next_dev;
+ }
+
+ if (pcnet32_have_pci)
+ pci_unregister_driver(&pcnet32_driver);
+}
+
+module_init(pcnet32_init_module);
+module_exit(pcnet32_cleanup_module);
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/net/ethernet/amd/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c
new file mode 100644
index 00000000000..3d8c6b2cdea
--- /dev/null
+++ b/drivers/net/ethernet/amd/sun3lance.c
@@ -0,0 +1,956 @@
+/* sun3lance.c: Ethernet driver for SUN3 Lance chip */
+/*
+
+ Sun3 Lance ethernet driver, by Sam Creasey (sammy@users.qual.net).
+ This driver is a part of the linux kernel, and is thus distributed
+ under the GNU General Public License.
+
+ The values used in LANCE_OBIO and LANCE_IRQ seem to be empirically
+ true for the correct IRQ and address of the lance registers. They
+ have not been widely tested, however. What we probably need is a
+ "proper" way to search for a device in the sun3's prom, but, alas,
+ linux has no such thing.
+
+ This driver is largely based on atarilance.c, by Roman Hodek. Other
+ sources of inspiration were the NetBSD sun3 am7990 driver, and the
+ linux sparc lance driver (sunlance.c).
+
+ There are more assumptions made throughout this driver, it almost
+ certainly still needs work, but it does work at least for RARP/BOOTP and
+ mounting the root NFS filesystem.
+
+*/
+
+static char *version = "sun3lance.c: v1.2 1/12/2001 Sam Creasey (sammy@sammy.net)\n";
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/cacheflush.h>
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/dvma.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
+#ifdef CONFIG_SUN3
+#include <asm/sun3mmu.h>
+#else
+#include <asm/sun3xprom.h>
+#endif
+
+/* sun3/60 addr/irq for the lance chip. If your sun is different,
+ change this. */
+#define LANCE_OBIO 0x120000
+#define LANCE_IRQ IRQ_AUTO_3
+
+/* Debug level:
+ * 0 = silent, print only serious errors
+ * 1 = normal, print error messages
+ * 2 = debug, print debug infos
+ * 3 = debug, print even more debug infos (packet data)
+ */
+
+#define LANCE_DEBUG 0
+
+#ifdef LANCE_DEBUG
+static int lance_debug = LANCE_DEBUG;
+#else
+static int lance_debug = 1;
+#endif
+module_param(lance_debug, int, 0);
+MODULE_PARM_DESC(lance_debug, "SUN3 Lance debug level (0-3)");
+MODULE_LICENSE("GPL");
+
+#define DPRINTK(n,a) \
+ do { \
+ if (lance_debug >= n) \
+ printk a; \
+ } while( 0 )
+
+
+/* we're only using 32k of memory, so we use 4 TX
+ buffers and 16 RX buffers. These values are expressed as log2. */
+
+#define TX_LOG_RING_SIZE 3
+#define RX_LOG_RING_SIZE 5
+
+/* These are the derived values */
+
+#define TX_RING_SIZE (1 << TX_LOG_RING_SIZE)
+#define TX_RING_LEN_BITS (TX_LOG_RING_SIZE << 5)
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+
+#define RX_RING_SIZE (1 << RX_LOG_RING_SIZE)
+#define RX_RING_LEN_BITS (RX_LOG_RING_SIZE << 5)
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+
+/* Definitions for packet buffer access: */
+#define PKT_BUF_SZ 1544
+
+/* Get the address of a packet buffer corresponding to a given buffer head */
+#define PKTBUF_ADDR(head) (void *)((unsigned long)(MEM) | (head)->base)
+
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ unsigned short base; /* Low word of base addr */
+ volatile unsigned char flag;
+ unsigned char base_hi; /* High word of base addr (unused) */
+ short buf_length; /* This length is 2s complement! */
+ volatile short msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ unsigned short base; /* Low word of base addr */
+ volatile unsigned char flag;
+ unsigned char base_hi; /* High word of base addr (unused) */
+ short length; /* Length is 2s complement! */
+ volatile short misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode */
+ unsigned char hwaddr[6]; /* Physical ethernet address */
+ unsigned int filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with length bits. */
+ unsigned short rdra;
+ unsigned short rlen;
+ unsigned short tdra;
+ unsigned short tlen;
+ unsigned short pad[4]; /* is thie needed? */
+};
+
+/* The whole layout of the Lance shared memory */
+struct lance_memory {
+ struct lance_init_block init;
+ struct lance_tx_head tx_head[TX_RING_SIZE];
+ struct lance_rx_head rx_head[RX_RING_SIZE];
+ char rx_data[RX_RING_SIZE][PKT_BUF_SZ];
+ char tx_data[TX_RING_SIZE][PKT_BUF_SZ];
+};
+
+/* The driver's private device structure */
+
+struct lance_private {
+ volatile unsigned short *iobase;
+ struct lance_memory *mem;
+ int new_rx, new_tx; /* The next free ring entry */
+ int old_tx, old_rx; /* ring entry to be processed */
+/* These two must be longs for set_bit() */
+ long tx_full;
+ long lock;
+};
+
+/* I/O register access macros */
+
+#define MEM lp->mem
+#define DREG lp->iobase[0]
+#define AREG lp->iobase[1]
+#define REGA(a) (*( AREG = (a), &DREG ))
+
+/* Definitions for the Lance */
+
+/* tx_head flags */
+#define TMD1_ENP 0x01 /* end of packet */
+#define TMD1_STP 0x02 /* start of packet */
+#define TMD1_DEF 0x04 /* deferred */
+#define TMD1_ONE 0x08 /* one retry needed */
+#define TMD1_MORE 0x10 /* more than one retry needed */
+#define TMD1_ERR 0x40 /* error summary */
+#define TMD1_OWN 0x80 /* ownership (set: chip owns) */
+
+#define TMD1_OWN_CHIP TMD1_OWN
+#define TMD1_OWN_HOST 0
+
+/* tx_head misc field */
+#define TMD3_TDR 0x03FF /* Time Domain Reflectometry counter */
+#define TMD3_RTRY 0x0400 /* failed after 16 retries */
+#define TMD3_LCAR 0x0800 /* carrier lost */
+#define TMD3_LCOL 0x1000 /* late collision */
+#define TMD3_UFLO 0x4000 /* underflow (late memory) */
+#define TMD3_BUFF 0x8000 /* buffering error (no ENP) */
+
+/* rx_head flags */
+#define RMD1_ENP 0x01 /* end of packet */
+#define RMD1_STP 0x02 /* start of packet */
+#define RMD1_BUFF 0x04 /* buffer error */
+#define RMD1_CRC 0x08 /* CRC error */
+#define RMD1_OFLO 0x10 /* overflow */
+#define RMD1_FRAM 0x20 /* framing error */
+#define RMD1_ERR 0x40 /* error summary */
+#define RMD1_OWN 0x80 /* ownership (set: ship owns) */
+
+#define RMD1_OWN_CHIP RMD1_OWN
+#define RMD1_OWN_HOST 0
+
+/* register names */
+#define CSR0 0 /* mode/status */
+#define CSR1 1 /* init block addr (low) */
+#define CSR2 2 /* init block addr (high) */
+#define CSR3 3 /* misc */
+#define CSR8 8 /* address filter */
+#define CSR15 15 /* promiscuous mode */
+
+/* CSR0 */
+/* (R=readable, W=writeable, S=set on write, C=clear on write) */
+#define CSR0_INIT 0x0001 /* initialize (RS) */
+#define CSR0_STRT 0x0002 /* start (RS) */
+#define CSR0_STOP 0x0004 /* stop (RS) */
+#define CSR0_TDMD 0x0008 /* transmit demand (RS) */
+#define CSR0_TXON 0x0010 /* transmitter on (R) */
+#define CSR0_RXON 0x0020 /* receiver on (R) */
+#define CSR0_INEA 0x0040 /* interrupt enable (RW) */
+#define CSR0_INTR 0x0080 /* interrupt active (R) */
+#define CSR0_IDON 0x0100 /* initialization done (RC) */
+#define CSR0_TINT 0x0200 /* transmitter interrupt (RC) */
+#define CSR0_RINT 0x0400 /* receiver interrupt (RC) */
+#define CSR0_MERR 0x0800 /* memory error (RC) */
+#define CSR0_MISS 0x1000 /* missed frame (RC) */
+#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) (RC) */
+#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits (RC) */
+#define CSR0_ERR 0x8000 /* error (RC) */
+
+/* CSR3 */
+#define CSR3_BCON 0x0001 /* byte control */
+#define CSR3_ACON 0x0002 /* ALE control */
+#define CSR3_BSWP 0x0004 /* byte swap (1=big endian) */
+
+/***************************** Prototypes *****************************/
+
+static int lance_probe( struct net_device *dev);
+static int lance_open( struct net_device *dev );
+static void lance_init_ring( struct net_device *dev );
+static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev );
+static irqreturn_t lance_interrupt( int irq, void *dev_id);
+static int lance_rx( struct net_device *dev );
+static int lance_close( struct net_device *dev );
+static void set_multicast_list( struct net_device *dev );
+
+/************************* End of Prototypes **************************/
+
+struct net_device * __init sun3lance_probe(int unit)
+{
+ struct net_device *dev;
+ static int found;
+ int err = -ENODEV;
+
+ if (!MACH_IS_SUN3 && !MACH_IS_SUN3X)
+ return ERR_PTR(-ENODEV);
+
+ /* check that this machine has an onboard lance */
+ switch(idprom->id_machtype) {
+ case SM_SUN3|SM_3_50:
+ case SM_SUN3|SM_3_60:
+ case SM_SUN3X|SM_3_80:
+ /* these machines have lance */
+ break;
+
+ default:
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (found)
+ return ERR_PTR(-ENODEV);
+
+ dev = alloc_etherdev(sizeof(struct lance_private));
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ if (!lance_probe(dev))
+ goto out;
+
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ found = 1;
+ return dev;
+
+out1:
+#ifdef CONFIG_SUN3
+ iounmap((void __iomem *)dev->base_addr);
+#endif
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static const struct net_device_ops lance_netdev_ops = {
+ .ndo_open = lance_open,
+ .ndo_stop = lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_set_mac_address = NULL,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __init lance_probe( struct net_device *dev)
+{
+ unsigned long ioaddr;
+
+ struct lance_private *lp;
+ int i;
+ static int did_version;
+ volatile unsigned short *ioaddr_probe;
+ unsigned short tmp1, tmp2;
+
+#ifdef CONFIG_SUN3
+ ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE);
+ if (!ioaddr)
+ return 0;
+#else
+ ioaddr = SUN3X_LANCE;
+#endif
+
+ /* test to see if there's really a lance here */
+ /* (CSRO_INIT shouldn't be readable) */
+
+ ioaddr_probe = (volatile unsigned short *)ioaddr;
+ tmp1 = ioaddr_probe[0];
+ tmp2 = ioaddr_probe[1];
+
+ ioaddr_probe[1] = CSR0;
+ ioaddr_probe[0] = CSR0_INIT | CSR0_STOP;
+
+ if(ioaddr_probe[0] != CSR0_STOP) {
+ ioaddr_probe[0] = tmp1;
+ ioaddr_probe[1] = tmp2;
+
+#ifdef CONFIG_SUN3
+ iounmap((void __iomem *)ioaddr);
+#endif
+ return 0;
+ }
+
+ lp = netdev_priv(dev);
+
+ /* XXX - leak? */
+ MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000);
+ if (MEM == NULL) {
+#ifdef CONFIG_SUN3
+ iounmap((void __iomem *)ioaddr);
+#endif
+ printk(KERN_WARNING "SUN3 Lance couldn't allocate DVMA memory\n");
+ return 0;
+ }
+
+ lp->iobase = (volatile unsigned short *)ioaddr;
+ dev->base_addr = (unsigned long)ioaddr; /* informational only */
+
+ REGA(CSR0) = CSR0_STOP;
+
+ if (request_irq(LANCE_IRQ, lance_interrupt, 0, "SUN3 Lance", dev) < 0) {
+#ifdef CONFIG_SUN3
+ iounmap((void __iomem *)ioaddr);
+#endif
+ dvma_free((void *)MEM);
+ printk(KERN_WARNING "SUN3 Lance unable to allocate IRQ\n");
+ return 0;
+ }
+ dev->irq = (unsigned short)LANCE_IRQ;
+
+
+ printk("%s: SUN3 Lance at io %#lx, mem %#lx, irq %d, hwaddr ",
+ dev->name,
+ (unsigned long)ioaddr,
+ (unsigned long)MEM,
+ dev->irq);
+
+ /* copy in the ethernet address from the prom */
+ for(i = 0; i < 6 ; i++)
+ dev->dev_addr[i] = idprom->id_ethaddr[i];
+
+ /* tell the card it's ether address, bytes swapped */
+ MEM->init.hwaddr[0] = dev->dev_addr[1];
+ MEM->init.hwaddr[1] = dev->dev_addr[0];
+ MEM->init.hwaddr[2] = dev->dev_addr[3];
+ MEM->init.hwaddr[3] = dev->dev_addr[2];
+ MEM->init.hwaddr[4] = dev->dev_addr[5];
+ MEM->init.hwaddr[5] = dev->dev_addr[4];
+
+ printk("%pM\n", dev->dev_addr);
+
+ MEM->init.mode = 0x0000;
+ MEM->init.filter[0] = 0x00000000;
+ MEM->init.filter[1] = 0x00000000;
+ MEM->init.rdra = dvma_vtob(MEM->rx_head);
+ MEM->init.rlen = (RX_LOG_RING_SIZE << 13) |
+ (dvma_vtob(MEM->rx_head) >> 16);
+ MEM->init.tdra = dvma_vtob(MEM->tx_head);
+ MEM->init.tlen = (TX_LOG_RING_SIZE << 13) |
+ (dvma_vtob(MEM->tx_head) >> 16);
+
+ DPRINTK(2, ("initaddr: %08lx rx_ring: %08lx tx_ring: %08lx\n",
+ dvma_vtob(&(MEM->init)), dvma_vtob(MEM->rx_head),
+ (dvma_vtob(MEM->tx_head))));
+
+ if (did_version++ == 0)
+ printk( version );
+
+ dev->netdev_ops = &lance_netdev_ops;
+// KLUDGE -- REMOVE ME
+ set_bit(__LINK_STATE_PRESENT, &dev->state);
+
+
+ return 1;
+}
+
+static int lance_open( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int i;
+
+ DPRINTK( 2, ( "%s: lance_open()\n", dev->name ));
+
+ REGA(CSR0) = CSR0_STOP;
+
+ lance_init_ring(dev);
+
+ /* From now on, AREG is kept to point to CSR0 */
+ REGA(CSR0) = CSR0_INIT;
+
+ i = 1000000;
+ while (--i > 0)
+ if (DREG & CSR0_IDON)
+ break;
+ if (i <= 0 || (DREG & CSR0_ERR)) {
+ DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n",
+ dev->name, i, DREG ));
+ DREG = CSR0_STOP;
+ return -EIO;
+ }
+
+ DREG = CSR0_IDON | CSR0_STRT | CSR0_INEA;
+
+ netif_start_queue(dev);
+
+ DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG ));
+
+ return 0;
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+
+static void lance_init_ring( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int i;
+
+ lp->lock = 0;
+ lp->tx_full = 0;
+ lp->new_rx = lp->new_tx = 0;
+ lp->old_rx = lp->old_tx = 0;
+
+ for( i = 0; i < TX_RING_SIZE; i++ ) {
+ MEM->tx_head[i].base = dvma_vtob(MEM->tx_data[i]);
+ MEM->tx_head[i].flag = 0;
+ MEM->tx_head[i].base_hi =
+ (dvma_vtob(MEM->tx_data[i])) >>16;
+ MEM->tx_head[i].length = 0;
+ MEM->tx_head[i].misc = 0;
+ }
+
+ for( i = 0; i < RX_RING_SIZE; i++ ) {
+ MEM->rx_head[i].base = dvma_vtob(MEM->rx_data[i]);
+ MEM->rx_head[i].flag = RMD1_OWN_CHIP;
+ MEM->rx_head[i].base_hi =
+ (dvma_vtob(MEM->rx_data[i])) >> 16;
+ MEM->rx_head[i].buf_length = -PKT_BUF_SZ | 0xf000;
+ MEM->rx_head[i].msg_length = 0;
+ }
+
+ /* tell the card it's ether address, bytes swapped */
+ MEM->init.hwaddr[0] = dev->dev_addr[1];
+ MEM->init.hwaddr[1] = dev->dev_addr[0];
+ MEM->init.hwaddr[2] = dev->dev_addr[3];
+ MEM->init.hwaddr[3] = dev->dev_addr[2];
+ MEM->init.hwaddr[4] = dev->dev_addr[5];
+ MEM->init.hwaddr[5] = dev->dev_addr[4];
+
+ MEM->init.mode = 0x0000;
+ MEM->init.filter[0] = 0x00000000;
+ MEM->init.filter[1] = 0x00000000;
+ MEM->init.rdra = dvma_vtob(MEM->rx_head);
+ MEM->init.rlen = (RX_LOG_RING_SIZE << 13) |
+ (dvma_vtob(MEM->rx_head) >> 16);
+ MEM->init.tdra = dvma_vtob(MEM->tx_head);
+ MEM->init.tlen = (TX_LOG_RING_SIZE << 13) |
+ (dvma_vtob(MEM->tx_head) >> 16);
+
+
+ /* tell the lance the address of its init block */
+ REGA(CSR1) = dvma_vtob(&(MEM->init));
+ REGA(CSR2) = dvma_vtob(&(MEM->init)) >> 16;
+
+#ifdef CONFIG_SUN3X
+ REGA(CSR3) = CSR3_BSWP | CSR3_ACON | CSR3_BCON;
+#else
+ REGA(CSR3) = CSR3_BSWP;
+#endif
+
+}
+
+
+static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int entry, len;
+ struct lance_tx_head *head;
+ unsigned long flags;
+
+ DPRINTK( 1, ( "%s: transmit start.\n",
+ dev->name));
+
+ /* Transmitter timeout, serious problems. */
+ if (netif_queue_stopped(dev)) {
+ int tickssofar = jiffies - dev_trans_start(dev);
+ if (tickssofar < HZ/5)
+ return NETDEV_TX_BUSY;
+
+ DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n",
+ dev->name, DREG ));
+ DREG = CSR0_STOP;
+ /*
+ * Always set BSWP after a STOP as STOP puts it back into
+ * little endian mode.
+ */
+ REGA(CSR3) = CSR3_BSWP;
+ dev->stats.tx_errors++;
+
+ if(lance_debug >= 2) {
+ int i;
+ printk("Ring data: old_tx %d new_tx %d%s new_rx %d\n",
+ lp->old_tx, lp->new_tx,
+ lp->tx_full ? " (full)" : "",
+ lp->new_rx );
+ for( i = 0 ; i < RX_RING_SIZE; i++ )
+ printk( "rx #%d: base=%04x blen=%04x mlen=%04x\n",
+ i, MEM->rx_head[i].base,
+ -MEM->rx_head[i].buf_length,
+ MEM->rx_head[i].msg_length);
+ for( i = 0 ; i < TX_RING_SIZE; i++ )
+ printk("tx #%d: base=%04x len=%04x misc=%04x\n",
+ i, MEM->tx_head[i].base,
+ -MEM->tx_head[i].length,
+ MEM->tx_head[i].misc );
+ }
+
+ lance_init_ring(dev);
+ REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT;
+
+ netif_start_queue(dev);
+
+ return NETDEV_TX_OK;
+ }
+
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+
+ /* Block a timer-based transmit from overlapping with us by
+ stopping the queue for a bit... */
+
+ netif_stop_queue(dev);
+
+ if (test_and_set_bit( 0, (void*)&lp->lock ) != 0) {
+ printk( "%s: tx queue lock!.\n", dev->name);
+ /* don't clear dev->tbusy flag. */
+ return NETDEV_TX_BUSY;
+ }
+
+ AREG = CSR0;
+ DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
+ dev->name, DREG ));
+
+#ifdef CONFIG_SUN3X
+ /* this weirdness doesn't appear on sun3... */
+ if(!(DREG & CSR0_INIT)) {
+ DPRINTK( 1, ("INIT not set, reinitializing...\n"));
+ REGA( CSR0 ) = CSR0_STOP;
+ lance_init_ring(dev);
+ REGA( CSR0 ) = CSR0_INIT | CSR0_STRT;
+ }
+#endif
+
+ /* Fill in a Tx ring entry */
+#if 0
+ if (lance_debug >= 2) {
+ printk( "%s: TX pkt %d type 0x%04x"
+ " from %s to %s"
+ " data at 0x%08x len %d\n",
+ dev->name, lp->new_tx, ((u_short *)skb->data)[6],
+ DEV_ADDR(&skb->data[6]), DEV_ADDR(skb->data),
+ (int)skb->data, (int)skb->len );
+ }
+#endif
+ /* We're not prepared for the int until the last flags are set/reset.
+ * And the int may happen already after setting the OWN_CHIP... */
+ local_irq_save(flags);
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->new_tx;
+ head = &(MEM->tx_head[entry]);
+
+ /* Caution: the write order is important here, set the "ownership" bits
+ * last.
+ */
+
+ /* the sun3's lance needs it's buffer padded to the minimum
+ size */
+ len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+// head->length = -len;
+ head->length = (-len) | 0xf000;
+ head->misc = 0;
+
+ skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len);
+ if (len != skb->len)
+ memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len);
+
+ head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
+ lp->new_tx = (lp->new_tx + 1) & TX_RING_MOD_MASK;
+ dev->stats.tx_bytes += skb->len;
+
+ /* Trigger an immediate send poll. */
+ REGA(CSR0) = CSR0_INEA | CSR0_TDMD | CSR0_STRT;
+ AREG = CSR0;
+ DPRINTK( 2, ( "%s: lance_start_xmit() exiting, csr0 %4.4x.\n",
+ dev->name, DREG ));
+ dev_kfree_skb(skb);
+
+ lp->lock = 0;
+ if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) ==
+ TMD1_OWN_HOST)
+ netif_start_queue(dev);
+
+ local_irq_restore(flags);
+
+ return NETDEV_TX_OK;
+}
+
+/* The LANCE interrupt handler. */
+
+static irqreturn_t lance_interrupt( int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ int csr0;
+ static int in_interrupt;
+
+ if (dev == NULL) {
+ DPRINTK( 1, ( "lance_interrupt(): invalid dev_id\n" ));
+ return IRQ_NONE;
+ }
+
+ if (in_interrupt)
+ DPRINTK( 2, ( "%s: Re-entering the interrupt handler.\n", dev->name ));
+ in_interrupt = 1;
+
+ still_more:
+ flush_cache_all();
+
+ AREG = CSR0;
+ csr0 = DREG;
+
+ /* ack interrupts */
+ DREG = csr0 & (CSR0_TINT | CSR0_RINT | CSR0_IDON);
+
+ /* clear errors */
+ if(csr0 & CSR0_ERR)
+ DREG = CSR0_BABL | CSR0_MERR | CSR0_CERR | CSR0_MISS;
+
+
+ DPRINTK( 2, ( "%s: interrupt csr0=%04x new csr=%04x.\n",
+ dev->name, csr0, DREG ));
+
+ if (csr0 & CSR0_TINT) { /* Tx-done interrupt */
+ int old_tx = lp->old_tx;
+
+// if(lance_debug >= 3) {
+// int i;
+//
+// printk("%s: tx int\n", dev->name);
+//
+// for(i = 0; i < TX_RING_SIZE; i++)
+// printk("ring %d flag=%04x\n", i,
+// MEM->tx_head[i].flag);
+// }
+
+ while( old_tx != lp->new_tx) {
+ struct lance_tx_head *head = &(MEM->tx_head[old_tx]);
+
+ DPRINTK(3, ("on tx_ring %d\n", old_tx));
+
+ if (head->flag & TMD1_OWN_CHIP)
+ break; /* It still hasn't been Txed */
+
+ if (head->flag & TMD1_ERR) {
+ int status = head->misc;
+ dev->stats.tx_errors++;
+ if (status & TMD3_RTRY) dev->stats.tx_aborted_errors++;
+ if (status & TMD3_LCAR) dev->stats.tx_carrier_errors++;
+ if (status & TMD3_LCOL) dev->stats.tx_window_errors++;
+ if (status & (TMD3_UFLO | TMD3_BUFF)) {
+ dev->stats.tx_fifo_errors++;
+ printk("%s: Tx FIFO error\n",
+ dev->name);
+ REGA(CSR0) = CSR0_STOP;
+ REGA(CSR3) = CSR3_BSWP;
+ lance_init_ring(dev);
+ REGA(CSR0) = CSR0_STRT | CSR0_INEA;
+ return IRQ_HANDLED;
+ }
+ } else if(head->flag & (TMD1_ENP | TMD1_STP)) {
+
+ head->flag &= ~(TMD1_ENP | TMD1_STP);
+ if(head->flag & (TMD1_ONE | TMD1_MORE))
+ dev->stats.collisions++;
+
+ dev->stats.tx_packets++;
+ DPRINTK(3, ("cleared tx ring %d\n", old_tx));
+ }
+ old_tx = (old_tx +1) & TX_RING_MOD_MASK;
+ }
+
+ lp->old_tx = old_tx;
+ }
+
+
+ if (netif_queue_stopped(dev)) {
+ /* The ring is no longer full, clear tbusy. */
+ netif_start_queue(dev);
+ netif_wake_queue(dev);
+ }
+
+ if (csr0 & CSR0_RINT) /* Rx interrupt */
+ lance_rx( dev );
+
+ /* Log misc errors. */
+ if (csr0 & CSR0_BABL) dev->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & CSR0_MISS) dev->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & CSR0_MERR) {
+ DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), "
+ "status %04x.\n", dev->name, csr0 ));
+ /* Restart the chip. */
+ REGA(CSR0) = CSR0_STOP;
+ REGA(CSR3) = CSR3_BSWP;
+ lance_init_ring(dev);
+ REGA(CSR0) = CSR0_STRT | CSR0_INEA;
+ }
+
+
+ /* Clear any other interrupt, and set interrupt enable. */
+// DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR |
+// CSR0_IDON | CSR0_INEA;
+
+ REGA(CSR0) = CSR0_INEA;
+
+ if(DREG & (CSR0_RINT | CSR0_TINT)) {
+ DPRINTK(2, ("restarting interrupt, csr0=%#04x\n", DREG));
+ goto still_more;
+ }
+
+ DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n",
+ dev->name, DREG ));
+ in_interrupt = 0;
+ return IRQ_HANDLED;
+}
+
+/* get packet, toss into skbuff */
+static int lance_rx( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int entry = lp->new_rx;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) {
+ struct lance_rx_head *head = &(MEM->rx_head[entry]);
+ int status = head->flag;
+
+ if (status != (RMD1_ENP|RMD1_STP)) { /* There was an error. */
+ /* There is a tricky error noted by John Murphy,
+ <murf@perftech.com> to Russ Nelson: Even with
+ full-sized buffers it's possible for a jabber packet to use two
+ buffers, with only the last correctly noting the error. */
+ if (status & RMD1_ENP) /* Only count a general error at the */
+ dev->stats.rx_errors++; /* end of a packet.*/
+ if (status & RMD1_FRAM) dev->stats.rx_frame_errors++;
+ if (status & RMD1_OFLO) dev->stats.rx_over_errors++;
+ if (status & RMD1_CRC) dev->stats.rx_crc_errors++;
+ if (status & RMD1_BUFF) dev->stats.rx_fifo_errors++;
+ head->flag &= (RMD1_ENP|RMD1_STP);
+ } else {
+ /* Malloc up new buffer, compatible with net-3. */
+// short pkt_len = head->msg_length;// & 0xfff;
+ short pkt_len = (head->msg_length & 0xfff) - 4;
+ struct sk_buff *skb;
+
+ if (pkt_len < 60) {
+ printk( "%s: Runt packet!\n", dev->name );
+ dev->stats.rx_errors++;
+ }
+ else {
+ skb = netdev_alloc_skb(dev, pkt_len + 2);
+ if (skb == NULL) {
+ dev->stats.rx_dropped++;
+ head->msg_length = 0;
+ head->flag |= RMD1_OWN_CHIP;
+ lp->new_rx = (lp->new_rx+1) &
+ RX_RING_MOD_MASK;
+ }
+
+#if 0
+ if (lance_debug >= 3) {
+ u_char *data = PKTBUF_ADDR(head);
+ printk("%s: RX pkt %d type 0x%04x"
+ " from %pM to %pM",
+ dev->name, lp->new_tx, ((u_short *)data)[6],
+ &data[6], data);
+
+ printk(" data %02x %02x %02x %02x %02x %02x %02x %02x "
+ "len %d at %08x\n",
+ data[15], data[16], data[17], data[18],
+ data[19], data[20], data[21], data[22],
+ pkt_len, data);
+ }
+#endif
+ if (lance_debug >= 3) {
+ u_char *data = PKTBUF_ADDR(head);
+ printk( "%s: RX pkt %d type 0x%04x len %d\n ", dev->name, entry, ((u_short *)data)[6], pkt_len);
+ }
+
+
+ skb_reserve( skb, 2 ); /* 16 byte align */
+ skb_put( skb, pkt_len ); /* Make room */
+ skb_copy_to_linear_data(skb,
+ PKTBUF_ADDR(head),
+ pkt_len);
+
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ }
+
+// head->buf_length = -PKT_BUF_SZ | 0xf000;
+ head->msg_length = 0;
+ head->flag = RMD1_OWN_CHIP;
+
+ entry = lp->new_rx = (lp->new_rx +1) & RX_RING_MOD_MASK;
+ }
+
+ /* From lance.c (Donald Becker): */
+ /* We should check that at least two ring entries are free.
+ If not, we should free one and mark stats->rx_dropped++. */
+
+ return 0;
+}
+
+
+static int lance_close( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ AREG = CSR0;
+
+ DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, DREG ));
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ DREG = CSR0_STOP;
+ return 0;
+}
+
+
+/* Set or clear the multicast filter for this adaptor.
+ 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.
+ */
+
+/* completely untested on a sun3 */
+static void set_multicast_list( struct net_device *dev )
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ if(netif_queue_stopped(dev))
+ /* Only possible if board is already started */
+ return;
+
+ /* We take the simple way out and always enable promiscuous mode. */
+ DREG = CSR0_STOP; /* Temporarily stop the lance. */
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Log any net taps. */
+ DPRINTK( 3, ( "%s: Promiscuous mode enabled.\n", dev->name ));
+ REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int num_addrs = netdev_mc_count(dev);
+ int i;
+ /* We don't use the multicast table, but rely on upper-layer
+ * filtering. */
+ memset( multicast_table, (num_addrs == 0) ? 0 : -1,
+ sizeof(multicast_table) );
+ for( i = 0; i < 4; i++ )
+ REGA( CSR8+i ) = multicast_table[i];
+ REGA( CSR15 ) = 0; /* Unset promiscuous mode */
+ }
+
+ /*
+ * Always set BSWP after a STOP as STOP puts it back into
+ * little endian mode.
+ */
+ REGA( CSR3 ) = CSR3_BSWP;
+
+ /* Resume normal operation and reset AREG to CSR0 */
+ REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT;
+}
+
+
+#ifdef MODULE
+
+static struct net_device *sun3lance_dev;
+
+int __init init_module(void)
+{
+ sun3lance_dev = sun3lance_probe(-1);
+ return PTR_ERR_OR_ZERO(sun3lance_dev);
+}
+
+void __exit cleanup_module(void)
+{
+ unregister_netdev(sun3lance_dev);
+#ifdef CONFIG_SUN3
+ iounmap((void __iomem *)sun3lance_dev->base_addr);
+#endif
+ free_netdev(sun3lance_dev);
+}
+
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c
new file mode 100644
index 00000000000..5e4273b7aa2
--- /dev/null
+++ b/drivers/net/ethernet/amd/sunlance.c
@@ -0,0 +1,1534 @@
+/* $Id: sunlance.c,v 1.112 2002/01/15 06:48:55 davem Exp $
+ * lance.c: Linux/Sparc/Lance driver
+ *
+ * Written 1995, 1996 by Miguel de Icaza
+ * Sources:
+ * The Linux depca driver
+ * The Linux lance driver.
+ * The Linux skeleton driver.
+ * The NetBSD Sparc/Lance driver.
+ * Theo de Raadt (deraadt@openbsd.org)
+ * NCR92C990 Lan Controller manual
+ *
+ * 1.4:
+ * Added support to run with a ledma on the Sun4m
+ *
+ * 1.5:
+ * Added multiple card detection.
+ *
+ * 4/17/96: Burst sizes and tpe selection on sun4m by Eddie C. Dost
+ * (ecd@skynet.be)
+ *
+ * 5/15/96: auto carrier detection on sun4m by Eddie C. Dost
+ * (ecd@skynet.be)
+ *
+ * 5/17/96: lebuffer on scsi/ether cards now work David S. Miller
+ * (davem@caip.rutgers.edu)
+ *
+ * 5/29/96: override option 'tpe-link-test?', if it is 'false', as
+ * this disables auto carrier detection on sun4m. Eddie C. Dost
+ * (ecd@skynet.be)
+ *
+ * 1.7:
+ * 6/26/96: Bug fix for multiple ledmas, miguel.
+ *
+ * 1.8:
+ * Stole multicast code from depca.c, fixed lance_tx.
+ *
+ * 1.9:
+ * 8/21/96: Fixed the multicast code (Pedro Roque)
+ *
+ * 8/28/96: Send fake packet in lance_open() if auto_select is true,
+ * so we can detect the carrier loss condition in time.
+ * Eddie C. Dost (ecd@skynet.be)
+ *
+ * 9/15/96: Align rx_buf so that eth_copy_and_sum() won't cause an
+ * MNA trap during chksum_partial_copy(). (ecd@skynet.be)
+ *
+ * 11/17/96: Handle LE_C0_MERR in lance_interrupt(). (ecd@skynet.be)
+ *
+ * 12/22/96: Don't loop forever in lance_rx() on incomplete packets.
+ * This was the sun4c killer. Shit, stupid bug.
+ * (ecd@skynet.be)
+ *
+ * 1.10:
+ * 1/26/97: Modularize driver. (ecd@skynet.be)
+ *
+ * 1.11:
+ * 12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz)
+ *
+ * 1.12:
+ * 11/3/99: Fixed SMP race in lance_start_xmit found by davem.
+ * Anton Blanchard (anton@progsoc.uts.edu.au)
+ * 2.00: 11/9/99: Massive overhaul and port to new SBUS driver interfaces.
+ * David S. Miller (davem@redhat.com)
+ * 2.01:
+ * 11/08/01: Use library crc32 functions (Matt_Domsch@dell.com)
+ *
+ */
+
+#undef DEBUG_DRIVER
+
+static char lancestr[] = "LANCE";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/errno.h>
+#include <linux/socket.h> /* Used for the temporal inet entries and routing */
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gfp.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/pgtable.h>
+#include <asm/byteorder.h> /* Used by the checksum routines */
+#include <asm/idprom.h>
+#include <asm/prom.h>
+#include <asm/auxio.h> /* For tpe-link-test? setting */
+#include <asm/irq.h>
+
+#define DRV_NAME "sunlance"
+#define DRV_VERSION "2.02"
+#define DRV_RELDATE "8/24/03"
+#define DRV_AUTHOR "Miguel de Icaza (miguel@nuclecu.unam.mx)"
+
+static char version[] =
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION("Sun Lance ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* Define: 2^4 Tx buffers and 2^4 Rx buffers */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define LE_CSR0 0
+#define LE_CSR1 1
+#define LE_CSR2 2
+#define LE_CSR3 3
+
+#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */
+
+#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */
+#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */
+#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */
+#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */
+#define LE_C0_MERR 0x0800 /* ME: Memory error */
+#define LE_C0_RINT 0x0400 /* Received interrupt */
+#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */
+#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */
+#define LE_C0_INTR 0x0080 /* Interrupt or error */
+#define LE_C0_INEA 0x0040 /* Interrupt enable */
+#define LE_C0_RXON 0x0020 /* Receiver on */
+#define LE_C0_TXON 0x0010 /* Transmitter on */
+#define LE_C0_TDMD 0x0008 /* Transmitter demand */
+#define LE_C0_STOP 0x0004 /* Stop the card */
+#define LE_C0_STRT 0x0002 /* Start the card */
+#define LE_C0_INIT 0x0001 /* Init the card */
+
+#define LE_C3_BSWP 0x4 /* SWAP */
+#define LE_C3_ACON 0x2 /* ALE Control */
+#define LE_C3_BCON 0x1 /* Byte control */
+
+/* Receive message descriptor 1 */
+#define LE_R1_OWN 0x80 /* Who owns the entry */
+#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */
+#define LE_R1_FRA 0x20 /* FRA: Frame error */
+#define LE_R1_OFL 0x10 /* OFL: Frame overflow */
+#define LE_R1_CRC 0x08 /* CRC error */
+#define LE_R1_BUF 0x04 /* BUF: Buffer error */
+#define LE_R1_SOP 0x02 /* Start of packet */
+#define LE_R1_EOP 0x01 /* End of packet */
+#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+#define LE_T1_OWN 0x80 /* Lance owns the packet */
+#define LE_T1_ERR 0x40 /* Error summary */
+#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */
+#define LE_T1_EONE 0x08 /* Error: one retry needed */
+#define LE_T1_EDEF 0x04 /* Error: deferred */
+#define LE_T1_SOP 0x02 /* Start of packet */
+#define LE_T1_EOP 0x01 /* End of packet */
+#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+#define LE_T3_BUF 0x8000 /* Buffer error */
+#define LE_T3_UFL 0x4000 /* Error underflow */
+#define LE_T3_LCOL 0x1000 /* Error late collision */
+#define LE_T3_CLOS 0x0800 /* Error carrier loss */
+#define LE_T3_RTY 0x0400 /* Error retry */
+#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+#define TX_NEXT(__x) (((__x)+1) & TX_RING_MOD_MASK)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+#define RX_NEXT(__x) (((__x)+1) & RX_RING_MOD_MASK)
+
+#define PKT_BUF_SZ 1544
+#define RX_BUFF_SIZE PKT_BUF_SZ
+#define TX_BUFF_SIZE PKT_BUF_SZ
+
+struct lance_rx_desc {
+ u16 rmd0; /* low address of packet */
+ u8 rmd1_bits; /* descriptor bits */
+ u8 rmd1_hadr; /* high address of packet */
+ s16 length; /* This length is 2s complement (negative)!
+ * Buffer length
+ */
+ u16 mblength; /* This is the actual number of bytes received */
+};
+
+struct lance_tx_desc {
+ u16 tmd0; /* low address of packet */
+ u8 tmd1_bits; /* descriptor bits */
+ u8 tmd1_hadr; /* high address of packet */
+ s16 length; /* Length is 2s complement (negative)! */
+ u16 misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+/* On the Sparc, this block should be on a DMA region */
+struct lance_init_block {
+ u16 mode; /* Pre-set mode (reg. 15) */
+ u8 phys_addr[6]; /* Physical ethernet address */
+ u32 filter[2]; /* Multicast filter. */
+
+ /* Receive and transmit ring base, along with extra bits. */
+ u16 rx_ptr; /* receive descriptor addr */
+ u16 rx_len; /* receive len and high addr */
+ u16 tx_ptr; /* transmit descriptor addr */
+ u16 tx_len; /* transmit len and high addr */
+
+ /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
+ struct lance_rx_desc brx_ring[RX_RING_SIZE];
+ struct lance_tx_desc btx_ring[TX_RING_SIZE];
+
+ u8 tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
+ u8 pad[2]; /* align rx_buf for copy_and_sum(). */
+ u8 rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
+};
+
+#define libdesc_offset(rt, elem) \
+((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem])))))
+
+#define libbuff_offset(rt, elem) \
+((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem][0])))))
+
+struct lance_private {
+ void __iomem *lregs; /* Lance RAP/RDP regs. */
+ void __iomem *dregs; /* DMA controller regs. */
+ struct lance_init_block __iomem *init_block_iomem;
+ struct lance_init_block *init_block_mem;
+
+ spinlock_t lock;
+
+ int rx_new, tx_new;
+ int rx_old, tx_old;
+
+ struct platform_device *ledma; /* If set this points to ledma */
+ char tpe; /* cable-selection is TPE */
+ char auto_select; /* cable-selection by carrier */
+ char burst_sizes; /* ledma SBus burst sizes */
+ char pio_buffer; /* init block in PIO space? */
+
+ unsigned short busmaster_regval;
+
+ void (*init_ring)(struct net_device *);
+ void (*rx)(struct net_device *);
+ void (*tx)(struct net_device *);
+
+ char *name;
+ dma_addr_t init_block_dvma;
+ struct net_device *dev; /* Backpointer */
+ struct platform_device *op;
+ struct platform_device *lebuffer;
+ struct timer_list multicast_timer;
+};
+
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
+ lp->tx_old - lp->tx_new-1)
+
+/* Lance registers. */
+#define RDP 0x00UL /* register data port */
+#define RAP 0x02UL /* register address port */
+#define LANCE_REG_SIZE 0x04UL
+
+#define STOP_LANCE(__lp) \
+do { void __iomem *__base = (__lp)->lregs; \
+ sbus_writew(LE_CSR0, __base + RAP); \
+ sbus_writew(LE_C0_STOP, __base + RDP); \
+} while (0)
+
+int sparc_lance_debug = 2;
+
+/* The Lance uses 24 bit addresses */
+/* On the Sun4c the DVMA will provide the remaining bytes for us */
+/* On the Sun4m we have to instruct the ledma to provide them */
+/* Even worse, on scsi/ether SBUS cards, the init block and the
+ * transmit/receive buffers are addresses as offsets from absolute
+ * zero on the lebuffer PIO area. -DaveM
+ */
+
+#define LANCE_ADDR(x) ((long)(x) & ~0xff000000)
+
+/* Load the CSR registers */
+static void load_csrs(struct lance_private *lp)
+{
+ u32 leptr;
+
+ if (lp->pio_buffer)
+ leptr = 0;
+ else
+ leptr = LANCE_ADDR(lp->init_block_dvma);
+
+ sbus_writew(LE_CSR1, lp->lregs + RAP);
+ sbus_writew(leptr & 0xffff, lp->lregs + RDP);
+ sbus_writew(LE_CSR2, lp->lregs + RAP);
+ sbus_writew(leptr >> 16, lp->lregs + RDP);
+ sbus_writew(LE_CSR3, lp->lregs + RAP);
+ sbus_writew(lp->busmaster_regval, lp->lregs + RDP);
+
+ /* Point back to csr0 */
+ sbus_writew(LE_CSR0, lp->lregs + RAP);
+}
+
+/* Setup the Lance Rx and Tx rings */
+static void lance_init_ring_dvma(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block *ib = lp->init_block_mem;
+ dma_addr_t aib = lp->init_block_dvma;
+ __u32 leptr;
+ int i;
+
+ /* Lock out other processes while setting up hardware */
+ netif_stop_queue(dev);
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ /* Copy the ethernet address to the lance init block
+ * Note that on the sparc you need to swap the ethernet address.
+ */
+ ib->phys_addr [0] = dev->dev_addr [1];
+ ib->phys_addr [1] = dev->dev_addr [0];
+ ib->phys_addr [2] = dev->dev_addr [3];
+ ib->phys_addr [3] = dev->dev_addr [2];
+ ib->phys_addr [4] = dev->dev_addr [5];
+ ib->phys_addr [5] = dev->dev_addr [4];
+
+ /* Setup the Tx ring entries */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i));
+ ib->btx_ring [i].tmd0 = leptr;
+ ib->btx_ring [i].tmd1_hadr = leptr >> 16;
+ ib->btx_ring [i].tmd1_bits = 0;
+ ib->btx_ring [i].length = 0xf000; /* The ones required by tmd2 */
+ ib->btx_ring [i].misc = 0;
+ }
+
+ /* Setup the Rx ring entries */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ leptr = LANCE_ADDR(aib + libbuff_offset(rx_buf, i));
+
+ ib->brx_ring [i].rmd0 = leptr;
+ ib->brx_ring [i].rmd1_hadr = leptr >> 16;
+ ib->brx_ring [i].rmd1_bits = LE_R1_OWN;
+ ib->brx_ring [i].length = -RX_BUFF_SIZE | 0xf000;
+ ib->brx_ring [i].mblength = 0;
+ }
+
+ /* Setup the initialization block */
+
+ /* Setup rx descriptor pointer */
+ leptr = LANCE_ADDR(aib + libdesc_offset(brx_ring, 0));
+ ib->rx_len = (LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16);
+ ib->rx_ptr = leptr;
+
+ /* Setup tx descriptor pointer */
+ leptr = LANCE_ADDR(aib + libdesc_offset(btx_ring, 0));
+ ib->tx_len = (LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16);
+ ib->tx_ptr = leptr;
+}
+
+static void lance_init_ring_pio(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ u32 leptr;
+ int i;
+
+ /* Lock out other processes while setting up hardware */
+ netif_stop_queue(dev);
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ /* Copy the ethernet address to the lance init block
+ * Note that on the sparc you need to swap the ethernet address.
+ */
+ sbus_writeb(dev->dev_addr[1], &ib->phys_addr[0]);
+ sbus_writeb(dev->dev_addr[0], &ib->phys_addr[1]);
+ sbus_writeb(dev->dev_addr[3], &ib->phys_addr[2]);
+ sbus_writeb(dev->dev_addr[2], &ib->phys_addr[3]);
+ sbus_writeb(dev->dev_addr[5], &ib->phys_addr[4]);
+ sbus_writeb(dev->dev_addr[4], &ib->phys_addr[5]);
+
+ /* Setup the Tx ring entries */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ leptr = libbuff_offset(tx_buf, i);
+ sbus_writew(leptr, &ib->btx_ring [i].tmd0);
+ sbus_writeb(leptr >> 16,&ib->btx_ring [i].tmd1_hadr);
+ sbus_writeb(0, &ib->btx_ring [i].tmd1_bits);
+
+ /* The ones required by tmd2 */
+ sbus_writew(0xf000, &ib->btx_ring [i].length);
+ sbus_writew(0, &ib->btx_ring [i].misc);
+ }
+
+ /* Setup the Rx ring entries */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ leptr = libbuff_offset(rx_buf, i);
+
+ sbus_writew(leptr, &ib->brx_ring [i].rmd0);
+ sbus_writeb(leptr >> 16,&ib->brx_ring [i].rmd1_hadr);
+ sbus_writeb(LE_R1_OWN, &ib->brx_ring [i].rmd1_bits);
+ sbus_writew(-RX_BUFF_SIZE|0xf000,
+ &ib->brx_ring [i].length);
+ sbus_writew(0, &ib->brx_ring [i].mblength);
+ }
+
+ /* Setup the initialization block */
+
+ /* Setup rx descriptor pointer */
+ leptr = libdesc_offset(brx_ring, 0);
+ sbus_writew((LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16),
+ &ib->rx_len);
+ sbus_writew(leptr, &ib->rx_ptr);
+
+ /* Setup tx descriptor pointer */
+ leptr = libdesc_offset(btx_ring, 0);
+ sbus_writew((LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16),
+ &ib->tx_len);
+ sbus_writew(leptr, &ib->tx_ptr);
+}
+
+static void init_restart_ledma(struct lance_private *lp)
+{
+ u32 csr = sbus_readl(lp->dregs + DMA_CSR);
+
+ if (!(csr & DMA_HNDL_ERROR)) {
+ /* E-Cache draining */
+ while (sbus_readl(lp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN)
+ barrier();
+ }
+
+ csr = sbus_readl(lp->dregs + DMA_CSR);
+ csr &= ~DMA_E_BURSTS;
+ if (lp->burst_sizes & DMA_BURST32)
+ csr |= DMA_E_BURST32;
+ else
+ csr |= DMA_E_BURST16;
+
+ csr |= (DMA_DSBL_RD_DRN | DMA_DSBL_WR_INV | DMA_FIFO_INV);
+
+ if (lp->tpe)
+ csr |= DMA_EN_ENETAUI;
+ else
+ csr &= ~DMA_EN_ENETAUI;
+ udelay(20);
+ sbus_writel(csr, lp->dregs + DMA_CSR);
+ udelay(200);
+}
+
+static int init_restart_lance(struct lance_private *lp)
+{
+ u16 regval = 0;
+ int i;
+
+ if (lp->dregs)
+ init_restart_ledma(lp);
+
+ sbus_writew(LE_CSR0, lp->lregs + RAP);
+ sbus_writew(LE_C0_INIT, lp->lregs + RDP);
+
+ /* Wait for the lance to complete initialization */
+ for (i = 0; i < 100; i++) {
+ regval = sbus_readw(lp->lregs + RDP);
+
+ if (regval & (LE_C0_ERR | LE_C0_IDON))
+ break;
+ barrier();
+ }
+ if (i == 100 || (regval & LE_C0_ERR)) {
+ printk(KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n",
+ i, regval);
+ if (lp->dregs)
+ printk("dcsr=%8.8x\n", sbus_readl(lp->dregs + DMA_CSR));
+ return -1;
+ }
+
+ /* Clear IDON by writing a "1", enable interrupts and start lance */
+ sbus_writew(LE_C0_IDON, lp->lregs + RDP);
+ sbus_writew(LE_C0_INEA | LE_C0_STRT, lp->lregs + RDP);
+
+ if (lp->dregs) {
+ u32 csr = sbus_readl(lp->dregs + DMA_CSR);
+
+ csr |= DMA_INT_ENAB;
+ sbus_writel(csr, lp->dregs + DMA_CSR);
+ }
+
+ return 0;
+}
+
+static void lance_rx_dvma(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block *ib = lp->init_block_mem;
+ struct lance_rx_desc *rd;
+ u8 bits;
+ int len, entry = lp->rx_new;
+ struct sk_buff *skb;
+
+ for (rd = &ib->brx_ring [entry];
+ !((bits = rd->rmd1_bits) & LE_R1_OWN);
+ rd = &ib->brx_ring [entry]) {
+
+ /* We got an incomplete frame? */
+ if ((bits & LE_R1_POK) != LE_R1_POK) {
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ } else if (bits & LE_R1_ERR) {
+ /* Count only the end frame as a rx error,
+ * not the beginning
+ */
+ if (bits & LE_R1_BUF) dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC) dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL) dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA) dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP) dev->stats.rx_errors++;
+ } else {
+ len = (rd->mblength & 0xfff) - 4;
+ skb = netdev_alloc_skb(dev, len + 2);
+
+ if (skb == NULL) {
+ dev->stats.rx_dropped++;
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ lp->rx_new = RX_NEXT(entry);
+ return;
+ }
+
+ dev->stats.rx_bytes += len;
+
+ skb_reserve(skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
+ skb_copy_to_linear_data(skb,
+ (unsigned char *)&(ib->rx_buf [entry][0]),
+ len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+
+ /* Return the packet to the pool */
+ rd->mblength = 0;
+ rd->rmd1_bits = LE_R1_OWN;
+ entry = RX_NEXT(entry);
+ }
+
+ lp->rx_new = entry;
+}
+
+static void lance_tx_dvma(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block *ib = lp->init_block_mem;
+ int i, j;
+
+ spin_lock(&lp->lock);
+
+ j = lp->tx_old;
+ for (i = j; i != lp->tx_new; i = j) {
+ struct lance_tx_desc *td = &ib->btx_ring [i];
+ u8 bits = td->tmd1_bits;
+
+ /* If we hit a packet not owned by us, stop */
+ if (bits & LE_T1_OWN)
+ break;
+
+ if (bits & LE_T1_ERR) {
+ u16 status = td->misc;
+
+ dev->stats.tx_errors++;
+ if (status & LE_T3_RTY) dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL) dev->stats.tx_window_errors++;
+
+ if (status & LE_T3_CLOS) {
+ dev->stats.tx_carrier_errors++;
+ if (lp->auto_select) {
+ lp->tpe = 1 - lp->tpe;
+ printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n",
+ dev->name, lp->tpe?"TPE":"AUI");
+ STOP_LANCE(lp);
+ lp->init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ }
+
+ /* Buffer errors and underflows turn off the
+ * transmitter, restart the adapter.
+ */
+ if (status & (LE_T3_BUF|LE_T3_UFL)) {
+ dev->stats.tx_fifo_errors++;
+
+ printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+ dev->name);
+ STOP_LANCE(lp);
+ lp->init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ } else if ((bits & LE_T1_POK) == LE_T1_POK) {
+ /*
+ * So we don't count the packet more than once.
+ */
+ td->tmd1_bits = bits & ~(LE_T1_POK);
+
+ /* One collision before packet was sent. */
+ if (bits & LE_T1_EONE)
+ dev->stats.collisions++;
+
+ /* More than one collision, be optimistic. */
+ if (bits & LE_T1_EMORE)
+ dev->stats.collisions += 2;
+
+ dev->stats.tx_packets++;
+ }
+
+ j = TX_NEXT(j);
+ }
+ lp->tx_old = j;
+out:
+ if (netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL > 0)
+ netif_wake_queue(dev);
+
+ spin_unlock(&lp->lock);
+}
+
+static void lance_piocopy_to_skb(struct sk_buff *skb, void __iomem *piobuf, int len)
+{
+ u16 *p16 = (u16 *) skb->data;
+ u32 *p32;
+ u8 *p8;
+ void __iomem *pbuf = piobuf;
+
+ /* We know here that both src and dest are on a 16bit boundary. */
+ *p16++ = sbus_readw(pbuf);
+ p32 = (u32 *) p16;
+ pbuf += 2;
+ len -= 2;
+
+ while (len >= 4) {
+ *p32++ = sbus_readl(pbuf);
+ pbuf += 4;
+ len -= 4;
+ }
+ p8 = (u8 *) p32;
+ if (len >= 2) {
+ p16 = (u16 *) p32;
+ *p16++ = sbus_readw(pbuf);
+ pbuf += 2;
+ len -= 2;
+ p8 = (u8 *) p16;
+ }
+ if (len >= 1)
+ *p8 = sbus_readb(pbuf);
+}
+
+static void lance_rx_pio(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ struct lance_rx_desc __iomem *rd;
+ unsigned char bits;
+ int len, entry;
+ struct sk_buff *skb;
+
+ entry = lp->rx_new;
+ for (rd = &ib->brx_ring [entry];
+ !((bits = sbus_readb(&rd->rmd1_bits)) & LE_R1_OWN);
+ rd = &ib->brx_ring [entry]) {
+
+ /* We got an incomplete frame? */
+ if ((bits & LE_R1_POK) != LE_R1_POK) {
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ } else if (bits & LE_R1_ERR) {
+ /* Count only the end frame as a rx error,
+ * not the beginning
+ */
+ if (bits & LE_R1_BUF) dev->stats.rx_fifo_errors++;
+ if (bits & LE_R1_CRC) dev->stats.rx_crc_errors++;
+ if (bits & LE_R1_OFL) dev->stats.rx_over_errors++;
+ if (bits & LE_R1_FRA) dev->stats.rx_frame_errors++;
+ if (bits & LE_R1_EOP) dev->stats.rx_errors++;
+ } else {
+ len = (sbus_readw(&rd->mblength) & 0xfff) - 4;
+ skb = netdev_alloc_skb(dev, len + 2);
+
+ if (skb == NULL) {
+ dev->stats.rx_dropped++;
+ sbus_writew(0, &rd->mblength);
+ sbus_writeb(LE_R1_OWN, &rd->rmd1_bits);
+ lp->rx_new = RX_NEXT(entry);
+ return;
+ }
+
+ dev->stats.rx_bytes += len;
+
+ skb_reserve (skb, 2); /* 16 byte align */
+ skb_put(skb, len); /* make room */
+ lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+
+ /* Return the packet to the pool */
+ sbus_writew(0, &rd->mblength);
+ sbus_writeb(LE_R1_OWN, &rd->rmd1_bits);
+ entry = RX_NEXT(entry);
+ }
+
+ lp->rx_new = entry;
+}
+
+static void lance_tx_pio(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ int i, j;
+
+ spin_lock(&lp->lock);
+
+ j = lp->tx_old;
+ for (i = j; i != lp->tx_new; i = j) {
+ struct lance_tx_desc __iomem *td = &ib->btx_ring [i];
+ u8 bits = sbus_readb(&td->tmd1_bits);
+
+ /* If we hit a packet not owned by us, stop */
+ if (bits & LE_T1_OWN)
+ break;
+
+ if (bits & LE_T1_ERR) {
+ u16 status = sbus_readw(&td->misc);
+
+ dev->stats.tx_errors++;
+ if (status & LE_T3_RTY) dev->stats.tx_aborted_errors++;
+ if (status & LE_T3_LCOL) dev->stats.tx_window_errors++;
+
+ if (status & LE_T3_CLOS) {
+ dev->stats.tx_carrier_errors++;
+ if (lp->auto_select) {
+ lp->tpe = 1 - lp->tpe;
+ printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n",
+ dev->name, lp->tpe?"TPE":"AUI");
+ STOP_LANCE(lp);
+ lp->init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ }
+
+ /* Buffer errors and underflows turn off the
+ * transmitter, restart the adapter.
+ */
+ if (status & (LE_T3_BUF|LE_T3_UFL)) {
+ dev->stats.tx_fifo_errors++;
+
+ printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+ dev->name);
+ STOP_LANCE(lp);
+ lp->init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ goto out;
+ }
+ } else if ((bits & LE_T1_POK) == LE_T1_POK) {
+ /*
+ * So we don't count the packet more than once.
+ */
+ sbus_writeb(bits & ~(LE_T1_POK), &td->tmd1_bits);
+
+ /* One collision before packet was sent. */
+ if (bits & LE_T1_EONE)
+ dev->stats.collisions++;
+
+ /* More than one collision, be optimistic. */
+ if (bits & LE_T1_EMORE)
+ dev->stats.collisions += 2;
+
+ dev->stats.tx_packets++;
+ }
+
+ j = TX_NEXT(j);
+ }
+ lp->tx_old = j;
+
+ if (netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL > 0)
+ netif_wake_queue(dev);
+out:
+ spin_unlock(&lp->lock);
+}
+
+static irqreturn_t lance_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct lance_private *lp = netdev_priv(dev);
+ int csr0;
+
+ sbus_writew(LE_CSR0, lp->lregs + RAP);
+ csr0 = sbus_readw(lp->lregs + RDP);
+
+ /* Acknowledge all the interrupt sources ASAP */
+ sbus_writew(csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT),
+ lp->lregs + RDP);
+
+ if ((csr0 & LE_C0_ERR) != 0) {
+ /* Clear the error condition */
+ sbus_writew((LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
+ LE_C0_CERR | LE_C0_MERR),
+ lp->lregs + RDP);
+ }
+
+ if (csr0 & LE_C0_RINT)
+ lp->rx(dev);
+
+ if (csr0 & LE_C0_TINT)
+ lp->tx(dev);
+
+ if (csr0 & LE_C0_BABL)
+ dev->stats.tx_errors++;
+
+ if (csr0 & LE_C0_MISS)
+ dev->stats.rx_errors++;
+
+ if (csr0 & LE_C0_MERR) {
+ if (lp->dregs) {
+ u32 addr = sbus_readl(lp->dregs + DMA_ADDR);
+
+ printk(KERN_ERR "%s: Memory error, status %04x, addr %06x\n",
+ dev->name, csr0, addr & 0xffffff);
+ } else {
+ printk(KERN_ERR "%s: Memory error, status %04x\n",
+ dev->name, csr0);
+ }
+
+ sbus_writew(LE_C0_STOP, lp->lregs + RDP);
+
+ if (lp->dregs) {
+ u32 dma_csr = sbus_readl(lp->dregs + DMA_CSR);
+
+ dma_csr |= DMA_FIFO_INV;
+ sbus_writel(dma_csr, lp->dregs + DMA_CSR);
+ }
+
+ lp->init_ring(dev);
+ load_csrs(lp);
+ init_restart_lance(lp);
+ netif_wake_queue(dev);
+ }
+
+ sbus_writew(LE_C0_INEA, lp->lregs + RDP);
+
+ return IRQ_HANDLED;
+}
+
+/* Build a fake network packet and send it to ourselves. */
+static void build_fake_packet(struct lance_private *lp)
+{
+ struct net_device *dev = lp->dev;
+ int i, entry;
+
+ entry = lp->tx_new & TX_RING_MOD_MASK;
+ if (lp->pio_buffer) {
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ u16 __iomem *packet = (u16 __iomem *) &(ib->tx_buf[entry][0]);
+ struct ethhdr __iomem *eth = (struct ethhdr __iomem *) packet;
+ for (i = 0; i < (ETH_ZLEN / sizeof(u16)); i++)
+ sbus_writew(0, &packet[i]);
+ for (i = 0; i < 6; i++) {
+ sbus_writeb(dev->dev_addr[i], &eth->h_dest[i]);
+ sbus_writeb(dev->dev_addr[i], &eth->h_source[i]);
+ }
+ sbus_writew((-ETH_ZLEN) | 0xf000, &ib->btx_ring[entry].length);
+ sbus_writew(0, &ib->btx_ring[entry].misc);
+ sbus_writeb(LE_T1_POK|LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits);
+ } else {
+ struct lance_init_block *ib = lp->init_block_mem;
+ u16 *packet = (u16 *) &(ib->tx_buf[entry][0]);
+ struct ethhdr *eth = (struct ethhdr *) packet;
+ memset(packet, 0, ETH_ZLEN);
+ for (i = 0; i < 6; i++) {
+ eth->h_dest[i] = dev->dev_addr[i];
+ eth->h_source[i] = dev->dev_addr[i];
+ }
+ ib->btx_ring[entry].length = (-ETH_ZLEN) | 0xf000;
+ ib->btx_ring[entry].misc = 0;
+ ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
+ }
+ lp->tx_new = TX_NEXT(entry);
+}
+
+static int lance_open(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int status = 0;
+
+ STOP_LANCE(lp);
+
+ if (request_irq(dev->irq, lance_interrupt, IRQF_SHARED,
+ lancestr, (void *) dev)) {
+ printk(KERN_ERR "Lance: Can't get irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+
+ /* On the 4m, setup the ledma to provide the upper bits for buffers */
+ if (lp->dregs) {
+ u32 regval = lp->init_block_dvma & 0xff000000;
+
+ sbus_writel(regval, lp->dregs + DMA_TEST);
+ }
+
+ /* Set mode and clear multicast filter only at device open,
+ * so that lance_init_ring() called at any error will not
+ * forget multicast filters.
+ *
+ * BTW it is common bug in all lance drivers! --ANK
+ */
+ if (lp->pio_buffer) {
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ sbus_writew(0, &ib->mode);
+ sbus_writel(0, &ib->filter[0]);
+ sbus_writel(0, &ib->filter[1]);
+ } else {
+ struct lance_init_block *ib = lp->init_block_mem;
+ ib->mode = 0;
+ ib->filter [0] = 0;
+ ib->filter [1] = 0;
+ }
+
+ lp->init_ring(dev);
+ load_csrs(lp);
+
+ netif_start_queue(dev);
+
+ status = init_restart_lance(lp);
+ if (!status && lp->auto_select) {
+ build_fake_packet(lp);
+ sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);
+ }
+
+ return status;
+}
+
+static int lance_close(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->multicast_timer);
+
+ STOP_LANCE(lp);
+
+ free_irq(dev->irq, (void *) dev);
+ return 0;
+}
+
+static int lance_reset(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int status;
+
+ STOP_LANCE(lp);
+
+ /* On the 4m, reset the dma too */
+ if (lp->dregs) {
+ u32 csr, addr;
+
+ printk(KERN_ERR "resetting ledma\n");
+ csr = sbus_readl(lp->dregs + DMA_CSR);
+ sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);
+ udelay(200);
+ sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR);
+
+ addr = lp->init_block_dvma & 0xff000000;
+ sbus_writel(addr, lp->dregs + DMA_TEST);
+ }
+ lp->init_ring(dev);
+ load_csrs(lp);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ status = init_restart_lance(lp);
+ return status;
+}
+
+static void lance_piocopy_from_skb(void __iomem *dest, unsigned char *src, int len)
+{
+ void __iomem *piobuf = dest;
+ u32 *p32;
+ u16 *p16;
+ u8 *p8;
+
+ switch ((unsigned long)src & 0x3) {
+ case 0:
+ p32 = (u32 *) src;
+ while (len >= 4) {
+ sbus_writel(*p32, piobuf);
+ p32++;
+ piobuf += 4;
+ len -= 4;
+ }
+ src = (char *) p32;
+ break;
+ case 1:
+ case 3:
+ p8 = (u8 *) src;
+ while (len >= 4) {
+ u32 val;
+
+ val = p8[0] << 24;
+ val |= p8[1] << 16;
+ val |= p8[2] << 8;
+ val |= p8[3];
+ sbus_writel(val, piobuf);
+ p8 += 4;
+ piobuf += 4;
+ len -= 4;
+ }
+ src = (char *) p8;
+ break;
+ case 2:
+ p16 = (u16 *) src;
+ while (len >= 4) {
+ u32 val = p16[0]<<16 | p16[1];
+ sbus_writel(val, piobuf);
+ p16 += 2;
+ piobuf += 4;
+ len -= 4;
+ }
+ src = (char *) p16;
+ break;
+ }
+ if (len >= 2) {
+ u16 val = src[0] << 8 | src[1];
+ sbus_writew(val, piobuf);
+ src += 2;
+ piobuf += 2;
+ len -= 2;
+ }
+ if (len >= 1)
+ sbus_writeb(src[0], piobuf);
+}
+
+static void lance_piozero(void __iomem *dest, int len)
+{
+ void __iomem *piobuf = dest;
+
+ if ((unsigned long)piobuf & 1) {
+ sbus_writeb(0, piobuf);
+ piobuf += 1;
+ len -= 1;
+ if (len == 0)
+ return;
+ }
+ if (len == 1) {
+ sbus_writeb(0, piobuf);
+ return;
+ }
+ if ((unsigned long)piobuf & 2) {
+ sbus_writew(0, piobuf);
+ piobuf += 2;
+ len -= 2;
+ if (len == 0)
+ return;
+ }
+ while (len >= 4) {
+ sbus_writel(0, piobuf);
+ piobuf += 4;
+ len -= 4;
+ }
+ if (len >= 2) {
+ sbus_writew(0, piobuf);
+ piobuf += 2;
+ len -= 2;
+ }
+ if (len >= 1)
+ sbus_writeb(0, piobuf);
+}
+
+static void lance_tx_timeout(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+
+ printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
+ dev->name, sbus_readw(lp->lregs + RDP));
+ lance_reset(dev);
+ netif_wake_queue(dev);
+}
+
+static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ int entry, skblen, len;
+
+ skblen = skb->len;
+
+ len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
+
+ spin_lock_irq(&lp->lock);
+
+ dev->stats.tx_bytes += len;
+
+ entry = lp->tx_new & TX_RING_MOD_MASK;
+ if (lp->pio_buffer) {
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ sbus_writew((-len) | 0xf000, &ib->btx_ring[entry].length);
+ sbus_writew(0, &ib->btx_ring[entry].misc);
+ lance_piocopy_from_skb(&ib->tx_buf[entry][0], skb->data, skblen);
+ if (len != skblen)
+ lance_piozero(&ib->tx_buf[entry][skblen], len - skblen);
+ sbus_writeb(LE_T1_POK | LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits);
+ } else {
+ struct lance_init_block *ib = lp->init_block_mem;
+ ib->btx_ring [entry].length = (-len) | 0xf000;
+ ib->btx_ring [entry].misc = 0;
+ skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);
+ if (len != skblen)
+ memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);
+ ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
+ }
+
+ lp->tx_new = TX_NEXT(entry);
+
+ if (TX_BUFFS_AVAIL <= 0)
+ netif_stop_queue(dev);
+
+ /* Kick the lance: transmit now */
+ sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);
+
+ /* Read back CSR to invalidate the E-Cache.
+ * This is needed, because DMA_DSBL_WR_INV is set.
+ */
+ if (lp->dregs)
+ sbus_readw(lp->lregs + RDP);
+
+ spin_unlock_irq(&lp->lock);
+
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/* taken from the depca driver */
+static void lance_load_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ u32 crc;
+ u32 val;
+
+ /* set all multicast bits */
+ if (dev->flags & IFF_ALLMULTI)
+ val = ~0;
+ else
+ val = 0;
+
+ if (lp->pio_buffer) {
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ sbus_writel(val, &ib->filter[0]);
+ sbus_writel(val, &ib->filter[1]);
+ } else {
+ struct lance_init_block *ib = lp->init_block_mem;
+ ib->filter [0] = val;
+ ib->filter [1] = val;
+ }
+
+ if (dev->flags & IFF_ALLMULTI)
+ return;
+
+ /* Add addresses */
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc_le(6, ha->addr);
+ crc = crc >> 26;
+ if (lp->pio_buffer) {
+ struct lance_init_block __iomem *ib = lp->init_block_iomem;
+ u16 __iomem *mcast_table = (u16 __iomem *) &ib->filter;
+ u16 tmp = sbus_readw(&mcast_table[crc>>4]);
+ tmp |= 1 << (crc & 0xf);
+ sbus_writew(tmp, &mcast_table[crc>>4]);
+ } else {
+ struct lance_init_block *ib = lp->init_block_mem;
+ u16 *mcast_table = (u16 *) &ib->filter;
+ mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+ }
+ }
+}
+
+static void lance_set_multicast(struct net_device *dev)
+{
+ struct lance_private *lp = netdev_priv(dev);
+ struct lance_init_block *ib_mem = lp->init_block_mem;
+ struct lance_init_block __iomem *ib_iomem = lp->init_block_iomem;
+ u16 mode;
+
+ if (!netif_running(dev))
+ return;
+
+ if (lp->tx_old != lp->tx_new) {
+ mod_timer(&lp->multicast_timer, jiffies + 4);
+ netif_wake_queue(dev);
+ return;
+ }
+
+ netif_stop_queue(dev);
+
+ STOP_LANCE(lp);
+ lp->init_ring(dev);
+
+ if (lp->pio_buffer)
+ mode = sbus_readw(&ib_iomem->mode);
+ else
+ mode = ib_mem->mode;
+ if (dev->flags & IFF_PROMISC) {
+ mode |= LE_MO_PROM;
+ if (lp->pio_buffer)
+ sbus_writew(mode, &ib_iomem->mode);
+ else
+ ib_mem->mode = mode;
+ } else {
+ mode &= ~LE_MO_PROM;
+ if (lp->pio_buffer)
+ sbus_writew(mode, &ib_iomem->mode);
+ else
+ ib_mem->mode = mode;
+ lance_load_multicast(dev);
+ }
+ load_csrs(lp);
+ init_restart_lance(lp);
+ netif_wake_queue(dev);
+}
+
+static void lance_set_multicast_retry(unsigned long _opaque)
+{
+ struct net_device *dev = (struct net_device *) _opaque;
+
+ lance_set_multicast(dev);
+}
+
+static void lance_free_hwresources(struct lance_private *lp)
+{
+ if (lp->lregs)
+ of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE);
+ if (lp->dregs) {
+ struct platform_device *ledma = lp->ledma;
+
+ of_iounmap(&ledma->resource[0], lp->dregs,
+ resource_size(&ledma->resource[0]));
+ }
+ if (lp->init_block_iomem) {
+ of_iounmap(&lp->lebuffer->resource[0], lp->init_block_iomem,
+ sizeof(struct lance_init_block));
+ } else if (lp->init_block_mem) {
+ dma_free_coherent(&lp->op->dev,
+ sizeof(struct lance_init_block),
+ lp->init_block_mem,
+ lp->init_block_dvma);
+ }
+}
+
+/* Ethtool support... */
+static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, "sunlance", sizeof(info->driver));
+ strlcpy(info->version, "2.02", sizeof(info->version));
+}
+
+static const struct ethtool_ops sparc_lance_ethtool_ops = {
+ .get_drvinfo = sparc_lance_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static const struct net_device_ops sparc_lance_ops = {
+ .ndo_open = lance_open,
+ .ndo_stop = lance_close,
+ .ndo_start_xmit = lance_start_xmit,
+ .ndo_set_rx_mode = lance_set_multicast,
+ .ndo_tx_timeout = lance_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int sparc_lance_probe_one(struct platform_device *op,
+ struct platform_device *ledma,
+ struct platform_device *lebuffer)
+{
+ struct device_node *dp = op->dev.of_node;
+ static unsigned version_printed;
+ struct lance_private *lp;
+ struct net_device *dev;
+ int i;
+
+ dev = alloc_etherdev(sizeof(struct lance_private) + 8);
+ if (!dev)
+ return -ENOMEM;
+
+ lp = netdev_priv(dev);
+
+ if (sparc_lance_debug && version_printed++ == 0)
+ printk (KERN_INFO "%s", version);
+
+ spin_lock_init(&lp->lock);
+
+ /* Copy the IDPROM ethernet address to the device structure, later we
+ * will copy the address in the device structure to the lance
+ * initialization block.
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = idprom->id_ethaddr[i];
+
+ /* Get the IO region */
+ lp->lregs = of_ioremap(&op->resource[0], 0,
+ LANCE_REG_SIZE, lancestr);
+ if (!lp->lregs) {
+ printk(KERN_ERR "SunLance: Cannot map registers.\n");
+ goto fail;
+ }
+
+ lp->ledma = ledma;
+ if (lp->ledma) {
+ lp->dregs = of_ioremap(&ledma->resource[0], 0,
+ resource_size(&ledma->resource[0]),
+ "ledma");
+ if (!lp->dregs) {
+ printk(KERN_ERR "SunLance: Cannot map "
+ "ledma registers.\n");
+ goto fail;
+ }
+ }
+
+ lp->op = op;
+ lp->lebuffer = lebuffer;
+ if (lebuffer) {
+ /* sanity check */
+ if (lebuffer->resource[0].start & 7) {
+ printk(KERN_ERR "SunLance: ERROR: Rx and Tx rings not on even boundary.\n");
+ goto fail;
+ }
+ lp->init_block_iomem =
+ of_ioremap(&lebuffer->resource[0], 0,
+ sizeof(struct lance_init_block), "lebuffer");
+ if (!lp->init_block_iomem) {
+ printk(KERN_ERR "SunLance: Cannot map PIO buffer.\n");
+ goto fail;
+ }
+ lp->init_block_dvma = 0;
+ lp->pio_buffer = 1;
+ lp->init_ring = lance_init_ring_pio;
+ lp->rx = lance_rx_pio;
+ lp->tx = lance_tx_pio;
+ } else {
+ lp->init_block_mem =
+ dma_alloc_coherent(&op->dev,
+ sizeof(struct lance_init_block),
+ &lp->init_block_dvma, GFP_ATOMIC);
+ if (!lp->init_block_mem)
+ goto fail;
+
+ lp->pio_buffer = 0;
+ lp->init_ring = lance_init_ring_dvma;
+ lp->rx = lance_rx_dvma;
+ lp->tx = lance_tx_dvma;
+ }
+ lp->busmaster_regval = of_getintprop_default(dp, "busmaster-regval",
+ (LE_C3_BSWP |
+ LE_C3_ACON |
+ LE_C3_BCON));
+
+ lp->name = lancestr;
+
+ lp->burst_sizes = 0;
+ if (lp->ledma) {
+ struct device_node *ledma_dp = ledma->dev.of_node;
+ struct device_node *sbus_dp;
+ unsigned int sbmask;
+ const char *prop;
+ u32 csr;
+
+ /* Find burst-size property for ledma */
+ lp->burst_sizes = of_getintprop_default(ledma_dp,
+ "burst-sizes", 0);
+
+ /* ledma may be capable of fast bursts, but sbus may not. */
+ sbus_dp = ledma_dp->parent;
+ sbmask = of_getintprop_default(sbus_dp, "burst-sizes",
+ DMA_BURSTBITS);
+ lp->burst_sizes &= sbmask;
+
+ /* Get the cable-selection property */
+ prop = of_get_property(ledma_dp, "cable-selection", NULL);
+ if (!prop || prop[0] == '\0') {
+ struct device_node *nd;
+
+ printk(KERN_INFO "SunLance: using "
+ "auto-carrier-detection.\n");
+
+ nd = of_find_node_by_path("/options");
+ if (!nd)
+ goto no_link_test;
+
+ prop = of_get_property(nd, "tpe-link-test?", NULL);
+ if (!prop)
+ goto no_link_test;
+
+ if (strcmp(prop, "true")) {
+ printk(KERN_NOTICE "SunLance: warning: overriding option "
+ "'tpe-link-test?'\n");
+ printk(KERN_NOTICE "SunLance: warning: mail any problems "
+ "to ecd@skynet.be\n");
+ auxio_set_lte(AUXIO_LTE_ON);
+ }
+no_link_test:
+ lp->auto_select = 1;
+ lp->tpe = 0;
+ } else if (!strcmp(prop, "aui")) {
+ lp->auto_select = 0;
+ lp->tpe = 0;
+ } else {
+ lp->auto_select = 0;
+ lp->tpe = 1;
+ }
+
+ /* Reset ledma */
+ csr = sbus_readl(lp->dregs + DMA_CSR);
+ sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);
+ udelay(200);
+ sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR);
+ } else
+ lp->dregs = NULL;
+
+ lp->dev = dev;
+ SET_NETDEV_DEV(dev, &op->dev);
+ dev->watchdog_timeo = 5*HZ;
+ dev->ethtool_ops = &sparc_lance_ethtool_ops;
+ dev->netdev_ops = &sparc_lance_ops;
+
+ dev->irq = op->archdata.irqs[0];
+
+ /* We cannot sleep if the chip is busy during a
+ * multicast list update event, because such events
+ * can occur from interrupts (ex. IPv6). So we
+ * use a timer to try again later when necessary. -DaveM
+ */
+ init_timer(&lp->multicast_timer);
+ lp->multicast_timer.data = (unsigned long) dev;
+ lp->multicast_timer.function = lance_set_multicast_retry;
+
+ if (register_netdev(dev)) {
+ printk(KERN_ERR "SunLance: Cannot register device.\n");
+ goto fail;
+ }
+
+ platform_set_drvdata(op, lp);
+
+ printk(KERN_INFO "%s: LANCE %pM\n",
+ dev->name, dev->dev_addr);
+
+ return 0;
+
+fail:
+ lance_free_hwresources(lp);
+ free_netdev(dev);
+ return -ENODEV;
+}
+
+static int sunlance_sbus_probe(struct platform_device *op)
+{
+ struct platform_device *parent = to_platform_device(op->dev.parent);
+ struct device_node *parent_dp = parent->dev.of_node;
+ int err;
+
+ if (!strcmp(parent_dp->name, "ledma")) {
+ err = sparc_lance_probe_one(op, parent, NULL);
+ } else if (!strcmp(parent_dp->name, "lebuffer")) {
+ err = sparc_lance_probe_one(op, NULL, parent);
+ } else
+ err = sparc_lance_probe_one(op, NULL, NULL);
+
+ return err;
+}
+
+static int sunlance_sbus_remove(struct platform_device *op)
+{
+ struct lance_private *lp = platform_get_drvdata(op);
+ struct net_device *net_dev = lp->dev;
+
+ unregister_netdev(net_dev);
+
+ lance_free_hwresources(lp);
+
+ free_netdev(net_dev);
+
+ return 0;
+}
+
+static const struct of_device_id sunlance_sbus_match[] = {
+ {
+ .name = "le",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sunlance_sbus_match);
+
+static struct platform_driver sunlance_sbus_driver = {
+ .driver = {
+ .name = "sunlance",
+ .owner = THIS_MODULE,
+ .of_match_table = sunlance_sbus_match,
+ },
+ .probe = sunlance_sbus_probe,
+ .remove = sunlance_sbus_remove,
+};
+
+module_platform_driver(sunlance_sbus_driver);
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
new file mode 100644
index 00000000000..26cf9af1642
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
+
+amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
+ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o
+
+amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
new file mode 100644
index 00000000000..bf462ee86f5
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -0,0 +1,1007 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ * (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ * Inc. unless otherwise expressly agreed to in writing between Synopsys
+ * and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for Licensed
+ * Product with Synopsys or any supplement thereto. Permission is hereby
+ * granted, free of charge, to any person obtaining a copy of this software
+ * annotated with this license and the Software, to deal in the Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Advanced Micro Devices, Inc. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ * (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ * Inc. unless otherwise expressly agreed to in writing between Synopsys
+ * and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for Licensed
+ * Product with Synopsys or any supplement thereto. Permission is hereby
+ * granted, free of charge, to any person obtaining a copy of this software
+ * annotated with this license and the Software, to deal in the Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __XGBE_COMMON_H__
+#define __XGBE_COMMON_H__
+
+/* DMA register offsets */
+#define DMA_MR 0x3000
+#define DMA_SBMR 0x3004
+#define DMA_ISR 0x3008
+#define DMA_AXIARCR 0x3010
+#define DMA_AXIAWCR 0x3018
+#define DMA_DSR0 0x3020
+#define DMA_DSR1 0x3024
+#define DMA_DSR2 0x3028
+#define DMA_DSR3 0x302c
+#define DMA_DSR4 0x3030
+
+/* DMA register entry bit positions and sizes */
+#define DMA_AXIARCR_DRC_INDEX 0
+#define DMA_AXIARCR_DRC_WIDTH 4
+#define DMA_AXIARCR_DRD_INDEX 4
+#define DMA_AXIARCR_DRD_WIDTH 2
+#define DMA_AXIARCR_TEC_INDEX 8
+#define DMA_AXIARCR_TEC_WIDTH 4
+#define DMA_AXIARCR_TED_INDEX 12
+#define DMA_AXIARCR_TED_WIDTH 2
+#define DMA_AXIARCR_THC_INDEX 16
+#define DMA_AXIARCR_THC_WIDTH 4
+#define DMA_AXIARCR_THD_INDEX 20
+#define DMA_AXIARCR_THD_WIDTH 2
+#define DMA_AXIAWCR_DWC_INDEX 0
+#define DMA_AXIAWCR_DWC_WIDTH 4
+#define DMA_AXIAWCR_DWD_INDEX 4
+#define DMA_AXIAWCR_DWD_WIDTH 2
+#define DMA_AXIAWCR_RPC_INDEX 8
+#define DMA_AXIAWCR_RPC_WIDTH 4
+#define DMA_AXIAWCR_RPD_INDEX 12
+#define DMA_AXIAWCR_RPD_WIDTH 2
+#define DMA_AXIAWCR_RHC_INDEX 16
+#define DMA_AXIAWCR_RHC_WIDTH 4
+#define DMA_AXIAWCR_RHD_INDEX 20
+#define DMA_AXIAWCR_RHD_WIDTH 2
+#define DMA_AXIAWCR_TDC_INDEX 24
+#define DMA_AXIAWCR_TDC_WIDTH 4
+#define DMA_AXIAWCR_TDD_INDEX 28
+#define DMA_AXIAWCR_TDD_WIDTH 2
+#define DMA_DSR0_RPS_INDEX 8
+#define DMA_DSR0_RPS_WIDTH 4
+#define DMA_DSR0_TPS_INDEX 12
+#define DMA_DSR0_TPS_WIDTH 4
+#define DMA_ISR_MACIS_INDEX 17
+#define DMA_ISR_MACIS_WIDTH 1
+#define DMA_ISR_MTLIS_INDEX 16
+#define DMA_ISR_MTLIS_WIDTH 1
+#define DMA_MR_SWR_INDEX 0
+#define DMA_MR_SWR_WIDTH 1
+#define DMA_SBMR_EAME_INDEX 11
+#define DMA_SBMR_EAME_WIDTH 1
+#define DMA_SBMR_UNDEF_INDEX 0
+#define DMA_SBMR_UNDEF_WIDTH 1
+
+/* DMA channel register offsets
+ * Multiple channels can be active. The first channel has registers
+ * that begin at 0x3100. Each subsequent channel has registers that
+ * are accessed using an offset of 0x80 from the previous channel.
+ */
+#define DMA_CH_BASE 0x3100
+#define DMA_CH_INC 0x80
+
+#define DMA_CH_CR 0x00
+#define DMA_CH_TCR 0x04
+#define DMA_CH_RCR 0x08
+#define DMA_CH_TDLR_HI 0x10
+#define DMA_CH_TDLR_LO 0x14
+#define DMA_CH_RDLR_HI 0x18
+#define DMA_CH_RDLR_LO 0x1c
+#define DMA_CH_TDTR_LO 0x24
+#define DMA_CH_RDTR_LO 0x2c
+#define DMA_CH_TDRLR 0x30
+#define DMA_CH_RDRLR 0x34
+#define DMA_CH_IER 0x38
+#define DMA_CH_RIWT 0x3c
+#define DMA_CH_CATDR_LO 0x44
+#define DMA_CH_CARDR_LO 0x4c
+#define DMA_CH_CATBR_HI 0x50
+#define DMA_CH_CATBR_LO 0x54
+#define DMA_CH_CARBR_HI 0x58
+#define DMA_CH_CARBR_LO 0x5c
+#define DMA_CH_SR 0x60
+
+/* DMA channel register entry bit positions and sizes */
+#define DMA_CH_CR_PBLX8_INDEX 16
+#define DMA_CH_CR_PBLX8_WIDTH 1
+#define DMA_CH_IER_AIE_INDEX 15
+#define DMA_CH_IER_AIE_WIDTH 1
+#define DMA_CH_IER_FBEE_INDEX 12
+#define DMA_CH_IER_FBEE_WIDTH 1
+#define DMA_CH_IER_NIE_INDEX 16
+#define DMA_CH_IER_NIE_WIDTH 1
+#define DMA_CH_IER_RBUE_INDEX 7
+#define DMA_CH_IER_RBUE_WIDTH 1
+#define DMA_CH_IER_RIE_INDEX 6
+#define DMA_CH_IER_RIE_WIDTH 1
+#define DMA_CH_IER_RSE_INDEX 8
+#define DMA_CH_IER_RSE_WIDTH 1
+#define DMA_CH_IER_TBUE_INDEX 2
+#define DMA_CH_IER_TBUE_WIDTH 1
+#define DMA_CH_IER_TIE_INDEX 0
+#define DMA_CH_IER_TIE_WIDTH 1
+#define DMA_CH_IER_TXSE_INDEX 1
+#define DMA_CH_IER_TXSE_WIDTH 1
+#define DMA_CH_RCR_PBL_INDEX 16
+#define DMA_CH_RCR_PBL_WIDTH 6
+#define DMA_CH_RCR_RBSZ_INDEX 1
+#define DMA_CH_RCR_RBSZ_WIDTH 14
+#define DMA_CH_RCR_SR_INDEX 0
+#define DMA_CH_RCR_SR_WIDTH 1
+#define DMA_CH_RIWT_RWT_INDEX 0
+#define DMA_CH_RIWT_RWT_WIDTH 8
+#define DMA_CH_SR_FBE_INDEX 12
+#define DMA_CH_SR_FBE_WIDTH 1
+#define DMA_CH_SR_RBU_INDEX 7
+#define DMA_CH_SR_RBU_WIDTH 1
+#define DMA_CH_SR_RI_INDEX 6
+#define DMA_CH_SR_RI_WIDTH 1
+#define DMA_CH_SR_RPS_INDEX 8
+#define DMA_CH_SR_RPS_WIDTH 1
+#define DMA_CH_SR_TBU_INDEX 2
+#define DMA_CH_SR_TBU_WIDTH 1
+#define DMA_CH_SR_TI_INDEX 0
+#define DMA_CH_SR_TI_WIDTH 1
+#define DMA_CH_SR_TPS_INDEX 1
+#define DMA_CH_SR_TPS_WIDTH 1
+#define DMA_CH_TCR_OSP_INDEX 4
+#define DMA_CH_TCR_OSP_WIDTH 1
+#define DMA_CH_TCR_PBL_INDEX 16
+#define DMA_CH_TCR_PBL_WIDTH 6
+#define DMA_CH_TCR_ST_INDEX 0
+#define DMA_CH_TCR_ST_WIDTH 1
+#define DMA_CH_TCR_TSE_INDEX 12
+#define DMA_CH_TCR_TSE_WIDTH 1
+
+/* DMA channel register values */
+#define DMA_OSP_DISABLE 0x00
+#define DMA_OSP_ENABLE 0x01
+#define DMA_PBL_1 1
+#define DMA_PBL_2 2
+#define DMA_PBL_4 4
+#define DMA_PBL_8 8
+#define DMA_PBL_16 16
+#define DMA_PBL_32 32
+#define DMA_PBL_64 64 /* 8 x 8 */
+#define DMA_PBL_128 128 /* 8 x 16 */
+#define DMA_PBL_256 256 /* 8 x 32 */
+#define DMA_PBL_X8_DISABLE 0x00
+#define DMA_PBL_X8_ENABLE 0x01
+
+
+/* MAC register offsets */
+#define MAC_TCR 0x0000
+#define MAC_RCR 0x0004
+#define MAC_PFR 0x0008
+#define MAC_WTR 0x000c
+#define MAC_HTR0 0x0010
+#define MAC_HTR1 0x0014
+#define MAC_HTR2 0x0018
+#define MAC_HTR3 0x001c
+#define MAC_HTR4 0x0020
+#define MAC_HTR5 0x0024
+#define MAC_HTR6 0x0028
+#define MAC_HTR7 0x002c
+#define MAC_VLANTR 0x0050
+#define MAC_VLANHTR 0x0058
+#define MAC_VLANIR 0x0060
+#define MAC_IVLANIR 0x0064
+#define MAC_RETMR 0x006c
+#define MAC_Q0TFCR 0x0070
+#define MAC_RFCR 0x0090
+#define MAC_RQC0R 0x00a0
+#define MAC_RQC1R 0x00a4
+#define MAC_RQC2R 0x00a8
+#define MAC_RQC3R 0x00ac
+#define MAC_ISR 0x00b0
+#define MAC_IER 0x00b4
+#define MAC_RTSR 0x00b8
+#define MAC_PMTCSR 0x00c0
+#define MAC_RWKPFR 0x00c4
+#define MAC_LPICSR 0x00d0
+#define MAC_LPITCR 0x00d4
+#define MAC_VR 0x0110
+#define MAC_DR 0x0114
+#define MAC_HWF0R 0x011c
+#define MAC_HWF1R 0x0120
+#define MAC_HWF2R 0x0124
+#define MAC_GPIOCR 0x0278
+#define MAC_GPIOSR 0x027c
+#define MAC_MACA0HR 0x0300
+#define MAC_MACA0LR 0x0304
+#define MAC_MACA1HR 0x0308
+#define MAC_MACA1LR 0x030c
+
+#define MAC_QTFCR_INC 4
+#define MAC_MACA_INC 4
+
+/* MAC register entry bit positions and sizes */
+#define MAC_HWF0R_ADDMACADRSEL_INDEX 18
+#define MAC_HWF0R_ADDMACADRSEL_WIDTH 5
+#define MAC_HWF0R_ARPOFFSEL_INDEX 9
+#define MAC_HWF0R_ARPOFFSEL_WIDTH 1
+#define MAC_HWF0R_EEESEL_INDEX 13
+#define MAC_HWF0R_EEESEL_WIDTH 1
+#define MAC_HWF0R_GMIISEL_INDEX 1
+#define MAC_HWF0R_GMIISEL_WIDTH 1
+#define MAC_HWF0R_MGKSEL_INDEX 7
+#define MAC_HWF0R_MGKSEL_WIDTH 1
+#define MAC_HWF0R_MMCSEL_INDEX 8
+#define MAC_HWF0R_MMCSEL_WIDTH 1
+#define MAC_HWF0R_RWKSEL_INDEX 6
+#define MAC_HWF0R_RWKSEL_WIDTH 1
+#define MAC_HWF0R_RXCOESEL_INDEX 16
+#define MAC_HWF0R_RXCOESEL_WIDTH 1
+#define MAC_HWF0R_SAVLANINS_INDEX 27
+#define MAC_HWF0R_SAVLANINS_WIDTH 1
+#define MAC_HWF0R_SMASEL_INDEX 5
+#define MAC_HWF0R_SMASEL_WIDTH 1
+#define MAC_HWF0R_TSSEL_INDEX 12
+#define MAC_HWF0R_TSSEL_WIDTH 1
+#define MAC_HWF0R_TSSTSSEL_INDEX 25
+#define MAC_HWF0R_TSSTSSEL_WIDTH 2
+#define MAC_HWF0R_TXCOESEL_INDEX 14
+#define MAC_HWF0R_TXCOESEL_WIDTH 1
+#define MAC_HWF0R_VLHASH_INDEX 4
+#define MAC_HWF0R_VLHASH_WIDTH 1
+#define MAC_HWF1R_ADVTHWORD_INDEX 13
+#define MAC_HWF1R_ADVTHWORD_WIDTH 1
+#define MAC_HWF1R_DBGMEMA_INDEX 19
+#define MAC_HWF1R_DBGMEMA_WIDTH 1
+#define MAC_HWF1R_DCBEN_INDEX 16
+#define MAC_HWF1R_DCBEN_WIDTH 1
+#define MAC_HWF1R_HASHTBLSZ_INDEX 24
+#define MAC_HWF1R_HASHTBLSZ_WIDTH 3
+#define MAC_HWF1R_L3L4FNUM_INDEX 27
+#define MAC_HWF1R_L3L4FNUM_WIDTH 4
+#define MAC_HWF1R_RSSEN_INDEX 20
+#define MAC_HWF1R_RSSEN_WIDTH 1
+#define MAC_HWF1R_RXFIFOSIZE_INDEX 0
+#define MAC_HWF1R_RXFIFOSIZE_WIDTH 5
+#define MAC_HWF1R_SPHEN_INDEX 17
+#define MAC_HWF1R_SPHEN_WIDTH 1
+#define MAC_HWF1R_TSOEN_INDEX 18
+#define MAC_HWF1R_TSOEN_WIDTH 1
+#define MAC_HWF1R_TXFIFOSIZE_INDEX 6
+#define MAC_HWF1R_TXFIFOSIZE_WIDTH 5
+#define MAC_HWF2R_AUXSNAPNUM_INDEX 28
+#define MAC_HWF2R_AUXSNAPNUM_WIDTH 3
+#define MAC_HWF2R_PPSOUTNUM_INDEX 24
+#define MAC_HWF2R_PPSOUTNUM_WIDTH 3
+#define MAC_HWF2R_RXCHCNT_INDEX 12
+#define MAC_HWF2R_RXCHCNT_WIDTH 4
+#define MAC_HWF2R_RXQCNT_INDEX 0
+#define MAC_HWF2R_RXQCNT_WIDTH 4
+#define MAC_HWF2R_TXCHCNT_INDEX 18
+#define MAC_HWF2R_TXCHCNT_WIDTH 4
+#define MAC_HWF2R_TXQCNT_INDEX 6
+#define MAC_HWF2R_TXQCNT_WIDTH 4
+#define MAC_ISR_MMCRXIS_INDEX 9
+#define MAC_ISR_MMCRXIS_WIDTH 1
+#define MAC_ISR_MMCTXIS_INDEX 10
+#define MAC_ISR_MMCTXIS_WIDTH 1
+#define MAC_ISR_PMTIS_INDEX 4
+#define MAC_ISR_PMTIS_WIDTH 1
+#define MAC_MACA1HR_AE_INDEX 31
+#define MAC_MACA1HR_AE_WIDTH 1
+#define MAC_PFR_HMC_INDEX 2
+#define MAC_PFR_HMC_WIDTH 1
+#define MAC_PFR_HUC_INDEX 1
+#define MAC_PFR_HUC_WIDTH 1
+#define MAC_PFR_PM_INDEX 4
+#define MAC_PFR_PM_WIDTH 1
+#define MAC_PFR_PR_INDEX 0
+#define MAC_PFR_PR_WIDTH 1
+#define MAC_PMTCSR_MGKPKTEN_INDEX 1
+#define MAC_PMTCSR_MGKPKTEN_WIDTH 1
+#define MAC_PMTCSR_PWRDWN_INDEX 0
+#define MAC_PMTCSR_PWRDWN_WIDTH 1
+#define MAC_PMTCSR_RWKFILTRST_INDEX 31
+#define MAC_PMTCSR_RWKFILTRST_WIDTH 1
+#define MAC_PMTCSR_RWKPKTEN_INDEX 2
+#define MAC_PMTCSR_RWKPKTEN_WIDTH 1
+#define MAC_Q0TFCR_PT_INDEX 16
+#define MAC_Q0TFCR_PT_WIDTH 16
+#define MAC_Q0TFCR_TFE_INDEX 1
+#define MAC_Q0TFCR_TFE_WIDTH 1
+#define MAC_RCR_ACS_INDEX 1
+#define MAC_RCR_ACS_WIDTH 1
+#define MAC_RCR_CST_INDEX 2
+#define MAC_RCR_CST_WIDTH 1
+#define MAC_RCR_DCRCC_INDEX 3
+#define MAC_RCR_DCRCC_WIDTH 1
+#define MAC_RCR_IPC_INDEX 9
+#define MAC_RCR_IPC_WIDTH 1
+#define MAC_RCR_JE_INDEX 8
+#define MAC_RCR_JE_WIDTH 1
+#define MAC_RCR_LM_INDEX 10
+#define MAC_RCR_LM_WIDTH 1
+#define MAC_RCR_RE_INDEX 0
+#define MAC_RCR_RE_WIDTH 1
+#define MAC_RFCR_RFE_INDEX 0
+#define MAC_RFCR_RFE_WIDTH 1
+#define MAC_RQC0R_RXQ0EN_INDEX 0
+#define MAC_RQC0R_RXQ0EN_WIDTH 2
+#define MAC_TCR_SS_INDEX 29
+#define MAC_TCR_SS_WIDTH 2
+#define MAC_TCR_TE_INDEX 0
+#define MAC_TCR_TE_WIDTH 1
+#define MAC_VLANTR_DOVLTC_INDEX 20
+#define MAC_VLANTR_DOVLTC_WIDTH 1
+#define MAC_VLANTR_ERSVLM_INDEX 19
+#define MAC_VLANTR_ERSVLM_WIDTH 1
+#define MAC_VLANTR_ESVL_INDEX 18
+#define MAC_VLANTR_ESVL_WIDTH 1
+#define MAC_VLANTR_EVLS_INDEX 21
+#define MAC_VLANTR_EVLS_WIDTH 2
+#define MAC_VLANTR_EVLRXS_INDEX 24
+#define MAC_VLANTR_EVLRXS_WIDTH 1
+#define MAC_VR_DEVID_INDEX 8
+#define MAC_VR_DEVID_WIDTH 8
+#define MAC_VR_SNPSVER_INDEX 0
+#define MAC_VR_SNPSVER_WIDTH 8
+#define MAC_VR_USERVER_INDEX 16
+#define MAC_VR_USERVER_WIDTH 8
+
+/* MMC register offsets */
+#define MMC_CR 0x0800
+#define MMC_RISR 0x0804
+#define MMC_TISR 0x0808
+#define MMC_RIER 0x080c
+#define MMC_TIER 0x0810
+#define MMC_TXOCTETCOUNT_GB_LO 0x0814
+#define MMC_TXOCTETCOUNT_GB_HI 0x0818
+#define MMC_TXFRAMECOUNT_GB_LO 0x081c
+#define MMC_TXFRAMECOUNT_GB_HI 0x0820
+#define MMC_TXBROADCASTFRAMES_G_LO 0x0824
+#define MMC_TXBROADCASTFRAMES_G_HI 0x0828
+#define MMC_TXMULTICASTFRAMES_G_LO 0x082c
+#define MMC_TXMULTICASTFRAMES_G_HI 0x0830
+#define MMC_TX64OCTETS_GB_LO 0x0834
+#define MMC_TX64OCTETS_GB_HI 0x0838
+#define MMC_TX65TO127OCTETS_GB_LO 0x083c
+#define MMC_TX65TO127OCTETS_GB_HI 0x0840
+#define MMC_TX128TO255OCTETS_GB_LO 0x0844
+#define MMC_TX128TO255OCTETS_GB_HI 0x0848
+#define MMC_TX256TO511OCTETS_GB_LO 0x084c
+#define MMC_TX256TO511OCTETS_GB_HI 0x0850
+#define MMC_TX512TO1023OCTETS_GB_LO 0x0854
+#define MMC_TX512TO1023OCTETS_GB_HI 0x0858
+#define MMC_TX1024TOMAXOCTETS_GB_LO 0x085c
+#define MMC_TX1024TOMAXOCTETS_GB_HI 0x0860
+#define MMC_TXUNICASTFRAMES_GB_LO 0x0864
+#define MMC_TXUNICASTFRAMES_GB_HI 0x0868
+#define MMC_TXMULTICASTFRAMES_GB_LO 0x086c
+#define MMC_TXMULTICASTFRAMES_GB_HI 0x0870
+#define MMC_TXBROADCASTFRAMES_GB_LO 0x0874
+#define MMC_TXBROADCASTFRAMES_GB_HI 0x0878
+#define MMC_TXUNDERFLOWERROR_LO 0x087c
+#define MMC_TXUNDERFLOWERROR_HI 0x0880
+#define MMC_TXOCTETCOUNT_G_LO 0x0884
+#define MMC_TXOCTETCOUNT_G_HI 0x0888
+#define MMC_TXFRAMECOUNT_G_LO 0x088c
+#define MMC_TXFRAMECOUNT_G_HI 0x0890
+#define MMC_TXPAUSEFRAMES_LO 0x0894
+#define MMC_TXPAUSEFRAMES_HI 0x0898
+#define MMC_TXVLANFRAMES_G_LO 0x089c
+#define MMC_TXVLANFRAMES_G_HI 0x08a0
+#define MMC_RXFRAMECOUNT_GB_LO 0x0900
+#define MMC_RXFRAMECOUNT_GB_HI 0x0904
+#define MMC_RXOCTETCOUNT_GB_LO 0x0908
+#define MMC_RXOCTETCOUNT_GB_HI 0x090c
+#define MMC_RXOCTETCOUNT_G_LO 0x0910
+#define MMC_RXOCTETCOUNT_G_HI 0x0914
+#define MMC_RXBROADCASTFRAMES_G_LO 0x0918
+#define MMC_RXBROADCASTFRAMES_G_HI 0x091c
+#define MMC_RXMULTICASTFRAMES_G_LO 0x0920
+#define MMC_RXMULTICASTFRAMES_G_HI 0x0924
+#define MMC_RXCRCERROR_LO 0x0928
+#define MMC_RXCRCERROR_HI 0x092c
+#define MMC_RXRUNTERROR 0x0930
+#define MMC_RXJABBERERROR 0x0934
+#define MMC_RXUNDERSIZE_G 0x0938
+#define MMC_RXOVERSIZE_G 0x093c
+#define MMC_RX64OCTETS_GB_LO 0x0940
+#define MMC_RX64OCTETS_GB_HI 0x0944
+#define MMC_RX65TO127OCTETS_GB_LO 0x0948
+#define MMC_RX65TO127OCTETS_GB_HI 0x094c
+#define MMC_RX128TO255OCTETS_GB_LO 0x0950
+#define MMC_RX128TO255OCTETS_GB_HI 0x0954
+#define MMC_RX256TO511OCTETS_GB_LO 0x0958
+#define MMC_RX256TO511OCTETS_GB_HI 0x095c
+#define MMC_RX512TO1023OCTETS_GB_LO 0x0960
+#define MMC_RX512TO1023OCTETS_GB_HI 0x0964
+#define MMC_RX1024TOMAXOCTETS_GB_LO 0x0968
+#define MMC_RX1024TOMAXOCTETS_GB_HI 0x096c
+#define MMC_RXUNICASTFRAMES_G_LO 0x0970
+#define MMC_RXUNICASTFRAMES_G_HI 0x0974
+#define MMC_RXLENGTHERROR_LO 0x0978
+#define MMC_RXLENGTHERROR_HI 0x097c
+#define MMC_RXOUTOFRANGETYPE_LO 0x0980
+#define MMC_RXOUTOFRANGETYPE_HI 0x0984
+#define MMC_RXPAUSEFRAMES_LO 0x0988
+#define MMC_RXPAUSEFRAMES_HI 0x098c
+#define MMC_RXFIFOOVERFLOW_LO 0x0990
+#define MMC_RXFIFOOVERFLOW_HI 0x0994
+#define MMC_RXVLANFRAMES_GB_LO 0x0998
+#define MMC_RXVLANFRAMES_GB_HI 0x099c
+#define MMC_RXWATCHDOGERROR 0x09a0
+
+/* MMC register entry bit positions and sizes */
+#define MMC_CR_CR_INDEX 0
+#define MMC_CR_CR_WIDTH 1
+#define MMC_CR_CSR_INDEX 1
+#define MMC_CR_CSR_WIDTH 1
+#define MMC_CR_ROR_INDEX 2
+#define MMC_CR_ROR_WIDTH 1
+#define MMC_CR_MCF_INDEX 3
+#define MMC_CR_MCF_WIDTH 1
+#define MMC_CR_MCT_INDEX 4
+#define MMC_CR_MCT_WIDTH 2
+#define MMC_RIER_ALL_INTERRUPTS_INDEX 0
+#define MMC_RIER_ALL_INTERRUPTS_WIDTH 23
+#define MMC_RISR_RXFRAMECOUNT_GB_INDEX 0
+#define MMC_RISR_RXFRAMECOUNT_GB_WIDTH 1
+#define MMC_RISR_RXOCTETCOUNT_GB_INDEX 1
+#define MMC_RISR_RXOCTETCOUNT_GB_WIDTH 1
+#define MMC_RISR_RXOCTETCOUNT_G_INDEX 2
+#define MMC_RISR_RXOCTETCOUNT_G_WIDTH 1
+#define MMC_RISR_RXBROADCASTFRAMES_G_INDEX 3
+#define MMC_RISR_RXBROADCASTFRAMES_G_WIDTH 1
+#define MMC_RISR_RXMULTICASTFRAMES_G_INDEX 4
+#define MMC_RISR_RXMULTICASTFRAMES_G_WIDTH 1
+#define MMC_RISR_RXCRCERROR_INDEX 5
+#define MMC_RISR_RXCRCERROR_WIDTH 1
+#define MMC_RISR_RXRUNTERROR_INDEX 6
+#define MMC_RISR_RXRUNTERROR_WIDTH 1
+#define MMC_RISR_RXJABBERERROR_INDEX 7
+#define MMC_RISR_RXJABBERERROR_WIDTH 1
+#define MMC_RISR_RXUNDERSIZE_G_INDEX 8
+#define MMC_RISR_RXUNDERSIZE_G_WIDTH 1
+#define MMC_RISR_RXOVERSIZE_G_INDEX 9
+#define MMC_RISR_RXOVERSIZE_G_WIDTH 1
+#define MMC_RISR_RX64OCTETS_GB_INDEX 10
+#define MMC_RISR_RX64OCTETS_GB_WIDTH 1
+#define MMC_RISR_RX65TO127OCTETS_GB_INDEX 11
+#define MMC_RISR_RX65TO127OCTETS_GB_WIDTH 1
+#define MMC_RISR_RX128TO255OCTETS_GB_INDEX 12
+#define MMC_RISR_RX128TO255OCTETS_GB_WIDTH 1
+#define MMC_RISR_RX256TO511OCTETS_GB_INDEX 13
+#define MMC_RISR_RX256TO511OCTETS_GB_WIDTH 1
+#define MMC_RISR_RX512TO1023OCTETS_GB_INDEX 14
+#define MMC_RISR_RX512TO1023OCTETS_GB_WIDTH 1
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_INDEX 15
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_WIDTH 1
+#define MMC_RISR_RXUNICASTFRAMES_G_INDEX 16
+#define MMC_RISR_RXUNICASTFRAMES_G_WIDTH 1
+#define MMC_RISR_RXLENGTHERROR_INDEX 17
+#define MMC_RISR_RXLENGTHERROR_WIDTH 1
+#define MMC_RISR_RXOUTOFRANGETYPE_INDEX 18
+#define MMC_RISR_RXOUTOFRANGETYPE_WIDTH 1
+#define MMC_RISR_RXPAUSEFRAMES_INDEX 19
+#define MMC_RISR_RXPAUSEFRAMES_WIDTH 1
+#define MMC_RISR_RXFIFOOVERFLOW_INDEX 20
+#define MMC_RISR_RXFIFOOVERFLOW_WIDTH 1
+#define MMC_RISR_RXVLANFRAMES_GB_INDEX 21
+#define MMC_RISR_RXVLANFRAMES_GB_WIDTH 1
+#define MMC_RISR_RXWATCHDOGERROR_INDEX 22
+#define MMC_RISR_RXWATCHDOGERROR_WIDTH 1
+#define MMC_TIER_ALL_INTERRUPTS_INDEX 0
+#define MMC_TIER_ALL_INTERRUPTS_WIDTH 18
+#define MMC_TISR_TXOCTETCOUNT_GB_INDEX 0
+#define MMC_TISR_TXOCTETCOUNT_GB_WIDTH 1
+#define MMC_TISR_TXFRAMECOUNT_GB_INDEX 1
+#define MMC_TISR_TXFRAMECOUNT_GB_WIDTH 1
+#define MMC_TISR_TXBROADCASTFRAMES_G_INDEX 2
+#define MMC_TISR_TXBROADCASTFRAMES_G_WIDTH 1
+#define MMC_TISR_TXMULTICASTFRAMES_G_INDEX 3
+#define MMC_TISR_TXMULTICASTFRAMES_G_WIDTH 1
+#define MMC_TISR_TX64OCTETS_GB_INDEX 4
+#define MMC_TISR_TX64OCTETS_GB_WIDTH 1
+#define MMC_TISR_TX65TO127OCTETS_GB_INDEX 5
+#define MMC_TISR_TX65TO127OCTETS_GB_WIDTH 1
+#define MMC_TISR_TX128TO255OCTETS_GB_INDEX 6
+#define MMC_TISR_TX128TO255OCTETS_GB_WIDTH 1
+#define MMC_TISR_TX256TO511OCTETS_GB_INDEX 7
+#define MMC_TISR_TX256TO511OCTETS_GB_WIDTH 1
+#define MMC_TISR_TX512TO1023OCTETS_GB_INDEX 8
+#define MMC_TISR_TX512TO1023OCTETS_GB_WIDTH 1
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_INDEX 9
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_WIDTH 1
+#define MMC_TISR_TXUNICASTFRAMES_GB_INDEX 10
+#define MMC_TISR_TXUNICASTFRAMES_GB_WIDTH 1
+#define MMC_TISR_TXMULTICASTFRAMES_GB_INDEX 11
+#define MMC_TISR_TXMULTICASTFRAMES_GB_WIDTH 1
+#define MMC_TISR_TXBROADCASTFRAMES_GB_INDEX 12
+#define MMC_TISR_TXBROADCASTFRAMES_GB_WIDTH 1
+#define MMC_TISR_TXUNDERFLOWERROR_INDEX 13
+#define MMC_TISR_TXUNDERFLOWERROR_WIDTH 1
+#define MMC_TISR_TXOCTETCOUNT_G_INDEX 14
+#define MMC_TISR_TXOCTETCOUNT_G_WIDTH 1
+#define MMC_TISR_TXFRAMECOUNT_G_INDEX 15
+#define MMC_TISR_TXFRAMECOUNT_G_WIDTH 1
+#define MMC_TISR_TXPAUSEFRAMES_INDEX 16
+#define MMC_TISR_TXPAUSEFRAMES_WIDTH 1
+#define MMC_TISR_TXVLANFRAMES_G_INDEX 17
+#define MMC_TISR_TXVLANFRAMES_G_WIDTH 1
+
+/* MTL register offsets */
+#define MTL_OMR 0x1000
+#define MTL_FDCR 0x1008
+#define MTL_FDSR 0x100c
+#define MTL_FDDR 0x1010
+#define MTL_ISR 0x1020
+#define MTL_RQDCM0R 0x1030
+#define MTL_TCPM0R 0x1040
+#define MTL_TCPM1R 0x1044
+
+#define MTL_RQDCM_INC 4
+#define MTL_RQDCM_Q_PER_REG 4
+
+/* MTL register entry bit positions and sizes */
+#define MTL_OMR_ETSALG_INDEX 5
+#define MTL_OMR_ETSALG_WIDTH 2
+#define MTL_OMR_RAA_INDEX 2
+#define MTL_OMR_RAA_WIDTH 1
+
+/* MTL queue register offsets
+ * Multiple queues can be active. The first queue has registers
+ * that begin at 0x1100. Each subsequent queue has registers that
+ * are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_Q_BASE 0x1100
+#define MTL_Q_INC 0x80
+
+#define MTL_Q_TQOMR 0x00
+#define MTL_Q_TQUR 0x04
+#define MTL_Q_TQDR 0x08
+#define MTL_Q_TCECR 0x10
+#define MTL_Q_TCESR 0x14
+#define MTL_Q_TCQWR 0x18
+#define MTL_Q_RQOMR 0x40
+#define MTL_Q_RQMPOCR 0x44
+#define MTL_Q_RQDR 0x4c
+#define MTL_Q_IER 0x70
+#define MTL_Q_ISR 0x74
+
+/* MTL queue register entry bit positions and sizes */
+#define MTL_Q_TCQWR_QW_INDEX 0
+#define MTL_Q_TCQWR_QW_WIDTH 21
+#define MTL_Q_RQOMR_EHFC_INDEX 7
+#define MTL_Q_RQOMR_EHFC_WIDTH 1
+#define MTL_Q_RQOMR_RFA_INDEX 8
+#define MTL_Q_RQOMR_RFA_WIDTH 3
+#define MTL_Q_RQOMR_RFD_INDEX 13
+#define MTL_Q_RQOMR_RFD_WIDTH 3
+#define MTL_Q_RQOMR_RQS_INDEX 16
+#define MTL_Q_RQOMR_RQS_WIDTH 9
+#define MTL_Q_RQOMR_RSF_INDEX 5
+#define MTL_Q_RQOMR_RSF_WIDTH 1
+#define MTL_Q_RQOMR_RTC_INDEX 0
+#define MTL_Q_RQOMR_RTC_WIDTH 2
+#define MTL_Q_TQOMR_FTQ_INDEX 0
+#define MTL_Q_TQOMR_FTQ_WIDTH 1
+#define MTL_Q_TQOMR_TQS_INDEX 16
+#define MTL_Q_TQOMR_TQS_WIDTH 10
+#define MTL_Q_TQOMR_TSF_INDEX 1
+#define MTL_Q_TQOMR_TSF_WIDTH 1
+#define MTL_Q_TQOMR_TTC_INDEX 4
+#define MTL_Q_TQOMR_TTC_WIDTH 3
+#define MTL_Q_TQOMR_TXQEN_INDEX 2
+#define MTL_Q_TQOMR_TXQEN_WIDTH 2
+
+/* MTL queue register value */
+#define MTL_RSF_DISABLE 0x00
+#define MTL_RSF_ENABLE 0x01
+#define MTL_TSF_DISABLE 0x00
+#define MTL_TSF_ENABLE 0x01
+
+#define MTL_RX_THRESHOLD_64 0x00
+#define MTL_RX_THRESHOLD_96 0x02
+#define MTL_RX_THRESHOLD_128 0x03
+#define MTL_TX_THRESHOLD_32 0x01
+#define MTL_TX_THRESHOLD_64 0x00
+#define MTL_TX_THRESHOLD_96 0x02
+#define MTL_TX_THRESHOLD_128 0x03
+#define MTL_TX_THRESHOLD_192 0x04
+#define MTL_TX_THRESHOLD_256 0x05
+#define MTL_TX_THRESHOLD_384 0x06
+#define MTL_TX_THRESHOLD_512 0x07
+
+#define MTL_ETSALG_WRR 0x00
+#define MTL_ETSALG_WFQ 0x01
+#define MTL_ETSALG_DWRR 0x02
+#define MTL_RAA_SP 0x00
+#define MTL_RAA_WSP 0x01
+
+#define MTL_Q_DISABLED 0x00
+#define MTL_Q_ENABLED 0x02
+
+
+/* MTL traffic class register offsets
+ * Multiple traffic classes can be active. The first class has registers
+ * that begin at 0x1100. Each subsequent queue has registers that
+ * are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_TC_BASE MTL_Q_BASE
+#define MTL_TC_INC MTL_Q_INC
+
+#define MTL_TC_ETSCR 0x10
+
+/* MTL traffic class register entry bit positions and sizes */
+#define MTL_TC_ETSCR_TSA_INDEX 0
+#define MTL_TC_ETSCR_TSA_WIDTH 2
+
+/* MTL traffic class register value */
+#define MTL_TSA_SP 0x00
+#define MTL_TSA_ETS 0x02
+
+
+/* PCS MMD select register offset
+ * The MMD select register is used for accessing PCS registers
+ * when the underlying APB3 interface is using indirect addressing.
+ * Indirect addressing requires accessing registers in two phases,
+ * an address phase and a data phase. The address phases requires
+ * writing an address selection value to the MMD select regiesters.
+ */
+#define PCS_MMD_SELECT 0xff
+
+
+/* Descriptor/Packet entry bit positions and sizes */
+#define RX_PACKET_ERRORS_CRC_INDEX 2
+#define RX_PACKET_ERRORS_CRC_WIDTH 1
+#define RX_PACKET_ERRORS_FRAME_INDEX 3
+#define RX_PACKET_ERRORS_FRAME_WIDTH 1
+#define RX_PACKET_ERRORS_LENGTH_INDEX 0
+#define RX_PACKET_ERRORS_LENGTH_WIDTH 1
+#define RX_PACKET_ERRORS_OVERRUN_INDEX 1
+#define RX_PACKET_ERRORS_OVERRUN_WIDTH 1
+
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_INDEX 0
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH 1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX 2
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH 1
+
+#define RX_NORMAL_DESC0_OVT_INDEX 0
+#define RX_NORMAL_DESC0_OVT_WIDTH 16
+#define RX_NORMAL_DESC3_ES_INDEX 15
+#define RX_NORMAL_DESC3_ES_WIDTH 1
+#define RX_NORMAL_DESC3_ETLT_INDEX 16
+#define RX_NORMAL_DESC3_ETLT_WIDTH 4
+#define RX_NORMAL_DESC3_INTE_INDEX 30
+#define RX_NORMAL_DESC3_INTE_WIDTH 1
+#define RX_NORMAL_DESC3_LD_INDEX 28
+#define RX_NORMAL_DESC3_LD_WIDTH 1
+#define RX_NORMAL_DESC3_OWN_INDEX 31
+#define RX_NORMAL_DESC3_OWN_WIDTH 1
+#define RX_NORMAL_DESC3_PL_INDEX 0
+#define RX_NORMAL_DESC3_PL_WIDTH 14
+
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_INDEX 0
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_WIDTH 1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_INDEX 1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_WIDTH 1
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 2
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1
+
+#define TX_CONTEXT_DESC2_MSS_INDEX 0
+#define TX_CONTEXT_DESC2_MSS_WIDTH 15
+#define TX_CONTEXT_DESC3_CTXT_INDEX 30
+#define TX_CONTEXT_DESC3_CTXT_WIDTH 1
+#define TX_CONTEXT_DESC3_TCMSSV_INDEX 26
+#define TX_CONTEXT_DESC3_TCMSSV_WIDTH 1
+#define TX_CONTEXT_DESC3_VLTV_INDEX 16
+#define TX_CONTEXT_DESC3_VLTV_WIDTH 1
+#define TX_CONTEXT_DESC3_VT_INDEX 0
+#define TX_CONTEXT_DESC3_VT_WIDTH 16
+
+#define TX_NORMAL_DESC2_HL_B1L_INDEX 0
+#define TX_NORMAL_DESC2_HL_B1L_WIDTH 14
+#define TX_NORMAL_DESC2_IC_INDEX 31
+#define TX_NORMAL_DESC2_IC_WIDTH 1
+#define TX_NORMAL_DESC2_VTIR_INDEX 14
+#define TX_NORMAL_DESC2_VTIR_WIDTH 2
+#define TX_NORMAL_DESC3_CIC_INDEX 16
+#define TX_NORMAL_DESC3_CIC_WIDTH 2
+#define TX_NORMAL_DESC3_CPC_INDEX 26
+#define TX_NORMAL_DESC3_CPC_WIDTH 2
+#define TX_NORMAL_DESC3_CTXT_INDEX 30
+#define TX_NORMAL_DESC3_CTXT_WIDTH 1
+#define TX_NORMAL_DESC3_FD_INDEX 29
+#define TX_NORMAL_DESC3_FD_WIDTH 1
+#define TX_NORMAL_DESC3_FL_INDEX 0
+#define TX_NORMAL_DESC3_FL_WIDTH 15
+#define TX_NORMAL_DESC3_LD_INDEX 28
+#define TX_NORMAL_DESC3_LD_WIDTH 1
+#define TX_NORMAL_DESC3_OWN_INDEX 31
+#define TX_NORMAL_DESC3_OWN_WIDTH 1
+#define TX_NORMAL_DESC3_TCPHDRLEN_INDEX 19
+#define TX_NORMAL_DESC3_TCPHDRLEN_WIDTH 4
+#define TX_NORMAL_DESC3_TCPPL_INDEX 0
+#define TX_NORMAL_DESC3_TCPPL_WIDTH 18
+#define TX_NORMAL_DESC3_TSE_INDEX 18
+#define TX_NORMAL_DESC3_TSE_WIDTH 1
+
+#define TX_NORMAL_DESC2_VLAN_INSERT 0x2
+
+/* MDIO undefined or vendor specific registers */
+#ifndef MDIO_AN_COMP_STAT
+#define MDIO_AN_COMP_STAT 0x0030
+#endif
+
+
+/* Bit setting and getting macros
+ * The get macro will extract the current bit field value from within
+ * the variable
+ *
+ * The set macro will clear the current bit field value within the
+ * variable and then set the bit field of the variable to the
+ * specified value
+ */
+#define GET_BITS(_var, _index, _width) \
+ (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val) \
+do { \
+ (_var) &= ~(((0x1 << (_width)) - 1) << (_index)); \
+ (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \
+} while (0)
+
+#define GET_BITS_LE(_var, _index, _width) \
+ ((le32_to_cpu((_var)) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS_LE(_var, _index, _width, _val) \
+do { \
+ (_var) &= cpu_to_le32(~(((0x1 << (_width)) - 1) << (_index))); \
+ (_var) |= cpu_to_le32((((_val) & \
+ ((0x1 << (_width)) - 1)) << (_index))); \
+} while (0)
+
+
+/* Bit setting and getting macros based on register fields
+ * The get macro uses the bit field definitions formed using the input
+ * names to extract the current bit field value from within the
+ * variable
+ *
+ * The set macro uses the bit field definitions formed using the input
+ * names to set the bit field of the variable to the specified value
+ */
+#define XGMAC_GET_BITS(_var, _prefix, _field) \
+ GET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS(_var, _prefix, _field, _val) \
+ SET_BITS((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH, (_val))
+
+#define XGMAC_GET_BITS_LE(_var, _prefix, _field) \
+ GET_BITS_LE((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS_LE(_var, _prefix, _field, _val) \
+ SET_BITS_LE((_var), \
+ _prefix##_##_field##_INDEX, \
+ _prefix##_##_field##_WIDTH, (_val))
+
+
+/* Macros for reading or writing registers
+ * The ioread macros will get bit fields or full values using the
+ * register definitions formed using the input names
+ *
+ * The iowrite macros will set bit fields or full values using the
+ * register definitions formed using the input names
+ */
+#define XGMAC_IOREAD(_pdata, _reg) \
+ ioread32((_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOREAD_BITS(_pdata, _reg, _field) \
+ GET_BITS(XGMAC_IOREAD((_pdata), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XGMAC_IOWRITE(_pdata, _reg, _val) \
+ iowrite32((_val), (_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOWRITE_BITS(_pdata, _reg, _field, _val) \
+do { \
+ u32 reg_val = XGMAC_IOREAD((_pdata), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XGMAC_IOWRITE((_pdata), _reg, reg_val); \
+} while (0)
+
+
+/* Macros for reading or writing MTL queue or traffic class registers
+ * Similar to the standard read and write macros except that the
+ * base register value is calculated by the queue or traffic class number
+ */
+#define XGMAC_MTL_IOREAD(_pdata, _n, _reg) \
+ ioread32((_pdata)->xgmac_regs + \
+ MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOREAD_BITS(_pdata, _n, _reg, _field) \
+ GET_BITS(XGMAC_MTL_IOREAD((_pdata), (_n), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XGMAC_MTL_IOWRITE(_pdata, _n, _reg, _val) \
+ iowrite32((_val), (_pdata)->xgmac_regs + \
+ MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOWRITE_BITS(_pdata, _n, _reg, _field, _val) \
+do { \
+ u32 reg_val = XGMAC_MTL_IOREAD((_pdata), (_n), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XGMAC_MTL_IOWRITE((_pdata), (_n), _reg, reg_val); \
+} while (0)
+
+
+/* Macros for reading or writing DMA channel registers
+ * Similar to the standard read and write macros except that the
+ * base register value is obtained from the ring
+ */
+#define XGMAC_DMA_IOREAD(_channel, _reg) \
+ ioread32((_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOREAD_BITS(_channel, _reg, _field) \
+ GET_BITS(XGMAC_DMA_IOREAD((_channel), _reg), \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH)
+
+#define XGMAC_DMA_IOWRITE(_channel, _reg, _val) \
+ iowrite32((_val), (_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOWRITE_BITS(_channel, _reg, _field, _val) \
+do { \
+ u32 reg_val = XGMAC_DMA_IOREAD((_channel), _reg); \
+ SET_BITS(reg_val, \
+ _reg##_##_field##_INDEX, \
+ _reg##_##_field##_WIDTH, (_val)); \
+ XGMAC_DMA_IOWRITE((_channel), _reg, reg_val); \
+} while (0)
+
+
+/* Macros for building, reading or writing register values or bits
+ * within the register values of XPCS registers.
+ */
+#define XPCS_IOWRITE(_pdata, _off, _val) \
+ iowrite32(_val, (_pdata)->xpcs_regs + (_off))
+
+#define XPCS_IOREAD(_pdata, _off) \
+ ioread32((_pdata)->xpcs_regs + (_off))
+
+
+/* Macros for building, reading or writing register values or bits
+ * using MDIO. Different from above because of the use of standardized
+ * Linux include values. No shifting is performed with the bit
+ * operations, everything works on mask values.
+ */
+#define XMDIO_READ(_pdata, _mmd, _reg) \
+ ((_pdata)->hw_if.read_mmd_regs((_pdata), 0, \
+ MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff)))
+
+#define XMDIO_READ_BITS(_pdata, _mmd, _reg, _mask) \
+ (XMDIO_READ((_pdata), _mmd, _reg) & _mask)
+
+#define XMDIO_WRITE(_pdata, _mmd, _reg, _val) \
+ ((_pdata)->hw_if.write_mmd_regs((_pdata), 0, \
+ MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff), (_val)))
+
+#define XMDIO_WRITE_BITS(_pdata, _mmd, _reg, _mask, _val) \
+do { \
+ u32 mmd_val = XMDIO_READ((_pdata), _mmd, _reg); \
+ mmd_val &= ~_mask; \
+ mmd_val |= (_val); \
+ XMDIO_WRITE((_pdata), _mmd, _reg, mmd_val); \
+} while (0)
+
+#endif
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
new file mode 100644