mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 20:40:53 +07:00
Merge branch 'legacy-isa-delete' of git://git.kernel.org/pub/scm/linux/kernel/git/paulg/linux
Paul Gortmaker says: ==================== The Ethernet-HowTo was maintained for roughly 10 years, from 1993 to 2003. Fortunately sane hardware probing and auto detection (via PCI and ISA/PnP) largely made the document a relic of the past, hence it being abandoned a decade ago. However, there is one last useful thing that we can extract from the effort made in maintaining that document. We can use it to guide us with respect to what rare, experimental and/or super ancient 10Mbit ISA drivers don't make sense to maintain in-tree anymore. Nobody will argue that ISA is obsolete. Availability went away at about the time Pentium3 motherboards moved from 500MHz Slot1/SECC processors to the green 500MHz Socket 370 Pentium3 chips, at the turn of the century. In theory, it is possible that someone could still be running one of these 12+ year old P3 machines and want 3.9+ bleeding edge kernels (but unlikely). In light of the above (remote) possibility, we can defer the removal of some ISA network drivers that were highly popular and well tested. Typically that means the stuff more from the mid to late '90s, some with ISA PnP support, like the 3c509, the wd/SMC 8390 based stuff, PCnet/lance etc. But a lot of other drivers, typically from the early 1990s were for rare hardware, and experimental (to the point of requiring a cron job that would do a test ping, and then ifconfig down/up and/or a rmmod/insmod!). And some of these drivers (znet, and lp486e to name two) are physically tied to platforms with on motherboard ethernet -- of 486 machines that date from the early 1990s and can only have single digit amounts of memory. What I'd like to achieve here with this series, is to get rid of those old drivers that are no longer being used. In an earlier discussion where I'd proposed deleting a single driver, Alan suggested we instead dump all the historical stuff in one go, to make it "...immediately obvious where the break point is..."[1] and that it was "perfectly reasonable it (and a pile of other ISA cards) ought to be shown the door"[2]. So that is the goal here - make a clear line in the sand where the really ancient stuff finally gets kicked to the curb. Two old parallel port drivers are considered for removal here as well, since in early 386/486 ISA machines, the parallel port was typically found with the UARTS on the multi-I/O ISA controller card. These drivers also date from the early 1990's; parallel ports are no longer found on modern boards, and their performance was not even capable of 10% of 10Mbit bandwidth. Allow me a preemptive justification against the inevitable comments from well meaning bystanders who suggest "why not just leave all this alone?". Dead drivers cost us all if they are left in tree. If you think that is false, then please first consider: -every time you type "git status", you are checking to see if modifications have been made by you to all that dead code. -every time you type "git grep <regex>" you are searching through files which contain that dead code that simply does not interest you. -every time you build a "allyesconfig" and an "allmodconfig" (don't tell me you skip this step before submitting your changes to a maintainer), you waste CPU cycles building this dead code. -every time there is a tree wide API change, or cleanup, or file relocation, we pay the cost of updating dead code, or moving dead code. -daily regression tests (take linux-next as the most transparent example) spend time building (and possibly running) this dead code. -hard working people who regularly run auditing tools looking for lurking bugs (sparse/coverity/smatch/coccinelle) are wasting time checking for, and fixing bugs in this dead code. This last one is key. Please take a look at the git history for the files that are proposed for removal here. Look at the git history for any one of them ("git whatchanged --follow drivers/net/.../driver.c") Mentally sort the changes into two bins -- (1) the robotic tree-wide changes, and (2) the "look I found a real run-time bug while using this" category. You will see that category #2 is essentially empty. Further to that, realize that drivers don't simply disappear. We are not operating in the binary-only distribution space like other OS. All these drivers remain in the git history forever. If a person is an enthusiast for extreme legacy hardware, they are probably already customizing their kernel source and building it themselves to support such systems. Also keep in mind that they could still build the 3.8 kernel exactly as-is, and run it (or a 3.8.x stable variant of it) for several more years if they were really determined to cling to these old experimental ISA drivers for some reason. In summary, I hope that folks can be pragmatic about this, and not get swept up in nostalgia. Ask yourself whether it is realistic to expect a person would have a genuine use case where they would need to build a 3.9+ modern kernel and install it on some legacy hardware that has no option but to absolutely _require_ one of the drivers that are deleted here. The following series was created with --irreversible-delete for ease of review (it skips showing the content of files that are deleted); however the complete patches can be pulled as per below. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
930d52c012
@ -52,8 +52,6 @@ de4x5.txt
|
||||
- the Digital EtherWORKS DE4?? and DE5?? PCI Ethernet driver
|
||||
decnet.txt
|
||||
- info on using the DECnet networking layer in Linux.
|
||||
depca.txt
|
||||
- the Digital DEPCA/EtherWORKS DE1?? and DE2?? LANCE Ethernet driver
|
||||
dl2k.txt
|
||||
- README for D-Link DL2000-based Gigabit Ethernet Adapters (dl2k.ko).
|
||||
dm9000.txt
|
||||
@ -72,8 +70,6 @@ e1000e.txt
|
||||
- README for the Intel Gigabit Ethernet Driver (e1000e).
|
||||
eql.txt
|
||||
- serial IP load balancing
|
||||
ewrk3.txt
|
||||
- the Digital EtherWORKS 3 DE203/4/5 Ethernet driver
|
||||
fib_trie.txt
|
||||
- Level Compressed Trie (LC-trie) notes: a structure for routing.
|
||||
filter.txt
|
||||
|
@ -1,203 +0,0 @@
|
||||
Released 1994-06-13
|
||||
|
||||
|
||||
CONTENTS:
|
||||
|
||||
1. Introduction.
|
||||
2. License.
|
||||
3. Files in this release.
|
||||
4. Installation.
|
||||
5. Problems and tuning.
|
||||
6. Using the drivers with earlier releases.
|
||||
7. Acknowledgments.
|
||||
|
||||
|
||||
1. INTRODUCTION.
|
||||
|
||||
This is a set of Ethernet drivers for the D-Link DE-600/DE-620
|
||||
pocket adapters, for the parallel port on a Linux based machine.
|
||||
Some adapter "clones" will also work. Xircom is _not_ a clone...
|
||||
These drivers _can_ be used as loadable modules,
|
||||
and were developed for use on Linux 1.1.13 and above.
|
||||
For use on Linux 1.0.X, or earlier releases, see below.
|
||||
|
||||
I have used these drivers for NFS, ftp, telnet and X-clients on
|
||||
remote machines. Transmissions with ftp seems to work as
|
||||
good as can be expected (i.e. > 80k bytes/sec) from a
|
||||
parallel port...:-) Receive speeds will be about 60-80% of this.
|
||||
Depending on your machine, somewhat higher speeds can be achieved.
|
||||
|
||||
All comments/fixes to Bjorn Ekwall (bj0rn@blox.se).
|
||||
|
||||
|
||||
2. LICENSE.
|
||||
|
||||
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, 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, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
|
||||
02139, USA.
|
||||
|
||||
|
||||
3. FILES IN THIS RELEASE.
|
||||
|
||||
README.DLINK This file.
|
||||
de600.c The Source (may it be with You :-) for the DE-600
|
||||
de620.c ditto for the DE-620
|
||||
de620.h Macros for de620.c
|
||||
|
||||
If you are upgrading from the d-link tar release, there will
|
||||
also be a "dlink-patches" file that will patch Linux 1.1.18:
|
||||
linux/drivers/net/Makefile
|
||||
linux/drivers/net/CONFIG
|
||||
linux/drivers/net/MODULES
|
||||
linux/drivers/net/Space.c
|
||||
linux/config.in
|
||||
Apply the patch by:
|
||||
"cd /usr/src; patch -p0 < linux/drivers/net/dlink-patches"
|
||||
The old source, "linux/drivers/net/d_link.c", can be removed.
|
||||
|
||||
|
||||
4. INSTALLATION.
|
||||
|
||||
o Get the latest net binaries, according to current net.wisdom.
|
||||
|
||||
o Read the NET-2 and Ethernet HOWTOs and modify your setup.
|
||||
|
||||
o If your parallel port has a strange address or irq,
|
||||
modify "linux/drivers/net/CONFIG" accordingly, or adjust
|
||||
the parameters in the "tuning" section in the sources.
|
||||
|
||||
If you are going to use the drivers as loadable modules, do _not_
|
||||
enable them while doing "make config", but instead make sure that
|
||||
the drivers are included in "linux/drivers/net/MODULES".
|
||||
|
||||
If you are _not_ going to use the driver(s) as loadable modules,
|
||||
but instead have them included in the kernel, remember to enable
|
||||
the drivers while doing "make config".
|
||||
|
||||
o To include networking and DE600/DE620 support in your kernel:
|
||||
# cd /linux
|
||||
(as modules:)
|
||||
# make config (answer yes on CONFIG_NET and CONFIG_INET)
|
||||
(else included in the kernel:)
|
||||
# make config (answer yes on CONFIG _NET, _INET and _DE600 or _DE620)
|
||||
# make clean
|
||||
# make zImage (or whatever magic you usually do)
|
||||
|
||||
o I use lilo to boot multiple kernels, so that I at least
|
||||
can have one working kernel :-). If you do too, append
|
||||
these lines to /etc/lilo/config:
|
||||
|
||||
image = /linux/zImage
|
||||
label = newlinux
|
||||
root = /dev/hda2 (or whatever YOU have...)
|
||||
|
||||
# /etc/lilo/install
|
||||
|
||||
o Do "sync" and reboot the new kernel with a D-Link
|
||||
DE-600/DE-620 pocket adapter connected.
|
||||
|
||||
o The adapter can be configured with ifconfig eth?
|
||||
where the actual number is decided by the kernel
|
||||
when the drivers are initialized.
|
||||
|
||||
|
||||
5. "PROBLEMS" AND TUNING,
|
||||
|
||||
o If you see error messages from the driver, and if the traffic
|
||||
stops on the adapter, try to do "ifconfig" and "route" once
|
||||
more, just as in "rc.inet1". This should take care of most
|
||||
problems, including effects from power loss, or adapters that
|
||||
aren't connected to the printer port in some way or another.
|
||||
You can somewhat change the behaviour by enabling/disabling
|
||||
the macro SHUTDOWN_WHEN_LOST in the "tuning" section.
|
||||
For the DE-600 there is another macro, CHECK_LOST_DE600,
|
||||
that you might want to read about in the "tuning" section.
|
||||
|
||||
o Some machines have trouble handling the parallel port and
|
||||
the adapter at high speed. If you experience problems:
|
||||
|
||||
DE-600:
|
||||
- The adapter is not recognized at boot, i.e. an Ethernet
|
||||
address of 00:80:c8:... is not shown, try to add another
|
||||
"; SLOW_DOWN_IO"
|
||||
at DE600_SLOW_DOWN in the "tuning" section. As a last resort,
|
||||
uncomment: "#define REALLY_SLOW_IO" (see <asm/io.h> for hints).
|
||||
|
||||
- You experience "timeout" messages: first try to add another
|
||||
"; SLOW_DOWN_IO"
|
||||
at DE600_SLOW_DOWN in the "tuning" section, _then_ try to
|
||||
increase the value (original value: 5) at
|
||||
"if (tickssofar < 5)" near line 422.
|
||||
|
||||
DE-620:
|
||||
- Your parallel port might be "sluggish". To cater for
|
||||
this, there are the macros LOWSPEED and READ_DELAY/WRITE_DELAY
|
||||
in the "tuning" section. Your first step should be to enable
|
||||
LOWSPEED, and after that you can "tune" the XXX_DELAY values.
|
||||
|
||||
o If the adapter _is_ recognized at boot but you get messages
|
||||
about "Network Unreachable", then the problem is probably
|
||||
_not_ with the driver. Check your net configuration instead
|
||||
(ifconfig and route) in "rc.inet1".
|
||||
|
||||
o There is some rudimentary support for debugging, look at
|
||||
the source. Use "-DDE600_DEBUG=3" or "-DDE620_DEBUG=3"
|
||||
when compiling, or include it in "linux/drivers/net/CONFIG".
|
||||
IF YOU HAVE PROBLEMS YOU CAN'T SOLVE: PLEASE COMPILE THE DRIVER
|
||||
WITH DEBUGGING ENABLED, AND SEND ME THE RESULTING OUTPUT!
|
||||
|
||||
|
||||
6. USING THE DRIVERS WITH EARLIER RELEASES.
|
||||
|
||||
The later 1.1.X releases of the Linux kernel include some
|
||||
changes in the networking layer (a.k.a. NET3). This affects
|
||||
these drivers in a few places. The hints that follow are
|
||||
_not_ tested by me, since I don't have the disk space to keep
|
||||
all releases on-line.
|
||||
Known needed changes to date:
|
||||
- release patchfile: some patches will fail, but they should
|
||||
be easy to apply "by hand", since they are trivial.
|
||||
(Space.c: d_link_init() is now called de600_probe())
|
||||
- de600.c: change "mark_bh(NET_BH)" to "mark_bh(INET_BH)".
|
||||
- de620.c: (maybe) change the code around "netif_rx(skb);" to be
|
||||
similar to the code around "dev_rint(...)" in de600.c
|
||||
|
||||
|
||||
7. ACKNOWLEDGMENTS.
|
||||
|
||||
These drivers wouldn't have been done without the base
|
||||
(and support) from Ross Biro, and D-Link Systems Inc.
|
||||
The driver relies upon GPL-ed source from D-Link Systems Inc.
|
||||
and from Russel Nelson at Crynwr Software <nelson@crynwr.com>.
|
||||
|
||||
Additional input also from:
|
||||
Donald Becker <becker@super.org>, Alan Cox <A.Cox@swansea.ac.uk>
|
||||
and Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
|
||||
|
||||
DE-600 alpha release primary victim^H^H^H^H^H^Htester:
|
||||
- Erik Proper <erikp@cs.kun.nl>.
|
||||
Good input also from several users, most notably
|
||||
- Mark Burton <markb@ordern.demon.co.uk>.
|
||||
|
||||
DE-620 alpha release victims^H^H^H^H^H^H^Htesters:
|
||||
- J. Joshua Kopper <kopper@rtsg.mot.com>
|
||||
- Olav Kvittem <Olav.Kvittem@uninett.no>
|
||||
- Germano Caronni <caronni@nessie.cs.id.ethz.ch>
|
||||
- Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
|
||||
|
||||
|
||||
Happy hacking!
|
||||
|
||||
Bjorn Ekwall == bj0rn@blox.se
|
@ -1,92 +0,0 @@
|
||||
|
||||
DE10x
|
||||
=====
|
||||
|
||||
Memory Addresses:
|
||||
|
||||
SW1 SW2 SW3 SW4
|
||||
64K on on on on d0000 dbfff
|
||||
off on on on c0000 cbfff
|
||||
off off on on e0000 ebfff
|
||||
|
||||
32K on on off on d8000 dbfff
|
||||
off on off on c8000 cbfff
|
||||
off off off on e8000 ebfff
|
||||
|
||||
DBR ROM on on dc000 dffff
|
||||
off on cc000 cffff
|
||||
off off ec000 effff
|
||||
|
||||
Note that the 2K mode is set by SW3/SW4 on/off or off/off. Address
|
||||
assignment is through the RBSA register.
|
||||
|
||||
I/O Address:
|
||||
SW5
|
||||
0x300 on
|
||||
0x200 off
|
||||
|
||||
Remote Boot:
|
||||
SW6
|
||||
Disable on
|
||||
Enable off
|
||||
|
||||
Remote Boot Timeout:
|
||||
SW7
|
||||
2.5min on
|
||||
30s off
|
||||
|
||||
IRQ:
|
||||
SW8 SW9 SW10 SW11 SW12
|
||||
2 on off off off off
|
||||
3 off on off off off
|
||||
4 off off on off off
|
||||
5 off off off on off
|
||||
7 off off off off on
|
||||
|
||||
DE20x
|
||||
=====
|
||||
|
||||
Memory Size:
|
||||
|
||||
SW3 SW4
|
||||
64K on on
|
||||
32K off on
|
||||
2K on off
|
||||
2K off off
|
||||
|
||||
Start Addresses:
|
||||
|
||||
SW1 SW2 SW3 SW4
|
||||
64K on on on on c0000 cffff
|
||||
on off on on d0000 dffff
|
||||
off on on on e0000 effff
|
||||
|
||||
32K on on off off c8000 cffff
|
||||
on off off off d8000 dffff
|
||||
off on off off e8000 effff
|
||||
|
||||
Illegal off off - - - -
|
||||
|
||||
I/O Address:
|
||||
SW5
|
||||
0x300 on
|
||||
0x200 off
|
||||
|
||||
Remote Boot:
|
||||
SW6
|
||||
Disable on
|
||||
Enable off
|
||||
|
||||
Remote Boot Timeout:
|
||||
SW7
|
||||
2.5min on
|
||||
30s off
|
||||
|
||||
IRQ:
|
||||
SW8 SW9 SW10 SW11 SW12
|
||||
5 on off off off off
|
||||
9 off on off off off
|
||||
10 off off on off off
|
||||
11 off off off on off
|
||||
15 off off off off on
|
||||
|
@ -1,46 +0,0 @@
|
||||
The EtherWORKS 3 driver in this distribution is designed to work with all
|
||||
kernels > 1.1.33 (approx) and includes tools in the 'ewrk3tools'
|
||||
subdirectory to allow set up of the card, similar to the MSDOS
|
||||
'NICSETUP.EXE' tools provided on the DOS drivers disk (type 'make' in that
|
||||
subdirectory to make the tools).
|
||||
|
||||
The supported cards are DE203, DE204 and DE205. All other cards are NOT
|
||||
supported - refer to 'depca.c' for running the LANCE based network cards and
|
||||
'de4x5.c' for the DIGITAL Semiconductor PCI chip based adapters from
|
||||
Digital.
|
||||
|
||||
The ability to load this driver as a loadable module has been included and
|
||||
used extensively during the driver development (to save those long reboot
|
||||
sequences). To utilise this ability, you have to do 8 things:
|
||||
|
||||
0) have a copy of the loadable modules code installed on your system.
|
||||
1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
|
||||
temporary directory.
|
||||
2) edit the source code near line 1898 to reflect the I/O address and
|
||||
IRQ you're using.
|
||||
3) compile ewrk3.c, but include -DMODULE in the command line to ensure
|
||||
that the correct bits are compiled (see end of source code).
|
||||
4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
|
||||
kernel with the ewrk3 configuration turned off and reboot.
|
||||
5) insmod ewrk3.o
|
||||
[Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
|
||||
[Adam Kropelin: Multiple cards now supported by irq=x1,x2 io=y1,y2]
|
||||
6) run the net startup bits for your new eth?? interface manually
|
||||
(usually /etc/rc.inet[12] at boot time).
|
||||
7) enjoy!
|
||||
|
||||
Note that autoprobing is not allowed in loadable modules - the system is
|
||||
already up and running and you're messing with interrupts.
|
||||
|
||||
To unload a module, turn off the associated interface
|
||||
'ifconfig eth?? down' then 'rmmod ewrk3'.
|
||||
|
||||
The performance we've achieved so far has been measured through the 'ttcp'
|
||||
tool at 975kB/s. This measures the total TCP stack performance which
|
||||
includes the card, so don't expect to get much nearer the 1.25MB/s
|
||||
theoretical Ethernet rate.
|
||||
|
||||
|
||||
Enjoy!
|
||||
|
||||
Dave
|
12
MAINTAINERS
12
MAINTAINERS
@ -2974,11 +2974,6 @@ S: Maintained
|
||||
F: include/linux/netfilter_bridge/
|
||||
F: net/bridge/
|
||||
|
||||
ETHERTEAM 16I DRIVER
|
||||
M: Mika Kuoppala <miku@iki.fi>
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/fujitsu/eth16i.c
|
||||
|
||||
EXT2 FILE SYSTEM
|
||||
M: Jan Kara <jack@suse.cz>
|
||||
L: linux-ext4@vger.kernel.org
|
||||
@ -5370,13 +5365,6 @@ F: include/linux/sunrpc/
|
||||
F: include/uapi/linux/nfs*
|
||||
F: include/uapi/linux/sunrpc/
|
||||
|
||||
NI5010 NETWORK DRIVER
|
||||
M: Jan-Pascal van Best <janpascal@vanbest.org>
|
||||
M: Andreas Mohr <andi@lisas.de>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/racal/ni5010.*
|
||||
|
||||
NILFS2 FILESYSTEM
|
||||
M: KONISHI Ryusuke <konishi.ryusuke@lab.ntt.co.jp>
|
||||
L: linux-nilfs@vger.kernel.org
|
||||
|
@ -40,26 +40,11 @@
|
||||
extern struct net_device *hp100_probe(int unit);
|
||||
extern struct net_device *ultra_probe(int unit);
|
||||
extern struct net_device *wd_probe(int unit);
|
||||
extern struct net_device *el2_probe(int unit);
|
||||
extern struct net_device *ne_probe(int unit);
|
||||
extern struct net_device *hp_probe(int unit);
|
||||
extern struct net_device *hp_plus_probe(int unit);
|
||||
extern struct net_device *express_probe(int unit);
|
||||
extern struct net_device *eepro_probe(int unit);
|
||||
extern struct net_device *at1700_probe(int unit);
|
||||
extern struct net_device *fmv18x_probe(int unit);
|
||||
extern struct net_device *eth16i_probe(int unit);
|
||||
extern struct net_device *i82596_probe(int unit);
|
||||
extern struct net_device *ewrk3_probe(int unit);
|
||||
extern struct net_device *el1_probe(int unit);
|
||||
extern struct net_device *el16_probe(int unit);
|
||||
extern struct net_device *elplus_probe(int unit);
|
||||
extern struct net_device *e2100_probe(int unit);
|
||||
extern struct net_device *ni5010_probe(int unit);
|
||||
extern struct net_device *ni52_probe(int unit);
|
||||
extern struct net_device *ni65_probe(int unit);
|
||||
extern struct net_device *sonic_probe(int unit);
|
||||
extern struct net_device *seeq8005_probe(int unit);
|
||||
extern struct net_device *smc_init(int unit);
|
||||
extern struct net_device *atarilance_probe(int unit);
|
||||
extern struct net_device *sun3lance_probe(int unit);
|
||||
@ -74,9 +59,6 @@ extern struct net_device *mac89x0_probe(int unit);
|
||||
extern struct net_device *cops_probe(int unit);
|
||||
extern struct net_device *ltpc_probe(void);
|
||||
|
||||
/* Detachable devices ("pocket adaptors") */
|
||||
extern struct net_device *de620_probe(int unit);
|
||||
|
||||
/* Fibre Channel adapters */
|
||||
extern int iph5526_probe(struct net_device *dev);
|
||||
|
||||
@ -120,18 +102,6 @@ static struct devprobe2 isa_probes[] __initdata = {
|
||||
#ifdef CONFIG_WD80x3
|
||||
{wd_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_EL2 /* 3c503 */
|
||||
{el2_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_HPLAN
|
||||
{hp_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_HPLAN_PLUS
|
||||
{hp_plus_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
|
||||
{e2100_probe, 0},
|
||||
#endif
|
||||
#if defined(CONFIG_NE2000) || \
|
||||
defined(CONFIG_NE_H8300) /* ISA (use ne2k-pci for PCI cards) */
|
||||
{ne_probe, 0},
|
||||
@ -142,60 +112,20 @@ static struct devprobe2 isa_probes[] __initdata = {
|
||||
#ifdef CONFIG_SMC9194
|
||||
{smc_init, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_SEEQ8005
|
||||
{seeq8005_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_CS89x0
|
||||
#ifndef CONFIG_CS89x0_PLATFORM
|
||||
{cs89x0_probe, 0},
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_AT1700
|
||||
{at1700_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_ETH16I
|
||||
{eth16i_probe, 0}, /* ICL EtherTeam 16i/32 */
|
||||
#endif
|
||||
#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */
|
||||
{express_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
|
||||
{eepro_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
|
||||
{ewrk3_probe, 0},
|
||||
#endif
|
||||
#if defined(CONFIG_APRICOT) || defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */
|
||||
#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */
|
||||
{i82596_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_EL1 /* 3c501 */
|
||||
{el1_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_EL16 /* 3c507 */
|
||||
{el16_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_ELPLUS /* 3c505 */
|
||||
{elplus_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_NI5010
|
||||
{ni5010_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_NI52
|
||||
{ni52_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_NI65
|
||||
{ni65_probe, 0},
|
||||
#endif
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static struct devprobe2 parport_probes[] __initdata = {
|
||||
#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
|
||||
{de620_probe, 0},
|
||||
#endif
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static struct devprobe2 m68k_probes[] __initdata = {
|
||||
#ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */
|
||||
{atarilance_probe, 0},
|
||||
@ -234,8 +164,7 @@ static void __init ethif_probe2(int unit)
|
||||
return;
|
||||
|
||||
(void)( probe_list2(unit, m68k_probes, base_addr == 0) &&
|
||||
probe_list2(unit, isa_probes, base_addr == 0) &&
|
||||
probe_list2(unit, parport_probes, base_addr == 0));
|
||||
probe_list2(unit, isa_probes, base_addr == 0));
|
||||
}
|
||||
|
||||
/* Statically configured drivers -- order matters here. */
|
||||
|
@ -1,897 +0,0 @@
|
||||
/* 3c501.c: A 3Com 3c501 Ethernet driver for Linux. */
|
||||
/*
|
||||
Written 1992,1993,1994 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 is a device driver for the 3Com Etherlink 3c501.
|
||||
Do not purchase this card, even as a joke. It's performance is horrible,
|
||||
and it breaks in many ways.
|
||||
|
||||
The original author may be reached as becker@scyld.com, or C/O
|
||||
Scyld Computing Corporation
|
||||
410 Severn Ave., Suite 210
|
||||
Annapolis MD 21403
|
||||
|
||||
Fixed (again!) the missing interrupt locking on TX/RX shifting.
|
||||
Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
|
||||
Removed calls to init_etherdev since they are no longer needed, and
|
||||
cleaned up modularization just a bit. The driver still allows only
|
||||
the default address for cards when loaded as a module, but that's
|
||||
really less braindead than anyone using a 3c501 board. :)
|
||||
19950208 (invid@msen.com)
|
||||
|
||||
Added traps for interrupts hitting the window as we clear and TX load
|
||||
the board. Now getting 150K/second FTP with a 3c501 card. Still playing
|
||||
with a TX-TX optimisation to see if we can touch 180-200K/second as seems
|
||||
theoretically maximum.
|
||||
19950402 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
|
||||
Cleaned up for 2.3.x because we broke SMP now.
|
||||
20000208 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
|
||||
Check up pass for 2.5. Nothing significant changed
|
||||
20021009 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
|
||||
Fixed zero fill corner case
|
||||
20030104 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
|
||||
|
||||
For the avoidance of doubt the "preferred form" of this code is one which
|
||||
is in an open non patent encumbered format. Where cryptographic key signing
|
||||
forms part of the process of creating an executable the information
|
||||
including keys needed to generate an equivalently functional executable
|
||||
are deemed to be part of the source code.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* DOC: 3c501 Card Notes
|
||||
*
|
||||
* Some notes on this thing if you have to hack it. [Alan]
|
||||
*
|
||||
* Some documentation is available from 3Com. Due to the boards age
|
||||
* standard responses when you ask for this will range from 'be serious'
|
||||
* to 'give it to a museum'. The documentation is incomplete and mostly
|
||||
* of historical interest anyway.
|
||||
*
|
||||
* The basic system is a single buffer which can be used to receive or
|
||||
* transmit a packet. A third command mode exists when you are setting
|
||||
* things up.
|
||||
*
|
||||
* If it's transmitting it's not receiving and vice versa. In fact the
|
||||
* time to get the board back into useful state after an operation is
|
||||
* quite large.
|
||||
*
|
||||
* The driver works by keeping the board in receive mode waiting for a
|
||||
* packet to arrive. When one arrives it is copied out of the buffer
|
||||
* and delivered to the kernel. The card is reloaded and off we go.
|
||||
*
|
||||
* When transmitting lp->txing is set and the card is reset (from
|
||||
* receive mode) [possibly losing a packet just received] to command
|
||||
* mode. A packet is loaded and transmit mode triggered. The interrupt
|
||||
* handler runs different code for transmit interrupts and can handle
|
||||
* returning to receive mode or retransmissions (yes you have to help
|
||||
* out with those too).
|
||||
*
|
||||
* DOC: Problems
|
||||
*
|
||||
* There are a wide variety of undocumented error returns from the card
|
||||
* and you basically have to kick the board and pray if they turn up. Most
|
||||
* only occur under extreme load or if you do something the board doesn't
|
||||
* like (eg touching a register at the wrong time).
|
||||
*
|
||||
* The driver is less efficient than it could be. It switches through
|
||||
* receive mode even if more transmits are queued. If this worries you buy
|
||||
* a real Ethernet card.
|
||||
*
|
||||
* The combination of slow receive restart and no real multicast
|
||||
* filter makes the board unusable with a kernel compiled for IP
|
||||
* multicasting in a real multicast environment. That's down to the board,
|
||||
* but even with no multicast programs running a multicast IP kernel is
|
||||
* in group 224.0.0.1 and you will therefore be listening to all multicasts.
|
||||
* One nv conference running over that Ethernet and you can give up.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DRV_NAME "3c501"
|
||||
#define DRV_VERSION "2002/10/09"
|
||||
|
||||
|
||||
static const char version[] =
|
||||
DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@lxorguk.ukuu.org.uk).\n";
|
||||
|
||||
/*
|
||||
* Braindamage remaining:
|
||||
* The 3c501 board.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "3c501.h"
|
||||
|
||||
/*
|
||||
* The boilerplate probe code.
|
||||
*/
|
||||
|
||||
static int io = 0x280;
|
||||
static int irq = 5;
|
||||
static int mem_start;
|
||||
|
||||
/**
|
||||
* el1_probe - probe for a 3c501
|
||||
* @dev: The device structure passed in to probe.
|
||||
*
|
||||
* This can be called from two places. The network layer will probe using
|
||||
* a device structure passed in with the probe information completed. For a
|
||||
* modular driver we use #init_module to fill in our own structure and probe
|
||||
* for it.
|
||||
*
|
||||
* Returns 0 on success. ENXIO if asked not to probe and ENODEV if asked to
|
||||
* probe and failing to find anything.
|
||||
*/
|
||||
|
||||
struct net_device * __init el1_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
|
||||
static const unsigned ports[] = { 0x280, 0x300, 0};
|
||||
const unsigned *port;
|
||||
int err = 0;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (unit >= 0) {
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
io = dev->base_addr;
|
||||
irq = dev->irq;
|
||||
mem_start = dev->mem_start & 7;
|
||||
}
|
||||
|
||||
if (io > 0x1ff) { /* Check a single specified location. */
|
||||
err = el1_probe1(dev, io);
|
||||
} else if (io != 0) {
|
||||
err = -ENXIO; /* Don't probe at all. */
|
||||
} else {
|
||||
for (port = ports; *port && el1_probe1(dev, *port); port++)
|
||||
;
|
||||
if (!*port)
|
||||
err = -ENODEV;
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out1;
|
||||
return dev;
|
||||
out1:
|
||||
release_region(dev->base_addr, EL1_IO_EXTENT);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static const struct net_device_ops el_netdev_ops = {
|
||||
.ndo_open = el_open,
|
||||
.ndo_stop = el1_close,
|
||||
.ndo_start_xmit = el_start_xmit,
|
||||
.ndo_tx_timeout = el_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,
|
||||
};
|
||||
|
||||
/**
|
||||
* el1_probe1:
|
||||
* @dev: The device structure to use
|
||||
* @ioaddr: An I/O address to probe at.
|
||||
*
|
||||
* The actual probe. This is iterated over by #el1_probe in order to
|
||||
* check all the applicable device locations.
|
||||
*
|
||||
* Returns 0 for a success, in which case the device is activated,
|
||||
* EAGAIN if the IRQ is in use by another driver, and ENODEV if the
|
||||
* board cannot be found.
|
||||
*/
|
||||
|
||||
static int __init el1_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
struct net_local *lp;
|
||||
const char *mname; /* Vendor name */
|
||||
unsigned char station_addr[6];
|
||||
int autoirq = 0;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Reserve I/O resource for exclusive use by this driver
|
||||
*/
|
||||
|
||||
if (!request_region(ioaddr, EL1_IO_EXTENT, DRV_NAME))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Read the station address PROM data from the special port.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
outw(i, ioaddr + EL1_DATAPTR);
|
||||
station_addr[i] = inb(ioaddr + EL1_SAPROM);
|
||||
}
|
||||
/*
|
||||
* Check the first three octets of the S.A. for 3Com's prefix, or
|
||||
* for the Sager NP943 prefix.
|
||||
*/
|
||||
|
||||
if (station_addr[0] == 0x02 && station_addr[1] == 0x60 &&
|
||||
station_addr[2] == 0x8c)
|
||||
mname = "3c501";
|
||||
else if (station_addr[0] == 0x00 && station_addr[1] == 0x80 &&
|
||||
station_addr[2] == 0xC8)
|
||||
mname = "NP943";
|
||||
else {
|
||||
release_region(ioaddr, EL1_IO_EXTENT);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* We auto-IRQ by shutting off the interrupt line and letting it
|
||||
* float high.
|
||||
*/
|
||||
|
||||
dev->irq = irq;
|
||||
|
||||
if (dev->irq < 2) {
|
||||
unsigned long irq_mask;
|
||||
|
||||
irq_mask = probe_irq_on();
|
||||
inb(RX_STATUS); /* Clear pending interrupts. */
|
||||
inb(TX_STATUS);
|
||||
outb(AX_LOOP + 1, AX_CMD);
|
||||
|
||||
outb(0x00, AX_CMD);
|
||||
|
||||
mdelay(20);
|
||||
autoirq = probe_irq_off(irq_mask);
|
||||
|
||||
if (autoirq == 0) {
|
||||
pr_warning("%s probe at %#x failed to detect IRQ line.\n",
|
||||
mname, ioaddr);
|
||||
release_region(ioaddr, EL1_IO_EXTENT);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
outb(AX_RESET+AX_LOOP, AX_CMD); /* Loopback mode. */
|
||||
dev->base_addr = ioaddr;
|
||||
memcpy(dev->dev_addr, station_addr, ETH_ALEN);
|
||||
|
||||
if (mem_start & 0xf)
|
||||
el_debug = mem_start & 0x7;
|
||||
if (autoirq)
|
||||
dev->irq = autoirq;
|
||||
|
||||
pr_info("%s: %s EtherLink at %#lx, using %sIRQ %d.\n",
|
||||
dev->name, mname, dev->base_addr,
|
||||
autoirq ? "auto":"assigned ", dev->irq);
|
||||
|
||||
#ifdef CONFIG_IP_MULTICAST
|
||||
pr_warning("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
|
||||
#endif
|
||||
|
||||
if (el_debug)
|
||||
pr_debug("%s", version);
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
memset(lp, 0, sizeof(struct net_local));
|
||||
spin_lock_init(&lp->lock);
|
||||
|
||||
/*
|
||||
* The EL1-specific entries in the device structure.
|
||||
*/
|
||||
|
||||
dev->netdev_ops = &el_netdev_ops;
|
||||
dev->watchdog_timeo = HZ;
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* el1_open:
|
||||
* @dev: device that is being opened
|
||||
*
|
||||
* When an ifconfig is issued which changes the device flags to include
|
||||
* IFF_UP this function is called. It is only called when the change
|
||||
* occurs, not when the interface remains up. #el1_close will be called
|
||||
* when it goes down.
|
||||
*
|
||||
* Returns 0 for a successful open, or -EAGAIN if someone has run off
|
||||
* with our interrupt line.
|
||||
*/
|
||||
|
||||
static int el_open(struct net_device *dev)
|
||||
{
|
||||
int retval;
|
||||
int ioaddr = dev->base_addr;
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (el_debug > 2)
|
||||
pr_debug("%s: Doing el_open()...\n", dev->name);
|
||||
|
||||
retval = request_irq(dev->irq, el_interrupt, 0, dev->name, dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
el_reset(dev);
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
lp->txing = 0; /* Board in RX mode */
|
||||
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* el_timeout:
|
||||
* @dev: The 3c501 card that has timed out
|
||||
*
|
||||
* Attempt to restart the board. This is basically a mixture of extreme
|
||||
* violence and prayer
|
||||
*
|
||||
*/
|
||||
|
||||
static void el_timeout(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (el_debug)
|
||||
pr_debug("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
|
||||
dev->name, inb(TX_STATUS),
|
||||
inb(AX_STATUS), inb(RX_STATUS));
|
||||
dev->stats.tx_errors++;
|
||||
outb(TX_NORM, TX_CMD);
|
||||
outb(RX_NORM, RX_CMD);
|
||||
outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */
|
||||
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
|
||||
lp->txing = 0; /* Ripped back in to RX */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* el_start_xmit:
|
||||
* @skb: The packet that is queued to be sent
|
||||
* @dev: The 3c501 card we want to throw it down
|
||||
*
|
||||
* Attempt to send a packet to a 3c501 card. There are some interesting
|
||||
* catches here because the 3c501 is an extremely old and therefore
|
||||
* stupid piece of technology.
|
||||
*
|
||||
* If we are handling an interrupt on the other CPU we cannot load a packet
|
||||
* as we may still be attempting to retrieve the last RX packet buffer.
|
||||
*
|
||||
* When a transmit times out we dump the card into control mode and just
|
||||
* start again. It happens enough that it isn't worth logging.
|
||||
*
|
||||
* We avoid holding the spin locks when doing the packet load to the board.
|
||||
* The device is very slow, and its DMA mode is even slower. If we held the
|
||||
* lock while loading 1500 bytes onto the controller we would drop a lot of
|
||||
* serial port characters. This requires we do extra locking, but we have
|
||||
* no real choice.
|
||||
*/
|
||||
|
||||
static netdev_tx_t el_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Avoid incoming interrupts between us flipping txing and flipping
|
||||
* mode as the driver assumes txing is a faithful indicator of card
|
||||
* state
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
|
||||
/*
|
||||
* Avoid timer-based retransmission conflicts.
|
||||
*/
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
do {
|
||||
int len = skb->len;
|
||||
int pad = 0;
|
||||
int gp_start;
|
||||
unsigned char *buf = skb->data;
|
||||
|
||||
if (len < ETH_ZLEN)
|
||||
pad = ETH_ZLEN - len;
|
||||
|
||||
gp_start = 0x800 - (len + pad);
|
||||
|
||||
lp->tx_pkt_start = gp_start;
|
||||
lp->collisions = 0;
|
||||
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
/*
|
||||
* Command mode with status cleared should [in theory]
|
||||
* mean no more interrupts can be pending on the card.
|
||||
*/
|
||||
|
||||
outb_p(AX_SYS, AX_CMD);
|
||||
inb_p(RX_STATUS);
|
||||
inb_p(TX_STATUS);
|
||||
|
||||
lp->loading = 1;
|
||||
lp->txing = 1;
|
||||
|
||||
/*
|
||||
* Turn interrupts back on while we spend a pleasant
|
||||
* afternoon loading bytes into the board
|
||||
*/
|
||||
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
/* Set rx packet area to 0. */
|
||||
outw(0x00, RX_BUF_CLR);
|
||||
/* aim - packet will be loaded into buffer start */
|
||||
outw(gp_start, GP_LOW);
|
||||
/* load buffer (usual thing each byte increments the pointer) */
|
||||
outsb(DATAPORT, buf, len);
|
||||
if (pad) {
|
||||
while (pad--) /* Zero fill buffer tail */
|
||||
outb(0, DATAPORT);
|
||||
}
|
||||
/* the board reuses the same register */
|
||||
outw(gp_start, GP_LOW);
|
||||
|
||||
if (lp->loading != 2) {
|
||||
/* fire ... Trigger xmit. */
|
||||
outb(AX_XMIT, AX_CMD);
|
||||
lp->loading = 0;
|
||||
if (el_debug > 2)
|
||||
pr_debug(" queued xmit.\n");
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
/* A receive upset our load, despite our best efforts */
|
||||
if (el_debug > 2)
|
||||
pr_debug("%s: burped during tx load.\n", dev->name);
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/**
|
||||
* el_interrupt:
|
||||
* @irq: Interrupt number
|
||||
* @dev_id: The 3c501 that burped
|
||||
*
|
||||
* Handle the ether interface interrupts. The 3c501 needs a lot more
|
||||
* hand holding than most cards. In particular we get a transmit interrupt
|
||||
* with a collision error because the board firmware isn't capable of rewinding
|
||||
* its own transmit buffer pointers. It can however count to 16 for us.
|
||||
*
|
||||
* On the receive side the card is also very dumb. It has no buffering to
|
||||
* speak of. We simply pull the packet out of its PIO buffer (which is slow)
|
||||
* and queue it for the kernel. Then we reset the card for the next packet.
|
||||
*
|
||||
* We sometimes get surprise interrupts late both because the SMP IRQ delivery
|
||||
* is message passing and because the card sometimes seems to deliver late. I
|
||||
* think if it is part way through a receive and the mode is changed it carries
|
||||
* on receiving and sends us an interrupt. We have to band aid all these cases
|
||||
* to get a sensible 150kBytes/second performance. Even then you want a small
|
||||
* TCP window.
|
||||
*/
|
||||
|
||||
static irqreturn_t el_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct net_local *lp;
|
||||
int ioaddr;
|
||||
int axsr; /* Aux. status reg. */
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
|
||||
/*
|
||||
* What happened ?
|
||||
*/
|
||||
|
||||
axsr = inb(AX_STATUS);
|
||||
|
||||
/*
|
||||
* Log it
|
||||
*/
|
||||
|
||||
if (el_debug > 3)
|
||||
pr_debug("%s: el_interrupt() aux=%#02x\n", dev->name, axsr);
|
||||
|
||||
if (lp->loading == 1 && !lp->txing)
|
||||
pr_warning("%s: Inconsistent state loading while not in tx\n",
|
||||
dev->name);
|
||||
|
||||
if (lp->txing) {
|
||||
/*
|
||||
* Board in transmit mode. May be loading. If we are
|
||||
* loading we shouldn't have got this.
|
||||
*/
|
||||
int txsr = inb(TX_STATUS);
|
||||
|
||||
if (lp->loading == 1) {
|
||||
if (el_debug > 2)
|
||||
pr_debug("%s: Interrupt while loading [txsr=%02x gp=%04x rp=%04x]\n",
|
||||
dev->name, txsr, inw(GP_LOW), inw(RX_LOW));
|
||||
|
||||
/* Force a reload */
|
||||
lp->loading = 2;
|
||||
spin_unlock(&lp->lock);
|
||||
goto out;
|
||||
}
|
||||
if (el_debug > 6)
|
||||
pr_debug("%s: txsr=%02x gp=%04x rp=%04x\n", dev->name,
|
||||
txsr, inw(GP_LOW), inw(RX_LOW));
|
||||
|
||||
if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
|
||||
/*
|
||||
* FIXME: is there a logic to whether to keep
|
||||
* on trying or reset immediately ?
|
||||
*/
|
||||
if (el_debug > 1)
|
||||
pr_debug("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x gp=%03x rp=%03x.\n",
|
||||
dev->name, txsr, axsr,
|
||||
inw(ioaddr + EL1_DATAPTR),
|
||||
inw(ioaddr + EL1_RXPTR));
|
||||
lp->txing = 0;
|
||||
netif_wake_queue(dev);
|
||||
} else if (txsr & TX_16COLLISIONS) {
|
||||
/*
|
||||
* Timed out
|
||||
*/
|
||||
if (el_debug)
|
||||
pr_debug("%s: Transmit failed 16 times, Ethernet jammed?\n", dev->name);
|
||||
outb(AX_SYS, AX_CMD);
|
||||
lp->txing = 0;
|
||||
dev->stats.tx_aborted_errors++;
|
||||
netif_wake_queue(dev);
|
||||
} else if (txsr & TX_COLLISION) {
|
||||
/*
|
||||
* Retrigger xmit.
|
||||
*/
|
||||
|
||||
if (el_debug > 6)
|
||||
pr_debug("%s: retransmitting after a collision.\n", dev->name);
|
||||
/*
|
||||
* Poor little chip can't reset its own start
|
||||
* pointer
|
||||
*/
|
||||
|
||||
outb(AX_SYS, AX_CMD);
|
||||
outw(lp->tx_pkt_start, GP_LOW);
|
||||
outb(AX_XMIT, AX_CMD);
|
||||
dev->stats.collisions++;
|
||||
spin_unlock(&lp->lock);
|
||||
goto out;
|
||||
} else {
|
||||
/*
|
||||
* It worked.. we will now fall through and receive
|
||||
*/
|
||||
dev->stats.tx_packets++;
|
||||
if (el_debug > 6)
|
||||
pr_debug("%s: Tx succeeded %s\n", dev->name,
|
||||
(txsr & TX_RDY) ? "." : "but tx is busy!");
|
||||
/*
|
||||
* This is safe the interrupt is atomic WRT itself.
|
||||
*/
|
||||
lp->txing = 0;
|
||||
/* In case more to transmit */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* In receive mode.
|
||||
*/
|
||||
|
||||
int rxsr = inb(RX_STATUS);
|
||||
if (el_debug > 5)
|
||||
pr_debug("%s: rxsr=%02x txsr=%02x rp=%04x\n",
|
||||
dev->name, rxsr, inb(TX_STATUS), inw(RX_LOW));
|
||||
/*
|
||||
* Just reading rx_status fixes most errors.
|
||||
*/
|
||||
if (rxsr & RX_MISSED)
|
||||
dev->stats.rx_missed_errors++;
|
||||
else if (rxsr & RX_RUNT) {
|
||||
/* Handled to avoid board lock-up. */
|
||||
dev->stats.rx_length_errors++;
|
||||
if (el_debug > 5)
|
||||
pr_debug("%s: runt.\n", dev->name);
|
||||
} else if (rxsr & RX_GOOD) {
|
||||
/*
|
||||
* Receive worked.
|
||||
*/
|
||||
el_receive(dev);
|
||||
} else {
|
||||
/*
|
||||
* Nothing? Something is broken!
|
||||
*/
|
||||
if (el_debug > 2)
|
||||
pr_debug("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
|
||||
dev->name, rxsr);
|
||||
el_reset(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Move into receive mode
|
||||
*/
|
||||
|
||||
outb(AX_RX, AX_CMD);
|
||||
outw(0x00, RX_BUF_CLR);
|
||||
inb(RX_STATUS); /* Be certain that interrupts are cleared. */
|
||||
inb(TX_STATUS);
|
||||
spin_unlock(&lp->lock);
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* el_receive:
|
||||
* @dev: Device to pull the packets from
|
||||
*
|
||||
* We have a good packet. Well, not really "good", just mostly not broken.
|
||||
* We must check everything to see if it is good. In particular we occasionally
|
||||
* get wild packet sizes from the card. If the packet seems sane we PIO it
|
||||
* off the card and queue it for the protocol layers.
|
||||
*/
|
||||
|
||||
static void el_receive(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
int pkt_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
pkt_len = inw(RX_LOW);
|
||||
|
||||
if (el_debug > 4)
|
||||
pr_debug(" el_receive %d.\n", pkt_len);
|
||||
|
||||
if (pkt_len < 60 || pkt_len > 1536) {
|
||||
if (el_debug)
|
||||
pr_debug("%s: bogus packet, length=%d\n",
|
||||
dev->name, pkt_len);
|
||||
dev->stats.rx_over_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command mode so we can empty the buffer
|
||||
*/
|
||||
|
||||
outb(AX_SYS, AX_CMD);
|
||||
skb = netdev_alloc_skb(dev, pkt_len + 2);
|
||||
|
||||
/*
|
||||
* Start of frame
|
||||
*/
|
||||
|
||||
outw(0x00, GP_LOW);
|
||||
if (skb == NULL) {
|
||||
pr_info("%s: Memory squeeze, dropping packet.\n", dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
return;
|
||||
} else {
|
||||
skb_reserve(skb, 2); /* Force 16 byte alignment */
|
||||
/*
|
||||
* The read increments through the bytes. The interrupt
|
||||
* handler will fix the pointer when it returns to
|
||||
* receive mode.
|
||||
*/
|
||||
insb(DATAPORT, skb_put(skb, pkt_len), pkt_len);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* el_reset: Reset a 3c501 card
|
||||
* @dev: The 3c501 card about to get zapped
|
||||
*
|
||||
* Even resetting a 3c501 isn't simple. When you activate reset it loses all
|
||||
* its configuration. You must hold the lock when doing this. The function
|
||||
* cannot take the lock itself as it is callable from the irq handler.
|
||||
*/
|
||||
|
||||
static void el_reset(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (el_debug > 2)
|
||||
pr_info("3c501 reset...\n");
|
||||
outb(AX_RESET, AX_CMD); /* Reset the chip */
|
||||
/* Aux control, irq and loopback enabled */
|
||||
outb(AX_LOOP, AX_CMD);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 6; i++) /* Set the station address. */
|
||||
outb(dev->dev_addr[i], ioaddr + i);
|
||||
}
|
||||
|
||||
outw(0, RX_BUF_CLR); /* Set rx packet area to 0. */
|
||||
outb(TX_NORM, TX_CMD); /* tx irq on done, collision */
|
||||
outb(RX_NORM, RX_CMD); /* Set Rx commands. */
|
||||
inb(RX_STATUS); /* Clear status. */
|
||||
inb(TX_STATUS);
|
||||
lp->txing = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* el1_close:
|
||||
* @dev: 3c501 card to shut down
|
||||
*
|
||||
* Close a 3c501 card. The IFF_UP flag has been cleared by the user via
|
||||
* the SIOCSIFFLAGS ioctl. We stop any further transmissions being queued,
|
||||
* and then disable the interrupts. Finally we reset the chip. The effects
|
||||
* of the rest will be cleaned up by #el1_open. Always returns 0 indicating
|
||||
* a success.
|
||||
*/
|
||||
|
||||
static int el1_close(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (el_debug > 2)
|
||||
pr_info("%s: Shutting down Ethernet card at %#x.\n",
|
||||
dev->name, ioaddr);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/*
|
||||
* Free and disable the IRQ.
|
||||
*/
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
outb(AX_RESET, AX_CMD); /* Reset the chip */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_multicast_list:
|
||||
* @dev: The device to adjust
|
||||
*
|
||||
* Set or clear the multicast filter for this adaptor to use the best-effort
|
||||
* filtering supported. The 3c501 supports only three modes of filtering.
|
||||
* It always receives broadcasts and packets for itself. You can choose to
|
||||
* optionally receive all packets, or all multicast packets on top of this.
|
||||
*/
|
||||
|
||||
static void set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
outb(RX_PROM, RX_CMD);
|
||||
inb(RX_STATUS);
|
||||
} else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
|
||||
/* Multicast or all multicast is the same */
|
||||
outb(RX_MULT, RX_CMD);
|
||||
inb(RX_STATUS); /* Clear status. */
|
||||
} else {
|
||||
outb(RX_NORM, RX_CMD);
|
||||
inb(RX_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 debug;
|
||||
}
|
||||
|
||||
static void netdev_set_msglevel(struct net_device *dev, u32 level)
|
||||
{
|
||||
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
|
||||
|
||||
static struct net_device *dev_3c501;
|
||||
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(io, "EtherLink I/O base address");
|
||||
MODULE_PARM_DESC(irq, "EtherLink IRQ number");
|
||||
|
||||
/**
|
||||
* init_module:
|
||||
*
|
||||
* When the driver is loaded as a module this function is called. We fake up
|
||||
* a device structure with the base I/O and interrupt set as if it were being
|
||||
* called from Space.c. This minimises the extra code that would otherwise
|
||||
* be required.
|
||||
*
|
||||
* Returns 0 for success or -EIO if a card is not found. Returning an error
|
||||
* here also causes the module to be unloaded
|
||||
*/
|
||||
|
||||
int __init init_module(void)
|
||||
{
|
||||
dev_3c501 = el1_probe(-1);
|
||||
if (IS_ERR(dev_3c501))
|
||||
return PTR_ERR(dev_3c501);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup_module:
|
||||
*
|
||||
* The module is being unloaded. We unhook our network device from the system
|
||||
* and then free up the resources we took when the card was found.
|
||||
*/
|
||||
|
||||
void __exit cleanup_module(void)
|
||||
{
|
||||
struct net_device *dev = dev_3c501;
|
||||
unregister_netdev(dev);
|
||||
release_region(dev->base_addr, EL1_IO_EXTENT);
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
MODULE_AUTHOR("Donald Becker, Alan Cox");
|
||||
MODULE_DESCRIPTION("Support for the ancient 3Com 3c501 ethernet card");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,91 +0,0 @@
|
||||
|
||||
/*
|
||||
* Index to functions.
|
||||
*/
|
||||
|
||||
static int el1_probe1(struct net_device *dev, int ioaddr);
|
||||
static int el_open(struct net_device *dev);
|
||||
static void el_timeout(struct net_device *dev);
|
||||
static netdev_tx_t el_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
static irqreturn_t el_interrupt(int irq, void *dev_id);
|
||||
static void el_receive(struct net_device *dev);
|
||||
static void el_reset(struct net_device *dev);
|
||||
static int el1_close(struct net_device *dev);
|
||||
static void set_multicast_list(struct net_device *dev);
|
||||
static const struct ethtool_ops netdev_ethtool_ops;
|
||||
|
||||
#define EL1_IO_EXTENT 16
|
||||
|
||||
#ifndef EL_DEBUG
|
||||
#define EL_DEBUG 0 /* use 0 for production, 1 for devel., >2 for debug */
|
||||
#endif /* Anything above 5 is wordy death! */
|
||||
#define debug el_debug
|
||||
static int el_debug = EL_DEBUG;
|
||||
|
||||
/*
|
||||
* Board-specific info in netdev_priv(dev).
|
||||
*/
|
||||
|
||||
struct net_local
|
||||
{
|
||||
int tx_pkt_start; /* The length of the current Tx packet. */
|
||||
int collisions; /* Tx collisions this packet */
|
||||
int loading; /* Spot buffer load collisions */
|
||||
int txing; /* True if card is in TX mode */
|
||||
spinlock_t lock; /* Serializing lock */
|
||||
};
|
||||
|
||||
|
||||
#define RX_STATUS (ioaddr + 0x06)
|
||||
#define RX_CMD RX_STATUS
|
||||
#define TX_STATUS (ioaddr + 0x07)
|
||||
#define TX_CMD TX_STATUS
|
||||
#define GP_LOW (ioaddr + 0x08)
|
||||
#define GP_HIGH (ioaddr + 0x09)
|
||||
#define RX_BUF_CLR (ioaddr + 0x0A)
|
||||
#define RX_LOW (ioaddr + 0x0A)
|
||||
#define RX_HIGH (ioaddr + 0x0B)
|
||||
#define SAPROM (ioaddr + 0x0C)
|
||||
#define AX_STATUS (ioaddr + 0x0E)
|
||||
#define AX_CMD AX_STATUS
|
||||
#define DATAPORT (ioaddr + 0x0F)
|
||||
#define TX_RDY 0x08 /* In TX_STATUS */
|
||||
|
||||
#define EL1_DATAPTR 0x08
|
||||
#define EL1_RXPTR 0x0A
|
||||
#define EL1_SAPROM 0x0C
|
||||
#define EL1_DATAPORT 0x0f
|
||||
|
||||
/*
|
||||
* Writes to the ax command register.
|
||||
*/
|
||||
|
||||
#define AX_OFF 0x00 /* Irq off, buffer access on */
|
||||
#define AX_SYS 0x40 /* Load the buffer */
|
||||
#define AX_XMIT 0x44 /* Transmit a packet */
|
||||
#define AX_RX 0x48 /* Receive a packet */
|
||||
#define AX_LOOP 0x0C /* Loopback mode */
|
||||
#define AX_RESET 0x80
|
||||
|
||||
/*
|
||||
* Normal receive mode written to RX_STATUS. We must intr on short packets
|
||||
* to avoid bogus rx lockups.
|
||||
*/
|
||||
|
||||
#define RX_NORM 0xA8 /* 0x68 == all addrs, 0xA8 only to me. */
|
||||
#define RX_PROM 0x68 /* Senior Prom, uhmm promiscuous mode. */
|
||||
#define RX_MULT 0xE8 /* Accept multicast packets. */
|
||||
#define TX_NORM 0x0A /* Interrupt on everything that might hang the chip */
|
||||
|
||||
/*
|
||||
* TX_STATUS register.
|
||||
*/
|
||||
|
||||
#define TX_COLLISION 0x02
|
||||
#define TX_16COLLISIONS 0x04
|
||||
#define TX_READY 0x08
|
||||
|
||||
#define RX_RUNT 0x08
|
||||
#define RX_MISSED 0x01 /* Missed a packet due to 3c501 braindamage. */
|
||||
#define RX_GOOD 0x30 /* Good packet 0x20, or simple overflow 0x10. */
|
||||
|
@ -18,20 +18,6 @@ config NET_VENDOR_3COM
|
||||
|
||||
if NET_VENDOR_3COM
|
||||
|
||||
config EL1
|
||||
tristate "3c501 \"EtherLink\" support"
|
||||
depends on ISA
|
||||
---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>. Also, consider buying a
|
||||
new card, since the 3c501 is slow, broken, and obsolete: you will
|
||||
have problems. Some people suggest to ping ("man ping") a nearby
|
||||
machine every minute ("man cron") when using this card.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called 3c501.
|
||||
|
||||
config EL3
|
||||
tristate "3c509/3c579 \"EtherLink III\" support"
|
||||
depends on (ISA || EISA)
|
||||
|
@ -2,7 +2,6 @@
|
||||
# Makefile for the 3Com Ethernet device drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EL1) += 3c501.o
|
||||
obj-$(CONFIG_EL3) += 3c509.o
|
||||
obj-$(CONFIG_3C515) += 3c515.o
|
||||
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
|
||||
|
@ -1,778 +0,0 @@
|
||||
/* 3c503.c: A 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 the 3c503 and 3c503/16. It should be used
|
||||
in shared memory mode for best performance, although it may also work
|
||||
in programmed-I/O mode.
|
||||
|
||||
Sources:
|
||||
EtherLink II Technical Reference Manual,
|
||||
EtherLink II/16 Technical Reference Manual Supplement,
|
||||
3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
|
||||
|
||||
The Crynwr 3c503 packet driver.
|
||||
|
||||
Changelog:
|
||||
|
||||
Paul Gortmaker : add support for the 2nd 8kB of RAM on 16 bit cards.
|
||||
Paul Gortmaker : multiple card support for module users.
|
||||
rjohnson@analogic.com : Fix up PIO interface for efficient operation.
|
||||
Jeff Garzik : ethtool support
|
||||
|
||||
*/
|
||||
|
||||
#define DRV_NAME "3c503"
|
||||
#define DRV_VERSION "1.10a"
|
||||
#define DRV_RELDATE "11/17/2001"
|
||||
|
||||
|
||||
static const char version[] =
|
||||
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ethtool.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "8390.h"
|
||||
#include "3c503.h"
|
||||
#define WRD_COUNT 4
|
||||
|
||||
static int el2_pio_probe(struct net_device *dev);
|
||||
static int el2_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
|
||||
static unsigned int netcard_portlist[] __initdata =
|
||||
{ 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
|
||||
|
||||
#define EL2_IO_EXTENT 16
|
||||
|
||||
static int el2_open(struct net_device *dev);
|
||||
static int el2_close(struct net_device *dev);
|
||||
static void el2_reset_8390(struct net_device *dev);
|
||||
static void el2_init_card(struct net_device *dev);
|
||||
static void el2_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
static void el2_block_input(struct net_device *dev, int count, struct sk_buff *skb,
|
||||
int ring_offset);
|
||||
static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static const struct ethtool_ops netdev_ethtool_ops;
|
||||
|
||||
|
||||
/* This routine probes for a memory-mapped 3c503 board by looking for
|
||||
the "location register" at the end of the jumpered boot PROM space.
|
||||
This works even if a PROM isn't there.
|
||||
|
||||
If the ethercard isn't found there is an optional probe for
|
||||
ethercard jumpered to programmed-I/O mode.
|
||||
*/
|
||||
static int __init do_el2_probe(struct net_device *dev)
|
||||
{
|
||||
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
|
||||
int base_addr = dev->base_addr;
|
||||
int irq = dev->irq;
|
||||
|
||||
if (base_addr > 0x1ff) /* Check a single specified location. */
|
||||
return el2_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (addr = addrs; *addr; addr++) {
|
||||
void __iomem *p = ioremap(*addr, 1);
|
||||
unsigned base_bits;
|
||||
int i;
|
||||
|
||||
if (!p)
|
||||
continue;
|
||||
base_bits = readb(p);
|
||||
iounmap(p);
|
||||
i = ffs(base_bits) - 1;
|
||||
if (i == -1 || base_bits != (1 << i))
|
||||
continue;
|
||||
if (el2_probe1(dev, netcard_portlist[i]) == 0)
|
||||
return 0;
|
||||
dev->irq = irq;
|
||||
}
|
||||
#if ! defined(no_probe_nonshared_memory)
|
||||
return el2_pio_probe(dev);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Try all of the locations that aren't obviously empty. This touches
|
||||
a lot of locations, and is much riskier than the code above. */
|
||||
static int __init
|
||||
el2_pio_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 el2_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (i = 0; netcard_portlist[i]; i++) {
|
||||
if (el2_probe1(dev, netcard_portlist[i]) == 0)
|
||||
return 0;
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init el2_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_eip_netdev();
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
err = do_el2_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops el2_netdev_ops = {
|
||||
.ndo_open = el2_open,
|
||||
.ndo_stop = el2_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
|
||||
};
|
||||
|
||||
/* Probe for the Etherlink II card at I/O port base IOADDR,
|
||||
returning non-zero on success. If found, set the station
|
||||
address and memory parameters in DEVICE. */
|
||||
static int __init
|
||||
el2_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i, iobase_reg, membase_reg, saved_406, wordlength, retval;
|
||||
static unsigned version_printed;
|
||||
unsigned long vendor_id;
|
||||
|
||||
if (!request_region(ioaddr, EL2_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
if (!request_region(ioaddr + 0x400, 8, DRV_NAME)) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reset and/or avoid any lurking NE2000 */
|
||||
if (inb(ioaddr + 0x408) == 0xff) {
|
||||
mdelay(1);
|
||||
retval = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* We verify that it's a 3C503 board by checking the first three octets
|
||||
of its ethernet address. */
|
||||
iobase_reg = inb(ioaddr+0x403);
|
||||
membase_reg = inb(ioaddr+0x404);
|
||||
/* ASIC location registers should be 0 or have only a single bit set. */
|
||||
if ((iobase_reg & (iobase_reg - 1)) ||
|
||||
(membase_reg & (membase_reg - 1))) {
|
||||
retval = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
saved_406 = inb_p(ioaddr + 0x406);
|
||||
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
|
||||
outb_p(ECNTRL_THIN, ioaddr + 0x406);
|
||||
/* Map the station addr PROM into the lower I/O ports. We now check
|
||||
for both the old and new 3Com prefix */
|
||||
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
|
||||
vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
|
||||
if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
|
||||
/* Restore the register we frobbed. */
|
||||
outb(saved_406, ioaddr + 0x406);
|
||||
retval = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
if (ei_debug && version_printed++ == 0)
|
||||
pr_debug("%s", version);
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
pr_info("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
|
||||
|
||||
/* Retrieve and print the ethernet address. */
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = inb(ioaddr + i);
|
||||
pr_cont("%pM", dev->dev_addr);
|
||||
|
||||
/* Map the 8390 back into the window. */
|
||||
outb(ECNTRL_THIN, ioaddr + 0x406);
|
||||
|
||||
/* Check for EL2/16 as described in tech. man. */
|
||||
outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
|
||||
outb_p(0, ioaddr + EN0_DCFG);
|
||||
outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
|
||||
wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
|
||||
outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
|
||||
|
||||
/* Probe for, turn on and clear the board's shared memory. */
|
||||
if (ei_debug > 2)
|
||||
pr_cont(" memory jumpers %2.2x ", membase_reg);
|
||||
outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
|
||||
|
||||
/* This should be probed for (or set via an ioctl()) at run-time.
|
||||
Right now we use a sleazy hack to pass in the interface number
|
||||
at boot-time via the low bits of the mem_end field. That value is
|
||||
unused, and the low bits would be discarded even if it was used. */
|
||||
#if defined(EI8390_THICK) || defined(EL2_AUI)
|
||||
ei_status.interface_num = 1;
|
||||
#else
|
||||
ei_status.interface_num = dev->mem_end & 0xf;
|
||||
#endif
|
||||
pr_cont(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
|
||||
|
||||
if ((membase_reg & 0xf0) == 0) {
|
||||
dev->mem_start = 0;
|
||||
ei_status.name = "3c503-PIO";
|
||||
ei_status.mem = NULL;
|
||||
} else {
|
||||
dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
|
||||
((membase_reg & 0xA0) ? 0x4000 : 0);
|
||||
#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
|
||||
ei_status.mem = ioremap(dev->mem_start, EL2_MEMSIZE);
|
||||
|
||||
#ifdef EL2MEMTEST
|
||||
/* This has never found an error, but someone might care.
|
||||
Note that it only tests the 2nd 8kB on 16kB 3c503/16
|
||||
cards between card addr. 0x2000 and 0x3fff. */
|
||||
{ /* Check the card's memory. */
|
||||
void __iomem *mem_base = ei_status.mem;
|
||||
unsigned int test_val = 0xbbadf00d;
|
||||
writel(0xba5eba5e, mem_base);
|
||||
for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
|
||||
writel(test_val, mem_base + i);
|
||||
if (readl(mem_base) != 0xba5eba5e ||
|
||||
readl(mem_base + i) != test_val) {
|
||||
pr_warning("3c503: memory failure or memory address conflict.\n");
|
||||
dev->mem_start = 0;
|
||||
ei_status.name = "3c503-PIO";
|
||||
iounmap(mem_base);
|
||||
ei_status.mem = NULL;
|
||||
break;
|
||||
}
|
||||
test_val += 0x55555555;
|
||||
writel(0, mem_base + i);
|
||||
}
|
||||
}
|
||||
#endif /* EL2MEMTEST */
|
||||
|
||||
if (dev->mem_start)
|
||||
dev->mem_end = dev->mem_start + EL2_MEMSIZE;
|
||||
|
||||
if (wordlength) { /* No Tx pages to skip over to get to Rx */
|
||||
ei_status.priv = 0;
|
||||
ei_status.name = "3c503/16";
|
||||
} else {
|
||||
ei_status.priv = TX_PAGES * 256;
|
||||
ei_status.name = "3c503";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Divide up the memory on the card. This is the same regardless of
|
||||
whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
|
||||
we use the entire 8k of bank1 for an Rx ring. We only use 3k
|
||||
of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
|
||||
(8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
|
||||
5kB for an Rx ring. */
|
||||
|
||||
if (wordlength) {
|
||||
ei_status.tx_start_page = EL2_MB0_START_PG;
|
||||
ei_status.rx_start_page = EL2_MB1_START_PG;
|
||||
} else {
|
||||
ei_status.tx_start_page = EL2_MB1_START_PG;
|
||||
ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
|
||||
}
|
||||
|
||||
/* Finish setting the board's parameters. */
|
||||
ei_status.stop_page = EL2_MB1_STOP_PG;
|
||||
ei_status.word16 = wordlength;
|
||||
ei_status.reset_8390 = el2_reset_8390;
|
||||
ei_status.get_8390_hdr = el2_get_8390_hdr;
|
||||
ei_status.block_input = el2_block_input;
|
||||
ei_status.block_output = el2_block_output;
|
||||
|
||||
if (dev->irq == 2)
|
||||
dev->irq = 9;
|
||||
else if (dev->irq > 5 && dev->irq != 9) {
|
||||
pr_warning("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
|
||||
dev->irq);
|
||||
dev->irq = 0;
|
||||
}
|
||||
|
||||
ei_status.saved_irq = dev->irq;
|
||||
|
||||
dev->netdev_ops = &el2_netdev_ops;
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval)
|
||||
goto out1;
|
||||
|
||||
if (dev->mem_start)
|
||||
pr_info("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
|
||||
dev->name, ei_status.name, (wordlength+1)<<3,
|
||||
dev->mem_start, dev->mem_end-1);
|
||||
|
||||
else
|
||||
{
|
||||
ei_status.tx_start_page = EL2_MB1_START_PG;
|
||||
ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
|
||||
pr_info("%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
|
||||
dev->name, ei_status.name, (wordlength+1)<<3);
|
||||
}
|
||||
release_region(ioaddr + 0x400, 8);
|
||||
return 0;
|
||||
out1:
|
||||
release_region(ioaddr + 0x400, 8);
|
||||
out:
|
||||
release_region(ioaddr, EL2_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static irqreturn_t el2_probe_interrupt(int irq, void *seen)
|
||||
{
|
||||
*(bool *)seen = true;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
el2_open(struct net_device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (dev->irq < 2) {
|
||||
static const int irqlist[] = {5, 9, 3, 4, 0};
|
||||
const int *irqp = irqlist;
|
||||
|
||||
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
|
||||
do {
|
||||
bool seen;
|
||||
|
||||
retval = request_irq(*irqp, el2_probe_interrupt, 0,
|
||||
dev->name, &seen);
|
||||
if (retval == -EBUSY)
|
||||
continue;
|
||||
if (retval < 0)
|
||||
goto err_disable;
|
||||
|
||||
/* Twinkle the interrupt, and check if it's seen. */
|
||||
seen = false;
|
||||
smp_wmb();
|
||||
outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
|
||||
outb_p(0x00, E33G_IDCFR);
|
||||
msleep(1);
|
||||
free_irq(*irqp, &seen);
|
||||
if (!seen)
|
||||
continue;
|
||||
|
||||
retval = request_irq(dev->irq = *irqp, eip_interrupt, 0,
|
||||
dev->name, dev);
|
||||
if (retval == -EBUSY)
|
||||
continue;
|
||||
if (retval < 0)
|
||||
goto err_disable;
|
||||
break;
|
||||
} while (*++irqp);
|
||||
|
||||
if (*irqp == 0) {
|
||||
err_disable:
|
||||
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
el2_init_card(dev);
|
||||
eip_open(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
el2_close(struct net_device *dev)
|
||||
{
|
||||
free_irq(dev->irq, dev);
|
||||
dev->irq = ei_status.saved_irq;
|
||||
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
|
||||
|
||||
eip_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is called whenever we have a unrecoverable failure:
|
||||
transmit timeout
|
||||
Bad ring buffer packet header
|
||||
*/
|
||||
static void
|
||||
el2_reset_8390(struct net_device *dev)
|
||||
{
|
||||
if (ei_debug > 1) {
|
||||
pr_debug("%s: Resetting the 3c503 board...", dev->name);
|
||||
pr_cont(" %#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
|
||||
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
|
||||
}
|
||||
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
|
||||
ei_status.txing = 0;
|
||||
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
|
||||
el2_init_card(dev);
|
||||
if (ei_debug > 1)
|
||||
pr_cont("done\n");
|
||||
}
|
||||
|
||||
/* Initialize the 3c503 GA registers after a reset. */
|
||||
static void
|
||||
el2_init_card(struct net_device *dev)
|
||||
{
|
||||
/* Unmap the station PROM and select the DIX or BNC connector. */
|
||||
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
|
||||
|
||||
/* Set ASIC copy of rx's first and last+1 buffer pages */
|
||||
/* These must be the same as in the 8390. */
|
||||
outb(ei_status.rx_start_page, E33G_STARTPG);
|
||||
outb(ei_status.stop_page, E33G_STOPPG);
|
||||
|
||||
/* Point the vector pointer registers somewhere ?harmless?. */
|
||||
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
|
||||
outb(0xff, E33G_VP1);
|
||||
outb(0x00, E33G_VP0);
|
||||
/* Turn off all interrupts until we're opened. */
|
||||
outb_p(0x00, dev->base_addr + EN0_IMR);
|
||||
/* Enable IRQs iff started. */
|
||||
outb(EGACFR_NORM, E33G_GACFR);
|
||||
|
||||
/* Set the interrupt line. */
|
||||
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||
outb_p((WRD_COUNT << 1), E33G_DRQCNT); /* Set burst size to 8 */
|
||||
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
|
||||
outb_p(0x00, E33G_DMAAL);
|
||||
return; /* We always succeed */
|
||||
}
|
||||
|
||||
/*
|
||||
* Either use the shared memory (if enabled on the board) or put the packet
|
||||
* out through the ASIC FIFO.
|
||||
*/
|
||||
static void
|
||||
el2_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page)
|
||||
{
|
||||
unsigned short int *wrd;
|
||||
int boguscount; /* timeout counter */
|
||||
unsigned short word; /* temporary for better machine code */
|
||||
void __iomem *base = ei_status.mem;
|
||||
|
||||
if (ei_status.word16) /* Tx packets go into bank 0 on EL2/16 card */
|
||||
outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
|
||||
else
|
||||
outb(EGACFR_NORM, E33G_GACFR);
|
||||
|
||||
if (base) { /* Shared memory transfer */
|
||||
memcpy_toio(base + ((start_page - ei_status.tx_start_page) << 8),
|
||||
buf, count);
|
||||
outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No shared memory, put the packet out the other way.
|
||||
* Set up then start the internal memory transfer to Tx Start Page
|
||||
*/
|
||||
|
||||
word = (unsigned short)start_page;
|
||||
outb(word&0xFF, E33G_DMAAH);
|
||||
outb(word>>8, E33G_DMAAL);
|
||||
|
||||
outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
|
||||
| ECNTRL_START, E33G_CNTRL);
|
||||
|
||||
/*
|
||||
* Here I am going to write data to the FIFO as quickly as possible.
|
||||
* Note that E33G_FIFOH is defined incorrectly. It is really
|
||||
* E33G_FIFOL, the lowest port address for both the byte and
|
||||
* word write. Variable 'count' is NOT checked. Caller must supply a
|
||||
* valid count. Note that I may write a harmless extra byte to the
|
||||
* 8390 if the byte-count was not even.
|
||||
*/
|
||||
wrd = (unsigned short int *) buf;
|
||||
count = (count + 1) >> 1;
|
||||
for(;;)
|
||||
{
|
||||
boguscount = 0x1000;
|
||||
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||
{
|
||||
if(!boguscount--)
|
||||
{
|
||||
pr_notice("%s: FIFO blocked in el2_block_output.\n", dev->name);
|
||||
el2_reset_8390(dev);
|
||||
goto blocked;
|
||||
}
|
||||
}
|
||||
if(count > WRD_COUNT)
|
||||
{
|
||||
outsw(E33G_FIFOH, wrd, WRD_COUNT);
|
||||
wrd += WRD_COUNT;
|
||||
count -= WRD_COUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
outsw(E33G_FIFOH, wrd, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
blocked:;
|
||||
outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
|
||||
}
|
||||
|
||||
/* Read the 4 byte, page aligned 8390 specific header. */
|
||||
static void
|
||||
el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
int boguscount;
|
||||
void __iomem *base = ei_status.mem;
|
||||
unsigned short word;
|
||||
|
||||
if (base) { /* Use the shared memory. */
|
||||
void __iomem *hdr_start = base + ((ring_page - EL2_MB1_START_PG)<<8);
|
||||
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
|
||||
hdr->count = le16_to_cpu(hdr->count);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No shared memory, use programmed I/O.
|
||||
*/
|
||||
|
||||
word = (unsigned short)ring_page;
|
||||
outb(word&0xFF, E33G_DMAAH);
|
||||
outb(word>>8, E33G_DMAAL);
|
||||
|
||||
outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
|
||||
| ECNTRL_START, E33G_CNTRL);
|
||||
boguscount = 0x1000;
|
||||
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||
{
|
||||
if(!boguscount--)
|
||||
{
|
||||
pr_notice("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
|
||||
memset(hdr, 0x00, sizeof(struct e8390_pkt_hdr));
|
||||
el2_reset_8390(dev);
|
||||
goto blocked;
|
||||
}
|
||||
}
|
||||
insw(E33G_FIFOH, hdr, (sizeof(struct e8390_pkt_hdr))>> 1);
|
||||
blocked:;
|
||||
outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
el2_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int boguscount = 0;
|
||||
void __iomem *base = ei_status.mem;
|
||||
unsigned short int *buf;
|
||||
unsigned short word;
|
||||
|
||||
/* Maybe enable shared memory just be to be safe... nahh.*/
|
||||
if (base) { /* Use the shared memory. */
|
||||
ring_offset -= (EL2_MB1_START_PG<<8);
|
||||
if (ring_offset + count > EL2_MEMSIZE) {
|
||||
/* We must wrap the input move. */
|
||||
int semi_count = EL2_MEMSIZE - ring_offset;
|
||||
memcpy_fromio(skb->data, base + ring_offset, semi_count);
|
||||
count -= semi_count;
|
||||
memcpy_fromio(skb->data + semi_count, base + ei_status.priv, count);
|
||||
} else {
|
||||
memcpy_fromio(skb->data, base + ring_offset, count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No shared memory, use programmed I/O.
|
||||
*/
|
||||
word = (unsigned short) ring_offset;
|
||||
outb(word>>8, E33G_DMAAH);
|
||||
outb(word&0xFF, E33G_DMAAL);
|
||||
|
||||
outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
|
||||
| ECNTRL_START, E33G_CNTRL);
|
||||
|
||||
/*
|
||||
* Here I also try to get data as fast as possible. I am betting that I
|
||||
* can read one extra byte without clobbering anything in the kernel because
|
||||
* this would only occur on an odd byte-count and allocation of skb->data
|
||||
* is word-aligned. Variable 'count' is NOT checked. Caller must check
|
||||
* for a valid count.
|
||||
* [This is currently quite safe.... but if one day the 3c503 explodes
|
||||
* you know where to come looking ;)]
|
||||
*/
|
||||
|
||||
buf = (unsigned short int *) skb->data;
|
||||
count = (count + 1) >> 1;
|
||||
for(;;)
|
||||
{
|
||||
boguscount = 0x1000;
|
||||
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||
{
|
||||
if(!boguscount--)
|
||||
{
|
||||
pr_notice("%s: FIFO blocked in el2_block_input.\n", dev->name);
|
||||
el2_reset_8390(dev);
|
||||
goto blocked;
|
||||
}
|
||||
}
|
||||
if(count > WRD_COUNT)
|
||||
{
|
||||
insw(E33G_FIFOH, buf, WRD_COUNT);
|
||||
buf += WRD_COUNT;
|
||||
count -= WRD_COUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
insw(E33G_FIFOH, buf, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
blocked:;
|
||||
outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
|
||||
}
|
||||
|
||||
|
||||
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 const struct ethtool_ops netdev_ethtool_ops = {
|
||||
.get_drvinfo = netdev_get_drvinfo,
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */
|
||||
|
||||
static struct net_device *dev_el2[MAX_EL2_CARDS];
|
||||
static int io[MAX_EL2_CARDS];
|
||||
static int irq[MAX_EL2_CARDS];
|
||||
static int xcvr[MAX_EL2_CARDS]; /* choose int. or ext. xcvr */
|
||||
module_param_array(io, int, NULL, 0);
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
module_param_array(xcvr, int, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
|
||||
MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
|
||||
MODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) 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_EL2_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
pr_notice("3c503.c: Presently autoprobing (not recommended) for a single card.\n");
|
||||
}
|
||||
dev = alloc_eip_netdev();
|
||||
if (!dev)
|
||||
break;
|
||||
dev->irq = irq[this_dev];
|
||||
dev->base_addr = io[this_dev];
|
||||
dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
|
||||
if (do_el2_probe(dev) == 0) {
|
||||
dev_el2[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
pr_warning("3c503.c: No 3c503 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: el2_close() handles free_irq */
|
||||
release_region(dev->base_addr, EL2_IO_EXTENT);
|
||||
if (ei_status.mem)
|
||||
iounmap(ei_status.mem);
|
||||
}
|
||||
|
||||
void __exit
|
||||
cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_el2[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MODULE */
|
@ -1,91 +0,0 @@
|
||||
/* Definitions for the 3Com 3c503 Etherlink 2. */
|
||||
/* This file is distributed under the GPL.
|
||||
Many of these names and comments are directly from the Crynwr packet
|
||||
drivers, which are released under the GPL. */
|
||||
|
||||
#define EL2H (dev->base_addr + 0x400)
|
||||
#define EL2L (dev->base_addr)
|
||||
|
||||
/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
|
||||
out of available addresses on the first one... */
|
||||
|
||||
#define OLD_3COM_ID 0x02608c
|
||||
#define NEW_3COM_ID 0x0020af
|
||||
|
||||
/* Shared memory management parameters. NB: The 8 bit cards have only
|
||||
one bank (MB1) which serves both Tx and Rx packet space. The 16bit
|
||||
cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.
|
||||
You choose which bank appears in the sh. mem window with EGACFR_MBSn */
|
||||
|
||||
#define EL2_MB0_START_PG (0x00) /* EL2/16 Tx packets go in bank 0 */
|
||||
#define EL2_MB1_START_PG (0x20) /* First page of bank 1 */
|
||||
#define EL2_MB1_STOP_PG (0x40) /* Last page +1 of bank 1 */
|
||||
|
||||
/* 3Com 3c503 ASIC registers */
|
||||
#define E33G_STARTPG (EL2H+0) /* Start page, matching EN0_STARTPG */
|
||||
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
|
||||
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
|
||||
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
|
||||
/* (non-useful, but it also appears at the end of EPROM space) */
|
||||
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
|
||||
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
|
||||
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
|
||||
#define E33G_STATUS (EL2H+7) /* Status on completions. */
|
||||
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
|
||||
/* (Which IRQ to assert, DMA chan to use) */
|
||||
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
|
||||
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
|
||||
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
|
||||
shared RAM) is mapped into memory space. */
|
||||
#define E33G_VP2 (EL2H+11)
|
||||
#define E33G_VP1 (EL2H+12)
|
||||
#define E33G_VP0 (EL2H+13)
|
||||
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
|
||||
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
|
||||
|
||||
/* Bits in E33G_CNTRL register: */
|
||||
|
||||
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
|
||||
#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
|
||||
#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
|
||||
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
|
||||
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
|
||||
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
|
||||
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
|
||||
#define ECNTRL_START (0x80) /* Start the DMA logic */
|
||||
|
||||
/* Bits in E33G_STATUS register: */
|
||||
|
||||
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
|
||||
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
|
||||
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
|
||||
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
|
||||
#define ESTAT_DIP (0x08) /* DMA In Progress */
|
||||
|
||||
/* Bits in E33G_GACFR register: */
|
||||
|
||||
#define EGACFR_NIM (0x80) /* NIC interrupt mask */
|
||||
#define EGACFR_TCM (0x40) /* DMA term. count interrupt mask */
|
||||
#define EGACFR_RSEL (0x08) /* Map a bank of card mem into system mem */
|
||||
#define EGACFR_MBS2 (0x04) /* Memory bank select, bit 2. */
|
||||
#define EGACFR_MBS1 (0x02) /* Memory bank select, bit 1. */
|
||||
#define EGACFR_MBS0 (0x01) /* Memory bank select, bit 0. */
|
||||
|
||||
#define EGACFR_NORM (0x49) /* TCM | RSEL | MBS0 */
|
||||
#define EGACFR_IRQOFF (0xc9) /* TCM | RSEL | MBS0 | NIM */
|
||||
|
||||
/*
|
||||
MBS2 MBS1 MBS0 Sh. mem windows card mem at:
|
||||
---- ---- ---- -----------------------------
|
||||
0 0 0 0x0000 -- bank 0
|
||||
0 0 1 0x2000 -- bank 1 (only choice for 8bit card)
|
||||
0 1 0 0x4000 -- bank 2, not used
|
||||
0 1 1 0x6000 -- bank 3, not used
|
||||
|
||||
There was going to be a 32k card that used bank 2 and 3, but it
|
||||
never got produced.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* End of 3C503 parameter definitions */
|
@ -21,18 +21,6 @@ config NET_VENDOR_8390
|
||||
|
||||
if NET_VENDOR_8390
|
||||
|
||||
config EL2
|
||||
tristate "3c503 \"EtherLink II\" 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 3c503.
|
||||
|
||||
config PCMCIA_AXNET
|
||||
tristate "Asix AX88190 PCMCIA support"
|
||||
depends on PCMCIA
|
||||
@ -62,42 +50,6 @@ config AX88796_93CX6
|
||||
---help---
|
||||
Select this if your platform comes with an external 93CX6 eeprom.
|
||||
|
||||
config E2100
|
||||
tristate "Cabletron E21xx 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 e2100.
|
||||
|
||||
config HPLAN_PLUS
|
||||
tristate "HP PCLAN+ (27247B and 27252A) 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 hp-plus.
|
||||
|
||||
config HPLAN
|
||||
tristate "HP PCLAN (27245 and other 27xxx series) 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 hp.
|
||||
|
||||
config HYDRA
|
||||
tristate "Hydra support"
|
||||
depends on ZORRO
|
||||
|
@ -6,10 +6,6 @@ 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_E2100) += e2100.o 8390.o
|
||||
obj-$(CONFIG_EL2) += 3c503.o 8390p.o
|
||||
obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
|
||||
obj-$(CONFIG_HPLAN) += hp.o 8390p.o
|
||||
obj-$(CONFIG_HYDRA) += hydra.o 8390.o
|
||||
obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o
|
||||
obj-$(CONFIG_NE2000) += ne.o 8390p.o
|
||||
|
@ -1,489 +0,0 @@
|
||||
/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
|
||||
/*
|
||||
Written 1993-1994 by Donald Becker.
|
||||
|
||||
Copyright 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.
|
||||
|
||||
This is a driver for the Cabletron E2100 series ethercards.
|
||||
|
||||
The Author may be reached as becker@scyld.com, or C/O
|
||||
Scyld Computing Corporation
|
||||
410 Severn Ave., Suite 210
|
||||
Annapolis MD 21403
|
||||
|
||||
The E2100 series ethercard is a fairly generic shared memory 8390
|
||||
implementation. The only unusual aspect is the way the shared memory
|
||||
registers are set: first you do an inb() in what is normally the
|
||||
station address region, and the low three bits of next outb() *address*
|
||||
is used as the write value for that register. Either someone wasn't
|
||||
too used to dem bit en bites, or they were trying to obfuscate the
|
||||
programming interface.
|
||||
|
||||
There is an additional complication when setting the window on the packet
|
||||
buffer. You must first do a read into the packet buffer region with the
|
||||
low 8 address bits the address setting the page for the start of the packet
|
||||
buffer window, and then do the above operation. See mem_on() for details.
|
||||
|
||||
One bug on the chip is that even a hard reset won't disable the memory
|
||||
window, usually resulting in a hung machine if mem_off() isn't called.
|
||||
If this happens, you must power down the machine for about 30 seconds.
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"e2100.c:v1.01 7/21/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/ioport.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define DRV_NAME "e2100"
|
||||
|
||||
static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0};
|
||||
|
||||
/* Offsets from the base_addr.
|
||||
Read from the ASIC register, and the low three bits of the next outb()
|
||||
address is used to set the corresponding register. */
|
||||
#define E21_NIC_OFFSET 0 /* Offset to the 8390 NIC. */
|
||||
#define E21_ASIC 0x10
|
||||
#define E21_MEM_ENABLE 0x10
|
||||
#define E21_MEM_ON 0x05 /* Enable memory in 16 bit mode. */
|
||||
#define E21_MEM_ON_8 0x07 /* Enable memory in 8 bit mode. */
|
||||
#define E21_MEM_BASE 0x11
|
||||
#define E21_IRQ_LOW 0x12 /* The low three bits of the IRQ number. */
|
||||
#define E21_IRQ_HIGH 0x14 /* The high IRQ bit and media select ... */
|
||||
#define E21_MEDIA 0x14 /* (alias). */
|
||||
#define E21_ALT_IFPORT 0x02 /* Set to use the other (BNC,AUI) port. */
|
||||
#define E21_BIG_MEM 0x04 /* Use a bigger (64K) buffer (we don't) */
|
||||
#define E21_SAPROM 0x10 /* Offset to station address data. */
|
||||
#define E21_IO_EXTENT 0x20
|
||||
|
||||
static inline void mem_on(short port, volatile char __iomem *mem_base,
|
||||
unsigned char start_page )
|
||||
{
|
||||
/* This is a little weird: set the shared memory window by doing a
|
||||
read. The low address bits specify the starting page. */
|
||||
readb(mem_base+start_page);
|
||||
inb(port + E21_MEM_ENABLE);
|
||||
outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
|
||||
}
|
||||
|
||||
static inline void mem_off(short port)
|
||||
{
|
||||
inb(port + E21_MEM_ENABLE);
|
||||
outb(0x00, port + E21_MEM_ENABLE);
|
||||
}
|
||||
|
||||
/* In other drivers I put the TX pages first, but the E2100 window circuitry
|
||||
is designed to have a 4K Tx region last. The windowing circuitry wraps the
|
||||
window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
|
||||
appear contiguously in the window. */
|
||||
#define E21_RX_START_PG 0x00 /* First page of RX buffer */
|
||||
#define E21_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
|
||||
#define E21_BIG_RX_STOP_PG 0xF0 /* Last page +1 of RX ring */
|
||||
#define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */
|
||||
|
||||
static int e21_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
static int e21_open(struct net_device *dev);
|
||||
static void e21_reset_8390(struct net_device *dev);
|
||||
static void e21_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void e21_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
static void e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static int e21_open(struct net_device *dev);
|
||||
static int e21_close(struct net_device *dev);
|
||||
|
||||
|
||||
/* Probe for the E2100 series ethercards. These cards have an 8390 at the
|
||||
base address and the station address at both offset 0x10 and 0x18. I read
|
||||
the station address from offset 0x18 to avoid the dataport of NE2000
|
||||
ethercards, and look for Ctron's unique ID (first three octets of the
|
||||
station address).
|
||||
*/
|
||||
|
||||
static int __init do_e2100_probe(struct net_device *dev)
|
||||
{
|
||||
int *port;
|
||||
int base_addr = dev->base_addr;
|
||||
int irq = dev->irq;
|
||||
|
||||
if (base_addr > 0x1ff) /* Check a single specified location. */
|
||||
return e21_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (port = e21_probe_list; *port; port++) {
|
||||
dev->irq = irq;
|
||||
if (e21_probe1(dev, *port) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init e2100_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_e2100_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops e21_netdev_ops = {
|
||||
.ndo_open = e21_open,
|
||||
.ndo_stop = e21_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 e21_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i, status, retval;
|
||||
unsigned char *station_addr = dev->dev_addr;
|
||||
static unsigned version_printed;
|
||||
|
||||
if (!request_region(ioaddr, E21_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* First check the station address for the Ctron prefix. */
|
||||
if (inb(ioaddr + E21_SAPROM + 0) != 0x00 ||
|
||||
inb(ioaddr + E21_SAPROM + 1) != 0x00 ||
|
||||
inb(ioaddr + E21_SAPROM + 2) != 0x1d) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Verify by making certain that there is a 8390 at there. */
|
||||
outb(E8390_NODMA + E8390_STOP, ioaddr);
|
||||
udelay(1); /* we want to delay one I/O cycle - which is 2MHz */
|
||||
status = inb(ioaddr);
|
||||
if (status != 0x21 && status != 0x23) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read the station address PROM. */
|
||||
for (i = 0; i < 6; i++)
|
||||
station_addr[i] = inb(ioaddr + E21_SAPROM + i);
|
||||
|
||||
inb(ioaddr + E21_MEDIA); /* Point to media selection. */
|
||||
outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */
|
||||
|
||||
if (ei_debug && version_printed++ == 0)
|
||||
printk(version);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
printk(" %02X", station_addr[i]);
|
||||
|
||||
if (dev->irq < 2) {
|
||||
static const int irqlist[] = {15, 11, 10, 12, 5, 9, 3, 4};
|
||||
for (i = 0; i < ARRAY_SIZE(irqlist); i++)
|
||||
if (request_irq (irqlist[i], NULL, 0, "bogus", NULL) != -EBUSY) {
|
||||
dev->irq = irqlist[i];
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(irqlist)) {
|
||||
printk(" unable to get IRQ %d.\n", dev->irq);
|
||||
retval = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
} else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */
|
||||
dev->irq = 9;
|
||||
|
||||
/* The 8390 is at the base address. */
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
ei_status.name = "E2100";
|
||||
ei_status.word16 = 1;
|
||||
ei_status.tx_start_page = E21_TX_START_PG;
|
||||
ei_status.rx_start_page = E21_RX_START_PG;
|
||||
ei_status.stop_page = E21_RX_STOP_PG;
|
||||
ei_status.saved_irq = dev->irq;
|
||||
|
||||
/* Check the media port used. The port can be passed in on the
|
||||
low mem_end bits. */
|
||||
if (dev->mem_end & 15)
|
||||
dev->if_port = dev->mem_end & 7;
|
||||
else {
|
||||
dev->if_port = 0;
|
||||
inb(ioaddr + E21_MEDIA); /* Turn automatic media detection on. */
|
||||
for(i = 0; i < 6; i++)
|
||||
if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) {
|
||||
dev->if_port = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Never map in the E21 shared memory unless you are actively using it.
|
||||
Also, the shared memory has effective only one setting -- spread all
|
||||
over the 128K region! */
|
||||
if (dev->mem_start == 0)
|
||||
dev->mem_start = 0xd0000;
|
||||
|
||||
ei_status.mem = ioremap(dev->mem_start, 2*1024);
|
||||
if (!ei_status.mem) {
|
||||
printk("unable to remap memory\n");
|
||||
retval = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef notdef
|
||||
/* These values are unused. The E2100 has a 2K window into the packet
|
||||
buffer. The window can be set to start on any page boundary. */
|
||||
ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
|
||||
dev->mem_end = ei_status.rmem_end = dev->mem_start + 2*1024;
|
||||
#endif
|
||||
|
||||
printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
|
||||
dev->if_port ? "secondary" : "primary", dev->mem_start);
|
||||
|
||||
ei_status.reset_8390 = &e21_reset_8390;
|
||||
ei_status.block_input = &e21_block_input;
|
||||
ei_status.block_output = &e21_block_output;
|
||||
ei_status.get_8390_hdr = &e21_get_8390_hdr;
|
||||
|
||||
dev->netdev_ops = &e21_netdev_ops;
|
||||
NS8390_init(dev, 0);
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval)
|
||||
goto out;
|
||||
return 0;
|
||||
out:
|
||||
release_region(ioaddr, E21_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
e21_open(struct net_device *dev)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
int retval;
|
||||
|
||||
if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)))
|
||||
return retval;
|
||||
|
||||
/* Set the interrupt line and memory base on the hardware. */
|
||||
inb(ioaddr + E21_IRQ_LOW);
|
||||
outb(0, ioaddr + E21_ASIC + (dev->irq & 7));
|
||||
inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
|
||||
outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
|
||||
+ (dev->if_port ? E21_ALT_IFPORT : 0));
|
||||
inb(ioaddr + E21_MEM_BASE);
|
||||
outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
|
||||
|
||||
ei_open(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
e21_reset_8390(struct net_device *dev)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
|
||||
outb(0x01, ioaddr);
|
||||
if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies);
|
||||
ei_status.txing = 0;
|
||||
|
||||
/* Set up the ASIC registers, just in case something changed them. */
|
||||
|
||||
if (ei_debug > 1) printk("reset done\n");
|
||||
}
|
||||
|
||||
/* Grab the 8390 specific header. We put the 2k window so the header page
|
||||
appears at the start of the shared memory. */
|
||||
|
||||
static void
|
||||
e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
|
||||
short ioaddr = dev->base_addr;
|
||||
char __iomem *shared_mem = ei_status.mem;
|
||||
|
||||
mem_on(ioaddr, shared_mem, ring_page);
|
||||
|
||||
#ifdef notdef
|
||||
/* Officially this is what we are doing, but the readl() is faster */
|
||||
memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
|
||||
#else
|
||||
((unsigned int*)hdr)[0] = readl(shared_mem);
|
||||
#endif
|
||||
|
||||
/* Turn off memory access: we would need to reprogram the window anyway. */
|
||||
mem_off(ioaddr);
|
||||
|
||||
}
|
||||
|
||||
/* Block input and output are easy on shared memory ethercards.
|
||||
The E21xx makes block_input() especially easy by wrapping the top
|
||||
ring buffer to the bottom automatically. */
|
||||
static void
|
||||
e21_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
char __iomem *shared_mem = ei_status.mem;
|
||||
|
||||
mem_on(ioaddr, shared_mem, (ring_offset>>8));
|
||||
|
||||
memcpy_fromio(skb->data, ei_status.mem + (ring_offset & 0xff), count);
|
||||
|
||||
mem_off(ioaddr);
|
||||
}
|
||||
|
||||
static void
|
||||
e21_block_output(struct net_device *dev, int count, const unsigned char *buf,
|
||||
int start_page)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
volatile char __iomem *shared_mem = ei_status.mem;
|
||||
|
||||
/* Set the shared memory window start by doing a read, with the low address
|
||||
bits specifying the starting page. */
|
||||
readb(shared_mem + start_page);
|
||||
mem_on(ioaddr, shared_mem, start_page);
|
||||
|
||||
memcpy_toio(shared_mem, buf, count);
|
||||
mem_off(ioaddr);
|
||||
}
|
||||
|
||||
static int
|
||||
e21_close(struct net_device *dev)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
|
||||
if (ei_debug > 1)
|
||||
printk("%s: Shutting down ethercard.\n", dev->name);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
dev->irq = ei_status.saved_irq;
|
||||
|
||||
/* Shut off the interrupt line and secondary interface. */
|
||||
inb(ioaddr + E21_IRQ_LOW);
|
||||
outb(0, ioaddr + E21_ASIC);
|
||||
inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
|
||||
outb(0, ioaddr + E21_ASIC);
|
||||
|
||||
ei_close(dev);
|
||||
|
||||
/* Double-check that the memory has been turned off, because really
|
||||
really bad things happen if it isn't. */
|
||||
mem_off(ioaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_E21_CARDS 4 /* Max number of E21 cards per module */
|
||||
static struct net_device *dev_e21[MAX_E21_CARDS];
|
||||
static int io[MAX_E21_CARDS];
|
||||
static int irq[MAX_E21_CARDS];
|
||||
static int mem[MAX_E21_CARDS];
|
||||
static int xcvr[MAX_E21_CARDS]; /* choose int. or ext. xcvr */
|
||||
|
||||
module_param_array(io, int, NULL, 0);
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
module_param_array(mem, int, NULL, 0);
|
||||
module_param_array(xcvr, int, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s)");
|
||||
MODULE_PARM_DESC(mem, " memory base address(es)");
|
||||
MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
|
||||
MODULE_DESCRIPTION("Cabletron E2100 ISA 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_E21_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
printk(KERN_NOTICE "e2100.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 = xcvr[this_dev]; /* low 4bits = xcvr sel. */
|
||||
if (do_e2100_probe(dev) == 0) {
|
||||
dev_e21[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
printk(KERN_WARNING "e2100.c: No E2100 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: e21_close() handles free_irq */
|
||||
iounmap(ei_status.mem);
|
||||
release_region(dev->base_addr, E21_IO_EXTENT);
|
||||
}
|
||||
|
||||
void __exit
|
||||
cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_e21[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MODULE */
|
@ -1,505 +0,0 @@
|
||||
/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
|
||||
/*
|
||||
Written 1994 by Donald Becker.
|
||||
|
||||
This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
|
||||
These cards are sold under several model numbers, usually 2724*.
|
||||
|
||||
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
|
||||
|
||||
As is often the case, a great deal of credit is owed to Russ Nelson.
|
||||
The Crynwr packet driver was my primary source of HP-specific
|
||||
programming information.
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/string.h> /* Important -- this inlines word moves. */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define DRV_NAME "hp-plus"
|
||||
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int hpplus_portlist[] __initdata =
|
||||
{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
|
||||
|
||||
/*
|
||||
The HP EtherTwist chip implementation is a fairly routine DP8390
|
||||
implementation. It allows both shared memory and programmed-I/O buffer
|
||||
access, using a custom interface for both. The programmed-I/O mode is
|
||||
entirely implemented in the HP EtherTwist chip, bypassing the problem
|
||||
ridden built-in 8390 facilities used on NE2000 designs. The shared
|
||||
memory mode is likewise special, with an offset register used to make
|
||||
packets appear at the shared memory base. Both modes use a base and bounds
|
||||
page register to hide the Rx ring buffer wrap -- a packet that spans the
|
||||
end of physical buffer memory appears continuous to the driver. (c.f. the
|
||||
3c503 and Cabletron E2100)
|
||||
|
||||
A special note: the internal buffer of the board is only 8 bits wide.
|
||||
This lays several nasty traps for the unaware:
|
||||
- the 8390 must be programmed for byte-wide operations
|
||||
- all I/O and memory operations must work on whole words (the access
|
||||
latches are serially preloaded and have no byte-swapping ability).
|
||||
|
||||
This board is laid out in I/O space much like the earlier HP boards:
|
||||
the first 16 locations are for the board registers, and the second 16 are
|
||||
for the 8390. The board is easy to identify, with both a dedicated 16 bit
|
||||
ID register and a constant 0x530* value in the upper bits of the paging
|
||||
register.
|
||||
*/
|
||||
|
||||
#define HP_ID 0x00 /* ID register, always 0x4850. */
|
||||
#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
|
||||
#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option. */
|
||||
#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page. */
|
||||
#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page. */
|
||||
#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page. */
|
||||
#define NIC_OFFSET 0x10 /* Offset to the 8390 registers. */
|
||||
#define HP_IO_EXTENT 32
|
||||
|
||||
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||
#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
|
||||
|
||||
/* The register set selected in HP_PAGING. */
|
||||
enum PageName {
|
||||
Perf_Page = 0, /* Normal operation. */
|
||||
MAC_Page = 1, /* The ethernet address (+checksum). */
|
||||
HW_Page = 2, /* EEPROM-loaded hardware parameters. */
|
||||
LAN_Page = 4, /* Transceiver selection, testing, etc. */
|
||||
ID_Page = 6 };
|
||||
|
||||
/* The bit definitions for the HPP_OPTION register. */
|
||||
enum HP_Option {
|
||||
NICReset = 1, ChipReset = 2, /* Active low, really UNreset. */
|
||||
EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
|
||||
MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
|
||||
|
||||
static int hpp_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
static void hpp_reset_8390(struct net_device *dev);
|
||||
static int hpp_open(struct net_device *dev);
|
||||
static int hpp_close(struct net_device *dev);
|
||||
static void hpp_mem_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void hpp_mem_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
static void hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static void hpp_io_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void hpp_io_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
static void hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
|
||||
|
||||
/* Probe a list of addresses for an HP LAN+ adaptor.
|
||||
This routine is almost boilerplate. */
|
||||
|
||||
static int __init do_hpp_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 hpp_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (i = 0; hpplus_portlist[i]; i++) {
|
||||
if (hpp_probe1(dev, hpplus_portlist[i]) == 0)
|
||||
return 0;
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init hp_plus_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_eip_netdev();
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
err = do_hpp_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops hpp_netdev_ops = {
|
||||
.ndo_open = hpp_open,
|
||||
.ndo_stop = hpp_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
|
||||
};
|
||||
|
||||
|
||||
/* Do the interesting part of the probe at a single address. */
|
||||
static int __init hpp_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i, retval;
|
||||
unsigned char checksum = 0;
|
||||
const char name[] = "HP-PC-LAN+";
|
||||
int mem_start;
|
||||
static unsigned version_printed;
|
||||
|
||||
if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* Check for the HP+ signature, 50 48 0x 53. */
|
||||
if (inw(ioaddr + HP_ID) != 0x4850 ||
|
||||
(inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ei_debug && version_printed++ == 0)
|
||||
printk(version);
|
||||
|
||||
printk("%s: %s at %#3x, ", dev->name, name, ioaddr);
|
||||
|
||||
/* Retrieve and checksum the station address. */
|
||||
outw(MAC_Page, ioaddr + HP_PAGING);
|
||||
|
||||
for(i = 0; i < ETH_ALEN; i++) {
|
||||
unsigned char inval = inb(ioaddr + 8 + i);
|
||||
dev->dev_addr[i] = inval;
|
||||
checksum += inval;
|
||||
}
|
||||
checksum += inb(ioaddr + 14);
|
||||
|
||||
printk("%pM", dev->dev_addr);
|
||||
|
||||
if (checksum != 0xff) {
|
||||
printk(" bad checksum %2.2x.\n", checksum);
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
} else {
|
||||
/* Point at the Software Configuration Flags. */
|
||||
outw(ID_Page, ioaddr + HP_PAGING);
|
||||
printk(" ID %4.4x", inw(ioaddr + 12));
|
||||
}
|
||||
|
||||
/* Read the IRQ line. */
|
||||
outw(HW_Page, ioaddr + HP_PAGING);
|
||||
{
|
||||
int irq = inb(ioaddr + 13) & 0x0f;
|
||||
int option = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
dev->irq = irq;
|
||||
if (option & MemEnable) {
|
||||
mem_start = inw(ioaddr + 9) << 8;
|
||||
printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
|
||||
} else {
|
||||
mem_start = 0;
|
||||
printk(", IRQ %d, programmed-I/O mode.\n", irq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the wrap registers for string I/O reads. */
|
||||
outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
|
||||
|
||||
/* Set the base address to point to the NIC, not the "real" base! */
|
||||
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||
|
||||
dev->netdev_ops = &hpp_netdev_ops;
|
||||
|
||||
ei_status.name = name;
|
||||
ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
|
||||
ei_status.tx_start_page = HP_START_PG;
|
||||
ei_status.rx_start_page = HP_START_PG + TX_PAGES/2;
|
||||
ei_status.stop_page = HP_STOP_PG;
|
||||
|
||||
ei_status.reset_8390 = &hpp_reset_8390;
|
||||
ei_status.block_input = &hpp_io_block_input;
|
||||
ei_status.block_output = &hpp_io_block_output;
|
||||
ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
|
||||
|
||||
/* Check if the memory_enable flag is set in the option register. */
|
||||
if (mem_start) {
|
||||
ei_status.block_input = &hpp_mem_block_input;
|
||||
ei_status.block_output = &hpp_mem_block_output;
|
||||
ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
|
||||
dev->mem_start = mem_start;
|
||||
ei_status.mem = ioremap(mem_start,
|
||||
(HP_STOP_PG - HP_START_PG)*256);
|
||||
if (!ei_status.mem) {
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ei_status.rmem_start = dev->mem_start + TX_PAGES/2*256;
|
||||
dev->mem_end = ei_status.rmem_end
|
||||
= dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
|
||||
}
|
||||
|
||||
outw(Perf_Page, ioaddr + HP_PAGING);
|
||||
NS8390p_init(dev, 0);
|
||||
/* Leave the 8390 and HP chip reset. */
|
||||
outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval)
|
||||
goto out1;
|
||||
return 0;
|
||||
out1:
|
||||
iounmap(ei_status.mem);
|
||||
out:
|
||||
release_region(ioaddr, HP_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
hpp_open(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg;
|
||||
int retval;
|
||||
|
||||
if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Reset the 8390 and HP chip. */
|
||||
option_reg = inw(ioaddr + HPP_OPTION);
|
||||
outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
|
||||
udelay(5);
|
||||
/* Unreset the board and enable interrupts. */
|
||||
outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
|
||||
|
||||
/* Set the wrap registers for programmed-I/O operation. */
|
||||
outw(HW_Page, ioaddr + HP_PAGING);
|
||||
outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
|
||||
|
||||
/* Select the operational page. */
|
||||
outw(Perf_Page, ioaddr + HP_PAGING);
|
||||
|
||||
return eip_open(dev);
|
||||
}
|
||||
|
||||
static int
|
||||
hpp_close(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
eip_close(dev);
|
||||
outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
|
||||
ioaddr + HPP_OPTION);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hpp_reset_8390(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
|
||||
|
||||
outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
|
||||
/* Pause a few cycles for the hardware reset to take place. */
|
||||
udelay(5);
|
||||
ei_status.txing = 0;
|
||||
outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
|
||||
|
||||
udelay(5);
|
||||
|
||||
|
||||
if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||
printk("%s: hp_reset_8390() did not complete.\n", dev->name);
|
||||
|
||||
if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
|
||||
}
|
||||
|
||||
/* The programmed-I/O version of reading the 4 byte 8390 specific header.
|
||||
Note that transfer with the EtherTwist+ must be on word boundaries. */
|
||||
|
||||
static void
|
||||
hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
|
||||
outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
|
||||
insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
|
||||
}
|
||||
|
||||
/* Block input and output, similar to the Crynwr packet driver. */
|
||||
|
||||
static void
|
||||
hpp_io_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
char *buf = skb->data;
|
||||
|
||||
outw(ring_offset, ioaddr + HPP_IN_ADDR);
|
||||
insw(ioaddr + HP_DATAPORT, buf, count>>1);
|
||||
if (count & 0x01)
|
||||
buf[count-1] = inw(ioaddr + HP_DATAPORT);
|
||||
}
|
||||
|
||||
/* The corresponding shared memory versions of the above 2 functions. */
|
||||
|
||||
static void
|
||||
hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
|
||||
outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
|
||||
memcpy_fromio(hdr, ei_status.mem, sizeof(struct e8390_pkt_hdr));
|
||||
outw(option_reg, ioaddr + HPP_OPTION);
|
||||
hdr->count = (le16_to_cpu(hdr->count) + 3) & ~3; /* Round up allocation. */
|
||||
}
|
||||
|
||||
static void
|
||||
hpp_mem_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
outw(ring_offset, ioaddr + HPP_IN_ADDR);
|
||||
|
||||
outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
|
||||
|
||||
/* Caution: this relies on get_8390_hdr() rounding up count!
|
||||
Also note that we *can't* use eth_io_copy_and_sum() because
|
||||
it will not always copy "count" bytes (e.g. padded IP). */
|
||||
|
||||
memcpy_fromio(skb->data, ei_status.mem, count);
|
||||
outw(option_reg, ioaddr + HPP_OPTION);
|
||||
}
|
||||
|
||||
/* A special note: we *must* always transfer >=16 bit words.
|
||||
It's always safe to round up, so we do. */
|
||||
static void
|
||||
hpp_io_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
|
||||
outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
|
||||
}
|
||||
|
||||
static void
|
||||
hpp_mem_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - NIC_OFFSET;
|
||||
int option_reg = inw(ioaddr + HPP_OPTION);
|
||||
|
||||
outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
|
||||
outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
|
||||
memcpy_toio(ei_status.mem, buf, (count + 3) & ~3);
|
||||
outw(option_reg, ioaddr + HPP_OPTION);
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */
|
||||
static struct net_device *dev_hpp[MAX_HPP_CARDS];
|
||||
static int io[MAX_HPP_CARDS];
|
||||
static int irq[MAX_HPP_CARDS];
|
||||
|
||||
module_param_array(io, int, NULL, 0);
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O port address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected");
|
||||
MODULE_DESCRIPTION("HP PC-LAN+ ISA 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_HPP_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
|
||||
}
|
||||
dev = alloc_eip_netdev();
|
||||
if (!dev)
|
||||
break;
|
||||
dev->irq = irq[this_dev];
|
||||
dev->base_addr = io[this_dev];
|
||||
if (do_hpp_probe(dev) == 0) {
|
||||
dev_hpp[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
printk(KERN_WARNING "hp-plus.c: No HP-Plus 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: hpp_close() handles free_irq */
|
||||
iounmap(ei_status.mem);
|
||||
release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
|
||||
}
|
||||
|
||||
void __exit
|
||||
cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_hpp[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MODULE */
|
@ -1,438 +0,0 @@
|
||||
/* hp.c: A HP LAN 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 the HP PC-LAN adaptors.
|
||||
|
||||
Sources:
|
||||
The Crynwr packet driver.
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"hp.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/ioport.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define DRV_NAME "hp"
|
||||
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int hppclan_portlist[] __initdata =
|
||||
{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
|
||||
|
||||
#define HP_IO_EXTENT 32
|
||||
|
||||
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
|
||||
#define HP_ID 0x07
|
||||
#define HP_CONFIGURE 0x08 /* Configuration register. */
|
||||
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
|
||||
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
|
||||
#define HP_DATAON 0x10 /* Turn on dataport */
|
||||
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
|
||||
|
||||
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
|
||||
#define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */
|
||||
|
||||
static int hp_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
static void hp_reset_8390(struct net_device *dev);
|
||||
static void hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static void hp_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb , int ring_offset);
|
||||
static void hp_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
|
||||
static void hp_init_card(struct net_device *dev);
|
||||
|
||||
/* The map from IRQ number to HP_CONFIGURE register setting. */
|
||||
/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||
static char irqmap[16] __initdata= { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
|
||||
|
||||
|
||||
/* Probe for an HP LAN adaptor.
|
||||
Also initialize the card and fill in STATION_ADDR with the station
|
||||
address. */
|
||||
|
||||
static int __init do_hp_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 hp_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (i = 0; hppclan_portlist[i]; i++) {
|
||||
if (hp_probe1(dev, hppclan_portlist[i]) == 0)
|
||||
return 0;
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init hp_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_eip_netdev();
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
err = do_hp_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init hp_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i, retval, board_id, wordmode;
|
||||
const char *name;
|
||||
static unsigned version_printed;
|
||||
|
||||
if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* Check for the HP physical address, 08 00 09 xx xx xx. */
|
||||
/* This really isn't good enough: we may pick up HP LANCE boards
|
||||
also! Avoid the lance 0x5757 signature. */
|
||||
if (inb(ioaddr) != 0x08
|
||||
|| inb(ioaddr+1) != 0x00
|
||||
|| inb(ioaddr+2) != 0x09
|
||||
|| inb(ioaddr+14) == 0x57) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set up the parameters based on the board ID.
|
||||
If you have additional mappings, please mail them to me -djb. */
|
||||
if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
|
||||
name = "HP27247";
|
||||
wordmode = 1;
|
||||
} else {
|
||||
name = "HP27250";
|
||||
wordmode = 0;
|
||||
}
|
||||
|
||||
if (ei_debug && version_printed++ == 0)
|
||||
printk(version);
|
||||
|
||||
printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
|
||||
|
||||
for(i = 0; i < ETH_ALEN; i++)
|
||||
dev->dev_addr[i] = inb(ioaddr + i);
|
||||
|
||||
printk(" %pM", dev->dev_addr);
|
||||
|
||||
/* Snarf the interrupt now. Someday this could be moved to open(). */
|
||||
if (dev->irq < 2) {
|
||||
static const int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
|
||||
static const int irq_8list[] = { 7, 5, 3, 4, 9, 0};
|
||||
const int *irqp = wordmode ? irq_16list : irq_8list;
|
||||
do {
|
||||
int irq = *irqp;
|
||||
if (request_irq (irq, NULL, 0, "bogus", NULL) != -EBUSY) {
|
||||
unsigned long cookie = probe_irq_on();
|
||||
/* Twinkle the interrupt, and check if it's seen. */
|
||||
outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||
outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||
if (irq == probe_irq_off(cookie) /* It's a good IRQ line! */
|
||||
&& request_irq (irq, eip_interrupt, 0, DRV_NAME, dev) == 0) {
|
||||
printk(" selecting IRQ %d.\n", irq);
|
||||
dev->irq = *irqp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (*++irqp);
|
||||
if (*irqp == 0) {
|
||||
printk(" no free IRQ lines.\n");
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (dev->irq == 2)
|
||||
dev->irq = 9;
|
||||
if ((retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev))) {
|
||||
printk (" unable to get IRQ %d.\n", dev->irq);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the base address to point to the NIC, not the "real" base! */
|
||||
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||
dev->netdev_ops = &eip_netdev_ops;
|
||||
|
||||
ei_status.name = name;
|
||||
ei_status.word16 = wordmode;
|
||||
ei_status.tx_start_page = HP_START_PG;
|
||||
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
|
||||
ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG;
|
||||
|
||||
ei_status.reset_8390 = hp_reset_8390;
|
||||
ei_status.get_8390_hdr = hp_get_8390_hdr;
|
||||
ei_status.block_input = hp_block_input;
|
||||
ei_status.block_output = hp_block_output;
|
||||
hp_init_card(dev);
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval)
|
||||
goto out1;
|
||||
return 0;
|
||||
out1:
|
||||
free_irq(dev->irq, dev);
|
||||
out:
|
||||
release_region(ioaddr, HP_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
hp_reset_8390(struct net_device *dev)
|
||||
{
|
||||
int hp_base = dev->base_addr - NIC_OFFSET;
|
||||
int saved_config = inb_p(hp_base + HP_CONFIGURE);
|
||||
|
||||
if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
|
||||
outb_p(0x00, hp_base + HP_CONFIGURE);
|
||||
ei_status.txing = 0;
|
||||
/* Pause just a few cycles for the hardware reset to take place. */
|
||||
udelay(5);
|
||||
|
||||
outb_p(saved_config, hp_base + HP_CONFIGURE);
|
||||
udelay(5);
|
||||
|
||||
if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||
printk("%s: hp_reset_8390() did not complete.\n", dev->name);
|
||||
|
||||
if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
|
||||
}
|
||||
|
||||
static void
|
||||
hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
int nic_base = dev->base_addr;
|
||||
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
|
||||
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||
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);
|
||||
|
||||
if (ei_status.word16)
|
||||
insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
|
||||
else
|
||||
insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
|
||||
|
||||
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
}
|
||||
|
||||
/* 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
|
||||
hp_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int nic_base = dev->base_addr;
|
||||
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
int xfer_count = count;
|
||||
char *buf = skb->data;
|
||||
|
||||
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||
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);
|
||||
if (ei_status.word16) {
|
||||
insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
|
||||
if (count & 0x01)
|
||||
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
|
||||
} else {
|
||||
insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||
}
|
||||
/* This is for the ALPHA version only, remove for later releases. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
int addr = (high << 8) + low;
|
||||
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||
printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
|
||||
dev->name, ring_offset + xfer_count, addr);
|
||||
}
|
||||
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
}
|
||||
|
||||
static void
|
||||
hp_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page)
|
||||
{
|
||||
int nic_base = dev->base_addr;
|
||||
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
|
||||
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
/* 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++;
|
||||
/* We should already be in page 0, but to be safe... */
|
||||
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
|
||||
|
||||
#ifdef NE8390_RW_BUGFIX
|
||||
/* Handle the read-before-write bug the same way as the
|
||||
Crynwr packet driver -- the NatSemi method doesn't work. */
|
||||
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||
outb_p(0, nic_base + EN0_RCNTHI);
|
||||
outb_p(0xff, nic_base + EN0_RSARLO);
|
||||
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||
#define NE_CMD 0x00
|
||||
outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
|
||||
/* Make certain that the dummy read has occurred. */
|
||||
inb_p(0x61);
|
||||
inb_p(0x61);
|
||||
#endif
|
||||
|
||||
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);
|
||||
if (ei_status.word16) {
|
||||
/* Use the 'rep' sequence for 16 bit boards. */
|
||||
outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
|
||||
} else {
|
||||
outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||
}
|
||||
|
||||
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||
|
||||
/* This is for the ALPHA version only, remove for later releases. */
|
||||
if (ei_debug > 0) { /* DMA termination address check... */
|
||||
int high = inb_p(nic_base + EN0_RSARHI);
|
||||
int low = inb_p(nic_base + EN0_RSARLO);
|
||||
int addr = (high << 8) + low;
|
||||
if ((start_page << 8) + count != addr)
|
||||
printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
|
||||
dev->name, (start_page << 8) + count, addr);
|
||||
}
|
||||
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||
}
|
||||
|
||||
/* This function resets the ethercard if something screws up. */
|
||||
static void __init
|
||||
hp_init_card(struct net_device *dev)
|
||||
{
|
||||
int irq = dev->irq;
|
||||
NS8390p_init(dev, 0);
|
||||
outb_p(irqmap[irq&0x0f] | HP_RUN,
|
||||
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_HP_CARDS 4 /* Max number of HP cards per module */
|
||||
static struct net_device *dev_hp[MAX_HP_CARDS];
|
||||
static int io[MAX_HP_CARDS];
|
||||
static int irq[MAX_HP_CARDS];
|
||||
|
||||
module_param_array(io, int, NULL, 0);
|
||||
module_param_array(irq, int, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
|
||||
MODULE_DESCRIPTION("HP PC-LAN ISA 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_HP_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
|
||||
}
|
||||
dev = alloc_eip_netdev();
|
||||
if (!dev)
|
||||
break;
|
||||
dev->irq = irq[this_dev];
|
||||
dev->base_addr = io[this_dev];
|
||||
if (do_hp_probe(dev) == 0) {
|
||||
dev_hp[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
printk(KERN_WARNING "hp.c: No HP 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 - NIC_OFFSET, HP_IO_EXTENT);
|
||||
}
|
||||
|
||||
void __exit
|
||||
cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_hp[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MODULE */
|
@ -135,7 +135,6 @@ config ETHOC
|
||||
source "drivers/net/ethernet/packetengines/Kconfig"
|
||||
source "drivers/net/ethernet/pasemi/Kconfig"
|
||||
source "drivers/net/ethernet/qlogic/Kconfig"
|
||||
source "drivers/net/ethernet/racal/Kconfig"
|
||||
source "drivers/net/ethernet/realtek/Kconfig"
|
||||
source "drivers/net/ethernet/renesas/Kconfig"
|
||||
source "drivers/net/ethernet/rdc/Kconfig"
|
||||
|
@ -53,7 +53,6 @@ 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_RACAL) += racal/
|
||||
obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
|
||||
obj-$(CONFIG_SH_ETH) += renesas/
|
||||
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
|
||||
|
@ -105,19 +105,6 @@ config DECLANCE
|
||||
DEC (now Compaq) based on the AMD LANCE chipset, including the
|
||||
DEPCA series. (This chipset is better known via the NE2100 cards.)
|
||||
|
||||
config DEPCA
|
||||
tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support"
|
||||
depends on (ISA || EISA)
|
||||
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> as well as
|
||||
<file:drivers/net/ethernet/amd/depca.c>.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called depca.
|
||||
|
||||
config HPLANCE
|
||||
bool "HP on-board LANCE support"
|
||||
depends on DIO
|
||||
|
@ -8,7 +8,6 @@ obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
|
||||
obj-$(CONFIG_ARIADNE) += ariadne.o
|
||||
obj-$(CONFIG_ATARILANCE) += atarilance.o
|
||||
obj-$(CONFIG_DECLANCE) += declance.o
|
||||
obj-$(CONFIG_DEPCA) += depca.o
|
||||
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
|
||||
obj-$(CONFIG_LANCE) += lance.o
|
||||
obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,183 +0,0 @@
|
||||
/*
|
||||
Written 1994 by David C. Davies.
|
||||
|
||||
Copyright 1994 David C. Davies. This software may be used and distributed
|
||||
according to the terms of the GNU General Public License, incorporated herein by
|
||||
reference.
|
||||
*/
|
||||
|
||||
/*
|
||||
** I/O addresses. Note that the 2k buffer option is not supported in
|
||||
** this driver.
|
||||
*/
|
||||
#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */
|
||||
#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
|
||||
#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
|
||||
#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
|
||||
#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
|
||||
#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
|
||||
#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
|
||||
#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
|
||||
|
||||
/*
|
||||
** These are LANCE registers addressable through DEPCA_ADDR
|
||||
*/
|
||||
#define CSR0 0
|
||||
#define CSR1 1
|
||||
#define CSR2 2
|
||||
#define CSR3 3
|
||||
|
||||
/*
|
||||
** NETWORK INTERFACE CSR (NI_CSR) bit definitions
|
||||
*/
|
||||
|
||||
#define TO 0x0100 /* Time Out for remote boot */
|
||||
#define SHE 0x0080 /* SHadow memory Enable */
|
||||
#define BS 0x0040 /* Bank Select */
|
||||
#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
|
||||
#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
|
||||
#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
|
||||
#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
|
||||
#define IM 0x0004 /* Interrupt Mask (1->mask) */
|
||||
#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
|
||||
#define LED 0x0001 /* LED control */
|
||||
|
||||
/*
|
||||
** Control and Status Register 0 (CSR0) bit definitions
|
||||
*/
|
||||
|
||||
#define ERR 0x8000 /* Error summary */
|
||||
#define BABL 0x4000 /* Babble transmitter timeout error */
|
||||
#define CERR 0x2000 /* Collision Error */
|
||||
#define MISS 0x1000 /* Missed packet */
|
||||
#define MERR 0x0800 /* Memory Error */
|
||||
#define RINT 0x0400 /* Receiver Interrupt */
|
||||
#define TINT 0x0200 /* Transmit Interrupt */
|
||||
#define IDON 0x0100 /* Initialization Done */
|
||||
#define INTR 0x0080 /* Interrupt Flag */
|
||||
#define INEA 0x0040 /* Interrupt Enable */
|
||||
#define RXON 0x0020 /* Receiver on */
|
||||
#define TXON 0x0010 /* Transmitter on */
|
||||
#define TDMD 0x0008 /* Transmit Demand */
|
||||
#define STOP 0x0004 /* Stop */
|
||||
#define STRT 0x0002 /* Start */
|
||||
#define INIT 0x0001 /* Initialize */
|
||||
#define INTM 0xff00 /* Interrupt Mask */
|
||||
#define INTE 0xfff0 /* Interrupt Enable */
|
||||
|
||||
/*
|
||||
** CONTROL AND STATUS REGISTER 3 (CSR3)
|
||||
*/
|
||||
|
||||
#define BSWP 0x0004 /* Byte SWaP */
|
||||
#define ACON 0x0002 /* ALE control */
|
||||
#define BCON 0x0001 /* Byte CONtrol */
|
||||
|
||||
/*
|
||||
** Initialization Block Mode Register
|
||||
*/
|
||||
|
||||
#define PROM 0x8000 /* Promiscuous Mode */
|
||||
#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
|
||||
#define INTL 0x0040 /* Internal Loopback */
|
||||
#define DRTY 0x0020 /* Disable Retry */
|
||||
#define COLL 0x0010 /* Force Collision */
|
||||
#define DTCR 0x0008 /* Disable Transmit CRC */
|
||||
#define LOOP 0x0004 /* Loopback */
|
||||
#define DTX 0x0002 /* Disable the Transmitter */
|
||||
#define DRX 0x0001 /* Disable the Receiver */
|
||||
|
||||
/*
|
||||
** Receive Message Descriptor 1 (RMD1) bit definitions.
|
||||
*/
|
||||
|
||||
#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
|
||||
#define R_ERR 0x4000 /* Error Summary */
|
||||
#define R_FRAM 0x2000 /* Framing Error */
|
||||
#define R_OFLO 0x1000 /* Overflow Error */
|
||||
#define R_CRC 0x0800 /* CRC Error */
|
||||
#define R_BUFF 0x0400 /* Buffer Error */
|
||||
#define R_STP 0x0200 /* Start of Packet */
|
||||
#define R_ENP 0x0100 /* End of Packet */
|
||||
|
||||
/*
|
||||
** Transmit Message Descriptor 1 (TMD1) bit definitions.
|
||||
*/
|
||||
|
||||
#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
|
||||
#define T_ERR 0x4000 /* Error Summary */
|
||||
#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
|
||||
#define T_MORE 0x1000 /* >1 retry to transmit packet */
|
||||
#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
|
||||
#define T_DEF 0x0400 /* Deferred */
|
||||
#define T_STP 0x02000000 /* Start of Packet */
|
||||
#define T_ENP 0x01000000 /* End of Packet */
|
||||
#define T_FLAGS 0xff000000 /* TX Flags Field */
|
||||
|
||||
/*
|
||||
** Transmit Message Descriptor 3 (TMD3) bit definitions.
|
||||
*/
|
||||
|
||||
#define TMD3_BUFF 0x8000 /* BUFFer error */
|
||||
#define TMD3_UFLO 0x4000 /* UnderFLOw error */
|
||||
#define TMD3_RES 0x2000 /* REServed */
|
||||
#define TMD3_LCOL 0x1000 /* Late COLlision */
|
||||
#define TMD3_LCAR 0x0800 /* Loss of CARrier */
|
||||
#define TMD3_RTRY 0x0400 /* ReTRY error */
|
||||
|
||||
/*
|
||||
** EISA configuration Register (CNFG) bit definitions
|
||||
*/
|
||||
|
||||
#define TIMEOUT 0x0100 /* 0:2.5 mins, 1: 30 secs */
|
||||
#define REMOTE 0x0080 /* Remote Boot Enable -> 1 */
|
||||
#define IRQ11 0x0040 /* Enable -> 1 */
|
||||
#define IRQ10 0x0020 /* Enable -> 1 */
|
||||
#define IRQ9 0x0010 /* Enable -> 1 */
|
||||
#define IRQ5 0x0008 /* Enable -> 1 */
|
||||
#define BUFF 0x0004 /* 0: 64kB or 128kB, 1: 32kB */
|
||||
#define PADR16 0x0002 /* RAM on 64kB boundary */
|
||||
#define PADR17 0x0001 /* RAM on 128kB boundary */
|
||||
|
||||
/*
|
||||
** Miscellaneous
|
||||
*/
|
||||
#define HASH_TABLE_LEN 64 /* Bits */
|
||||
#define HASH_BITS 0x003f /* 6 LS bits */
|
||||
|
||||
#define MASK_INTERRUPTS 1
|
||||
#define UNMASK_INTERRUPTS 0
|
||||
|
||||
#define EISA_EN 0x0001 /* Enable EISA bus buffers */
|
||||
#define EISA_ID iobase+0x0080 /* ID long word for EISA card */
|
||||
#define EISA_CTRL iobase+0x0084 /* Control word for EISA card */
|
||||
|
||||
/*
|
||||
** Include the IOCTL stuff
|
||||
*/
|
||||
#include <linux/sockios.h>
|
||||
|
||||
struct depca_ioctl {
|
||||
unsigned short cmd; /* Command to run */
|
||||
unsigned short len; /* Length of the data buffer */
|
||||
unsigned char __user *data; /* Pointer to the data buffer */
|
||||
};
|
||||
|
||||
/*
|
||||
** Recognised commands for the driver
|
||||
*/
|
||||
#define DEPCA_GET_HWADDR 0x01 /* Get the hardware address */
|
||||
#define DEPCA_SET_HWADDR 0x02 /* Get the hardware address */
|
||||
#define DEPCA_SET_PROM 0x03 /* Set Promiscuous Mode */
|
||||
#define DEPCA_CLR_PROM 0x04 /* Clear Promiscuous Mode */
|
||||
#define DEPCA_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
|
||||
#define DEPCA_GET_MCA 0x06 /* Get a multicast address */
|
||||
#define DEPCA_SET_MCA 0x07 /* Set a multicast address */
|
||||
#define DEPCA_CLR_MCA 0x08 /* Clear a multicast address */
|
||||
#define DEPCA_MCA_EN 0x09 /* Enable a multicast address group */
|
||||
#define DEPCA_GET_STATS 0x0a /* Get the driver statistics */
|
||||
#define DEPCA_CLR_STATS 0x0b /* Zero out the driver statistics */
|
||||
#define DEPCA_GET_REG 0x0c /* Get the Register contents */
|
||||
#define DEPCA_SET_REG 0x0d /* Set the Register contents */
|
||||
#define DEPCA_DUMP 0x0f /* Dump the DEPCA Status */
|
||||
|
@ -17,21 +17,5 @@ config NET_VENDOR_DEC
|
||||
your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_DEC
|
||||
|
||||
config EWRK3
|
||||
tristate "EtherWORKS 3 (DE203, DE204, DE205) support"
|
||||
depends on ISA
|
||||
select CRC32
|
||||
---help---
|
||||
This driver supports the DE203, DE204 and DE205 network (Ethernet)
|
||||
cards. If this is for you, say Y and read
|
||||
<file:Documentation/networking/ewrk3.txt> in the kernel source as
|
||||
well as 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 ewrk3.
|
||||
|
||||
source "drivers/net/ethernet/dec/tulip/Kconfig"
|
||||
|
||||
endif # NET_VENDOR_DEC
|
||||
|
@ -2,5 +2,4 @@
|
||||
# Makefile for the Digital Equipment Inc. network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EWRK3) += ewrk3.o
|
||||
obj-$(CONFIG_NET_TULIP) += tulip/
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,322 +0,0 @@
|
||||
/*
|
||||
Written 1994 by David C. Davies.
|
||||
|
||||
Copyright 1994 Digital Equipment Corporation.
|
||||
|
||||
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 davies@wanton.lkg.dec.com or Digital
|
||||
Equipment Corporation, 550 King Street, Littleton MA 01460.
|
||||
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** I/O Address Register Map
|
||||
*/
|
||||
#define EWRK3_CSR iobase+0x00 /* Control and Status Register */
|
||||
#define EWRK3_CR iobase+0x01 /* Control Register */
|
||||
#define EWRK3_ICR iobase+0x02 /* Interrupt Control Register */
|
||||
#define EWRK3_TSR iobase+0x03 /* Transmit Status Register */
|
||||
#define EWRK3_RSVD1 iobase+0x04 /* RESERVED */
|
||||
#define EWRK3_RSVD2 iobase+0x05 /* RESERVED */
|
||||
#define EWRK3_FMQ iobase+0x06 /* Free Memory Queue */
|
||||
#define EWRK3_FMQC iobase+0x07 /* Free Memory Queue Counter */
|
||||
#define EWRK3_RQ iobase+0x08 /* Receive Queue */
|
||||
#define EWRK3_RQC iobase+0x09 /* Receive Queue Counter */
|
||||
#define EWRK3_TQ iobase+0x0a /* Transmit Queue */
|
||||
#define EWRK3_TQC iobase+0x0b /* Transmit Queue Counter */
|
||||
#define EWRK3_TDQ iobase+0x0c /* Transmit Done Queue */
|
||||
#define EWRK3_TDQC iobase+0x0d /* Transmit Done Queue Counter */
|
||||
#define EWRK3_PIR1 iobase+0x0e /* Page Index Register 1 */
|
||||
#define EWRK3_PIR2 iobase+0x0f /* Page Index Register 2 */
|
||||
#define EWRK3_DATA iobase+0x10 /* Data Register */
|
||||
#define EWRK3_IOPR iobase+0x11 /* I/O Page Register */
|
||||
#define EWRK3_IOBR iobase+0x12 /* I/O Base Register */
|
||||
#define EWRK3_MPR iobase+0x13 /* Memory Page Register */
|
||||
#define EWRK3_MBR iobase+0x14 /* Memory Base Register */
|
||||
#define EWRK3_APROM iobase+0x15 /* Address PROM */
|
||||
#define EWRK3_EPROM1 iobase+0x16 /* EEPROM Data Register 1 */
|
||||
#define EWRK3_EPROM2 iobase+0x17 /* EEPROM Data Register 2 */
|
||||
#define EWRK3_PAR0 iobase+0x18 /* Physical Address Register 0 */
|
||||
#define EWRK3_PAR1 iobase+0x19 /* Physical Address Register 1 */
|
||||
#define EWRK3_PAR2 iobase+0x1a /* Physical Address Register 2 */
|
||||
#define EWRK3_PAR3 iobase+0x1b /* Physical Address Register 3 */
|
||||
#define EWRK3_PAR4 iobase+0x1c /* Physical Address Register 4 */
|
||||
#define EWRK3_PAR5 iobase+0x1d /* Physical Address Register 5 */
|
||||
#define EWRK3_CMR iobase+0x1e /* Configuration/Management Register */
|
||||
|
||||
/*
|
||||
** Control Page Map
|
||||
*/
|
||||
#define PAGE0_FMQ 0x000 /* Free Memory Queue */
|
||||
#define PAGE0_RQ 0x080 /* Receive Queue */
|
||||
#define PAGE0_TQ 0x100 /* Transmit Queue */
|
||||
#define PAGE0_TDQ 0x180 /* Transmit Done Queue */
|
||||
#define PAGE0_HTE 0x200 /* Hash Table Entries */
|
||||
#define PAGE0_RSVD 0x240 /* RESERVED */
|
||||
#define PAGE0_USRD 0x600 /* User Data */
|
||||
|
||||
/*
|
||||
** Control and Status Register bit definitions (EWRK3_CSR)
|
||||
*/
|
||||
#define CSR_RA 0x80 /* Runt Accept */
|
||||
#define CSR_PME 0x40 /* Promiscuous Mode Enable */
|
||||
#define CSR_MCE 0x20 /* Multicast Enable */
|
||||
#define CSR_TNE 0x08 /* TX Done Queue Not Empty */
|
||||
#define CSR_RNE 0x04 /* RX Queue Not Empty */
|
||||
#define CSR_TXD 0x02 /* TX Disable */
|
||||
#define CSR_RXD 0x01 /* RX Disable */
|
||||
|
||||
/*
|
||||
** Control Register bit definitions (EWRK3_CR)
|
||||
*/
|
||||
#define CR_APD 0x80 /* Auto Port Disable */
|
||||
#define CR_PSEL 0x40 /* Port Select (0->TP port) */
|
||||
#define CR_LBCK 0x20 /* LoopBaCK enable */
|
||||
#define CR_FDUP 0x10 /* Full DUPlex enable */
|
||||
#define CR_FBUS 0x08 /* Fast BUS enable (ISA clk > 8.33MHz) */
|
||||
#define CR_EN_16 0x04 /* ENable 16 bit memory accesses */
|
||||
#define CR_LED 0x02 /* LED (1-> turn on) */
|
||||
|
||||
/*
|
||||
** Interrupt Control Register bit definitions (EWRK3_ICR)
|
||||
*/
|
||||
#define ICR_IE 0x80 /* Interrupt Enable */
|
||||
#define ICR_IS 0x60 /* Interrupt Selected */
|
||||
#define ICR_TNEM 0x08 /* TNE Mask (0->mask) */
|
||||
#define ICR_RNEM 0x04 /* RNE Mask (0->mask) */
|
||||
#define ICR_TXDM 0x02 /* TXD Mask (0->mask) */
|
||||
#define ICR_RXDM 0x01 /* RXD Mask (0->mask) */
|
||||
|
||||
/*
|
||||
** Transmit Status Register bit definitions (EWRK3_TSR)
|
||||
*/
|
||||
#define TSR_NCL 0x80 /* No Carrier Loopback */
|
||||
#define TSR_ID 0x40 /* Initially Deferred */
|
||||
#define TSR_LCL 0x20 /* Late CoLlision */
|
||||
#define TSR_ECL 0x10 /* Excessive CoLlisions */
|
||||
#define TSR_RCNTR 0x0f /* Retries CouNTeR */
|
||||
|
||||
/*
|
||||
** I/O Page Register bit definitions (EWRK3_IOPR)
|
||||
*/
|
||||
#define EEPROM_INIT 0xc0 /* EEPROM INIT command */
|
||||
#define EEPROM_WR_EN 0xc8 /* EEPROM WRITE ENABLE command */
|
||||
#define EEPROM_WR 0xd0 /* EEPROM WRITE command */
|
||||
#define EEPROM_WR_DIS 0xd8 /* EEPROM WRITE DISABLE command */
|
||||
#define EEPROM_RD 0xe0 /* EEPROM READ command */
|
||||
|
||||
/*
|
||||
** I/O Base Register bit definitions (EWRK3_IOBR)
|
||||
*/
|
||||
#define EISA_REGS_EN 0x20 /* Enable EISA ID and Control Registers */
|
||||
#define EISA_IOB 0x1f /* Compare bits for I/O Base Address */
|
||||
|
||||
/*
|
||||
** I/O Configuration/Management Register bit definitions (EWRK3_CMR)
|
||||
*/
|
||||
#define CMR_RA 0x80 /* Read Ahead */
|
||||
#define CMR_WB 0x40 /* Write Behind */
|
||||
#define CMR_LINK 0x20 /* 0->TP */
|
||||
#define CMR_POLARITY 0x10 /* Informational */
|
||||
#define CMR_NO_EEPROM 0x0c /* NO_EEPROM<1:0> pin status */
|
||||
#define CMR_HS 0x08 /* Hard Strapped pin status (LeMAC2) */
|
||||
#define CMR_PNP 0x04 /* Plug 'n Play */
|
||||
#define CMR_DRAM 0x02 /* 0-> 1DRAM, 1-> 2 DRAM on board */
|
||||
#define CMR_0WS 0x01 /* Zero Wait State */
|
||||
|
||||
/*
|
||||
** MAC Receive Status Register bit definitions
|
||||
*/
|
||||
|
||||
#define R_ROK 0x80 /* Receive OK summary */
|
||||
#define R_IAM 0x10 /* Individual Address Match */
|
||||
#define R_MCM 0x08 /* MultiCast Match */
|
||||
#define R_DBE 0x04 /* Dribble Bit Error */
|
||||
#define R_CRC 0x02 /* CRC error */
|
||||
#define R_PLL 0x01 /* Phase Lock Lost */
|
||||
|
||||
/*
|
||||
** MAC Transmit Control Register bit definitions
|
||||
*/
|
||||
|
||||
#define TCR_SQEE 0x40 /* SQE Enable - look for heartbeat */
|
||||
#define TCR_SED 0x20 /* Stop when Error Detected */
|
||||
#define TCR_QMODE 0x10 /* Q_MODE */
|
||||
#define TCR_LAB 0x08 /* Less Aggressive Backoff */
|
||||
#define TCR_PAD 0x04 /* PAD Runt Packets */
|
||||
#define TCR_IFC 0x02 /* Insert Frame Check */
|
||||
#define TCR_ISA 0x01 /* Insert Source Address */
|
||||
|
||||
/*
|
||||
** MAC Transmit Status Register bit definitions
|
||||
*/
|
||||
|
||||
#define T_VSTS 0x80 /* Valid STatuS */
|
||||
#define T_CTU 0x40 /* Cut Through Used */
|
||||
#define T_SQE 0x20 /* Signal Quality Error */
|
||||
#define T_NCL 0x10 /* No Carrier Loopback */
|
||||
#define T_LCL 0x08 /* Late Collision */
|
||||
#define T_ID 0x04 /* Initially Deferred */
|
||||
#define T_COLL 0x03 /* COLLision status */
|
||||
#define T_XCOLL 0x03 /* Excessive Collisions */
|
||||
#define T_MCOLL 0x02 /* Multiple Collisions */
|
||||
#define T_OCOLL 0x01 /* One Collision */
|
||||
#define T_NOCOLL 0x00 /* No Collisions */
|
||||
#define T_XUR 0x03 /* Excessive Underruns */
|
||||
#define T_TXE 0x7f /* TX Errors */
|
||||
|
||||
/*
|
||||
** EISA Configuration Register bit definitions
|
||||
*/
|
||||
|
||||
#define EISA_ID iobase + 0x0c80 /* EISA ID Registers */
|
||||
#define EISA_ID0 iobase + 0x0c80 /* EISA ID Register 0 */
|
||||
#define EISA_ID1 iobase + 0x0c81 /* EISA ID Register 1 */
|
||||
#define EISA_ID2 iobase + 0x0c82 /* EISA ID Register 2 */
|
||||
#define EISA_ID3 iobase + 0x0c83 /* EISA ID Register 3 */
|
||||
#define EISA_CR iobase + 0x0c84 /* EISA Control Register */
|
||||
|
||||
/*
|
||||
** EEPROM BYTES
|
||||
*/
|
||||
#define EEPROM_MEMB 0x00
|
||||
#define EEPROM_IOB 0x01
|
||||
#define EEPROM_EISA_ID0 0x02
|
||||
#define EEPROM_EISA_ID1 0x03
|
||||
#define EEPROM_EISA_ID2 0x04
|
||||
#define EEPROM_EISA_ID3 0x05
|
||||
#define EEPROM_MISC0 0x06
|
||||
#define EEPROM_MISC1 0x07
|
||||
#define EEPROM_PNAME7 0x08
|
||||
#define EEPROM_PNAME6 0x09
|
||||
#define EEPROM_PNAME5 0x0a
|
||||
#define EEPROM_PNAME4 0x0b
|
||||
#define EEPROM_PNAME3 0x0c
|
||||
#define EEPROM_PNAME2 0x0d
|
||||
#define EEPROM_PNAME1 0x0e
|
||||
#define EEPROM_PNAME0 0x0f
|
||||
#define EEPROM_SWFLAGS 0x10
|
||||
#define EEPROM_HWCAT 0x11
|
||||
#define EEPROM_NETMAN2 0x12
|
||||
#define EEPROM_REVLVL 0x13
|
||||
#define EEPROM_NETMAN0 0x14
|
||||
#define EEPROM_NETMAN1 0x15
|
||||
#define EEPROM_CHIPVER 0x16
|
||||
#define EEPROM_SETUP 0x17
|
||||
#define EEPROM_PADDR0 0x18
|
||||
#define EEPROM_PADDR1 0x19
|
||||
#define EEPROM_PADDR2 0x1a
|
||||
#define EEPROM_PADDR3 0x1b
|
||||
#define EEPROM_PADDR4 0x1c
|
||||
#define EEPROM_PADDR5 0x1d
|
||||
#define EEPROM_PA_CRC 0x1e
|
||||
#define EEPROM_CHKSUM 0x1f
|
||||
|
||||
/*
|
||||
** EEPROM bytes for checksumming
|
||||
*/
|
||||
#define EEPROM_MAX 32 /* bytes */
|
||||
|
||||
/*
|
||||
** EEPROM MISCELLANEOUS FLAGS
|
||||
*/
|
||||
#define RBE_SHADOW 0x0100 /* Remote Boot Enable Shadow */
|
||||
#define READ_AHEAD 0x0080 /* Read Ahead feature */
|
||||
#define IRQ_SEL2 0x0070 /* IRQ line selection (LeMAC2) */
|
||||
#define IRQ_SEL 0x0060 /* IRQ line selection */
|
||||
#define FAST_BUS 0x0008 /* ISA Bus speeds > 8.33MHz */
|
||||
#define ENA_16 0x0004 /* Enables 16 bit memory transfers */
|
||||
#define WRITE_BEHIND 0x0002 /* Write Behind feature */
|
||||
#define _0WS_ENA 0x0001 /* Zero Wait State Enable */
|
||||
|
||||
/*
|
||||
** EEPROM NETWORK MANAGEMENT FLAGS
|
||||
*/
|
||||
#define NETMAN_POL 0x04 /* Polarity defeat */
|
||||
#define NETMAN_LINK 0x02 /* Link defeat */
|
||||
#define NETMAN_CCE 0x01 /* Custom Counters Enable */
|
||||
|
||||
/*
|
||||
** EEPROM SW FLAGS
|
||||
*/
|
||||
#define SW_SQE 0x10 /* Signal Quality Error */
|
||||
#define SW_LAB 0x08 /* Less Aggressive Backoff */
|
||||
#define SW_INIT 0x04 /* Initialized */
|
||||
#define SW_TIMEOUT 0x02 /* 0:2.5 mins, 1: 30 secs */
|
||||
#define SW_REMOTE 0x01 /* Remote Boot Enable -> 1 */
|
||||
|
||||
/*
|
||||
** EEPROM SETUP FLAGS
|
||||
*/
|
||||
#define SETUP_APD 0x80 /* AutoPort Disable */
|
||||
#define SETUP_PS 0x40 /* Port Select */
|
||||
#define SETUP_MP 0x20 /* MultiPort */
|
||||
#define SETUP_1TP 0x10 /* 1 port, TP */
|
||||
#define SETUP_1COAX 0x00 /* 1 port, Coax */
|
||||
#define SETUP_DRAM 0x02 /* Number of DRAMS on board */
|
||||
|
||||
/*
|
||||
** EEPROM MANAGEMENT FLAGS
|
||||
*/
|
||||
#define MGMT_CCE 0x01 /* Custom Counters Enable */
|
||||
|
||||
/*
|
||||
** EEPROM VERSIONS
|
||||
*/
|
||||
#define LeMAC 0x11
|
||||
#define LeMAC2 0x12
|
||||
|
||||
/*
|
||||
** Miscellaneous
|
||||
*/
|
||||
|
||||
#define EEPROM_WAIT_TIME 1000 /* Number of microseconds */
|
||||
#define EISA_EN 0x0001 /* Enable EISA bus buffers */
|
||||
|
||||
#define HASH_TABLE_LEN 512 /* Bits */
|
||||
|
||||
#define XCT 0x80 /* Transmit Cut Through */
|
||||
#define PRELOAD 16 /* 4 long words */
|
||||
|
||||
#define MASK_INTERRUPTS 1
|
||||
#define UNMASK_INTERRUPTS 0
|
||||
|
||||
#define EEPROM_OFFSET(a) ((u_short)((u_long)(a)))
|
||||
|
||||
/*
|
||||
** Include the IOCTL stuff
|
||||
*/
|
||||
#include <linux/sockios.h>
|
||||
|
||||
#define EWRK3IOCTL SIOCDEVPRIVATE
|
||||
|
||||
struct ewrk3_ioctl {
|
||||
unsigned short cmd; /* Command to run */
|
||||
unsigned short len; /* Length of the data buffer */
|
||||
unsigned char __user *data; /* Pointer to the data buffer */
|
||||
};
|
||||
|
||||
/*
|
||||
** Recognised commands for the driver
|
||||
*/
|
||||
#define EWRK3_GET_HWADDR 0x01 /* Get the hardware address */
|
||||
#define EWRK3_SET_HWADDR 0x02 /* Get the hardware address */
|
||||
#define EWRK3_SET_PROM 0x03 /* Set Promiscuous Mode */
|
||||
#define EWRK3_CLR_PROM 0x04 /* Clear Promiscuous Mode */
|
||||
#define EWRK3_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
|
||||
#define EWRK3_GET_MCA 0x06 /* Get a multicast address */
|
||||
#define EWRK3_SET_MCA 0x07 /* Set a multicast address */
|
||||
#define EWRK3_CLR_MCA 0x08 /* Clear a multicast address */
|
||||
#define EWRK3_MCA_EN 0x09 /* Enable a multicast address group */
|
||||
#define EWRK3_GET_STATS 0x0a /* Get the driver statistics */
|
||||
#define EWRK3_CLR_STATS 0x0b /* Zero out the driver statistics */
|
||||
#define EWRK3_GET_CSR 0x0c /* Get the CSR Register contents */
|
||||
#define EWRK3_SET_CSR 0x0d /* Set the CSR Register contents */
|
||||
#define EWRK3_GET_EEPROM 0x0e /* Get the EEPROM contents */
|
||||
#define EWRK3_SET_EEPROM 0x0f /* Set the EEPROM contents */
|
||||
#define EWRK3_GET_CMR 0x10 /* Get the CMR Register contents */
|
||||
#define EWRK3_CLR_TX_CUT_THRU 0x11 /* Clear the TX cut through mode */
|
||||
#define EWRK3_SET_TX_CUT_THRU 0x12 /* Set the TX cut through mode */
|
@ -5,7 +5,7 @@
|
||||
config NET_VENDOR_DLINK
|
||||
bool "D-Link devices"
|
||||
default y
|
||||
depends on PCI || PARPORT
|
||||
depends on PCI
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, say Y
|
||||
and read the Ethernet-HOWTO, available from
|
||||
@ -18,36 +18,6 @@ config NET_VENDOR_DLINK
|
||||
|
||||
if NET_VENDOR_DLINK
|
||||
|
||||
config DE600
|
||||
tristate "D-Link DE600 pocket adapter support"
|
||||
depends on PARPORT
|
||||
---help---
|
||||
This is a network (Ethernet) device which attaches to your parallel
|
||||
port. Read <file:Documentation/networking/DLINK.txt> as well as the
|
||||
Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>, if you want to use
|
||||
this. It is possible to have several devices share a single parallel
|
||||
port and it is safe to compile the corresponding drivers into the
|
||||
kernel.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called de600.
|
||||
|
||||
config DE620
|
||||
tristate "D-Link DE620 pocket adapter support"
|
||||
depends on PARPORT
|
||||
---help---
|
||||
This is a network (Ethernet) device which attaches to your parallel
|
||||
port. Read <file:Documentation/networking/DLINK.txt> as well as the
|
||||
Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>, if you want to use
|
||||
this. It is possible to have several devices share a single parallel
|
||||
port and it is safe to compile the corresponding drivers into the
|
||||
kernel.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called de620.
|
||||
|
||||
config DL2K
|
||||
tristate "DL2000/TC902x-based Gigabit Ethernet support"
|
||||
depends on PCI
|
||||
|
@ -2,7 +2,5 @@
|
||||
# Makefile for the D-Link network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DE600) += de600.o
|
||||
obj-$(CONFIG_DE620) += de620.o
|
||||
obj-$(CONFIG_DL2K) += dl2k.o
|
||||
obj-$(CONFIG_SUNDANCE) += sundance.o
|
||||
|
@ -1,529 +0,0 @@
|
||||
static const char version[] = "de600.c: $Revision: 1.41-2.5 $, Bjorn Ekwall (bj0rn@blox.se)\n";
|
||||
/*
|
||||
* de600.c
|
||||
*
|
||||
* Linux driver for the D-Link DE-600 Ethernet pocket adapter.
|
||||
*
|
||||
* Portions (C) Copyright 1993, 1994 by Bjorn Ekwall
|
||||
* The Author may be reached as bj0rn@blox.se
|
||||
*
|
||||
* Based on adapter information gathered from DE600.ASM by D-Link Inc.,
|
||||
* as included on disk C in the v.2.11 of PC/TCP from FTP Software.
|
||||
* For DE600.asm:
|
||||
* Portions (C) Copyright 1990 D-Link, Inc.
|
||||
* Copyright, 1988-1992, Russell Nelson, Crynwr Software
|
||||
*
|
||||
* Adapted to the sample network driver core for linux,
|
||||
* written by: Donald Becker <becker@super.org>
|
||||
* (Now at <becker@scyld.com>)
|
||||
*
|
||||
**************************************************************/
|
||||
/*
|
||||
* 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, 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
**************************************************************/
|
||||
|
||||
/* Add more time here if your adapter won't work OK: */
|
||||
#define DE600_SLOW_DOWN udelay(delay_time)
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "de600.h"
|
||||
|
||||
static bool check_lost = true;
|
||||
module_param(check_lost, bool, 0);
|
||||
MODULE_PARM_DESC(check_lost, "If set then check for unplugged de600");
|
||||
|
||||
static unsigned int delay_time = 10;
|
||||
module_param(delay_time, int, 0);
|
||||
MODULE_PARM_DESC(delay_time, "DE-600 deley on I/O in microseconds");
|
||||
|
||||
|
||||
/*
|
||||
* D-Link driver variables:
|
||||
*/
|
||||
|
||||
static volatile int rx_page;
|
||||
|
||||
#define TX_PAGES 2
|
||||
static volatile int tx_fifo[TX_PAGES];
|
||||
static volatile int tx_fifo_in;
|
||||
static volatile int tx_fifo_out;
|
||||
static volatile int free_tx_pages = TX_PAGES;
|
||||
static int was_down;
|
||||
static DEFINE_SPINLOCK(de600_lock);
|
||||
|
||||
static inline u8 de600_read_status(struct net_device *dev)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
outb_p(STATUS, DATA_PORT);
|
||||
status = inb(STATUS_PORT);
|
||||
outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline u8 de600_read_byte(unsigned char type, struct net_device *dev)
|
||||
{
|
||||
/* dev used by macros */
|
||||
u8 lo;
|
||||
outb_p((type), DATA_PORT);
|
||||
lo = ((unsigned char)inb(STATUS_PORT)) >> 4;
|
||||
outb_p((type) | HI_NIBBLE, DATA_PORT);
|
||||
return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open/initialize the board. This is called (in the current kernel)
|
||||
* after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1).
|
||||
*
|
||||
* 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 a non-reboot way to recover if something goes wrong.
|
||||
*/
|
||||
|
||||
static int de600_open(struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = request_irq(DE600_IRQ, de600_interrupt, 0, dev->name, dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
|
||||
return ret;
|
||||
}
|
||||
spin_lock_irqsave(&de600_lock, flags);
|
||||
ret = adapter_init(dev);
|
||||
spin_unlock_irqrestore(&de600_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The inverse routine to de600_open().
|
||||
*/
|
||||
|
||||
static int de600_close(struct net_device *dev)
|
||||
{
|
||||
select_nic();
|
||||
rx_page = 0;
|
||||
de600_put_command(RESET);
|
||||
de600_put_command(STOP_RESET);
|
||||
de600_put_command(0);
|
||||
select_prn();
|
||||
free_irq(DE600_IRQ, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void trigger_interrupt(struct net_device *dev)
|
||||
{
|
||||
de600_put_command(FLIP_IRQ);
|
||||
select_prn();
|
||||
DE600_SLOW_DOWN;
|
||||
select_nic();
|
||||
de600_put_command(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a buffer to the adapter transmit page memory.
|
||||
* Start sending.
|
||||
*/
|
||||
|
||||
static int de600_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int transmit_from;
|
||||
int len;
|
||||
int tickssofar;
|
||||
u8 *buffer = skb->data;
|
||||
int i;
|
||||
|
||||
if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */
|
||||
tickssofar = jiffies - dev_trans_start(dev);
|
||||
if (tickssofar < HZ/20)
|
||||
return NETDEV_TX_BUSY;
|
||||
/* else */
|
||||
printk(KERN_WARNING "%s: transmit timed out (%d), %s?\n", dev->name, tickssofar, "network cable problem");
|
||||
/* Restart the adapter. */
|
||||
spin_lock_irqsave(&de600_lock, flags);
|
||||
if (adapter_init(dev)) {
|
||||
spin_unlock_irqrestore(&de600_lock, flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
spin_unlock_irqrestore(&de600_lock, flags);
|
||||
}
|
||||
|
||||
/* Start real output */
|
||||
pr_debug("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages);
|
||||
|
||||
if ((len = skb->len) < RUNT)
|
||||
len = RUNT;
|
||||
|
||||
spin_lock_irqsave(&de600_lock, flags);
|
||||
select_nic();
|
||||
tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len;
|
||||
tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */
|
||||
|
||||
if(check_lost)
|
||||
{
|
||||
/* This costs about 40 instructions per packet... */
|
||||
de600_setup_address(NODE_ADDRESS, RW_ADDR);
|
||||
de600_read_byte(READ_DATA, dev);
|
||||
if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) {
|
||||
if (adapter_init(dev)) {
|
||||
spin_unlock_irqrestore(&de600_lock, flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
de600_setup_address(transmit_from, RW_ADDR);
|
||||
for (i = 0; i < skb->len ; ++i, ++buffer)
|
||||
de600_put_byte(*buffer);
|
||||
for (; i < len; ++i)
|
||||
de600_put_byte(0);
|
||||
|
||||
if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */
|
||||
dev->trans_start = jiffies;
|
||||
netif_start_queue(dev); /* allow more packets into adapter */
|
||||
/* Send page and generate a faked interrupt */
|
||||
de600_setup_address(transmit_from, TX_ADDR);
|
||||
de600_put_command(TX_ENABLE);
|
||||
}
|
||||
else {
|
||||
if (free_tx_pages)
|
||||
netif_start_queue(dev);
|
||||
else
|
||||
netif_stop_queue(dev);
|
||||
select_prn();
|
||||
}
|
||||
spin_unlock_irqrestore(&de600_lock, flags);
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The typical workload of the driver:
|
||||
* Handle the network interface interrupts.
|
||||
*/
|
||||
|
||||
static irqreturn_t de600_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
u8 irq_status;
|
||||
int retrig = 0;
|
||||
int boguscount = 0;
|
||||
|
||||
spin_lock(&de600_lock);
|
||||
|
||||
select_nic();
|
||||
irq_status = de600_read_status(dev);
|
||||
|
||||
do {
|
||||
pr_debug("de600_interrupt (%02X)\n", irq_status);
|
||||
|
||||
if (irq_status & RX_GOOD)
|
||||
de600_rx_intr(dev);
|
||||
else if (!(irq_status & RX_BUSY))
|
||||
de600_put_command(RX_ENABLE);
|
||||
|
||||
/* Any transmission in progress? */
|
||||
if (free_tx_pages < TX_PAGES)
|
||||
retrig = de600_tx_intr(dev, irq_status);
|
||||
else
|
||||
retrig = 0;
|
||||
|
||||
irq_status = de600_read_status(dev);
|
||||
} while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) );
|
||||
/*
|
||||
* Yeah, it _looks_ like busy waiting, smells like busy waiting
|
||||
* and I know it's not PC, but please, it will only occur once
|
||||
* in a while and then only for a loop or so (< 1ms for sure!)
|
||||
*/
|
||||
|
||||
/* Enable adapter interrupts */
|
||||
select_prn();
|
||||
if (retrig)
|
||||
trigger_interrupt(dev);
|
||||
spin_unlock(&de600_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int de600_tx_intr(struct net_device *dev, int irq_status)
|
||||
{
|
||||
/*
|
||||
* Returns 1 if tx still not done
|
||||
*/
|
||||
|
||||
/* Check if current transmission is done yet */
|
||||
if (irq_status & TX_BUSY)
|
||||
return 1; /* tx not done, try again */
|
||||
|
||||
/* else */
|
||||
/* If last transmission OK then bump fifo index */
|
||||
if (!(irq_status & TX_FAILED16)) {
|
||||
tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES;
|
||||
++free_tx_pages;
|
||||
dev->stats.tx_packets++;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
/* More to send, or resend last packet? */
|
||||
if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
|
||||
dev->trans_start = jiffies;
|
||||
de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR);
|
||||
de600_put_command(TX_ENABLE);
|
||||
return 1;
|
||||
}
|
||||
/* else */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a good packet, get it out of the adapter.
|
||||
*/
|
||||
static void de600_rx_intr(struct net_device *dev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int i;
|
||||
int read_from;
|
||||
int size;
|
||||
unsigned char *buffer;
|
||||
|
||||
/* Get size of received packet */
|
||||
size = de600_read_byte(RX_LEN, dev); /* low byte */
|
||||
size += (de600_read_byte(RX_LEN, dev) << 8); /* high byte */
|
||||
size -= 4; /* Ignore trailing 4 CRC-bytes */
|
||||
|
||||
/* Tell adapter where to store next incoming packet, enable receiver */
|
||||
read_from = rx_page_adr();
|
||||
next_rx_page();
|
||||
de600_put_command(RX_ENABLE);
|
||||
|
||||
if ((size < 32) || (size > 1535)) {
|
||||
printk(KERN_WARNING "%s: Bogus packet size %d.\n", dev->name, size);
|
||||
if (size > 10000)
|
||||
adapter_init(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
skb = netdev_alloc_skb(dev, size + 2);
|
||||
if (skb == NULL) {
|
||||
printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, size);
|
||||
return;
|
||||
}
|
||||
/* else */
|
||||
|
||||
skb_reserve(skb,2); /* Align */
|
||||
|
||||
/* 'skb->data' points to the start of sk_buff data area. */
|
||||
buffer = skb_put(skb,size);
|
||||
|
||||
/* copy the packet into the buffer */
|
||||
de600_setup_address(read_from, RW_ADDR);
|
||||
for (i = size; i > 0; --i, ++buffer)
|
||||
*buffer = de600_read_byte(READ_DATA, dev);
|
||||
|
||||
skb->protocol=eth_type_trans(skb,dev);
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
/* update stats */
|
||||
dev->stats.rx_packets++; /* count all receives */
|
||||
dev->stats.rx_bytes += size; /* count all received bytes */
|
||||
|
||||
/*
|
||||
* If any worth-while packets have been received, netif_rx()
|
||||
* will work on them when we get to the tasklets.
|
||||
*/
|
||||
}
|
||||
|
||||
static const struct net_device_ops de600_netdev_ops = {
|
||||
.ndo_open = de600_open,
|
||||
.ndo_stop = de600_close,
|
||||
.ndo_start_xmit = de600_start_xmit,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
|
||||
static struct net_device * __init de600_probe(void)
|
||||
{
|
||||
int i;
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
dev = alloc_etherdev(0);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
||||
if (!request_region(DE600_IO, 3, "de600")) {
|
||||
printk(KERN_WARNING "DE600: port 0x%x busy\n", DE600_IO);
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: D-Link DE-600 pocket adapter", dev->name);
|
||||
/* Alpha testers must have the version number to report bugs. */
|
||||
pr_debug("%s", version);
|
||||
|
||||
/* probe for adapter */
|
||||
err = -ENODEV;
|
||||
rx_page = 0;
|
||||
select_nic();
|
||||
(void)de600_read_status(dev);
|
||||
de600_put_command(RESET);
|
||||
de600_put_command(STOP_RESET);
|
||||
if (de600_read_status(dev) & 0xf0) {
|
||||
printk(": not at I/O %#3x.\n", DATA_PORT);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe we found one,
|
||||
* have to check if it is a D-Link DE-600 adapter...
|
||||
*/
|
||||
|
||||
/* Get the adapter ethernet address from the ROM */
|
||||
de600_setup_address(NODE_ADDRESS, RW_ADDR);
|
||||
for (i = 0; i < ETH_ALEN; i++) {
|
||||
dev->dev_addr[i] = de600_read_byte(READ_DATA, dev);
|
||||
dev->broadcast[i] = 0xff;
|
||||
}
|
||||
|
||||
/* Check magic code */
|
||||
if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
|
||||
/* OK, install real address */
|
||||
dev->dev_addr[0] = 0x00;
|
||||
dev->dev_addr[1] = 0x80;
|
||||
dev->dev_addr[2] = 0xc8;
|
||||
dev->dev_addr[3] &= 0x0f;
|
||||
dev->dev_addr[3] |= 0x70;
|
||||
} else {
|
||||
printk(" not identified in the printer port\n");
|
||||
goto out1;
|
||||
}
|
||||
|
||||
printk(", Ethernet Address: %pM\n", dev->dev_addr);
|
||||
|
||||
dev->netdev_ops = &de600_netdev_ops;
|
||||
|
||||
dev->flags&=~IFF_MULTICAST;
|
||||
|
||||
select_prn();
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out1;
|
||||
|
||||
return dev;
|
||||
|
||||
out1:
|
||||
release_region(DE600_IO, 3);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int adapter_init(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
select_nic();
|
||||
rx_page = 0; /* used by RESET */
|
||||
de600_put_command(RESET);
|
||||
de600_put_command(STOP_RESET);
|
||||
|
||||
/* Check if it is still there... */
|
||||
/* Get the some bytes of the adapter ethernet address from the ROM */
|
||||
de600_setup_address(NODE_ADDRESS, RW_ADDR);
|
||||
de600_read_byte(READ_DATA, dev);
|
||||
if ((de600_read_byte(READ_DATA, dev) != 0xde) ||
|
||||
(de600_read_byte(READ_DATA, dev) != 0x15)) {
|
||||
/* was: if (de600_read_status(dev) & 0xf0) { */
|
||||
printk("Something has happened to the DE-600! Please check it and do a new ifconfig!\n");
|
||||
/* Goodbye, cruel world... */
|
||||
dev->flags &= ~IFF_UP;
|
||||
de600_close(dev);
|
||||
was_down = 1;
|
||||
netif_stop_queue(dev); /* Transmit busy... */
|
||||
return 1; /* failed */
|
||||
}
|
||||
|
||||
if (was_down) {
|
||||
printk(KERN_INFO "%s: Thanks, I feel much better now!\n", dev->name);
|
||||
was_down = 0;
|
||||
}
|
||||
|
||||
tx_fifo_in = 0;
|
||||
tx_fifo_out = 0;
|
||||
free_tx_pages = TX_PAGES;
|
||||
|
||||
|
||||
/* set the ether address. */
|
||||
de600_setup_address(NODE_ADDRESS, RW_ADDR);
|
||||
for (i = 0; i < ETH_ALEN; i++)
|
||||
de600_put_byte(dev->dev_addr[i]);
|
||||
|
||||
/* where to start saving incoming packets */
|
||||
rx_page = RX_BP | RX_BASE_PAGE;
|
||||
de600_setup_address(MEM_4K, RW_ADDR);
|
||||
/* Enable receiver */
|
||||
de600_put_command(RX_ENABLE);
|
||||
select_prn();
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0; /* OK */
|
||||
}
|
||||
|
||||
static struct net_device *de600_dev;
|
||||
|
||||
static int __init de600_init(void)
|
||||
{
|
||||
de600_dev = de600_probe();
|
||||
if (IS_ERR(de600_dev))
|
||||
return PTR_ERR(de600_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit de600_exit(void)
|
||||
{
|
||||
unregister_netdev(de600_dev);
|
||||
release_region(DE600_IO, 3);
|
||||
free_netdev(de600_dev);
|
||||
}
|
||||
|
||||
module_init(de600_init);
|
||||
module_exit(de600_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -1,168 +0,0 @@
|
||||
/**************************************************
|
||||
* *
|
||||
* Definition of D-Link Ethernet Pocket adapter *
|
||||
* *
|
||||
**************************************************/
|
||||
/*
|
||||
* D-Link Ethernet pocket adapter ports
|
||||
*/
|
||||
/*
|
||||
* OK, so I'm cheating, but there are an awful lot of
|
||||
* reads and writes in order to get anything in and out
|
||||
* of the DE-600 with 4 bits at a time in the parallel port,
|
||||
* so every saved instruction really helps :-)
|
||||
*/
|
||||
|
||||
#ifndef DE600_IO
|
||||
#define DE600_IO 0x378
|
||||
#endif
|
||||
|
||||
#define DATA_PORT (DE600_IO)
|
||||
#define STATUS_PORT (DE600_IO + 1)
|
||||
#define COMMAND_PORT (DE600_IO + 2)
|
||||
|
||||
#ifndef DE600_IRQ
|
||||
#define DE600_IRQ 7
|
||||
#endif
|
||||
/*
|
||||
* It really should look like this, and autoprobing as well...
|
||||
*
|
||||
#define DATA_PORT (dev->base_addr + 0)
|
||||
#define STATUS_PORT (dev->base_addr + 1)
|
||||
#define COMMAND_PORT (dev->base_addr + 2)
|
||||
#define DE600_IRQ dev->irq
|
||||
*/
|
||||
|
||||
/*
|
||||
* D-Link COMMAND_PORT commands
|
||||
*/
|
||||
#define SELECT_NIC 0x04 /* select Network Interface Card */
|
||||
#define SELECT_PRN 0x1c /* select Printer */
|
||||
#define NML_PRN 0xec /* normal Printer situation */
|
||||
#define IRQEN 0x10 /* enable IRQ line */
|
||||
|
||||
/*
|
||||
* D-Link STATUS_PORT
|
||||
*/
|
||||
#define RX_BUSY 0x80
|
||||
#define RX_GOOD 0x40
|
||||
#define TX_FAILED16 0x10
|
||||
#define TX_BUSY 0x08
|
||||
|
||||
/*
|
||||
* D-Link DATA_PORT commands
|
||||
* command in low 4 bits
|
||||
* data in high 4 bits
|
||||
* select current data nibble with HI_NIBBLE bit
|
||||
*/
|
||||
#define WRITE_DATA 0x00 /* write memory */
|
||||
#define READ_DATA 0x01 /* read memory */
|
||||
#define STATUS 0x02 /* read status register */
|
||||
#define COMMAND 0x03 /* write command register (see COMMAND below) */
|
||||
#define NULL_COMMAND 0x04 /* null command */
|
||||
#define RX_LEN 0x05 /* read received packet length */
|
||||
#define TX_ADDR 0x06 /* set adapter transmit memory address */
|
||||
#define RW_ADDR 0x07 /* set adapter read/write memory address */
|
||||
#define HI_NIBBLE 0x08 /* read/write the high nibble of data,
|
||||
or-ed with rest of command */
|
||||
|
||||
/*
|
||||
* command register, accessed through DATA_PORT with low bits = COMMAND
|
||||
*/
|
||||
#define RX_ALL 0x01 /* PROMISCUOUS */
|
||||
#define RX_BP 0x02 /* default: BROADCAST & PHYSICAL ADDRESS */
|
||||
#define RX_MBP 0x03 /* MULTICAST, BROADCAST & PHYSICAL ADDRESS */
|
||||
|
||||
#define TX_ENABLE 0x04 /* bit 2 */
|
||||
#define RX_ENABLE 0x08 /* bit 3 */
|
||||
|
||||
#define RESET 0x80 /* set bit 7 high */
|
||||
#define STOP_RESET 0x00 /* set bit 7 low */
|
||||
|
||||
/*
|
||||
* data to command register
|
||||
* (high 4 bits in write to DATA_PORT)
|
||||
*/
|
||||
#define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */
|
||||
#define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */
|
||||
#define FLIP_IRQ 0x40 /* bit 6 */
|
||||
|
||||
/*
|
||||
* D-Link adapter internal memory:
|
||||
*
|
||||
* 0-2K 1:st transmit page (send from pointer up to 2K)
|
||||
* 2-4K 2:nd transmit page (send from pointer up to 4K)
|
||||
*
|
||||
* 4-6K 1:st receive page (data from 4K upwards)
|
||||
* 6-8K 2:nd receive page (data from 6K upwards)
|
||||
*
|
||||
* 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
|
||||
*/
|
||||
#define MEM_2K 0x0800 /* 2048 */
|
||||
#define MEM_4K 0x1000 /* 4096 */
|
||||
#define MEM_6K 0x1800 /* 6144 */
|
||||
#define NODE_ADDRESS 0x2000 /* 8192 */
|
||||
|
||||
#define RUNT 60 /* Too small Ethernet packet */
|
||||
|
||||
/**************************************************
|
||||
* *
|
||||
* End of definition *
|
||||
* *
|
||||
**************************************************/
|
||||
|
||||
/*
|
||||
* Index to functions, as function prototypes.
|
||||
*/
|
||||
/* Routines used internally. (See "convenience macros") */
|
||||
static u8 de600_read_status(struct net_device *dev);
|
||||
static u8 de600_read_byte(unsigned char type, struct net_device *dev);
|
||||
|
||||
/* Put in the device structure. */
|
||||
static int de600_open(struct net_device *dev);
|
||||
static int de600_close(struct net_device *dev);
|
||||
static int de600_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
|
||||
/* Dispatch from interrupts. */
|
||||
static irqreturn_t de600_interrupt(int irq, void *dev_id);
|
||||
static int de600_tx_intr(struct net_device *dev, int irq_status);
|
||||
static void de600_rx_intr(struct net_device *dev);
|
||||
|
||||
/* Initialization */
|
||||
static void trigger_interrupt(struct net_device *dev);
|
||||
static int adapter_init(struct net_device *dev);
|
||||
|
||||
/*
|
||||
* Convenience macros/functions for D-Link adapter
|
||||
*/
|
||||
|
||||
#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN
|
||||
#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN
|
||||
|
||||
/* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */
|
||||
#define de600_put_byte(data) ( \
|
||||
outb_p(((data) << 4) | WRITE_DATA , DATA_PORT), \
|
||||
outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT))
|
||||
|
||||
/*
|
||||
* The first two outb_p()'s below could perhaps be deleted if there
|
||||
* would be more delay in the last two. Not certain about it yet...
|
||||
*/
|
||||
#define de600_put_command(cmd) ( \
|
||||
outb_p(( rx_page << 4) | COMMAND , DATA_PORT), \
|
||||
outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \
|
||||
outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT), \
|
||||
outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT))
|
||||
|
||||
#define de600_setup_address(addr,type) ( \
|
||||
outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT), \
|
||||
outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT), \
|
||||
outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT), \
|
||||
outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT))
|
||||
|
||||
#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K))
|
||||
|
||||
/* Flip bit, only 2 pages */
|
||||
#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT)
|
||||
|
||||
#define tx_page_adr(a) (((a) + 1) * MEM_2K)
|
@ -1,987 +0,0 @@
|
||||
/*
|
||||
* de620.c $Revision: 1.40 $ BETA
|
||||
*
|
||||
*
|
||||
* Linux driver for the D-Link DE-620 Ethernet pocket adapter.
|
||||
*
|
||||
* Portions (C) Copyright 1993, 1994 by Bjorn Ekwall <bj0rn@blox.se>
|
||||
*
|
||||
* Based on adapter information gathered from DOS packetdriver
|
||||
* sources from D-Link Inc: (Special thanks to Henry Ngai of D-Link.)
|
||||
* Portions (C) Copyright D-Link SYSTEM Inc. 1991, 1992
|
||||
* Copyright, 1988, Russell Nelson, Crynwr Software
|
||||
*
|
||||
* Adapted to the sample network driver core for linux,
|
||||
* written by: Donald Becker <becker@super.org>
|
||||
* (Now at <becker@scyld.com>)
|
||||
*
|
||||
* Valuable assistance from:
|
||||
* J. Joshua Kopper <kopper@rtsg.mot.com>
|
||||
* Olav Kvittem <Olav.Kvittem@uninett.no>
|
||||
* Germano Caronni <caronni@nessie.cs.id.ethz.ch>
|
||||
* Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
|
||||
*
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* 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, 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
static const char version[] =
|
||||
"de620.c: $Revision: 1.40 $, Bjorn Ekwall <bj0rn@blox.se>\n";
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* "Tuning" section.
|
||||
*
|
||||
* Compile-time options: (see below for descriptions)
|
||||
* -DDE620_IO=0x378 (lpt1)
|
||||
* -DDE620_IRQ=7 (lpt1)
|
||||
* -DSHUTDOWN_WHEN_LOST
|
||||
* -DCOUNT_LOOPS
|
||||
* -DLOWSPEED
|
||||
* -DREAD_DELAY
|
||||
* -DWRITE_DELAY
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver assumes that the printer port is a "normal",
|
||||
* dumb, uni-directional port!
|
||||
* If your port is "fancy" in any way, please try to set it to "normal"
|
||||
* with your BIOS setup. I have no access to machines with bi-directional
|
||||
* ports, so I can't test such a driver :-(
|
||||
* (Yes, I _know_ it is possible to use DE620 with bidirectional ports...)
|
||||
*
|
||||
* There are some clones of DE620 out there, with different names.
|
||||
* If the current driver does not recognize a clone, try to change
|
||||
* the following #define to:
|
||||
*
|
||||
* #define DE620_CLONE 1
|
||||
*/
|
||||
#define DE620_CLONE 0
|
||||
|
||||
/*
|
||||
* If the adapter has problems with high speeds, enable this #define
|
||||
* otherwise full printerport speed will be attempted.
|
||||
*
|
||||
* You can tune the READ_DELAY/WRITE_DELAY below if you enable LOWSPEED
|
||||
*
|
||||
#define LOWSPEED
|
||||
*/
|
||||
|
||||
#ifndef READ_DELAY
|
||||
#define READ_DELAY 100 /* adapter internal read delay in 100ns units */
|
||||
#endif
|
||||
|
||||
#ifndef WRITE_DELAY
|
||||
#define WRITE_DELAY 100 /* adapter internal write delay in 100ns units */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable this #define if you want the adapter to do a "ifconfig down" on
|
||||
* itself when we have detected that something is possibly wrong with it.
|
||||
* The default behaviour is to retry with "adapter_init()" until success.
|
||||
* This should be used for debugging purposes only.
|
||||
*
|
||||
#define SHUTDOWN_WHEN_LOST
|
||||
*/
|
||||
|
||||
#ifdef LOWSPEED
|
||||
/*
|
||||
* Enable this #define if you want to see debugging output that show how long
|
||||
* we have to wait before the DE-620 is ready for the next read/write/command.
|
||||
*
|
||||
#define COUNT_LOOPS
|
||||
*/
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Constant definitions for the DE-620 registers, commands and bits */
|
||||
#include "de620.h"
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* Definition of D-Link DE-620 Ethernet Pocket adapter *
|
||||
* See also "de620.h" *
|
||||
* *
|
||||
*******************************************************/
|
||||
#ifndef DE620_IO /* Compile-time configurable */
|
||||
#define DE620_IO 0x378
|
||||
#endif
|
||||
|
||||
#ifndef DE620_IRQ /* Compile-time configurable */
|
||||
#define DE620_IRQ 7
|
||||
#endif
|
||||
|
||||
#define DATA_PORT (dev->base_addr)
|
||||
#define STATUS_PORT (dev->base_addr + 1)
|
||||
#define COMMAND_PORT (dev->base_addr + 2)
|
||||
|
||||
#define RUNT 60 /* Too small Ethernet packet */
|
||||
#define GIANT 1514 /* largest legal size packet, no fcs */
|
||||
|
||||
/*
|
||||
* Force media with insmod:
|
||||
* insmod de620.o bnc=1
|
||||
* or
|
||||
* insmod de620.o utp=1
|
||||
*
|
||||
* Force io and/or irq with insmod:
|
||||
* insmod de620.o io=0x378 irq=7
|
||||
*
|
||||
* Make a clone skip the Ethernet-address range check:
|
||||
* insmod de620.o clone=1
|
||||
*/
|
||||
static int bnc;
|
||||
static int utp;
|
||||
static int io = DE620_IO;
|
||||
static int irq = DE620_IRQ;
|
||||
static int clone = DE620_CLONE;
|
||||
|
||||
static spinlock_t de620_lock;
|
||||
|
||||
module_param(bnc, int, 0);
|
||||
module_param(utp, int, 0);
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
module_param(clone, int, 0);
|
||||
MODULE_PARM_DESC(bnc, "DE-620 set BNC medium (0-1)");
|
||||
MODULE_PARM_DESC(utp, "DE-620 set UTP medium (0-1)");
|
||||
MODULE_PARM_DESC(io, "DE-620 I/O base address,required");
|
||||
MODULE_PARM_DESC(irq, "DE-620 IRQ number,required");
|
||||
MODULE_PARM_DESC(clone, "Check also for non-D-Link DE-620 clones (0-1)");
|
||||
|
||||
/***********************************************
|
||||
* *
|
||||
* Index to functions, as function prototypes. *
|
||||
* *
|
||||
***********************************************/
|
||||
|
||||
/*
|
||||
* Routines used internally. (See also "convenience macros.. below")
|
||||
*/
|
||||
|
||||
/* Put in the device structure. */
|
||||
static int de620_open(struct net_device *);
|
||||
static int de620_close(struct net_device *);
|
||||
static void de620_set_multicast_list(struct net_device *);
|
||||
static int de620_start_xmit(struct sk_buff *, struct net_device *);
|
||||
|
||||
/* Dispatch from interrupts. */
|
||||
static irqreturn_t de620_interrupt(int, void *);
|
||||
static int de620_rx_intr(struct net_device *);
|
||||
|
||||
/* Initialization */
|
||||
static int adapter_init(struct net_device *);
|
||||
static int read_eeprom(struct net_device *);
|
||||
|
||||
|
||||
/*
|
||||
* D-Link driver variables:
|
||||
*/
|
||||
#define SCR_DEF NIBBLEMODE |INTON | SLEEP | AUTOTX
|
||||
#define TCR_DEF RXPB /* not used: | TXSUCINT | T16INT */
|
||||
#define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */
|
||||
#define DEF_NIC_CMD IRQEN | ICEN | DS1
|
||||
|
||||
static volatile byte NIC_Cmd;
|
||||
static volatile byte next_rx_page;
|
||||
static byte first_rx_page;
|
||||
static byte last_rx_page;
|
||||
static byte EIPRegister;
|
||||
|
||||
static struct nic {
|
||||
byte NodeID[6];
|
||||
byte RAM_Size;
|
||||
byte Model;
|
||||
byte Media;
|
||||
byte SCR;
|
||||
} nic_data;
|
||||
|
||||
/**********************************************************
|
||||
* *
|
||||
* Convenience macros/functions for D-Link DE-620 adapter *
|
||||
* *
|
||||
**********************************************************/
|
||||
#define de620_tx_buffs(dd) (inb(STATUS_PORT) & (TXBF0 | TXBF1))
|
||||
#define de620_flip_ds(dd) NIC_Cmd ^= DS0 | DS1; outb(NIC_Cmd, COMMAND_PORT);
|
||||
|
||||
/* Check for ready-status, and return a nibble (high 4 bits) for data input */
|
||||
#ifdef COUNT_LOOPS
|
||||
static int tot_cnt;
|
||||
#endif
|
||||
static inline byte
|
||||
de620_ready(struct net_device *dev)
|
||||
{
|
||||
byte value;
|
||||
register short int cnt = 0;
|
||||
|
||||
while ((((value = inb(STATUS_PORT)) & READY) == 0) && (cnt <= 1000))
|
||||
++cnt;
|
||||
|
||||
#ifdef COUNT_LOOPS
|
||||
tot_cnt += cnt;
|
||||
#endif
|
||||
return value & 0xf0; /* nibble */
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_send_command(struct net_device *dev, byte cmd)
|
||||
{
|
||||
de620_ready(dev);
|
||||
if (cmd == W_DUMMY)
|
||||
outb(NIC_Cmd, COMMAND_PORT);
|
||||
|
||||
outb(cmd, DATA_PORT);
|
||||
|
||||
outb(NIC_Cmd ^ CS0, COMMAND_PORT);
|
||||
de620_ready(dev);
|
||||
outb(NIC_Cmd, COMMAND_PORT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_put_byte(struct net_device *dev, byte value)
|
||||
{
|
||||
/* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
|
||||
de620_ready(dev);
|
||||
outb(value, DATA_PORT);
|
||||
de620_flip_ds(dev);
|
||||
}
|
||||
|
||||
static inline byte
|
||||
de620_read_byte(struct net_device *dev)
|
||||
{
|
||||
byte value;
|
||||
|
||||
/* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
|
||||
value = de620_ready(dev); /* High nibble */
|
||||
de620_flip_ds(dev);
|
||||
value |= de620_ready(dev) >> 4; /* Low nibble */
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_write_block(struct net_device *dev, byte *buffer, int count, int pad)
|
||||
{
|
||||
#ifndef LOWSPEED
|
||||
byte uflip = NIC_Cmd ^ (DS0 | DS1);
|
||||
byte dflip = NIC_Cmd;
|
||||
#else /* LOWSPEED */
|
||||
#ifdef COUNT_LOOPS
|
||||
int bytes = count;
|
||||
#endif /* COUNT_LOOPS */
|
||||
#endif /* LOWSPEED */
|
||||
|
||||
#ifdef LOWSPEED
|
||||
#ifdef COUNT_LOOPS
|
||||
tot_cnt = 0;
|
||||
#endif /* COUNT_LOOPS */
|
||||
/* No further optimization useful, the limit is in the adapter. */
|
||||
for ( ; count > 0; --count, ++buffer) {
|
||||
de620_put_byte(dev,*buffer);
|
||||
}
|
||||
for ( count = pad ; count > 0; --count, ++buffer) {
|
||||
de620_put_byte(dev, 0);
|
||||
}
|
||||
de620_send_command(dev,W_DUMMY);
|
||||
#ifdef COUNT_LOOPS
|
||||
/* trial debug output: loops per byte in de620_ready() */
|
||||
printk("WRITE(%d)\n", tot_cnt/((bytes?bytes:1)));
|
||||
#endif /* COUNT_LOOPS */
|
||||
#else /* not LOWSPEED */
|
||||
for ( ; count > 0; count -=2) {
|
||||
outb(*buffer++, DATA_PORT);
|
||||
outb(uflip, COMMAND_PORT);
|
||||
outb(*buffer++, DATA_PORT);
|
||||
outb(dflip, COMMAND_PORT);
|
||||
}
|
||||
de620_send_command(dev,W_DUMMY);
|
||||
#endif /* LOWSPEED */
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_read_block(struct net_device *dev, byte *data, int count)
|
||||
{
|
||||
#ifndef LOWSPEED
|
||||
byte value;
|
||||
byte uflip = NIC_Cmd ^ (DS0 | DS1);
|
||||
byte dflip = NIC_Cmd;
|
||||
#else /* LOWSPEED */
|
||||
#ifdef COUNT_LOOPS
|
||||
int bytes = count;
|
||||
|
||||
tot_cnt = 0;
|
||||
#endif /* COUNT_LOOPS */
|
||||
#endif /* LOWSPEED */
|
||||
|
||||
#ifdef LOWSPEED
|
||||
/* No further optimization useful, the limit is in the adapter. */
|
||||
while (count-- > 0) {
|
||||
*data++ = de620_read_byte(dev);
|
||||
de620_flip_ds(dev);
|
||||
}
|
||||
#ifdef COUNT_LOOPS
|
||||
/* trial debug output: loops per byte in de620_ready() */
|
||||
printk("READ(%d)\n", tot_cnt/(2*(bytes?bytes:1)));
|
||||
#endif /* COUNT_LOOPS */
|
||||
#else /* not LOWSPEED */
|
||||
while (count-- > 0) {
|
||||
value = inb(STATUS_PORT) & 0xf0; /* High nibble */
|
||||
outb(uflip, COMMAND_PORT);
|
||||
*data++ = value | inb(STATUS_PORT) >> 4; /* Low nibble */
|
||||
outb(dflip , COMMAND_PORT);
|
||||
}
|
||||
#endif /* LOWSPEED */
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_set_delay(struct net_device *dev)
|
||||
{
|
||||
de620_ready(dev);
|
||||
outb(W_DFR, DATA_PORT);
|
||||
outb(NIC_Cmd ^ CS0, COMMAND_PORT);
|
||||
|
||||
de620_ready(dev);
|
||||
#ifdef LOWSPEED
|
||||
outb(WRITE_DELAY, DATA_PORT);
|
||||
#else
|
||||
outb(0, DATA_PORT);
|
||||
#endif
|
||||
de620_flip_ds(dev);
|
||||
|
||||
de620_ready(dev);
|
||||
#ifdef LOWSPEED
|
||||
outb(READ_DELAY, DATA_PORT);
|
||||
#else
|
||||
outb(0, DATA_PORT);
|
||||
#endif
|
||||
de620_flip_ds(dev);
|
||||
}
|
||||
|
||||
static inline void
|
||||
de620_set_register(struct net_device *dev, byte reg, byte value)
|
||||
{
|
||||
de620_ready(dev);
|
||||
outb(reg, DATA_PORT);
|
||||
outb(NIC_Cmd ^ CS0, COMMAND_PORT);
|
||||
|
||||
de620_put_byte(dev, value);
|
||||
}
|
||||
|
||||
static inline byte
|
||||
de620_get_register(struct net_device *dev, byte reg)
|
||||
{
|
||||
byte value;
|
||||
|
||||
de620_send_command(dev,reg);
|
||||
value = de620_read_byte(dev);
|
||||
de620_send_command(dev,W_DUMMY);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Open/initialize the board.
|
||||
*
|
||||
* 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 a non-reboot way to recover if something goes wrong.
|
||||
*
|
||||
*/
|
||||
static int de620_open(struct net_device *dev)
|
||||
{
|
||||
int ret = request_irq(dev->irq, de620_interrupt, 0, dev->name, dev);
|
||||
if (ret) {
|
||||
printk (KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (adapter_init(dev)) {
|
||||
ret = -EIO;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_irq(dev->irq, dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
*
|
||||
* The inverse routine to de620_open().
|
||||
*
|
||||
*/
|
||||
|
||||
static int de620_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
/* disable recv */
|
||||
de620_set_register(dev, W_TCR, RXOFF);
|
||||
free_irq(dev->irq, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Set or clear the multicast filter for this adaptor.
|
||||
* (no real multicast implemented for the DE-620, but she can be promiscuous...)
|
||||
*
|
||||
*/
|
||||
|
||||
static void de620_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
if (!netdev_mc_empty(dev) || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
|
||||
{ /* Enable promiscuous mode */
|
||||
de620_set_register(dev, W_TCR, (TCR_DEF & ~RXPBM) | RXALL);
|
||||
}
|
||||
else
|
||||
{ /* Disable promiscuous mode, use normal mode */
|
||||
de620_set_register(dev, W_TCR, TCR_DEF);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
*
|
||||
* Handle timeouts on transmit
|
||||
*/
|
||||
|
||||
static void de620_timeout(struct net_device *dev)
|
||||
{
|
||||
printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, "network cable problem");
|
||||
/* Restart the adapter. */
|
||||
if (!adapter_init(dev)) /* maybe close it */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
*
|
||||
* Copy a buffer to the adapter transmit page memory.
|
||||
* Start sending.
|
||||
*/
|
||||
static int de620_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int len;
|
||||
byte *buffer = skb->data;
|
||||
byte using_txbuf;
|
||||
|
||||
using_txbuf = de620_tx_buffs(dev); /* Peek at the adapter */
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
|
||||
if ((len = skb->len) < RUNT)
|
||||
len = RUNT;
|
||||
if (len & 1) /* send an even number of bytes */
|
||||
++len;
|
||||
|
||||
/* Start real output */
|
||||
|
||||
spin_lock_irqsave(&de620_lock, flags);
|
||||
pr_debug("de620_start_xmit: len=%d, bufs 0x%02x\n",
|
||||
(int)skb->len, using_txbuf);
|
||||
|
||||
/* select a free tx buffer. if there is one... */
|
||||
switch (using_txbuf) {
|
||||
default: /* both are free: use TXBF0 */
|
||||
case TXBF1: /* use TXBF0 */
|
||||
de620_send_command(dev,W_CR | RW0);
|
||||
using_txbuf |= TXBF0;
|
||||
break;
|
||||
|
||||
case TXBF0: /* use TXBF1 */
|
||||
de620_send_command(dev,W_CR | RW1);
|
||||
using_txbuf |= TXBF1;
|
||||
break;
|
||||
|
||||
case (TXBF0 | TXBF1): /* NONE!!! */
|
||||
printk(KERN_WARNING "%s: No tx-buffer available!\n", dev->name);
|
||||
spin_unlock_irqrestore(&de620_lock, flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
de620_write_block(dev, buffer, skb->len, len-skb->len);
|
||||
|
||||
if(!(using_txbuf == (TXBF0 | TXBF1)))
|
||||
netif_wake_queue(dev);
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
spin_unlock_irqrestore(&de620_lock, flags);
|
||||
dev_kfree_skb (skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
*
|
||||
* Handle the network interface interrupts.
|
||||
*
|
||||
*/
|
||||
static irqreturn_t
|
||||
de620_interrupt(int irq_in, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
byte irq_status;
|
||||
int bogus_count = 0;
|
||||
int again = 0;
|
||||
|
||||
spin_lock(&de620_lock);
|
||||
|
||||
/* Read the status register (_not_ the status port) */
|
||||
irq_status = de620_get_register(dev, R_STS);
|
||||
|
||||
pr_debug("de620_interrupt (%2.2X)\n", irq_status);
|
||||
|
||||
if (irq_status & RXGOOD) {
|
||||
do {
|
||||
again = de620_rx_intr(dev);
|
||||
pr_debug("again=%d\n", again);
|
||||
}
|
||||
while (again && (++bogus_count < 100));
|
||||
}
|
||||
|
||||
if(de620_tx_buffs(dev) != (TXBF0 | TXBF1))
|
||||
netif_wake_queue(dev);
|
||||
|
||||
spin_unlock(&de620_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
*
|
||||
* Get a packet from the adapter
|
||||
*
|
||||
* Send it "upstairs"
|
||||
*
|
||||
*/
|
||||
static int de620_rx_intr(struct net_device *dev)
|
||||
{
|
||||
struct header_buf {
|
||||
byte status;
|
||||
byte Rx_NextPage;
|
||||
unsigned short Rx_ByteCount;
|
||||
} header_buf;
|
||||
struct sk_buff *skb;
|
||||
int size;
|
||||
byte *buffer;
|
||||
byte pagelink;
|
||||
byte curr_page;
|
||||
|
||||
pr_debug("de620_rx_intr: next_rx_page = %d\n", next_rx_page);
|
||||
|
||||
/* Tell the adapter that we are going to read data, and from where */
|
||||
de620_send_command(dev, W_CR | RRN);
|
||||
de620_set_register(dev, W_RSA1, next_rx_page);
|
||||
de620_set_register(dev, W_RSA0, 0);
|
||||
|
||||
/* Deep breath, and away we goooooo */
|
||||
de620_read_block(dev, (byte *)&header_buf, sizeof(struct header_buf));
|
||||
pr_debug("page status=0x%02x, nextpage=%d, packetsize=%d\n",
|
||||
header_buf.status, header_buf.Rx_NextPage,
|
||||
header_buf.Rx_ByteCount);
|
||||
|
||||
/* Plausible page header? */
|
||||
pagelink = header_buf.Rx_NextPage;
|
||||
if ((pagelink < first_rx_page) || (last_rx_page < pagelink)) {
|
||||
/* Ouch... Forget it! Skip all and start afresh... */
|
||||
printk(KERN_WARNING "%s: Ring overrun? Restoring...\n", dev->name);
|
||||
/* You win some, you lose some. And sometimes plenty... */
|
||||
adapter_init(dev);
|
||||
netif_wake_queue(dev);
|
||||
dev->stats.rx_over_errors++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK, this look good, so far. Let's see if it's consistent... */
|
||||
/* Let's compute the start of the next packet, based on where we are */
|
||||
pagelink = next_rx_page +
|
||||
((header_buf.Rx_ByteCount + (4 - 1 + 0x100)) >> 8);
|
||||
|
||||
/* Are we going to wrap around the page counter? */
|
||||
if (pagelink > last_rx_page)
|
||||
pagelink -= (last_rx_page - first_rx_page + 1);
|
||||
|
||||
/* Is the _computed_ next page number equal to what the adapter says? */
|
||||
if (pagelink != header_buf.Rx_NextPage) {
|
||||
/* Naah, we'll skip this packet. Probably bogus data as well */
|
||||
printk(KERN_WARNING "%s: Page link out of sync! Restoring...\n", dev->name);
|
||||
next_rx_page = header_buf.Rx_NextPage; /* at least a try... */
|
||||
de620_send_command(dev, W_DUMMY);
|
||||
de620_set_register(dev, W_NPRF, next_rx_page);
|
||||
dev->stats.rx_over_errors++;
|
||||
return 0;
|
||||
}
|
||||
next_rx_page = pagelink;
|
||||
|
||||
size = header_buf.Rx_ByteCount - 4;
|
||||
if ((size < RUNT) || (GIANT < size)) {
|
||||
printk(KERN_WARNING "%s: Illegal packet size: %d!\n", dev->name, size);
|
||||
}
|
||||
else { /* Good packet? */
|
||||
skb = netdev_alloc_skb(dev, size + 2);
|
||||
if (skb == NULL) { /* Yeah, but no place to put it... */
|
||||
printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, size);
|
||||
dev->stats.rx_dropped++;
|
||||
}
|
||||
else { /* Yep! Go get it! */
|
||||
skb_reserve(skb,2); /* Align */
|
||||
/* skb->data points to the start of sk_buff data area */
|
||||
buffer = skb_put(skb,size);
|
||||
/* copy the packet into the buffer */
|
||||
de620_read_block(dev, buffer, size);
|
||||
pr_debug("Read %d bytes\n", size);
|
||||
skb->protocol=eth_type_trans(skb,dev);
|
||||
netif_rx(skb); /* deliver it "upstairs" */
|
||||
/* count all receives */
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's peek ahead to see if we have read the last current packet */
|
||||
/* NOTE! We're _not_ checking the 'EMPTY'-flag! This seems better... */
|
||||
curr_page = de620_get_register(dev, R_CPR);
|
||||
de620_set_register(dev, W_NPRF, next_rx_page);
|
||||
pr_debug("next_rx_page=%d CPR=%d\n", next_rx_page, curr_page);
|
||||
|
||||
return next_rx_page != curr_page; /* That was slightly tricky... */
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Reset the adapter to a known state
|
||||
*
|
||||
*/
|
||||
static int adapter_init(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
static int was_down;
|
||||
|
||||
if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */
|
||||
EIPRegister = NCTL0;
|
||||
if (nic_data.Media != 1)
|
||||
EIPRegister |= NIS0; /* not BNC */
|
||||
}
|
||||
else if (nic_data.Model == 2) { /* UTP */
|
||||
EIPRegister = NCTL0 | NIS0;
|
||||
}
|
||||
|
||||
if (utp)
|
||||
EIPRegister = NCTL0 | NIS0;
|
||||
if (bnc)
|
||||
EIPRegister = NCTL0;
|
||||
|
||||
de620_send_command(dev, W_CR | RNOP | CLEAR);
|
||||
de620_send_command(dev, W_CR | RNOP);
|
||||
|
||||
de620_set_register(dev, W_SCR, SCR_DEF);
|
||||
/* disable recv to wait init */
|
||||
de620_set_register(dev, W_TCR, RXOFF);
|
||||
|
||||
/* Set the node ID in the adapter */
|
||||
for (i = 0; i < 6; ++i) { /* W_PARn = 0xaa + n */
|
||||
de620_set_register(dev, W_PAR0 + i, dev->dev_addr[i]);
|
||||
}
|
||||
|
||||
de620_set_register(dev, W_EIP, EIPRegister);
|
||||
|
||||
next_rx_page = first_rx_page = DE620_RX_START_PAGE;
|
||||
if (nic_data.RAM_Size)
|
||||
last_rx_page = nic_data.RAM_Size - 1;
|
||||
else /* 64k RAM */
|
||||
last_rx_page = 255;
|
||||
|
||||
de620_set_register(dev, W_SPR, first_rx_page); /* Start Page Register*/
|
||||
de620_set_register(dev, W_EPR, last_rx_page); /* End Page Register */
|
||||
de620_set_register(dev, W_CPR, first_rx_page);/*Current Page Register*/
|
||||
de620_send_command(dev, W_NPR | first_rx_page); /* Next Page Register*/
|
||||
de620_send_command(dev, W_DUMMY);
|
||||
de620_set_delay(dev);
|
||||
|
||||
/* Final sanity check: Anybody out there? */
|
||||
/* Let's hope some bits from the statusregister make a good check */
|
||||
#define CHECK_MASK ( 0 | TXSUC | T16 | 0 | RXCRC | RXSHORT | 0 | 0 )
|
||||
#define CHECK_OK ( 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 )
|
||||
/* success: X 0 0 X 0 0 X X */
|
||||
/* ignore: EEDI RXGOOD COLS LNKS*/
|
||||
|
||||
if (((i = de620_get_register(dev, R_STS)) & CHECK_MASK) != CHECK_OK) {
|
||||
printk(KERN_ERR "%s: Something has happened to the DE-620! Please check it"
|
||||
#ifdef SHUTDOWN_WHEN_LOST
|
||||
" and do a new ifconfig"
|
||||
#endif
|
||||
"! (%02x)\n", dev->name, i);
|
||||
#ifdef SHUTDOWN_WHEN_LOST
|
||||
/* Goodbye, cruel world... */
|
||||
dev->flags &= ~IFF_UP;
|
||||
de620_close(dev);
|
||||
#endif
|
||||
was_down = 1;
|
||||
return 1; /* failed */
|
||||
}
|
||||
if (was_down) {
|
||||
printk(KERN_WARNING "%s: Thanks, I feel much better now!\n", dev->name);
|
||||
was_down = 0;
|
||||
}
|
||||
|
||||
/* All OK, go ahead... */
|
||||
de620_set_register(dev, W_TCR, TCR_DEF);
|
||||
|
||||
return 0; /* all ok */
|
||||
}
|
||||
|
||||
static const struct net_device_ops de620_netdev_ops = {
|
||||
.ndo_open = de620_open,
|
||||
.ndo_stop = de620_close,
|
||||
.ndo_start_xmit = de620_start_xmit,
|
||||
.ndo_tx_timeout = de620_timeout,
|
||||
.ndo_set_rx_mode = de620_set_multicast_list,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Only start-up code below
|
||||
*
|
||||
*/
|
||||
/****************************************
|
||||
*
|
||||
* Check if there is a DE-620 connected
|
||||
*/
|
||||
struct net_device * __init de620_probe(int unit)
|
||||
{
|
||||
byte checkbyte = 0xa5;
|
||||
struct net_device *dev;
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
dev = alloc_etherdev(0);
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
spin_lock_init(&de620_lock);
|
||||
|
||||
/*
|
||||
* This is where the base_addr and irq gets set.
|
||||
* Tunable at compile-time and insmod-time
|
||||
*/
|
||||
dev->base_addr = io;
|
||||
dev->irq = irq;
|
||||
|
||||
/* allow overriding parameters on command line */
|
||||
if (unit >= 0) {
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
}
|
||||
|
||||
pr_debug("%s", version);
|
||||
|
||||
printk(KERN_INFO "D-Link DE-620 pocket adapter");
|
||||
|
||||
if (!request_region(dev->base_addr, 3, "de620")) {
|
||||
printk(" io 0x%3lX, which is busy.\n", dev->base_addr);
|
||||
err = -EBUSY;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Initially, configure basic nibble mode, so we can read the EEPROM */
|
||||
NIC_Cmd = DEF_NIC_CMD;
|
||||
de620_set_register(dev, W_EIP, EIPRegister);
|
||||
|
||||
/* Anybody out there? */
|
||||
de620_set_register(dev, W_CPR, checkbyte);
|
||||
checkbyte = de620_get_register(dev, R_CPR);
|
||||
|
||||
if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) {
|
||||
printk(" not identified in the printer port\n");
|
||||
err = -ENODEV;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* else, got it! */
|
||||
dev->dev_addr[0] = nic_data.NodeID[0];
|
||||
for (i = 1; i < ETH_ALEN; i++) {
|
||||
dev->dev_addr[i] = nic_data.NodeID[i];
|
||||
dev->broadcast[i] = 0xff;
|
||||
}
|
||||
|
||||
printk(", Ethernet Address: %pM", dev->dev_addr);
|
||||
|
||||
printk(" (%dk RAM,",
|
||||
(nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64);
|
||||
|
||||
if (nic_data.Media == 1)
|
||||
printk(" BNC)\n");
|
||||
else
|
||||
printk(" UTP)\n");
|
||||
|
||||
dev->netdev_ops = &de620_netdev_ops;
|
||||
dev->watchdog_timeo = HZ*2;
|
||||
|
||||
/* base_addr and irq are already set, see above! */
|
||||
|
||||
/* dump eeprom */
|
||||
pr_debug("\nEEPROM contents:\n"
|
||||
"RAM_Size = 0x%02X\n"
|
||||
"NodeID = %pM\n"
|
||||
"Model = %d\n"
|
||||
"Media = %d\n"
|
||||
"SCR = 0x%02x\n", nic_data.RAM_Size, nic_data.NodeID,
|
||||
nic_data.Model, nic_data.Media, nic_data.SCR);
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out2;
|
||||
return dev;
|
||||
|
||||
out2:
|
||||
release_region(dev->base_addr, 3);
|
||||
out1:
|
||||
free_netdev(dev);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/**********************************
|
||||
*
|
||||
* Read info from on-board EEPROM
|
||||
*
|
||||
* Note: Bitwise serial I/O to/from the EEPROM vi the status _register_!
|
||||
*/
|
||||
#define sendit(dev,data) de620_set_register(dev, W_EIP, data | EIPRegister);
|
||||
|
||||
static unsigned short __init ReadAWord(struct net_device *dev, int from)
|
||||
{
|
||||
unsigned short data;
|
||||
int nbits;
|
||||
|
||||
/* cs [__~~] SET SEND STATE */
|
||||
/* di [____] */
|
||||
/* sck [_~~_] */
|
||||
sendit(dev, 0); sendit(dev, 1); sendit(dev, 5); sendit(dev, 4);
|
||||
|
||||
/* Send the 9-bit address from where we want to read the 16-bit word */
|
||||
for (nbits = 9; nbits > 0; --nbits, from <<= 1) {
|
||||
if (from & 0x0100) { /* bit set? */
|
||||
/* cs [~~~~] SEND 1 */
|
||||
/* di [~~~~] */
|
||||
/* sck [_~~_] */
|
||||
sendit(dev, 6); sendit(dev, 7); sendit(dev, 7); sendit(dev, 6);
|
||||
}
|
||||
else {
|
||||
/* cs [~~~~] SEND 0 */
|
||||
/* di [____] */
|
||||
/* sck [_~~_] */
|
||||
sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shift in the 16-bit word. The bits appear serially in EEDI (=0x80) */
|
||||
for (data = 0, nbits = 16; nbits > 0; --nbits) {
|
||||
/* cs [~~~~] SEND 0 */
|
||||
/* di [____] */
|
||||
/* sck [_~~_] */
|
||||
sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
|
||||
data = (data << 1) | ((de620_get_register(dev, R_STS) & EEDI) >> 7);
|
||||
}
|
||||
/* cs [____] RESET SEND STATE */
|
||||
/* di [____] */
|
||||
/* sck [_~~_] */
|
||||
sendit(dev, 0); sendit(dev, 1); sendit(dev, 1); sendit(dev, 0);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int __init read_eeprom(struct net_device *dev)
|
||||
{
|
||||
unsigned short wrd;
|
||||
|
||||
/* D-Link Ethernet addresses are in the series 00:80:c8:7X:XX:XX:XX */
|
||||
wrd = ReadAWord(dev, 0x1aa); /* bytes 0 + 1 of NodeID */
|
||||
if (!clone && (wrd != htons(0x0080))) /* Valid D-Link ether sequence? */
|
||||
return -1; /* Nope, not a DE-620 */
|
||||
nic_data.NodeID[0] = wrd & 0xff;
|
||||
nic_data.NodeID[1] = wrd >> 8;
|
||||
|
||||
wrd = ReadAWord(dev, 0x1ab); /* bytes 2 + 3 of NodeID */
|
||||
if (!clone && ((wrd & 0xff) != 0xc8)) /* Valid D-Link ether sequence? */
|
||||
return -1; /* Nope, not a DE-620 */
|
||||
nic_data.NodeID[2] = wrd & 0xff;
|
||||
nic_data.NodeID[3] = wrd >> 8;
|
||||
|
||||
wrd = ReadAWord(dev, 0x1ac); /* bytes 4 + 5 of NodeID */
|
||||
nic_data.NodeID[4] = wrd & 0xff;
|
||||
nic_data.NodeID[5] = wrd >> 8;
|
||||
|
||||
wrd = ReadAWord(dev, 0x1ad); /* RAM size in pages (256 bytes). 0 = 64k */
|
||||
nic_data.RAM_Size = (wrd >> 8);
|
||||
|
||||
wrd = ReadAWord(dev, 0x1ae); /* hardware model (CT = 3) */
|
||||
nic_data.Model = (wrd & 0xff);
|
||||
|
||||
wrd = ReadAWord(dev, 0x1af); /* media (indicates BNC/UTP) */
|
||||
nic_data.Media = (wrd & 0xff);
|
||||
|
||||
wrd = ReadAWord(dev, 0x1a8); /* System Configuration Register */
|
||||
nic_data.SCR = (wrd >> 8);
|
||||
|
||||
return 0; /* no errors */
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Loadable module skeleton
|
||||
*
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static struct net_device *de620_dev;
|
||||
|
||||
int __init init_module(void)
|
||||
{
|
||||
de620_dev = de620_probe(-1);
|
||||
if (IS_ERR(de620_dev))
|
||||
return PTR_ERR(de620_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup_module(void)
|
||||
{
|
||||
unregister_netdev(de620_dev);
|
||||
release_region(de620_dev->base_addr, 3);
|
||||
free_netdev(de620_dev);
|
||||
}
|
||||
#endif /* MODULE */
|
||||
MODULE_LICENSE("GPL");
|
@ -1,117 +0,0 @@
|
||||
/*********************************************************
|
||||
* *
|
||||
* Definition of D-Link DE-620 Ethernet Pocket adapter *
|
||||
* *
|
||||
*********************************************************/
|
||||
|
||||
/* DE-620's CMD port Command */
|
||||
#define CS0 0x08 /* 1->0 command strobe */
|
||||
#define ICEN 0x04 /* 0=enable DL3520 host interface */
|
||||
#define DS0 0x02 /* 1->0 data strobe 0 */
|
||||
#define DS1 0x01 /* 1->0 data strobe 1 */
|
||||
|
||||
#define WDIR 0x20 /* general 0=read 1=write */
|
||||
#define RDIR 0x00 /* (not 100% confirm ) */
|
||||
#define PS2WDIR 0x00 /* ps/2 mode 1=read, 0=write */
|
||||
#define PS2RDIR 0x20
|
||||
|
||||
#define IRQEN 0x10 /* 1 = enable printer IRQ line */
|
||||
#define SELECTIN 0x08 /* 1 = select printer */
|
||||
#define INITP 0x04 /* 0 = initial printer */
|
||||
#define AUTOFEED 0x02 /* 1 = printer auto form feed */
|
||||
#define STROBE 0x01 /* 0->1 data strobe */
|
||||
|
||||
#define RESET 0x08
|
||||
#define NIS0 0x20 /* 0 = BNC, 1 = UTP */
|
||||
#define NCTL0 0x10
|
||||
|
||||
/* DE-620 DIC Command */
|
||||
#define W_DUMMY 0x00 /* DIC reserved command */
|
||||
#define W_CR 0x20 /* DIC write command register */
|
||||
#define W_NPR 0x40 /* DIC write Next Page Register */
|
||||
#define W_TBR 0x60 /* DIC write Tx Byte Count 1 reg */
|
||||
#define W_RSA 0x80 /* DIC write Remote Start Addr 1 */
|
||||
|
||||
/* DE-620's STAT port bits 7-4 */
|
||||
#define EMPTY 0x80 /* 1 = receive buffer empty */
|
||||
#define INTLEVEL 0x40 /* 1 = interrupt level is high */
|
||||
#define TXBF1 0x20 /* 1 = transmit buffer 1 is in use */
|
||||
#define TXBF0 0x10 /* 1 = transmit buffer 0 is in use */
|
||||
#define READY 0x08 /* 1 = h/w ready to accept cmd/data */
|
||||
|
||||
/* IDC 1 Command */
|
||||
#define W_RSA1 0xa0 /* write remote start address 1 */
|
||||
#define W_RSA0 0xa1 /* write remote start address 0 */
|
||||
#define W_NPRF 0xa2 /* write next page register NPR15-NPR8 */
|
||||
#define W_DFR 0xa3 /* write delay factor register */
|
||||
#define W_CPR 0xa4 /* write current page register */
|
||||
#define W_SPR 0xa5 /* write start page register */
|
||||
#define W_EPR 0xa6 /* write end page register */
|
||||
#define W_SCR 0xa7 /* write system configuration register */
|
||||
#define W_TCR 0xa8 /* write Transceiver Configuration reg */
|
||||
#define W_EIP 0xa9 /* write EEPM Interface port */
|
||||
#define W_PAR0 0xaa /* write physical address register 0 */
|
||||
#define W_PAR1 0xab /* write physical address register 1 */
|
||||
#define W_PAR2 0xac /* write physical address register 2 */
|
||||
#define W_PAR3 0xad /* write physical address register 3 */
|
||||
#define W_PAR4 0xae /* write physical address register 4 */
|
||||
#define W_PAR5 0xaf /* write physical address register 5 */
|
||||
|
||||
/* IDC 2 Command */
|
||||
#define R_STS 0xc0 /* read status register */
|
||||
#define R_CPR 0xc1 /* read current page register */
|
||||
#define R_BPR 0xc2 /* read boundary page register */
|
||||
#define R_TDR 0xc3 /* read time domain reflectometry reg */
|
||||
|
||||
/* STATUS Register */
|
||||
#define EEDI 0x80 /* EEPM DO pin */
|
||||
#define TXSUC 0x40 /* tx success */
|
||||
#define T16 0x20 /* tx fail 16 times */
|
||||
#define TS1 0x40 /* 0=Tx success, 1=T16 */
|
||||
#define TS0 0x20 /* 0=Tx success, 1=T16 */
|
||||
#define RXGOOD 0x10 /* rx a good packet */
|
||||
#define RXCRC 0x08 /* rx a CRC error packet */
|
||||
#define RXSHORT 0x04 /* rx a short packet */
|
||||
#define COLS 0x02 /* coaxial collision status */
|
||||
#define LNKS 0x01 /* UTP link status */
|
||||
|
||||
/* Command Register */
|
||||
#define CLEAR 0x10 /* reset part of hardware */
|
||||
#define NOPER 0x08 /* No Operation */
|
||||
#define RNOP 0x08
|
||||
#define RRA 0x06 /* After RR then auto-advance NPR & BPR(=NPR-1) */
|
||||
#define RRN 0x04 /* Normal Remote Read mode */
|
||||
#define RW1 0x02 /* Remote Write tx buffer 1 ( page 6 - 11 ) */
|
||||
#define RW0 0x00 /* Remote Write tx buffer 0 ( page 0 - 5 ) */
|
||||
#define TXEN 0x01 /* 0->1 tx enable */
|
||||
|
||||
/* System Configuration Register */
|
||||
#define TESTON 0x80 /* test host data transfer reliability */
|
||||
#define SLEEP 0x40 /* sleep mode */
|
||||
#if 0
|
||||
#define FASTMODE 0x04 /* fast mode for intel 82360SL fast mode */
|
||||
#define BYTEMODE 0x02 /* byte mode */
|
||||
#else
|
||||
#define FASTMODE 0x20 /* fast mode for intel 82360SL fast mode */
|
||||
#define BYTEMODE 0x10 /* byte mode */
|
||||
#endif
|
||||
#define NIBBLEMODE 0x00 /* nibble mode */
|
||||
#define IRQINV 0x08 /* turn off IRQ line inverter */
|
||||
#define IRQNML 0x00 /* turn on IRQ line inverter */
|
||||
#define INTON 0x04
|
||||
#define AUTOFFSET 0x02 /* auto shift address to TPR+12 */
|
||||
#define AUTOTX 0x01 /* auto tx when leave RW mode */
|
||||
|
||||
/* Transceiver Configuration Register */
|
||||
#define JABBER 0x80 /* generate jabber condition */
|
||||
#define TXSUCINT 0x40 /* enable tx success interrupt */
|
||||
#define T16INT 0x20 /* enable T16 interrupt */
|
||||
#define RXERRPKT 0x10 /* accept CRC error or short packet */
|
||||
#define EXTERNALB2 0x0C /* external loopback 2 */
|
||||
#define EXTERNALB1 0x08 /* external loopback 1 */
|
||||
#define INTERNALB 0x04 /* internal loopback */
|
||||
#define NMLOPERATE 0x00 /* normal operation */
|
||||
#define RXPBM 0x03 /* rx physical, broadcast, multicast */
|
||||
#define RXPB 0x02 /* rx physical, broadcast */
|
||||
#define RXALL 0x01 /* rx all packet */
|
||||
#define RXOFF 0x00 /* rx disable */
|
@ -17,18 +17,6 @@ config NET_VENDOR_FUJITSU
|
||||
|
||||
if NET_VENDOR_FUJITSU
|
||||
|
||||
config AT1700
|
||||
tristate "AT1700/1720 support (EXPERIMENTAL)"
|
||||
depends on ISA && EXPERIMENTAL
|
||||
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 at1700.
|
||||
|
||||
config PCMCIA_FMVJ18X
|
||||
tristate "Fujitsu FMV-J18x PCMCIA support"
|
||||
depends on PCMCIA
|
||||
@ -40,15 +28,4 @@ config PCMCIA_FMVJ18X
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called fmvj18x_cs. If unsure, say N.
|
||||
|
||||
config ETH16I
|
||||
tristate "ICL EtherTeam 16i/32 support"
|
||||
depends on ISA
|
||||
---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 eth16i.
|
||||
|
||||
endif # NET_VENDOR_FUJITSU
|
||||
|
@ -2,6 +2,4 @@
|
||||
# Makefile for the Fujitsu network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AT1700) += at1700.o
|
||||
obj-$(CONFIG_ETH16I) += eth16i.o
|
||||
obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
|
||||
|
@ -1,791 +0,0 @@
|
||||
/* at1700.c: A network device driver for the Allied Telesis AT1700.
|
||||
|
||||
Written 1993-98 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 device driver for the Allied Telesis AT1700, and
|
||||
Fujitsu FMV-181/182/181A/182A/183/184/183A/184A, which are
|
||||
straight-forward Fujitsu MB86965 implementations.
|
||||
|
||||
Modification for Fujitsu FMV-18X cards is done by Yutaka Tamiya
|
||||
(tamy@flab.fujitsu.co.jp).
|
||||
|
||||
Sources:
|
||||
The Fujitsu MB86965 datasheet.
|
||||
|
||||
After the initial version of this driver was written Gerry Sawkins of
|
||||
ATI provided their EEPROM configuration code header file.
|
||||
Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
|
||||
|
||||
MCA bus (AT1720) support (now deleted) by Rene Schmit <rene@bss.lu>
|
||||
|
||||
Bugs:
|
||||
The MB86965 has a design flaw that makes all probes unreliable. Not
|
||||
only is it difficult to detect, it also moves around in I/O space in
|
||||
response to inb()s from other device probes!
|
||||
*/
|
||||
|
||||
#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/skbuff.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
static char version[] __initdata =
|
||||
"at1700.c:v1.16 9/11/06 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
|
||||
|
||||
#define DRV_NAME "at1700"
|
||||
|
||||
/* Tunable parameters. */
|
||||
|
||||
/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */
|
||||
#define MC_FILTERBREAK 64
|
||||
|
||||
/* These unusual address orders are used to verify the CONFIG register. */
|
||||
|
||||
static int fmv18x_probe_list[] __initdata = {
|
||||
0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* ISA
|
||||
*/
|
||||
|
||||
static unsigned at1700_probe_list[] __initdata = {
|
||||
0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0
|
||||
};
|
||||
|
||||
/* use 0 for production, 1 for verification, >2 for debug */
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 1
|
||||
#endif
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
/* Information that need to be kept for each board. */
|
||||
struct net_local {
|
||||
spinlock_t lock;
|
||||
unsigned char mc_filter[8];
|
||||
uint jumpered:1; /* Set iff the board has jumper config. */
|
||||
uint tx_started:1; /* Packets are on the Tx queue. */
|
||||
uint tx_queue_ready:1; /* Tx queue is ready to be sent. */
|
||||
uint rx_started:1; /* Packets are Rxing. */
|
||||
uchar tx_queue; /* Number of packet on the Tx queue. */
|
||||
ushort tx_queue_len; /* Current length of the Tx queue. */
|
||||
};
|
||||
|
||||
|
||||
/* Offsets from the base address. */
|
||||
#define STATUS 0
|
||||
#define TX_STATUS 0
|
||||
#define RX_STATUS 1
|
||||
#define TX_INTR 2 /* Bit-mapped interrupt enable registers. */
|
||||
#define RX_INTR 3
|
||||
#define TX_MODE 4
|
||||
#define RX_MODE 5
|
||||
#define CONFIG_0 6 /* Misc. configuration settings. */
|
||||
#define CONFIG_1 7
|
||||
/* Run-time register bank 2 definitions. */
|
||||
#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
|
||||
#define TX_START 10
|
||||
#define COL16CNTL 11 /* Control Reg for 16 collisions */
|
||||
#define MODE13 13
|
||||
#define RX_CTRL 14
|
||||
/* Configuration registers only on the '865A/B chips. */
|
||||
#define EEPROM_Ctrl 16
|
||||
#define EEPROM_Data 17
|
||||
#define CARDSTATUS 16 /* FMV-18x Card Status */
|
||||
#define CARDSTATUS1 17 /* FMV-18x Card Status */
|
||||
#define IOCONFIG 18 /* Either read the jumper, or move the I/O. */
|
||||
#define IOCONFIG1 19
|
||||
#define SAPROM 20 /* The station address PROM, if no EEPROM. */
|
||||
#define MODE24 24
|
||||
#define RESET 31 /* Write to reset some parts of the chip. */
|
||||
#define AT1700_IO_EXTENT 32
|
||||
#define PORT_OFFSET(o) (o)
|
||||
|
||||
|
||||
#define TX_TIMEOUT (HZ/10)
|
||||
|
||||
|
||||
/* Index to functions, as function prototypes. */
|
||||
|
||||
static int at1700_probe1(struct net_device *dev, int ioaddr);
|
||||
static int read_eeprom(long ioaddr, int location);
|
||||
static int net_open(struct net_device *dev);
|
||||
static netdev_tx_t net_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
static irqreturn_t net_interrupt(int irq, void *dev_id);
|
||||
static void net_rx(struct net_device *dev);
|
||||
static int net_close(struct net_device *dev);
|
||||
static void set_rx_mode(struct net_device *dev);
|
||||
static void net_tx_timeout (struct net_device *dev);
|
||||
|
||||
|
||||
/* Check for a network adaptor of this type, and return '0' iff one exists.
|
||||
If dev->base_addr == 0, probe all likely locations.
|
||||
If dev->base_addr == 1, always return failure.
|
||||
If dev->base_addr == 2, allocate space for the device and return success
|
||||
(detachable devices only).
|
||||
*/
|
||||
|
||||
static int io = 0x260;
|
||||
|
||||
static int irq;
|
||||
|
||||
static void cleanup_card(struct net_device *dev)
|
||||
{
|
||||
free_irq(dev->irq, NULL);
|
||||
release_region(dev->base_addr, AT1700_IO_EXTENT);
|
||||
}
|
||||
|
||||
struct net_device * __init at1700_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
|
||||
unsigned *port;
|
||||
int err = 0;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (unit >= 0) {
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
io = dev->base_addr;
|
||||
irq = dev->irq;
|
||||
} else {
|
||||
dev->base_addr = io;
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
if (io > 0x1ff) { /* Check a single specified location. */
|
||||
err = at1700_probe1(dev, io);
|
||||
} else if (io != 0) { /* Don't probe at all. */
|
||||
err = -ENXIO;
|
||||
} else {
|
||||
for (port = at1700_probe_list; *port; port++) {
|
||||
if (at1700_probe1(dev, *port) == 0)
|
||||
break;
|
||||
dev->irq = irq;
|
||||
}
|
||||
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 at1700_netdev_ops = {
|
||||
.ndo_open = net_open,
|
||||
.ndo_stop = net_close,
|
||||
.ndo_start_xmit = net_send_packet,
|
||||
.ndo_set_rx_mode = set_rx_mode,
|
||||
.ndo_tx_timeout = net_tx_timeout,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
|
||||
"signature", the default bit pattern after a reset. This *doesn't* work --
|
||||
there is no way to reset the bus interface without a complete power-cycle!
|
||||
|
||||
It turns out that ATI came to the same conclusion I did: the only thing
|
||||
that can be done is checking a few bits and then diving right into an
|
||||
EEPROM read. */
|
||||
|
||||
static int __init at1700_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
static const char fmv_irqmap[4] = {3, 7, 10, 15};
|
||||
static const char fmv_irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15};
|
||||
static const char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
|
||||
unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0;
|
||||
int ret = -ENODEV;
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
|
||||
if (!request_region(ioaddr, AT1700_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* Resetting the chip doesn't reset the ISA interface, so don't bother.
|
||||
That means we have to be careful with the register values we probe
|
||||
for.
|
||||
*/
|
||||
#ifdef notdef
|
||||
printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
|
||||
ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
|
||||
read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
|
||||
#endif
|
||||
/* We must check for the EEPROM-config boards first, else accessing
|
||||
IOCONFIG0 will move the board! */
|
||||
if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr &&
|
||||
read_eeprom(ioaddr, 4) == 0x0000 &&
|
||||
(read_eeprom(ioaddr, 5) & 0xff00) == 0xF400)
|
||||
is_at1700 = 1;
|
||||
else if (inb(ioaddr + SAPROM ) == 0x00 &&
|
||||
inb(ioaddr + SAPROM + 1) == 0x00 &&
|
||||
inb(ioaddr + SAPROM + 2) == 0x0e)
|
||||
is_fmv18x = 1;
|
||||
else {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Reset the internal state machines. */
|
||||
outb(0, ioaddr + RESET);
|
||||
|
||||
if (is_at1700) {
|
||||
irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04)
|
||||
| (read_eeprom(ioaddr, 0)>>14)];
|
||||
} else {
|
||||
/* Check PnP mode for FMV-183/184/183A/184A. */
|
||||
/* This PnP routine is very poor. IO and IRQ should be known. */
|
||||
if (inb(ioaddr + CARDSTATUS1) & 0x20) {
|
||||
irq = dev->irq;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (irq == fmv_irqmap_pnp[i])
|
||||
break;
|
||||
}
|
||||
if (i == 8) {
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr)
|
||||
goto err_out;
|
||||
irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03];
|
||||
}
|
||||
}
|
||||
|
||||
printk("%s: %s found at %#3x, IRQ %d, address ", dev->name,
|
||||
is_at1700 ? "AT1700" : "FMV-18X", ioaddr, irq);
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
dev->irq = irq;
|
||||
|
||||
if (is_at1700) {
|
||||
for(i = 0; i < 3; i++) {
|
||||
unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
|
||||
((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
|
||||
}
|
||||
} else {
|
||||
for(i = 0; i < 6; i++) {
|
||||
unsigned char val = inb(ioaddr + SAPROM + i);
|
||||
dev->dev_addr[i] = val;
|
||||
}
|
||||
}
|
||||
printk("%pM", dev->dev_addr);
|
||||
|
||||
/* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
|
||||
rather than 150 ohm shielded twisted pair compensation.
|
||||
0x0000 == auto-sense the interface
|
||||
0x0800 == use TP interface
|
||||
0x1800 == use coax interface
|
||||
*/
|
||||
{
|
||||
const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
|
||||
if (is_at1700) {
|
||||
ushort setup_value = read_eeprom(ioaddr, 12);
|
||||
dev->if_port = setup_value >> 8;
|
||||
} else {
|
||||
ushort setup_value = inb(ioaddr + CARDSTATUS);
|
||||
switch (setup_value & 0x07) {
|
||||
case 0x01: /* 10base5 */
|
||||
case 0x02: /* 10base2 */
|
||||
dev->if_port = 0x18; break;
|
||||
case 0x04: /* 10baseT */
|
||||
dev->if_port = 0x08; break;
|
||||
default: /* auto-sense */
|
||||
dev->if_port = 0x00; break;
|
||||
}
|
||||
}
|
||||
printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
|
||||
}
|
||||
|
||||
/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
|
||||
bus access, two 4K Tx queues, and disabled Tx and Rx. */
|
||||
outb(0xda, ioaddr + CONFIG_0);
|
||||
|
||||
/* Set the station address in bank zero. */
|
||||
outb(0x00, ioaddr + CONFIG_1);
|
||||
for (i = 0; i < 6; i++)
|
||||
outb(dev->dev_addr[i], ioaddr + PORT_OFFSET(8 + i));
|
||||
|
||||
/* Switch to bank 1 and set the multicast table to accept none. */
|
||||
outb(0x04, ioaddr + CONFIG_1);
|
||||
for (i = 0; i < 8; i++)
|
||||
outb(0x00, ioaddr + PORT_OFFSET(8 + i));
|
||||
|
||||
|
||||
/* Switch to bank 2 */
|
||||
/* Lock our I/O address, and set manual processing mode for 16 collisions. */
|
||||
outb(0x08, ioaddr + CONFIG_1);
|
||||
outb(dev->if_port, ioaddr + MODE13);
|
||||
outb(0x00, ioaddr + COL16CNTL);
|
||||
|
||||
if (net_debug)
|
||||
printk(version);
|
||||
|
||||
dev->netdev_ops = &at1700_netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
|
||||
spin_lock_init(&lp->lock);
|
||||
|
||||
lp->jumpered = is_fmv18x;
|
||||
/* Snarf the interrupt vector now. */
|
||||
ret = request_irq(irq, net_interrupt, 0, DRV_NAME, dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "AT1700 at %#3x is unusable due to a "
|
||||
"conflict on IRQ %d.\n",
|
||||
ioaddr, irq);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
release_region(ioaddr, AT1700_IO_EXTENT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* EEPROM_Ctrl bits. */
|
||||
#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
|
||||
#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
|
||||
#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
|
||||
#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
|
||||
|
||||
/* The EEPROM commands include the alway-set leading bit. */
|
||||
#define EE_WRITE_CMD (5 << 6)
|
||||
#define EE_READ_CMD (6 << 6)
|
||||
#define EE_ERASE_CMD (7 << 6)
|
||||
|
||||
static int __init read_eeprom(long ioaddr, int location)
|
||||
{
|
||||
int i;
|
||||
unsigned short retval = 0;
|
||||
long ee_addr = ioaddr + EEPROM_Ctrl;
|
||||
long ee_daddr = ioaddr + EEPROM_Data;
|
||||
int read_cmd = location | EE_READ_CMD;
|
||||
|
||||
/* Shift the read command bits out. */
|
||||
for (i = 9; i >= 0; i--) {
|
||||
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
outb(EE_CS, ee_addr);
|
||||
outb(dataval, ee_daddr);
|
||||
outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
|
||||
}
|
||||
outb(EE_DATA_WRITE, ee_daddr);
|
||||
for (i = 16; i > 0; i--) {
|
||||
outb(EE_CS, ee_addr);
|
||||
outb(EE_CS | EE_SHIFT_CLK, ee_addr);
|
||||
retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Terminate the EEPROM access. */
|
||||
outb(EE_CS, ee_addr);
|
||||
outb(EE_SHIFT_CLK, ee_addr);
|
||||
outb(0, ee_addr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int net_open(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
|
||||
bus access, and two 4K Tx queues. */
|
||||
outb(0x5a, ioaddr + CONFIG_0);
|
||||
|
||||
/* Powerup, switch to register bank 2, and enable the Rx and Tx. */
|
||||
outb(0xe8, ioaddr + CONFIG_1);
|
||||
|
||||
lp->tx_started = 0;
|
||||
lp->tx_queue_ready = 1;
|
||||
lp->rx_started = 0;
|
||||
lp->tx_queue = 0;
|
||||
lp->tx_queue_len = 0;
|
||||
|
||||
/* Turn on hardware Tx and Rx interrupts. */
|
||||
outb(0x82, ioaddr + TX_INTR);
|
||||
outb(0x81, ioaddr + RX_INTR);
|
||||
|
||||
/* Enable the IRQ on boards of fmv18x it is feasible. */
|
||||
if (lp->jumpered) {
|
||||
outb(0x80, ioaddr + IOCONFIG1);
|
||||
}
|
||||
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void net_tx_timeout (struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
printk ("%s: transmit timed out with status %04x, %s?\n", dev->name,
|
||||
inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80
|
||||
? "IRQ conflict" : "network cable problem");
|
||||
printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
|
||||
dev->name, inw(ioaddr + TX_STATUS), inw(ioaddr + TX_INTR), inw(ioaddr + TX_MODE),
|
||||
inw(ioaddr + CONFIG_0), inw(ioaddr + DATAPORT), inw(ioaddr + TX_START),
|
||||
inw(ioaddr + MODE13 - 1), inw(ioaddr + RX_CTRL));
|
||||
dev->stats.tx_errors++;
|
||||
/* ToDo: We should try to restart the adaptor... */
|
||||
outw(0xffff, ioaddr + MODE24);
|
||||
outw (0xffff, ioaddr + TX_STATUS);
|
||||
outb (0x5a, ioaddr + CONFIG_0);
|
||||
outb (0xe8, ioaddr + CONFIG_1);
|
||||
outw (0x8182, ioaddr + TX_INTR);
|
||||
outb (0x00, ioaddr + TX_START);
|
||||
outb (0x03, ioaddr + COL16CNTL);
|
||||
|
||||
dev->trans_start = jiffies; /* prevent tx timeout */
|
||||
|
||||
lp->tx_started = 0;
|
||||
lp->tx_queue_ready = 1;
|
||||
lp->rx_started = 0;
|
||||
lp->tx_queue = 0;
|
||||
lp->tx_queue_len = 0;
|
||||
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
|
||||
static netdev_tx_t net_send_packet (struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
|
||||
short len = skb->len;
|
||||
unsigned char *buf = skb->data;
|
||||
static u8 pad[ETH_ZLEN];
|
||||
|
||||
netif_stop_queue (dev);
|
||||
|
||||
/* We may not start transmitting unless we finish transferring
|
||||
a packet into the Tx queue. During executing the following
|
||||
codes we possibly catch a Tx interrupt. Thus we flag off
|
||||
tx_queue_ready, so that we prevent the interrupt routine
|
||||
(net_interrupt) to start transmitting. */
|
||||
lp->tx_queue_ready = 0;
|
||||
{
|
||||
outw (length, ioaddr + DATAPORT);
|
||||
/* Packet data */
|
||||
outsw (ioaddr + DATAPORT, buf, len >> 1);
|
||||
/* Check for dribble byte */
|
||||
if (len & 1) {
|
||||
outw(skb->data[skb->len-1], ioaddr + DATAPORT);
|
||||
len++;
|
||||
}
|
||||
/* Check for packet padding */
|
||||
if (length != skb->len)
|
||||
outsw(ioaddr + DATAPORT, pad, (length - len + 1) >> 1);
|
||||
|
||||
lp->tx_queue++;
|
||||
lp->tx_queue_len += length + 2;
|
||||
}
|
||||
lp->tx_queue_ready = 1;
|
||||
|
||||
if (lp->tx_started == 0) {
|
||||
/* If the Tx is idle, always trigger a transmit. */
|
||||
outb (0x80 | lp->tx_queue, ioaddr + TX_START);
|
||||
lp->tx_queue = 0;
|
||||
lp->tx_queue_len = 0;
|
||||
lp->tx_started = 1;
|
||||
netif_start_queue (dev);
|
||||
} else if (lp->tx_queue_len < 4096 - 1502)
|
||||
/* Yes, there is room for one more packet. */
|
||||
netif_start_queue (dev);
|
||||
dev_kfree_skb (skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* The typical workload of the driver:
|
||||
Handle the network interface interrupts. */
|
||||
static irqreturn_t net_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct net_local *lp;
|
||||
int ioaddr, status;
|
||||
int handled = 0;
|
||||
|
||||
if (dev == NULL) {
|
||||
printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
spin_lock (&lp->lock);
|
||||
|
||||
status = inw(ioaddr + TX_STATUS);
|
||||
outw(status, ioaddr + TX_STATUS);
|
||||
|
||||
if (net_debug > 4)
|
||||
printk("%s: Interrupt with status %04x.\n", dev->name, status);
|
||||
if (lp->rx_started == 0 &&
|
||||
(status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) {
|
||||
/* Got a packet(s).
|
||||
We cannot execute net_rx more than once at the same time for
|
||||
the same device. During executing net_rx, we possibly catch a
|
||||
Tx interrupt. Thus we flag on rx_started, so that we prevent
|
||||
the interrupt routine (net_interrupt) to dive into net_rx
|
||||
again. */
|
||||
handled = 1;
|
||||
lp->rx_started = 1;
|
||||
outb(0x00, ioaddr + RX_INTR); /* Disable RX intr. */
|
||||
net_rx(dev);
|
||||
outb(0x81, ioaddr + RX_INTR); /* Enable RX intr. */
|
||||
lp->rx_started = 0;
|
||||
}
|
||||
if (status & 0x00ff) {
|
||||
handled = 1;
|
||||
if (status & 0x02) {
|
||||
/* More than 16 collisions occurred */
|
||||
if (net_debug > 4)
|
||||
printk("%s: 16 Collision occur during Txing.\n", dev->name);
|
||||
/* Cancel sending a packet. */
|
||||
outb(0x03, ioaddr + COL16CNTL);
|
||||
dev->stats.collisions++;
|
||||
}
|
||||
if (status & 0x82) {
|
||||
dev->stats.tx_packets++;
|
||||
/* The Tx queue has any packets and is not being
|
||||
transferred a packet from the host, start
|
||||
transmitting. */
|
||||
if (lp->tx_queue && lp->tx_queue_ready) {
|
||||
outb(0x80 | lp->tx_queue, ioaddr + TX_START);
|
||||
lp->tx_queue = 0;
|
||||
lp->tx_queue_len = 0;
|
||||
dev->trans_start = jiffies;
|
||||
netif_wake_queue (dev);
|
||||
} else {
|
||||
lp->tx_started = 0;
|
||||
netif_wake_queue (dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock (&lp->lock);
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
/* We have a good packet(s), get it/them out of the buffers. */
|
||||
static void
|
||||
net_rx(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
int boguscount = 5;
|
||||
|
||||
while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
|
||||
ushort status = inw(ioaddr + DATAPORT);
|
||||
ushort pkt_len = inw(ioaddr + DATAPORT);
|
||||
|
||||
if (net_debug > 4)
|
||||
printk("%s: Rxing packet mode %02x status %04x.\n",
|
||||
dev->name, inb(ioaddr + RX_MODE), status);
|
||||
#ifndef final_version
|
||||
if (status == 0) {
|
||||
outb(0x05, ioaddr + RX_CTRL);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((status & 0xF0) != 0x20) { /* There was an error. */
|
||||
dev->stats.rx_errors++;
|
||||
if (status & 0x08) dev->stats.rx_length_errors++;
|
||||
if (status & 0x04) dev->stats.rx_frame_errors++;
|
||||
if (status & 0x02) dev->stats.rx_crc_errors++;
|
||||
if (status & 0x01) dev->stats.rx_over_errors++;
|
||||
} else {
|
||||
/* Malloc up new buffer. */
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (pkt_len > 1550) {
|
||||
printk("%s: The AT1700 claimed a very large packet, size %d.\n",
|
||||
dev->name, pkt_len);
|
||||
/* Prime the FIFO and then flush the packet. */
|
||||
inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
|
||||
outb(0x05, ioaddr + RX_CTRL);
|
||||
dev->stats.rx_errors++;
|
||||
break;
|
||||
}
|
||||
skb = netdev_alloc_skb(dev, pkt_len + 3);
|
||||
if (skb == NULL) {
|
||||
printk("%s: Memory squeeze, dropping packet (len %d).\n",
|
||||
dev->name, pkt_len);
|
||||
/* Prime the FIFO and then flush the packet. */
|
||||
inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
|
||||
outb(0x05, ioaddr + RX_CTRL);
|
||||
dev->stats.rx_dropped++;
|
||||
break;
|
||||
}
|
||||
skb_reserve(skb,2);
|
||||
|
||||
insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
|
||||
skb->protocol=eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
}
|
||||
if (--boguscount <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If any worth-while packets have been received, dev_rint()
|
||||
has done a mark_bh(NET_BH) for us and will work on them
|
||||
when we get to the bottom-half routine. */
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 20; i++) {
|
||||
if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
|
||||
break;
|
||||
inw(ioaddr + DATAPORT); /* dummy status read */
|
||||
outb(0x05, ioaddr + RX_CTRL);
|
||||
}
|
||||
|
||||
if (net_debug > 5)
|
||||
printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
|
||||
dev->name, inb(ioaddr + RX_MODE), i);
|
||||
}
|
||||
}
|
||||
|
||||
/* The inverse routine to net_open(). */
|
||||
static int net_close(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/* Set configuration register 0 to disable Tx and Rx. */
|
||||
outb(0xda, ioaddr + CONFIG_0);
|
||||
|
||||
/* No statistic counters on the chip to update. */
|
||||
|
||||
/* Disable the IRQ on boards of fmv18x where it is feasible. */
|
||||
if (lp->jumpered)
|
||||
outb(0x00, ioaddr + IOCONFIG1);
|
||||
|
||||
/* Power-down the chip. Green, green, green! */
|
||||
outb(0x00, ioaddr + CONFIG_1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Set the multicast/promiscuous mode for this adaptor.
|
||||
*/
|
||||
|
||||
static void
|
||||
set_rx_mode(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
unsigned char mc_filter[8]; /* Multicast hash filter */
|
||||
unsigned long flags;
|
||||
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
memset(mc_filter, 0xff, sizeof(mc_filter));
|
||||
outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
|
||||
} else if (netdev_mc_count(dev) > MC_FILTERBREAK ||
|
||||
(dev->flags & IFF_ALLMULTI)) {
|
||||
/* Too many to filter perfectly -- accept all multicasts. */
|
||||
memset(mc_filter, 0xff, sizeof(mc_filter));
|
||||
outb(2, ioaddr + RX_MODE); /* Use normal mode. */
|
||||
} else if (netdev_mc_empty(dev)) {
|
||||
memset(mc_filter, 0x00, sizeof(mc_filter));
|
||||
outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */
|
||||
} else {
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
memset(mc_filter, 0, sizeof(mc_filter));
|
||||
netdev_for_each_mc_addr(ha, dev) {
|
||||
unsigned int bit =
|
||||
ether_crc_le(ETH_ALEN, ha->addr) >> 26;
|
||||
mc_filter[bit >> 3] |= (1 << bit);
|
||||
}
|
||||
outb(0x02, ioaddr + RX_MODE); /* Use normal mode. */
|
||||
}
|
||||
|
||||
spin_lock_irqsave (&lp->lock, flags);
|
||||
if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
|
||||
int i;
|
||||
int saved_bank = inw(ioaddr + CONFIG_0);
|
||||
/* Switch to bank 1 and set the multicast table. */
|
||||
outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0);
|
||||
for (i = 0; i < 8; i++)
|
||||
outb(mc_filter[i], ioaddr + PORT_OFFSET(8 + i));
|
||||
memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
|
||||
outw(saved_bank, ioaddr + CONFIG_0);
|
||||
}
|
||||
spin_unlock_irqrestore (&lp->lock, flags);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static struct net_device *dev_at1700;
|
||||
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
module_param(net_debug, int, 0);
|
||||
MODULE_PARM_DESC(io, "AT1700/FMV18X I/O base address");
|
||||
MODULE_PARM_DESC(irq, "AT1700/FMV18X IRQ number");
|
||||
MODULE_PARM_DESC(net_debug, "AT1700/FMV18X debug level (0-6)");
|
||||
|
||||
static int __init at1700_module_init(void)
|
||||
{
|
||||
if (io == 0)
|
||||
printk("at1700: You should not use auto-probing with insmod!\n");
|
||||
dev_at1700 = at1700_probe(-1);
|
||||
if (IS_ERR(dev_at1700))
|
||||
return PTR_ERR(dev_at1700);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit at1700_module_exit(void)
|
||||
{
|
||||
unregister_netdev(dev_at1700);
|
||||
cleanup_card(dev_at1700);
|
||||
free_netdev(dev_at1700);
|
||||
}
|
||||
module_init(at1700_module_init);
|
||||
module_exit(at1700_module_exit);
|
||||
#endif /* MODULE */
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,292 +0,0 @@
|
||||
/*****************************************************************
|
||||
*
|
||||
* defines for 3Com Etherlink Plus adapter
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#define ELP_DMA 6
|
||||
#define ELP_RX_PCBS 4
|
||||
#define ELP_MAX_CARDS 4
|
||||
|
||||
/*
|
||||
* I/O register offsets
|
||||
*/
|
||||
#define PORT_COMMAND 0x00 /* read/write, 8-bit */
|
||||
#define PORT_STATUS 0x02 /* read only, 8-bit */
|
||||
#define PORT_AUXDMA 0x02 /* write only, 8-bit */
|
||||
#define PORT_DATA 0x04 /* read/write, 16-bit */
|
||||
#define PORT_CONTROL 0x06 /* read/write, 8-bit */
|
||||
|
||||
#define ELP_IO_EXTENT 0x10 /* size of used IO registers */
|
||||
|
||||
/*
|
||||
* host control registers bits
|
||||
*/
|
||||
#define ATTN 0x80 /* attention */
|
||||
#define FLSH 0x40 /* flush data register */
|
||||
#define DMAE 0x20 /* DMA enable */
|
||||
#define DIR 0x10 /* direction */
|
||||
#define TCEN 0x08 /* terminal count interrupt enable */
|
||||
#define CMDE 0x04 /* command register interrupt enable */
|
||||
#define HSF2 0x02 /* host status flag 2 */
|
||||
#define HSF1 0x01 /* host status flag 1 */
|
||||
|
||||
/*
|
||||
* combinations of HSF flags used for PCB transmission
|
||||
*/
|
||||
#define HSF_PCB_ACK HSF1
|
||||
#define HSF_PCB_NAK HSF2
|
||||
#define HSF_PCB_END (HSF2|HSF1)
|
||||
#define HSF_PCB_MASK (HSF2|HSF1)
|
||||
|
||||
/*
|
||||
* host status register bits
|
||||
*/
|
||||
#define HRDY 0x80 /* data register ready */
|
||||
#define HCRE 0x40 /* command register empty */
|
||||
#define ACRF 0x20 /* adapter command register full */
|
||||
/* #define DIR 0x10 direction - same as in control register */
|
||||
#define DONE 0x08 /* DMA done */
|
||||
#define ASF3 0x04 /* adapter status flag 3 */
|
||||
#define ASF2 0x02 /* adapter status flag 2 */
|
||||
#define ASF1 0x01 /* adapter status flag 1 */
|
||||
|
||||
/*
|
||||
* combinations of ASF flags used for PCB reception
|
||||
*/
|
||||
#define ASF_PCB_ACK ASF1
|
||||
#define ASF_PCB_NAK ASF2
|
||||
#define ASF_PCB_END (ASF2|ASF1)
|
||||
#define ASF_PCB_MASK (ASF2|ASF1)
|
||||
|
||||
/*
|
||||
* host aux DMA register bits
|
||||
*/
|
||||
#define DMA_BRST 0x01 /* DMA burst */
|
||||
|
||||
/*
|
||||
* maximum amount of data allowed in a PCB
|
||||
*/
|
||||
#define MAX_PCB_DATA 62
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* timeout value
|
||||
* this is a rough value used for loops to stop them from
|
||||
* locking up the whole machine in the case of failure or
|
||||
* error conditions
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#define TIMEOUT 300
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* PCB commands
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
enum {
|
||||
/*
|
||||
* host PCB commands
|
||||
*/
|
||||
CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
|
||||
CMD_CONFIGURE_82586 = 0x02,
|
||||
CMD_STATION_ADDRESS = 0x03,
|
||||
CMD_DMA_DOWNLOAD = 0x04,
|
||||
CMD_DMA_UPLOAD = 0x05,
|
||||
CMD_PIO_DOWNLOAD = 0x06,
|
||||
CMD_PIO_UPLOAD = 0x07,
|
||||
CMD_RECEIVE_PACKET = 0x08,
|
||||
CMD_TRANSMIT_PACKET = 0x09,
|
||||
CMD_NETWORK_STATISTICS = 0x0a,
|
||||
CMD_LOAD_MULTICAST_LIST = 0x0b,
|
||||
CMD_CLEAR_PROGRAM = 0x0c,
|
||||
CMD_DOWNLOAD_PROGRAM = 0x0d,
|
||||
CMD_EXECUTE_PROGRAM = 0x0e,
|
||||
CMD_SELF_TEST = 0x0f,
|
||||
CMD_SET_STATION_ADDRESS = 0x10,
|
||||
CMD_ADAPTER_INFO = 0x11,
|
||||
NUM_TRANSMIT_CMDS,
|
||||
|
||||
/*
|
||||
* adapter PCB commands
|
||||
*/
|
||||
CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
|
||||
CMD_CONFIGURE_82586_RESPONSE = 0x32,
|
||||
CMD_ADDRESS_RESPONSE = 0x33,
|
||||
CMD_DOWNLOAD_DATA_REQUEST = 0x34,
|
||||
CMD_UPLOAD_DATA_REQUEST = 0x35,
|
||||
CMD_RECEIVE_PACKET_COMPLETE = 0x38,
|
||||
CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
|
||||
CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
|
||||
CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
|
||||
CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
|
||||
CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
|
||||
CMD_EXECUTE_RESPONSE = 0x3e,
|
||||
CMD_SELF_TEST_RESPONSE = 0x3f,
|
||||
CMD_SET_ADDRESS_RESPONSE = 0x40,
|
||||
CMD_ADAPTER_INFO_RESPONSE = 0x41
|
||||
};
|
||||
|
||||
/* Definitions for the PCB data structure */
|
||||
|
||||
/* Data units */
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short int word;
|
||||
typedef unsigned long int dword;
|
||||
|
||||
/* Data structures */
|
||||
struct Memconf {
|
||||
word cmd_q,
|
||||
rcv_q,
|
||||
mcast,
|
||||
frame,
|
||||
rcv_b,
|
||||
progs;
|
||||
};
|
||||
|
||||
struct Rcv_pkt {
|
||||
word buf_ofs,
|
||||
buf_seg,
|
||||
buf_len,
|
||||
timeout;
|
||||
};
|
||||
|
||||
struct Xmit_pkt {
|
||||
word buf_ofs,
|
||||
buf_seg,
|
||||
pkt_len;
|
||||
};
|
||||
|
||||
struct Rcv_resp {
|
||||
word buf_ofs,
|
||||
buf_seg,
|
||||
buf_len,
|
||||
pkt_len,
|
||||
timeout,
|
||||
status;
|
||||
dword timetag;
|
||||
};
|
||||
|
||||
struct Xmit_resp {
|
||||
word buf_ofs,
|
||||
buf_seg,
|
||||
c_stat,
|
||||
status;
|
||||
};
|
||||
|
||||
|
||||
struct Netstat {
|
||||
dword tot_recv,
|
||||
tot_xmit;
|
||||
word err_CRC,
|
||||
err_align,
|
||||
err_res,
|
||||
err_ovrrun;
|
||||
};
|
||||
|
||||
|
||||
struct Selftest {
|
||||
word error;
|
||||
union {
|
||||
word ROM_cksum;
|
||||
struct {
|
||||
word ofs, seg;
|
||||
} RAM;
|
||||
word i82586;
|
||||
} failure;
|
||||
};
|
||||
|
||||
struct Info {
|
||||
byte minor_vers,
|
||||
major_vers;
|
||||
word ROM_cksum,
|
||||
RAM_sz,
|
||||
free_ofs,
|
||||
free_seg;
|
||||
};
|
||||
|
||||
struct Memdump {
|
||||
word size,
|
||||
off,
|
||||
seg;
|
||||
};
|
||||
|
||||
/*
|
||||
Primary Command Block. The most important data structure. All communication
|
||||
between the host and the adapter is done with these. (Except for the actual
|
||||
Ethernet data, which has different packaging.)
|
||||
*/
|
||||
typedef struct {
|
||||
byte command;
|
||||
byte length;
|
||||
union {
|
||||
struct Memconf memconf;
|
||||
word configure;
|
||||
struct Rcv_pkt rcv_pkt;
|
||||
struct Xmit_pkt xmit_pkt;
|
||||
byte multicast[10][6];
|
||||
byte eth_addr[6];
|
||||
byte failed;
|
||||
struct Rcv_resp rcv_resp;
|
||||
struct Xmit_resp xmit_resp;
|
||||
struct Netstat netstat;
|
||||
struct Selftest selftest;
|
||||
struct Info info;
|
||||
struct Memdump memdump;
|
||||
byte raw[62];
|
||||
} data;
|
||||
} pcb_struct;
|
||||
|
||||
/* These defines for 'configure' */
|
||||
#define RECV_STATION 0x00
|
||||
#define RECV_BROAD 0x01
|
||||
#define RECV_MULTI 0x02
|
||||
#define RECV_PROMISC 0x04
|
||||
#define NO_LOOPBACK 0x00
|
||||
#define INT_LOOPBACK 0x08
|
||||
#define EXT_LOOPBACK 0x10
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* structure to hold context information for adapter
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#define DMA_BUFFER_SIZE 1600
|
||||
#define BACKLOG_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
volatile short got[NUM_TRANSMIT_CMDS]; /* flags for
|
||||
command completion */
|
||||
pcb_struct tx_pcb; /* PCB for foreground sending */
|
||||
pcb_struct rx_pcb; /* PCB for foreground receiving */
|
||||
pcb_struct itx_pcb; /* PCB for background sending */
|
||||
pcb_struct irx_pcb; /* PCB for background receiving */
|
||||
|
||||
void *dma_buffer;
|
||||
|
||||
struct {
|
||||
unsigned int length[BACKLOG_SIZE];
|
||||
unsigned int in;
|
||||
unsigned int out;
|
||||
} rx_backlog;
|
||||
|
||||
struct {
|
||||
unsigned int direction;
|
||||
unsigned int length;
|
||||
struct sk_buff *skb;
|
||||
void *target;
|
||||
unsigned long start_time;
|
||||
} current_dma;
|
||||
|
||||
/* flags */
|
||||
unsigned long send_pcb_semaphore;
|
||||
unsigned long dmaing;
|
||||
unsigned long busy;
|
||||
|
||||
unsigned int rx_active; /* number of receive PCBs */
|
||||
volatile unsigned char hcr_val; /* what we think the HCR contains */
|
||||
spinlock_t lock; /* Interrupt v tx lock */
|
||||
} elp_device;
|
@ -1,939 +0,0 @@
|
||||
/* 3c507.c: An EtherLink16 device driver for Linux. */
|
||||
/*
|
||||
Written 1993,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.
|
||||
|
||||
The author may be reached as becker@scyld.com, or C/O
|
||||
Scyld Computing Corporation
|
||||
410 Severn Ave., Suite 210
|
||||
Annapolis MD 21403
|
||||
|
||||
|
||||
Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
|
||||
and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
|
||||
Mark Salazar <leslie@access.digex.net> made the changes for cards with
|
||||
only 16K packet buffers.
|
||||
|
||||
Things remaining to do:
|
||||
Verify that the tx and rx buffers don't have fencepost errors.
|
||||
Move the theory of operation and memory map documentation.
|
||||
The statistics need to be updated correctly.
|
||||
*/
|
||||
|
||||
#define DRV_NAME "3c507"
|
||||
#define DRV_VERSION "1.10a"
|
||||
#define DRV_RELDATE "11/17/2001"
|
||||
|
||||
static const char version[] =
|
||||
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
|
||||
|
||||
/*
|
||||
Sources:
|
||||
This driver wouldn't have been written with the availability of the
|
||||
Crynwr driver source code. It provided a known-working implementation
|
||||
that filled in the gaping holes of the Intel documentation. Three cheers
|
||||
for Russ Nelson.
|
||||
|
||||
Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
|
||||
info that the casual reader might think that it documents the i82586 :-<.
|
||||
*/
|
||||
|
||||
#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/spinlock.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/* use 0 for production, 1 for verification, 2..7 for debug */
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 1
|
||||
#endif
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
#define debug net_debug
|
||||
|
||||
|
||||
/*
|
||||
Details of the i82586.
|
||||
|
||||
You'll really need the databook to understand the details of this part,
|
||||
but the outline is that the i82586 has two separate processing units.
|
||||
Both are started from a list of three configuration tables, of which only
|
||||
the last, the System Control Block (SCB), is used after reset-time. The SCB
|
||||
has the following fields:
|
||||
Status word
|
||||
Command word
|
||||
Tx/Command block addr.
|
||||
Rx block addr.
|
||||
The command word accepts the following controls for the Tx and Rx units:
|
||||
*/
|
||||
|
||||
#define CUC_START 0x0100
|
||||
#define CUC_RESUME 0x0200
|
||||
#define CUC_SUSPEND 0x0300
|
||||
#define RX_START 0x0010
|
||||
#define RX_RESUME 0x0020
|
||||
#define RX_SUSPEND 0x0030
|
||||
|
||||
/* The Rx unit uses a list of frame descriptors and a list of data buffer
|
||||
descriptors. We use full-sized (1518 byte) data buffers, so there is
|
||||
a one-to-one pairing of frame descriptors to buffer descriptors.
|
||||
|
||||
The Tx ("command") unit executes a list of commands that look like:
|
||||
Status word Written by the 82586 when the command is done.
|
||||
Command word Command in lower 3 bits, post-command action in upper 3
|
||||
Link word The address of the next command.
|
||||
Parameters (as needed).
|
||||
|
||||
Some definitions related to the Command Word are:
|
||||
*/
|
||||
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
|
||||
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
|
||||
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
|
||||
|
||||
enum commands {
|
||||
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
|
||||
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
|
||||
|
||||
/* Information that need to be kept for each board. */
|
||||
struct net_local {
|
||||
int last_restart;
|
||||
ushort rx_head;
|
||||
ushort rx_tail;
|
||||
ushort tx_head;
|
||||
ushort tx_cmd_link;
|
||||
ushort tx_reap;
|
||||
ushort tx_pkts_in_ring;
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
/*
|
||||
Details of the EtherLink16 Implementation
|
||||
The 3c507 is a generic shared-memory i82586 implementation.
|
||||
The host can map 16K, 32K, 48K, or 64K of the 64K memory into
|
||||
0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
|
||||
*/
|
||||
|
||||
/* Offsets from the base I/O address. */
|
||||
#define SA_DATA 0 /* Station address data, or 3Com signature. */
|
||||
#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
|
||||
#define RESET_IRQ 10 /* Reset the latched IRQ line. */
|
||||
#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
|
||||
#define ROM_CONFIG 13
|
||||
#define MEM_CONFIG 14
|
||||
#define IRQ_CONFIG 15
|
||||
#define EL16_IO_EXTENT 16
|
||||
|
||||
/* The ID port is used at boot-time to locate the ethercard. */
|
||||
#define ID_PORT 0x100
|
||||
|
||||
/* Offsets to registers in the mailbox (SCB). */
|
||||
#define iSCB_STATUS 0x8
|
||||
#define iSCB_CMD 0xA
|
||||
#define iSCB_CBL 0xC /* Command BLock offset. */
|
||||
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
|
||||
|
||||
/* Since the 3c507 maps the shared memory window so that the last byte is
|
||||
at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
|
||||
48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
|
||||
We can account for this be setting the 'SBC Base' entry in the ISCP table
|
||||
below for all the 16 bit offset addresses, and also adding the 'SCB Base'
|
||||
value to all 24 bit physical addresses (in the SCP table and the TX and RX
|
||||
Buffer Descriptors).
|
||||
-Mark
|
||||
*/
|
||||
#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start))
|
||||
|
||||
/*
|
||||
What follows in 'init_words[]' is the "program" that is downloaded to the
|
||||
82586 memory. It's mostly tables and command blocks, and starts at the
|
||||
reset address 0xfffff6. This is designed to be similar to the EtherExpress,
|
||||
thus the unusual location of the SCB at 0x0008.
|
||||
|
||||
Even with the additional "don't care" values, doing it this way takes less
|
||||
program space than initializing the individual tables, and I feel it's much
|
||||
cleaner.
|
||||
|
||||
The databook is particularly useless for the first two structures, I had
|
||||
to use the Crynwr driver as an example.
|
||||
|
||||
The memory setup is as follows:
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD 0x0018
|
||||
#define SET_SA_CMD 0x0024
|
||||
#define SA_OFFSET 0x002A
|
||||
#define IDLELOOP 0x30
|
||||
#define TDR_CMD 0x38
|
||||
#define TDR_TIME 0x3C
|
||||
#define DUMP_CMD 0x40
|
||||
#define DIAG_CMD 0x48
|
||||
#define SET_MC_CMD 0x4E
|
||||
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
|
||||
|
||||
#define TX_BUF_START 0x0100
|
||||
#define NUM_TX_BUFS 5
|
||||
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
|
||||
|
||||
#define RX_BUF_START 0x2000
|
||||
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
|
||||
#define RX_BUF_END (dev->mem_end - dev->mem_start)
|
||||
|
||||
#define TX_TIMEOUT (HZ/20)
|
||||
|
||||
/*
|
||||
That's it: only 86 bytes to set up the beast, including every extra
|
||||
command available. The 170 byte buffer at DUMP_DATA is shared between the
|
||||
Dump command (called only by the diagnostic program) and the SetMulticastList
|
||||
command.
|
||||
|
||||
To complete the memory setup you only have to write the station address at
|
||||
SA_OFFSET and create the Tx & Rx buffer lists.
|
||||
|
||||
The Tx command chain and buffer list is setup as follows:
|
||||
A Tx command table, with the data buffer pointing to...
|
||||
A Tx data buffer descriptor. The packet is in a single buffer, rather than
|
||||
chaining together several smaller buffers.
|
||||
A NoOp command, which initially points to itself,
|
||||
And the packet data.
|
||||
|
||||
A transmit is done by filling in the Tx command table and data buffer,
|
||||
re-writing the NoOp command, and finally changing the offset of the last
|
||||
command to point to the current Tx command. When the Tx command is finished,
|
||||
it jumps to the NoOp, when it loops until the next Tx command changes the
|
||||
"link offset" in the NoOp. This way the 82586 never has to go through the
|
||||
slow restart sequence.
|
||||
|
||||
The Rx buffer list is set up in the obvious ring structure. We have enough
|
||||
memory (and low enough interrupt latency) that we can avoid the complicated
|
||||
Rx buffer linked lists by alway associating a full-size Rx data buffer with
|
||||
each Rx data frame.
|
||||
|
||||
I current use four transmit buffers starting at TX_BUF_START (0x0100), and
|
||||
use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
|
||||
|
||||
*/
|
||||
|
||||
static unsigned short init_words[] = {
|
||||
/* System Configuration Pointer (SCP). */
|
||||
0x0000, /* Set bus size to 16 bits. */
|
||||
0,0, /* pad words. */
|
||||
0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
|
||||
|
||||
/* Intermediate System Configuration Pointer (ISCP). */
|
||||
0x0001, /* Status word that's cleared when init is done. */
|
||||
0x0008,0,0, /* SCB offset, (skip, skip) */
|
||||
|
||||
/* System Control Block (SCB). */
|
||||
0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
|
||||
CONFIG_CMD, /* Command list pointer, points to Configure. */
|
||||
RX_BUF_START, /* Rx block list. */
|
||||
0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
|
||||
|
||||
/* 0x0018: Configure command. Change to put MAC data with packet. */
|
||||
0, CmdConfigure, /* Status, command. */
|
||||
SET_SA_CMD, /* Next command is Set Station Addr. */
|
||||
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
|
||||
0x2e40, /* Magic values, including MAC data location. */
|
||||
0, /* Unused pad word. */
|
||||
|
||||
/* 0x0024: Setup station address command. */
|
||||
0, CmdSASetup,
|
||||
SET_MC_CMD, /* Next command. */
|
||||
0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
|
||||
|
||||
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
|
||||
0, CmdNOp, IDLELOOP, 0 /* pad */,
|
||||
|
||||
/* 0x0038: A unused Time-Domain Reflectometer command. */
|
||||
0, CmdTDR, IDLELOOP, 0,
|
||||
|
||||
/* 0x0040: An unused Dump State command. */
|
||||
0, CmdDump, IDLELOOP, DUMP_DATA,
|
||||
|
||||
/* 0x0048: An unused Diagnose command. */
|
||||
0, CmdDiagnose, IDLELOOP,
|
||||
|
||||
/* 0x004E: An empty set-multicast-list command. */
|
||||
0, CmdMulticastList, IDLELOOP, 0,
|
||||
};
|
||||
|
||||
/* Index to functions, as function prototypes. */
|
||||
|
||||
static int el16_probe1(struct net_device *dev, int ioaddr);
|
||||
static int el16_open(struct net_device *dev);
|
||||
static netdev_tx_t el16_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
static irqreturn_t el16_interrupt(int irq, void *dev_id);
|
||||
static void el16_rx(struct net_device *dev);
|
||||
static int el16_close(struct net_device *dev);
|
||||
static void el16_tx_timeout (struct net_device *dev);
|
||||
|
||||
static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad);
|
||||
static void init_82586_mem(struct net_device *dev);
|
||||
static const struct ethtool_ops netdev_ethtool_ops;
|
||||
static void init_rx_bufs(struct net_device *);
|
||||
|
||||
static int io = 0x300;
|
||||
static int irq;
|
||||
static int mem_start;
|
||||
|
||||
|
||||
/* Check for a network adaptor of this type, and return '0' iff one exists.
|
||||
If dev->base_addr == 0, probe all likely locations.
|
||||
If dev->base_addr == 1, always return failure.
|
||||
If dev->base_addr == 2, (detachable devices only) allocate space for the
|
||||
device and return success.
|
||||
*/
|
||||
|
||||
struct net_device * __init el16_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
|
||||
static const unsigned ports[] = { 0x300, 0x320, 0x340, 0x280, 0};
|
||||
const unsigned *port;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (unit >= 0) {
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
io = dev->base_addr;
|
||||
irq = dev->irq;
|
||||
mem_start = dev->mem_start & 15;
|
||||
}
|
||||
|
||||
if (io > 0x1ff) /* Check a single specified location. */
|
||||
err = el16_probe1(dev, io);
|
||||
else if (io != 0)
|
||||
err = -ENXIO; /* Don't probe at all. */
|
||||
else {
|
||||
for (port = ports; *port; port++) {
|
||||
err = el16_probe1(dev, *port);
|
||||
if (!err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out1;
|
||||
return dev;
|
||||
out1:
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(((struct net_local *)netdev_priv(dev))->base);
|
||||
release_region(dev->base_addr, EL16_IO_EXTENT);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static const struct net_device_ops netdev_ops = {
|
||||
.ndo_open = el16_open,
|
||||
.ndo_stop = el16_close,
|
||||
.ndo_start_xmit = el16_send_packet,
|
||||
.ndo_tx_timeout = el16_tx_timeout,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
static int __init el16_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
static unsigned char init_ID_done;
|
||||
int i, irq, irqval, retval;
|
||||
struct net_local *lp;
|
||||
|
||||
if (init_ID_done == 0) {
|
||||
ushort lrs_state = 0xff;
|
||||
/* Send the ID sequence to the ID_PORT to enable the board(s). */
|
||||
outb(0x00, ID_PORT);
|
||||
for(i = 0; i < 255; i++) {
|
||||
outb(lrs_state, ID_PORT);
|
||||
lrs_state <<= 1;
|
||||
if (lrs_state & 0x100)
|
||||
lrs_state ^= 0xe7;
|
||||
}
|
||||
outb(0x00, ID_PORT);
|
||||
init_ID_done = 1;
|
||||
}
|
||||
|
||||
if (!request_region(ioaddr, EL16_IO_EXTENT, DRV_NAME))
|
||||
return -ENODEV;
|
||||
|
||||
if ((inb(ioaddr) != '*') || (inb(ioaddr + 1) != '3') ||
|
||||
(inb(ioaddr + 2) != 'C') || (inb(ioaddr + 3) != 'O')) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_info("%s: 3c507 at %#x,", dev->name, ioaddr);
|
||||
|
||||
/* We should make a few more checks here, like the first three octets of
|
||||
the S.A. for the manufacturer's code. */
|
||||
|
||||
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
|
||||
|
||||
irqval = request_irq(irq, el16_interrupt, 0, DRV_NAME, dev);
|
||||
if (irqval) {
|
||||
pr_cont("\n");
|
||||
pr_err("3c507: unable to get IRQ %d (irqval=%d).\n", irq, irqval);
|
||||
retval = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We've committed to using the board, and can start filling in *dev. */
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
outb(0x01, ioaddr + MISC_CTRL);
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = inb(ioaddr + i);
|
||||
pr_cont(" %pM", dev->dev_addr);
|
||||
|
||||
if (mem_start)
|
||||
net_debug = mem_start & 7;
|
||||
|
||||
#ifdef MEM_BASE
|
||||
dev->mem_start = MEM_BASE;
|
||||
dev->mem_end = dev->mem_start + 0x10000;
|
||||
#else
|
||||
{
|
||||
int base;
|
||||
int size;
|
||||
char mem_config = inb(ioaddr + MEM_CONFIG);
|
||||
if (mem_config & 0x20) {
|
||||
size = 64*1024;
|
||||
base = 0xf00000 + (mem_config & 0x08 ? 0x080000
|
||||
: ((mem_config & 3) << 17));
|
||||
} else {
|
||||
size = ((mem_config & 3) + 1) << 14;
|
||||
base = 0x0c0000 + ( (mem_config & 0x18) << 12);
|
||||
}
|
||||
dev->mem_start = base;
|
||||
dev->mem_end = base + size;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
|
||||
dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
|
||||
|
||||
pr_cont(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq,
|
||||
dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
|
||||
|
||||
if (net_debug)
|
||||
pr_debug("%s", version);
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
spin_lock_init(&lp->lock);
|
||||
lp->base = ioremap(dev->mem_start, RX_BUF_END);
|
||||
if (!lp->base) {
|
||||
pr_err("3c507: unable to remap memory\n");
|
||||
retval = -EAGAIN;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
dev->netdev_ops = &netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */
|
||||
return 0;
|
||||
out1:
|
||||
free_irq(dev->irq, dev);
|
||||
out:
|
||||
release_region(ioaddr, EL16_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int el16_open(struct net_device *dev)
|
||||
{
|
||||
/* Initialize the 82586 memory and start it. */
|
||||
init_82586_mem(dev);
|
||||
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void el16_tx_timeout (struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
void __iomem *shmem = lp->base;
|
||||
|
||||
if (net_debug > 1)
|
||||
pr_debug("%s: transmit timed out, %s? ", dev->name,
|
||||
readw(shmem + iSCB_STATUS) & 0x8000 ? "IRQ conflict" :
|
||||
"network cable problem");
|
||||
/* Try to restart the adaptor. */
|
||||
if (lp->last_restart == dev->stats.tx_packets) {
|
||||
if (net_debug > 1)
|
||||
pr_cont("Resetting board.\n");
|
||||
/* Completely reset the adaptor. */
|
||||
init_82586_mem (dev);
|
||||
lp->tx_pkts_in_ring = 0;
|
||||
} else {
|
||||
/* Issue the channel attention signal and hope it "gets better". */
|
||||
if (net_debug > 1)
|
||||
pr_cont("Kicking board.\n");
|
||||
writew(0xf000 | CUC_START | RX_START, shmem + iSCB_CMD);
|
||||
outb (0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
|
||||
lp->last_restart = dev->stats.tx_packets;
|
||||
}
|
||||
dev->trans_start = jiffies; /* prevent tx timeout */
|
||||
netif_wake_queue (dev);
|
||||
}
|
||||
|
||||
|
||||
static netdev_tx_t el16_send_packet (struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
unsigned long flags;
|
||||
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
|
||||
unsigned char *buf = skb->data;
|
||||
|
||||
netif_stop_queue (dev);
|
||||
|
||||
spin_lock_irqsave (&lp->lock, flags);
|
||||
|
||||
dev->stats.tx_bytes += length;
|
||||
/* Disable the 82586's input to the interrupt line. */
|
||||
outb (0x80, ioaddr + MISC_CTRL);
|
||||
|
||||
hardware_send_packet (dev, buf, skb->len, length - skb->len);
|
||||
|
||||
/* Enable the 82586 interrupt input. */
|
||||
outb (0x84, ioaddr + MISC_CTRL);
|
||||
|
||||
spin_unlock_irqrestore (&lp->lock, flags);
|
||||
|
||||
dev_kfree_skb (skb);
|
||||
|
||||
/* You might need to clean up and record Tx statistics here. */
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* The typical workload of the driver:
|
||||
Handle the network interface interrupts. */
|
||||
static irqreturn_t el16_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct net_local *lp;
|
||||
int ioaddr, status, boguscount = 0;
|
||||
ushort ack_cmd = 0;
|
||||
void __iomem *shmem;
|
||||
|
||||
if (dev == NULL) {
|
||||
pr_err("net_interrupt(): irq %d for unknown device.\n", irq);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
lp = netdev_priv(dev);
|
||||
shmem = lp->base;
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
|
||||
status = readw(shmem+iSCB_STATUS);
|
||||
|
||||
if (net_debug > 4) {
|
||||
pr_debug("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
|
||||
}
|
||||
|
||||
/* Disable the 82586's input to the interrupt line. */
|
||||
outb(0x80, ioaddr + MISC_CTRL);
|
||||
|
||||
/* Reap the Tx packet buffers. */
|
||||
while (lp->tx_pkts_in_ring) {
|
||||
unsigned short tx_status = readw(shmem+lp->tx_reap);
|
||||
if (!(tx_status & 0x8000)) {
|
||||
if (net_debug > 5)
|
||||
pr_debug("Tx command incomplete (%#x).\n", lp->tx_reap);
|
||||
break;
|
||||
}
|
||||
/* Tx unsuccessful or some interesting status bit set. */
|
||||
if (!(tx_status & 0x2000) || (tx_status & 0x0f3f)) {
|
||||
dev->stats.tx_errors++;
|
||||
if (tx_status & 0x0600) dev->stats.tx_carrier_errors++;
|
||||
if (tx_status & 0x0100) dev->stats.tx_fifo_errors++;
|
||||
if (!(tx_status & 0x0040)) dev->stats.tx_heartbeat_errors++;
|
||||
if (tx_status & 0x0020) dev->stats.tx_aborted_errors++;
|
||||
dev->stats.collisions += tx_status & 0xf;
|
||||
}
|
||||
dev->stats.tx_packets++;
|
||||
if (net_debug > 5)
|
||||
pr_debug("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
|
||||
lp->tx_reap += TX_BUF_SIZE;
|
||||
if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
|
||||
lp->tx_reap = TX_BUF_START;
|
||||
|
||||
lp->tx_pkts_in_ring--;
|
||||
/* There is always more space in the Tx ring buffer now. */
|
||||
netif_wake_queue(dev);
|
||||
|
||||
if (++boguscount > 10)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status & 0x4000) { /* Packet received. */
|
||||
if (net_debug > 5)
|
||||
pr_debug("Received packet, rx_head %04x.\n", lp->rx_head);
|
||||
el16_rx(dev);
|
||||
}
|
||||
|
||||
/* Acknowledge the interrupt sources. */
|
||||
ack_cmd = status & 0xf000;
|
||||
|
||||
if ((status & 0x0700) != 0x0200 && netif_running(dev)) {
|
||||
if (net_debug)
|
||||
pr_debug("%s: Command unit stopped, status %04x, restarting.\n",
|
||||
dev->name, status);
|
||||
/* If this ever occurs we should really re-write the idle loop, reset
|
||||
the Tx list, and do a complete restart of the command unit.
|
||||
For now we rely on the Tx timeout if the resume doesn't work. */
|
||||
ack_cmd |= CUC_RESUME;
|
||||
}
|
||||
|
||||
if ((status & 0x0070) != 0x0040 && netif_running(dev)) {
|
||||
/* The Rx unit is not ready, it must be hung. Restart the receiver by
|
||||
initializing the rx buffers, and issuing an Rx start command. */
|
||||
if (net_debug)
|
||||
pr_debug("%s: Rx unit stopped, status %04x, restarting.\n",
|
||||
dev->name, status);
|
||||
init_rx_bufs(dev);
|
||||
writew(RX_BUF_START,shmem+iSCB_RFA);
|
||||
ack_cmd |= RX_START;
|
||||
}
|
||||
|
||||
writew(ack_cmd,shmem+iSCB_CMD);
|
||||
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
|
||||
|
||||
/* Clear the latched interrupt. */
|
||||
outb(0, ioaddr + RESET_IRQ);
|
||||
|
||||
/* Enable the 82586's interrupt input. */
|
||||
outb(0x84, ioaddr + MISC_CTRL);
|
||||
spin_unlock(&lp->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int el16_close(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
void __iomem *shmem = lp->base;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/* Flush the Tx and disable Rx. */
|
||||
writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD);
|
||||
outb(0, ioaddr + SIGNAL_CA);
|
||||
|
||||
/* Disable the 82586's input to the interrupt line. */
|
||||
outb(0x80, ioaddr + MISC_CTRL);
|
||||
|
||||
/* We always physically use the IRQ line, so we don't do free_irq(). */
|
||||
|
||||
/* Update the statistics here. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the Rx-block list. */
|
||||
static void init_rx_bufs(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
void __iomem *write_ptr;
|
||||
unsigned short SCB_base = SCB_BASE;
|
||||
|
||||
int cur_rxbuf = lp->rx_head = RX_BUF_START;
|
||||
|
||||
/* Initialize each Rx frame + data buffer. */
|
||||
do { /* While there is room for one more. */
|
||||
|
||||
write_ptr = lp->base + cur_rxbuf;
|
||||
|
||||
writew(0x0000,write_ptr); /* Status */
|
||||
writew(0x0000,write_ptr+=2); /* Command */
|
||||
writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */
|
||||
writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */
|
||||
writew(0x0000,write_ptr+=2); /* Pad for dest addr. */
|
||||
writew(0x0000,write_ptr+=2);
|
||||
writew(0x0000,write_ptr+=2);
|
||||
writew(0x0000,write_ptr+=2); /* Pad for source addr. */
|
||||
writew(0x0000,write_ptr+=2);
|
||||
writew(0x0000,write_ptr+=2);
|
||||
writew(0x0000,write_ptr+=2); /* Pad for protocol. */
|
||||
|
||||
writew(0x0000,write_ptr+=2); /* Buffer: Actual count */
|
||||
writew(-1,write_ptr+=2); /* Buffer: Next (none). */
|
||||
writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */
|
||||
writew(0x0000,write_ptr+=2);
|
||||
/* Finally, the number of bytes in the buffer. */
|
||||
writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2);
|
||||
|
||||
lp->rx_tail = cur_rxbuf;
|
||||
cur_rxbuf += RX_BUF_SIZE;
|
||||
} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
|
||||
|
||||
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
|
||||
the list a ring. */
|
||||
write_ptr = lp->base + lp->rx_tail + 2;
|
||||
writew(0xC000,write_ptr); /* Command, mark as last. */
|
||||
writew(lp->rx_head,write_ptr+2); /* Link */
|
||||
}
|
||||
|
||||
static void init_82586_mem(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
short ioaddr = dev->base_addr;
|
||||
void __iomem *shmem = lp->base;
|
||||
|
||||
/* Enable loopback to protect the wire while starting up,
|
||||
and hold the 586 in reset during the memory initialization. */
|
||||
outb(0x20, ioaddr + MISC_CTRL);
|
||||
|
||||
/* Fix the ISCP address and base. */
|
||||
init_words[3] = SCB_BASE;
|
||||
init_words[7] = SCB_BASE;
|
||||
|
||||
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
|
||||
memcpy_toio(lp->base + RX_BUF_END - 10, init_words, 10);
|
||||
|
||||
/* Write the words at 0x0000. */
|
||||
memcpy_toio(lp->base, init_words + 5, sizeof(init_words) - 10);
|
||||
|
||||
/* Fill in the station address. */
|
||||
memcpy_toio(lp->base+SA_OFFSET, dev->dev_addr, ETH_ALEN);
|
||||
|
||||
/* The Tx-block list is written as needed. We just set up the values. */
|
||||
lp->tx_cmd_link = IDLELOOP + 4;
|
||||
lp->tx_head = lp->tx_reap = TX_BUF_START;
|
||||
|
||||
init_rx_bufs(dev);
|
||||
|
||||
/* Start the 586 by releasing the reset line, but leave loopback. */
|
||||
outb(0xA0, ioaddr + MISC_CTRL);
|
||||
|
||||
/* This was time consuming to track down: you need to give two channel
|
||||
attention signals to reliably start up the i82586. */
|
||||
outb(0, ioaddr + SIGNAL_CA);
|
||||
|
||||
{
|
||||
int boguscnt = 50;
|
||||
while (readw(shmem+iSCB_STATUS) == 0)
|
||||
if (--boguscnt == 0) {
|
||||
pr_warning("%s: i82586 initialization timed out with status %04x, cmd %04x.\n",
|
||||
dev->name, readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD));
|
||||
break;
|
||||
}
|
||||
/* Issue channel-attn -- the 82586 won't start. */
|
||||
outb(0, ioaddr + SIGNAL_CA);
|
||||
}
|
||||
|
||||
/* Disable loopback and enable interrupts. */
|
||||
outb(0x84, ioaddr + MISC_CTRL);
|
||||
if (net_debug > 4)
|
||||
pr_debug("%s: Initialized 82586, status %04x.\n", dev->name,
|
||||
readw(shmem+iSCB_STATUS));
|
||||
}
|
||||
|
||||
static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
short ioaddr = dev->base_addr;
|
||||
ushort tx_block = lp->tx_head;
|
||||
void __iomem *write_ptr = lp->base + tx_block;
|
||||
static char padding[ETH_ZLEN];
|
||||
|
||||
/* Set the write pointer to the Tx block, and put out the header. */
|
||||
writew(0x0000,write_ptr); /* Tx status */
|
||||
writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */
|
||||
writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */
|
||||
writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */
|
||||
|
||||
/* Output the data buffer descriptor. */
|
||||
writew((pad + length) | 0x8000,write_ptr+=2); /* Byte count parameter. */
|
||||
writew(-1,write_ptr+=2); /* No next data buffer. */
|
||||
writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */
|
||||
writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */
|
||||
|
||||
/* Output the Loop-back NoOp command. */
|
||||
writew(0x0000,write_ptr+=2); /* Tx status */
|
||||
writew(CmdNOp,write_ptr+=2); /* Tx command */
|
||||
writew(tx_block+16,write_ptr+=2); /* Next is myself. */
|
||||
|
||||
/* Output the packet at the write pointer. */
|
||||
memcpy_toio(write_ptr+2, buf, length);
|
||||
if (pad)
|
||||
memcpy_toio(write_ptr+length+2, padding, pad);
|
||||
|
||||
/* Set the old command link pointing to this send packet. */
|
||||
writew(tx_block,lp->base + lp->tx_cmd_link);
|
||||
lp->tx_cmd_link = tx_block + 20;
|
||||
|
||||
/* Set the next free tx region. */
|
||||
lp->tx_head = tx_block + TX_BUF_SIZE;
|
||||
if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
|
||||
lp->tx_head = TX_BUF_START;
|
||||
|
||||
if (net_debug > 4) {
|
||||
pr_debug("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
|
||||
dev->name, ioaddr, length, tx_block, lp->tx_head);
|
||||
}
|
||||
|
||||
/* Grimly block further packets if there has been insufficient reaping. */
|
||||
if (++lp->tx_pkts_in_ring < NUM_TX_BUFS)
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static void el16_rx(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
void __iomem *shmem = lp->base;
|
||||
ushort rx_head = lp->rx_head;
|
||||
ushort rx_tail = lp->rx_tail;
|
||||
ushort boguscount = 10;
|
||||
short frame_status;
|
||||
|
||||
while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */
|
||||
void __iomem *read_frame = lp->base + rx_head;
|
||||
ushort rfd_cmd = readw(read_frame+2);
|
||||
ushort next_rx_frame = readw(read_frame+4);
|
||||
ushort data_buffer_addr = readw(read_frame+6);
|
||||
void __iomem *data_frame = lp->base + data_buffer_addr;
|
||||
ushort pkt_len = readw(data_frame);
|
||||
|
||||
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 ||
|
||||
(pkt_len & 0xC000) != 0xC000) {
|
||||
pr_err("%s: Rx frame at %#x corrupted, "
|
||||
"status %04x cmd %04x next %04x "
|
||||
"data-buf @%04x %04x.\n",
|
||||
dev->name, rx_head, frame_status, rfd_cmd,
|
||||
next_rx_frame, data_buffer_addr, pkt_len);
|
||||
} else if ((frame_status & 0x2000) == 0) {
|
||||
/* Frame Rxed, but with error. */
|
||||
dev->stats.rx_errors++;
|
||||
if (frame_status & 0x0800) dev->stats.rx_crc_errors++;
|
||||
if (frame_status & 0x0400) dev->stats.rx_frame_errors++;
|
||||
if (frame_status & 0x0200) dev->stats.rx_fifo_errors++;
|
||||
if (frame_status & 0x0100) dev->stats.rx_over_errors++;
|
||||
if (frame_status & 0x0080) dev->stats.rx_length_errors++;
|
||||
} else {
|
||||
/* Malloc up new buffer. */
|
||||
struct sk_buff *skb;
|
||||
|
||||
pkt_len &= 0x3fff;
|
||||
skb = netdev_alloc_skb(dev, pkt_len + 2);
|
||||
if (skb == NULL) {
|
||||
pr_err("%s: Memory squeeze, dropping packet.\n",
|
||||
dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
break;
|
||||
}
|
||||
|
||||
skb_reserve(skb,2);
|
||||
|
||||
/* 'skb->data' points to the start of sk_buff data area. */
|
||||
memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
|
||||
|
||||
skb->protocol=eth_type_trans(skb,dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
}
|
||||
|
||||
/* Clear the status word and set End-of-List on the rx frame. */
|
||||
writew(0,read_frame);
|
||||
writew(0xC000,read_frame+2);
|
||||
/* Clear the end-of-list on the prev. RFD. */
|
||||
writew(0x0000,lp->base + rx_tail + 2);
|
||||
|
||||
rx_tail = rx_head;
|
||||
rx_head = next_rx_frame;
|
||||
if (--boguscount == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
lp->rx_head = rx_head;
|
||||
lp->rx_tail = rx_tail;
|
||||
}
|
||||
|
||||
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 debug;
|
||||
}
|
||||
|
||||
static void netdev_set_msglevel(struct net_device *dev, u32 level)
|
||||
{
|
||||
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
|
||||
static struct net_device *dev_3c507;
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(io, "EtherLink16 I/O base address");
|
||||
MODULE_PARM_DESC(irq, "(ignored)");
|
||||
|
||||
int __init init_module(void)
|
||||
{
|
||||
if (io == 0)
|
||||
pr_notice("3c507: You should not use auto-probing with insmod!\n");
|
||||
dev_3c507 = el16_probe(-1);
|
||||
return IS_ERR(dev_3c507) ? PTR_ERR(dev_3c507) : 0;
|
||||
}
|
||||
|
||||
void __exit
|
||||
cleanup_module(void)
|
||||
{
|
||||
struct net_device *dev = dev_3c507;
|
||||
unregister_netdev(dev);
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(((struct net_local *)netdev_priv(dev))->base);
|
||||
release_region(dev->base_addr, EL16_IO_EXTENT);
|
||||
free_netdev(dev);
|
||||
}
|
||||
#endif /* MODULE */
|
||||
MODULE_LICENSE("GPL");
|
@ -95,9 +95,6 @@ static char version[] __initdata =
|
||||
#if defined(CONFIG_BVME6000_NET) || defined(CONFIG_BVME6000_NET_MODULE)
|
||||
#define ENABLE_BVME6000_NET
|
||||
#endif
|
||||
#if defined(CONFIG_APRICOT) || defined(CONFIG_APRICOT_MODULE)
|
||||
#define ENABLE_APRICOT
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MVME16x_NET
|
||||
#include <asm/mvme16xhw.h>
|
||||
@ -120,8 +117,15 @@ static char version[] __initdata =
|
||||
#define WSWAPtbd(x) ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
|
||||
#define WSWAPchar(x) ((char *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
|
||||
#define ISCP_BUSY 0x00010000
|
||||
#define MACH_IS_APRICOT 0
|
||||
#else
|
||||
#error 82596.c: unknown architecture
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These were the intel versions, left here for reference. There
|
||||
* are currently no x86 users of this legacy i82596 chip.
|
||||
*/
|
||||
#if 0
|
||||
#define WSWAPrfd(x) ((struct i596_rfd *)((long)x))
|
||||
#define WSWAPrbd(x) ((struct i596_rbd *)((long)x))
|
||||
#define WSWAPiscp(x) ((struct i596_iscp *)((long)x))
|
||||
@ -130,7 +134,6 @@ static char version[] __initdata =
|
||||
#define WSWAPtbd(x) ((struct i596_tbd *)((long)x))
|
||||
#define WSWAPchar(x) ((char *)((long)x))
|
||||
#define ISCP_BUSY 0x0001
|
||||
#define MACH_IS_APRICOT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -383,11 +386,6 @@ static inline void CA(struct net_device *dev)
|
||||
i = *(volatile u32 *) (dev->base_addr);
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_APRICOT
|
||||
if (MACH_IS_APRICOT) {
|
||||
outw(0, (short) (dev->base_addr) + 4);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -617,9 +615,6 @@ static void rebuild_rx_bufs(struct net_device *dev)
|
||||
static int init_i596_mem(struct net_device *dev)
|
||||
{
|
||||
struct i596_private *lp = dev->ml_priv;
|
||||
#if !defined(ENABLE_MVME16x_NET) && !defined(ENABLE_BVME6000_NET) || defined(ENABLE_APRICOT)
|
||||
short ioaddr = dev->base_addr;
|
||||
#endif
|
||||
unsigned long flags;
|
||||
|
||||
MPU_PORT(dev, PORT_RESET, NULL);
|
||||
@ -653,18 +648,6 @@ static int init_i596_mem(struct net_device *dev)
|
||||
|
||||
MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus((void *)&lp->scp));
|
||||
|
||||
#elif defined(ENABLE_APRICOT)
|
||||
|
||||
{
|
||||
u32 scp = virt_to_bus(&lp->scp);
|
||||
|
||||
/* change the scp address */
|
||||
outw(0, ioaddr);
|
||||
outw(0, ioaddr);
|
||||
outb(4, ioaddr + 0xf);
|
||||
outw(scp | 2, ioaddr);
|
||||
outw(scp >> 16, ioaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
lp->last_cmd = jiffies;
|
||||
@ -677,10 +660,6 @@ static int init_i596_mem(struct net_device *dev)
|
||||
if (MACH_IS_BVME6000)
|
||||
lp->scp.sysbus = 0x0000004c;
|
||||
#endif
|
||||
#ifdef ENABLE_APRICOT
|
||||
if (MACH_IS_APRICOT)
|
||||
lp->scp.sysbus = 0x00440000;
|
||||
#endif
|
||||
|
||||
lp->scp.iscp = WSWAPiscp(virt_to_bus((void *)&lp->iscp));
|
||||
lp->iscp.scb = WSWAPscb(virt_to_bus((void *)&lp->scb));
|
||||
@ -698,10 +677,6 @@ static int init_i596_mem(struct net_device *dev)
|
||||
|
||||
DEB(DEB_INIT,printk(KERN_DEBUG "%s: starting i82596.\n", dev->name));
|
||||
|
||||
#if defined(ENABLE_APRICOT)
|
||||
(void) inb(ioaddr + 0x10);
|
||||
outb(4, ioaddr + 0xf);
|
||||
#endif
|
||||
CA(dev);
|
||||
|
||||
if (wait_istat(dev,lp,1000,"initialization timed out"))
|
||||
@ -1202,43 +1177,6 @@ struct net_device * __init i82596_probe(int unit)
|
||||
dev->irq = (unsigned) BVME_IRQ_I596;
|
||||
goto found;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_APRICOT
|
||||
{
|
||||
int checksum = 0;
|
||||
int ioaddr = 0x300;
|
||||
|
||||
/* this is easy the ethernet interface can only be at 0x300 */
|
||||
/* first check nothing is already registered here */
|
||||
|
||||
if (!request_region(ioaddr, I596_TOTAL_SIZE, DRV_NAME)) {
|
||||
printk(KERN_ERR "82596: IO address 0x%04x in use\n", ioaddr);
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
eth_addr[i] = inb(ioaddr + 8 + i);
|
||||
checksum += eth_addr[i];
|
||||
}
|
||||
|
||||
/* checksum is a multiple of 0x100, got this wrong first time
|
||||
some machines have 0x100, some 0x200. The DOS driver doesn't
|
||||
even bother with the checksum.
|
||||
Some other boards trip the checksum.. but then appear as
|
||||
ether address 0. Trap these - AC */
|
||||
|
||||
if ((checksum % 0x100) ||
|
||||
(memcmp(eth_addr, "\x00\x00\x49", 3) != 0)) {
|
||||
err = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
dev->irq = 10;
|
||||
goto found;
|
||||
}
|
||||
#endif
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
@ -1296,9 +1234,6 @@ struct net_device * __init i82596_probe(int unit)
|
||||
#endif
|
||||
free_page ((u32)(dev->mem_start));
|
||||
out1:
|
||||
#ifdef ENABLE_APRICOT
|
||||
release_region(dev->base_addr, I596_TOTAL_SIZE);
|
||||
#endif
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
@ -1454,10 +1389,6 @@ static irqreturn_t i596_interrupt(int irq, void *dev_id)
|
||||
*ethirq = 1;
|
||||
*ethirq = 3;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_APRICOT
|
||||
(void) inb(ioaddr + 0x10);
|
||||
outb(4, ioaddr + 0xf);
|
||||
#endif
|
||||
CA(dev);
|
||||
|
||||
@ -1589,11 +1520,6 @@ static void set_multicast_list(struct net_device *dev)
|
||||
#ifdef MODULE
|
||||
static struct net_device *dev_82596;
|
||||
|
||||
#ifdef ENABLE_APRICOT
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(irq, "Apricot IRQ number");
|
||||
#endif
|
||||
|
||||
static int debug = -1;
|
||||
module_param(debug, int, 0);
|
||||
MODULE_PARM_DESC(debug, "i82596 debug mask");
|
||||
@ -1620,10 +1546,6 @@ void __exit cleanup_module(void)
|
||||
IOMAP_FULL_CACHING);
|
||||
#endif
|
||||
free_page ((u32)(dev_82596->mem_start));
|
||||
#ifdef ENABLE_APRICOT
|
||||
/* If we don't do this, we can't re-insmod it later. */
|
||||
release_region(dev_82596->base_addr, I596_TOTAL_SIZE);
|
||||
#endif
|
||||
free_netdev(dev_82596);
|
||||
}
|
||||
|
||||
|
@ -20,29 +20,6 @@ config NET_VENDOR_I825XX
|
||||
|
||||
if NET_VENDOR_I825XX
|
||||
|
||||
config ELPLUS
|
||||
tristate "3c505 \"EtherLink Plus\" support"
|
||||
depends on ISA && ISA_DMA_API
|
||||
---help---
|
||||
Information about this network (Ethernet) card can be found in
|
||||
<file:Documentation/networking/3c505.txt>. If you have a 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 3c505.
|
||||
|
||||
config EL16
|
||||
tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)"
|
||||
depends on ISA && EXPERIMENTAL
|
||||
---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 3c507.
|
||||
|
||||
config ARM_ETHER1
|
||||
tristate "Acorn Ether1 support"
|
||||
depends on ARM && ARCH_ACORN
|
||||
@ -50,17 +27,6 @@ config ARM_ETHER1
|
||||
If you have an Acorn system with one of these (AKA25) network cards,
|
||||
you should say Y to this option if you wish to use it with Linux.
|
||||
|
||||
config APRICOT
|
||||
tristate "Apricot Xen-II on board Ethernet"
|
||||
depends on ISA
|
||||
---help---
|
||||
If you have a network (Ethernet) controller 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 apricot.
|
||||
|
||||
config BVME6000_NET
|
||||
tristate "BVME6000 Ethernet support"
|
||||
depends on BVME6000
|
||||
@ -70,33 +36,6 @@ config BVME6000_NET
|
||||
in your kernel.
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config EEXPRESS
|
||||
tristate "EtherExpress 16 support"
|
||||
depends on ISA
|
||||
---help---
|
||||
If you have an EtherExpress16 network (Ethernet) card, say Y and
|
||||
read the Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>. Note that the Intel
|
||||
EtherExpress16 card used to be regarded as a very poor choice
|
||||
because the driver was very unreliable. We now have a new driver
|
||||
that should do better.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called eexpress.
|
||||
|
||||
config EEXPRESS_PRO
|
||||
tristate "EtherExpressPro support/EtherExpress 10 (i82595) support"
|
||||
depends on ISA
|
||||
---help---
|
||||
If you have a network (Ethernet) card of this type, say Y. This
|
||||
driver supports Intel i82595{FX,TX} based boards. Note however
|
||||
that the EtherExpress PRO/100 Ethernet card has its own separate
|
||||
driver. Please 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 eepro.
|
||||
|
||||
config LASI_82596
|
||||
tristate "Lasi ethernet"
|
||||
depends on GSC
|
||||
@ -104,14 +43,6 @@ config LASI_82596
|
||||
Say Y here to support the builtin Intel 82596 ethernet controller
|
||||
found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet.
|
||||
|
||||
config LP486E
|
||||
tristate "LP486E on board Ethernet"
|
||||
depends on ISA
|
||||
---help---
|
||||
Say Y here to support the 82596-based on-board Ethernet controller
|
||||
for the Panther motherboard, which is one of the two shipped in the
|
||||
Intel Professional Workstation.
|
||||
|
||||
config MVME16x_NET
|
||||
tristate "MVME16x Ethernet support"
|
||||
depends on MVME16x
|
||||
@ -121,17 +52,6 @@ config MVME16x_NET
|
||||
driver for this chip in your kernel.
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config NI52
|
||||
tristate "NI5210 support"
|
||||
depends on ISA
|
||||
---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 ni52.
|
||||
|
||||
config SNI_82596
|
||||
tristate "SNI RM ethernet"
|
||||
depends on SNI_RM
|
||||
@ -148,14 +68,4 @@ config SUN3_82586
|
||||
that this driver does not support 82586-based adapters on additional
|
||||
VME boards.
|
||||
|
||||
config ZNET
|
||||
tristate "Zenith Z-Note support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && ISA_DMA_API && X86
|
||||
---help---
|
||||
The Zenith Z-Note notebook computer has a built-in network
|
||||
(Ethernet) card, and this is the Linux driver for it. Note that the
|
||||
IBM Thinkpad 300 is compatible with the Z-Note and is also supported
|
||||
by this driver. Read the Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
endif # NET_VENDOR_I825XX
|
||||
|
@ -3,15 +3,7 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARM_ETHER1) += ether1.o
|
||||
obj-$(CONFIG_EEXPRESS) += eexpress.o
|
||||
obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
|
||||
obj-$(CONFIG_ELPLUS) += 3c505.o
|
||||
obj-$(CONFIG_EL16) += 3c507.o
|
||||
obj-$(CONFIG_LP486E) += lp486e.o
|
||||
obj-$(CONFIG_NI52) += ni52.o
|
||||
obj-$(CONFIG_SUN3_82586) += sun3_82586.o
|
||||
obj-$(CONFIG_ZNET) += znet.o
|
||||
obj-$(CONFIG_APRICOT) += 82596.o
|
||||
obj-$(CONFIG_LASI_82596) += lasi_82596.o
|
||||
obj-$(CONFIG_SNI_82596) += sni_82596.o
|
||||
obj-$(CONFIG_MVME16x_NET) += 82596.o
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* eexpress.h: Intel EtherExpress16 defines
|
||||
*/
|
||||
|
||||
/*
|
||||
* EtherExpress card register addresses
|
||||
* as offsets from the base IO region (dev->base_addr)
|
||||
*/
|
||||
|
||||
#define DATAPORT 0x0000
|
||||
#define WRITE_PTR 0x0002
|
||||
#define READ_PTR 0x0004
|
||||
#define SIGNAL_CA 0x0006
|
||||
#define SET_IRQ 0x0007
|
||||
#define SM_PTR 0x0008
|
||||
#define MEM_Dec 0x000a
|
||||
#define MEM_Ctrl 0x000b
|
||||
#define MEM_Page_Ctrl 0x000c
|
||||
#define Config 0x000d
|
||||
#define EEPROM_Ctrl 0x000e
|
||||
#define ID_PORT 0x000f
|
||||
#define MEM_ECtrl 0x000f
|
||||
|
||||
/*
|
||||
* card register defines
|
||||
*/
|
||||
|
||||
/* SET_IRQ */
|
||||
#define SIRQ_en 0x08
|
||||
#define SIRQ_dis 0x00
|
||||
|
||||
/* EEPROM_Ctrl */
|
||||
#define EC_Clk 0x01
|
||||
#define EC_CS 0x02
|
||||
#define EC_Wr 0x04
|
||||
#define EC_Rd 0x08
|
||||
#define ASIC_RST 0x40
|
||||
#define i586_RST 0x80
|
||||
|
||||
#define eeprom_delay() { udelay(40); }
|
||||
|
||||
/*
|
||||
* i82586 Memory Configuration
|
||||
*/
|
||||
|
||||
/* (System Configuration Pointer) System start up block, read after 586_RST */
|
||||
#define SCP_START 0xfff6
|
||||
|
||||
/* Intermediate System Configuration Pointer */
|
||||
#define ISCP_START 0x0000
|
||||
|
||||
/* System Command Block */
|
||||
#define SCB_START 0x0008
|
||||
|
||||
/* Start of buffer region. Everything before this is used for control
|
||||
* structures and the CU configuration program. The memory layout is
|
||||
* determined in eexp_hw_probe(), once we know how much memory is
|
||||
* available on the card.
|
||||
*/
|
||||
|
||||
#define TX_BUF_START 0x0100
|
||||
|
||||
#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f)
|
||||
#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f)
|
||||
|
||||
/*
|
||||
* SCB defines
|
||||
*/
|
||||
|
||||
/* these functions take the SCB status word and test the relevant status bit */
|
||||
#define SCB_complete(s) (((s) & 0x8000) != 0)
|
||||
#define SCB_rxdframe(s) (((s) & 0x4000) != 0)
|
||||
#define SCB_CUdead(s) (((s) & 0x2000) != 0)
|
||||
#define SCB_RUdead(s) (((s) & 0x1000) != 0)
|
||||
#define SCB_ack(s) ((s) & 0xf000)
|
||||
|
||||
/* Command unit status: 0=idle, 1=suspended, 2=active */
|
||||
#define SCB_CUstat(s) (((s)&0x0300)>>8)
|
||||
|
||||
/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */
|
||||
#define SCB_RUstat(s) (((s)&0x0070)>>4)
|
||||
|
||||
/* SCB commands */
|
||||
#define SCB_CUnop 0x0000
|
||||
#define SCB_CUstart 0x0100
|
||||
#define SCB_CUresume 0x0200
|
||||
#define SCB_CUsuspend 0x0300
|
||||
#define SCB_CUabort 0x0400
|
||||
#define SCB_resetchip 0x0080
|
||||
|
||||
#define SCB_RUnop 0x0000
|
||||
#define SCB_RUstart 0x0010
|
||||
#define SCB_RUresume 0x0020
|
||||
#define SCB_RUsuspend 0x0030
|
||||
#define SCB_RUabort 0x0040
|
||||
|
||||
/*
|
||||
* Command block defines
|
||||
*/
|
||||
|
||||
#define Stat_Done(s) (((s) & 0x8000) != 0)
|
||||
#define Stat_Busy(s) (((s) & 0x4000) != 0)
|
||||
#define Stat_OK(s) (((s) & 0x2000) != 0)
|
||||
#define Stat_Abort(s) (((s) & 0x1000) != 0)
|
||||
#define Stat_STFail (((s) & 0x0800) != 0)
|
||||
#define Stat_TNoCar(s) (((s) & 0x0400) != 0)
|
||||
#define Stat_TNoCTS(s) (((s) & 0x0200) != 0)
|
||||
#define Stat_TNoDMA(s) (((s) & 0x0100) != 0)
|
||||
#define Stat_TDefer(s) (((s) & 0x0080) != 0)
|
||||
#define Stat_TColl(s) (((s) & 0x0040) != 0)
|
||||
#define Stat_TXColl(s) (((s) & 0x0020) != 0)
|
||||
#define Stat_NoColl(s) ((s) & 0x000f)
|
||||
|
||||
/* Cmd_END will end AFTER the command if this is the first
|
||||
* command block after an SCB_CUstart, but BEFORE the command
|
||||
* for all subsequent commands. Best strategy is to place
|
||||
* Cmd_INT on the last command in the sequence, followed by a
|
||||
* dummy Cmd_Nop with Cmd_END after this.
|
||||
*/
|
||||
|
||||
#define Cmd_END 0x8000
|
||||
#define Cmd_SUS 0x4000
|
||||
#define Cmd_INT 0x2000
|
||||
|
||||
#define Cmd_Nop 0x0000
|
||||
#define Cmd_SetAddr 0x0001
|
||||
#define Cmd_Config 0x0002
|
||||
#define Cmd_MCast 0x0003
|
||||
#define Cmd_Xmit 0x0004
|
||||
#define Cmd_TDR 0x0005
|
||||
#define Cmd_Dump 0x0006
|
||||
#define Cmd_Diag 0x0007
|
||||
|
||||
|
||||
/*
|
||||
* Frame Descriptor (Receive block) defines
|
||||
*/
|
||||
|
||||
#define FD_Done(s) (((s) & 0x8000) != 0)
|
||||
#define FD_Busy(s) (((s) & 0x4000) != 0)
|
||||
#define FD_OK(s) (((s) & 0x2000) != 0)
|
||||
|
||||
#define FD_CRC(s) (((s) & 0x0800) != 0)
|
||||
#define FD_Align(s) (((s) & 0x0400) != 0)
|
||||
#define FD_Resrc(s) (((s) & 0x0200) != 0)
|
||||
#define FD_DMA(s) (((s) & 0x0100) != 0)
|
||||
#define FD_Short(s) (((s) & 0x0080) != 0)
|
||||
#define FD_NoEOF(s) (((s) & 0x0040) != 0)
|
||||
|
||||
struct rfd_header {
|
||||
volatile unsigned long flags;
|
||||
volatile unsigned short link;
|
||||
volatile unsigned short rbd_offset;
|
||||
volatile unsigned short dstaddr1;
|
||||
volatile unsigned short dstaddr2;
|
||||
volatile unsigned short dstaddr3;
|
||||
volatile unsigned short srcaddr1;
|
||||
volatile unsigned short srcaddr2;
|
||||
volatile unsigned short srcaddr3;
|
||||
volatile unsigned short length;
|
||||
|
||||
/* This is actually a Receive Buffer Descriptor. The way we
|
||||
* arrange memory means that an RBD always follows the RFD that
|
||||
* points to it, so they might as well be in the same structure.
|
||||
*/
|
||||
volatile unsigned short actual_count;
|
||||
volatile unsigned short next_rbd;
|
||||
volatile unsigned short buf_addr1;
|
||||
volatile unsigned short buf_addr2;
|
||||
volatile unsigned short size;
|
||||
};
|
||||
|
||||
/* Returned data from the Time Domain Reflectometer */
|
||||
|
||||
#define TDR_LINKOK (1<<15)
|
||||
#define TDR_XCVRPROBLEM (1<<14)
|
||||
#define TDR_OPEN (1<<13)
|
||||
#define TDR_SHORT (1<<12)
|
||||
#define TDR_TIME 0x7ff
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,310 +0,0 @@
|
||||
/*
|
||||
* Intel i82586 Ethernet definitions
|
||||
*
|
||||
* This is an extension to the Linux operating system, and is covered by the
|
||||
* same GNU General Public License that covers that work.
|
||||
*
|
||||
* copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de)
|
||||
*
|
||||
* I have done a look in the following sources:
|
||||
* crynwr-packet-driver by Russ Nelson
|
||||
* Garret A. Wollman's i82586-driver for BSD
|
||||
*/
|
||||
|
||||
|
||||
#define NI52_RESET 0 /* writing to this address, resets the i82586 */
|
||||
#define NI52_ATTENTION 1 /* channel attention, kick the 586 */
|
||||
#define NI52_TENA 3 /* 2-5 possibly wrong, Xmit enable */
|
||||
#define NI52_TDIS 2 /* Xmit disable */
|
||||
#define NI52_INTENA 5 /* Interrupt enable */
|
||||
#define NI52_INTDIS 4 /* Interrupt disable */
|
||||
#define NI52_MAGIC1 6 /* dunno exact function */
|
||||
#define NI52_MAGIC2 7 /* dunno exact function */
|
||||
|
||||
#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */
|
||||
#define NI52_MAGICVAL2 0x55
|
||||
|
||||
/*
|
||||
* where to find the System Configuration Pointer (SCP)
|
||||
*/
|
||||
#define SCP_DEFAULT_ADDRESS 0xfffff4
|
||||
|
||||
|
||||
/*
|
||||
* System Configuration Pointer Struct
|
||||
*/
|
||||
|
||||
struct scp_struct
|
||||
{
|
||||
u16 zero_dum0; /* has to be zero */
|
||||
u8 sysbus; /* 0=16Bit,1=8Bit */
|
||||
u8 zero_dum1; /* has to be zero for 586 */
|
||||
u16 zero_dum2;
|
||||
u16 zero_dum3;
|
||||
u32 iscp; /* pointer to the iscp-block */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Intermediate System Configuration Pointer (ISCP)
|
||||
*/
|
||||
struct iscp_struct
|
||||
{
|
||||
u8 busy; /* 586 clears after successful init */
|
||||
u8 zero_dummy; /* has to be zero */
|
||||
u16 scb_offset; /* pointeroffset to the scb_base */
|
||||
u32 scb_base; /* base-address of all 16-bit offsets */
|
||||
};
|
||||
|
||||
/*
|
||||
* System Control Block (SCB)
|
||||
*/
|
||||
struct scb_struct
|
||||
{
|
||||
u8 rus;
|
||||
u8 cus;
|
||||
u8 cmd_ruc; /* command word: RU part */
|
||||
u8 cmd_cuc; /* command word: CU part & ACK */
|
||||
u16 cbl_offset; /* pointeroffset, command block list */
|
||||
u16 rfa_offset; /* pointeroffset, receive frame area */
|
||||
u16 crc_errs; /* CRC-Error counter */
|
||||
u16 aln_errs; /* alignmenterror counter */
|
||||
u16 rsc_errs; /* Resourceerror counter */
|
||||
u16 ovrn_errs; /* OVerrunerror counter */
|
||||
};
|
||||
|
||||
/*
|
||||
* possible command values for the command word
|
||||
*/
|
||||
#define RUC_MASK 0x0070 /* mask for RU commands */
|
||||
#define RUC_NOP 0x0000 /* NOP-command */
|
||||
#define RUC_START 0x0010 /* start RU */
|
||||
#define RUC_RESUME 0x0020 /* resume RU after suspend */
|
||||
#define RUC_SUSPEND 0x0030 /* suspend RU */
|
||||
#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
|
||||
|
||||
#define CUC_MASK 0x07 /* mask for CU command */
|
||||
#define CUC_NOP 0x00 /* NOP-command */
|
||||
#define CUC_START 0x01 /* start execution of 1. cmd on the CBL */
|
||||
#define CUC_RESUME 0x02 /* resume after suspend */
|
||||
#define CUC_SUSPEND 0x03 /* Suspend CU */
|
||||
#define CUC_ABORT 0x04 /* abort command operation immediately */
|
||||
|
||||
#define ACK_MASK 0xf0 /* mask for ACK command */
|
||||
#define ACK_CX 0x80 /* acknowledges STAT_CX */
|
||||
#define ACK_FR 0x40 /* ack. STAT_FR */
|
||||
#define ACK_CNA 0x20 /* ack. STAT_CNA */
|
||||
#define ACK_RNR 0x10 /* ack. STAT_RNR */
|
||||
|
||||
/*
|
||||
* possible status values for the status word
|
||||
*/
|
||||
#define STAT_MASK 0xf0 /* mask for cause of interrupt */
|
||||
#define STAT_CX 0x80 /* CU finished cmd with its I bit set */
|
||||
#define STAT_FR 0x40 /* RU finished receiving a frame */
|
||||
#define STAT_CNA 0x20 /* CU left active state */
|
||||
#define STAT_RNR 0x10 /* RU left ready state */
|
||||
|
||||
#define CU_STATUS 0x7 /* CU status, 0=idle */
|
||||
#define CU_SUSPEND 0x1 /* CU is suspended */
|
||||
#define CU_ACTIVE 0x2 /* CU is active */
|
||||
|
||||
#define RU_STATUS 0x70 /* RU status, 0=idle */
|
||||
#define RU_SUSPEND 0x10 /* RU suspended */
|
||||
#define RU_NOSPACE 0x20 /* RU no resources */
|
||||
#define RU_READY 0x40 /* RU is ready */
|
||||
|
||||
/*
|
||||
* Receive Frame Descriptor (RFD)
|
||||
*/
|
||||
struct rfd_struct
|
||||
{
|
||||
u8 stat_low; /* status word */
|
||||
u8 stat_high; /* status word */
|
||||
u8 rfd_sf; /* 82596 mode only */
|
||||
u8 last; /* Bit15,Last Frame on List / Bit14,suspend */
|
||||
u16 next; /* linkoffset to next RFD */
|
||||
u16 rbd_offset; /* pointeroffset to RBD-buffer */
|
||||
u8 dest[6]; /* ethernet-address, destination */
|
||||
u8 source[6]; /* ethernet-address, source */
|
||||
u16 length; /* 802.3 frame-length */
|
||||
u16 zero_dummy; /* dummy */
|
||||
};
|
||||
|
||||
#define RFD_LAST 0x80 /* last: last rfd in the list */
|
||||
#define RFD_SUSP 0x40 /* last: suspend RU after */
|
||||
#define RFD_COMPL 0x80
|
||||
#define RFD_OK 0x20
|
||||
#define RFD_BUSY 0x40
|
||||
#define RFD_ERR_LEN 0x10 /* Length error (if enabled length-checking */
|
||||
#define RFD_ERR_CRC 0x08 /* CRC error */
|
||||
#define RFD_ERR_ALGN 0x04 /* Alignment error */
|
||||
#define RFD_ERR_RNR 0x02 /* status: receiver out of resources */
|
||||
#define RFD_ERR_OVR 0x01 /* DMA Overrun! */
|
||||
|
||||
#define RFD_ERR_FTS 0x0080 /* Frame to short */
|
||||
#define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */
|
||||
#define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */
|
||||
#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */
|
||||
#define RFD_COLLDET 0x0001 /* Detected collision during reception */
|
||||
|
||||
/*
|
||||
* Receive Buffer Descriptor (RBD)
|
||||
*/
|
||||
struct rbd_struct
|
||||
{
|
||||
u16 status; /* status word,number of used bytes in buff */
|
||||
u16 next; /* pointeroffset to next RBD */
|
||||
u32 buffer; /* receive buffer address pointer */
|
||||
u16 size; /* size of this buffer */
|
||||
u16 zero_dummy; /* dummy */
|
||||
};
|
||||
|
||||
#define RBD_LAST 0x8000 /* last buffer */
|
||||
#define RBD_USED 0x4000 /* this buffer has data */
|
||||
#define RBD_MASK 0x3fff /* size-mask for length */
|
||||
|
||||
/*
|
||||
* Statusvalues for Commands/RFD
|
||||
*/
|
||||
#define STAT_COMPL 0x8000 /* status: frame/command is complete */
|
||||
#define STAT_BUSY 0x4000 /* status: frame/command is busy */
|
||||
#define STAT_OK 0x2000 /* status: frame/command is ok */
|
||||
|
||||
/*
|
||||
* Action-Commands
|
||||
*/
|
||||
#define CMD_NOP 0x0000 /* NOP */
|
||||
#define CMD_IASETUP 0x0001 /* initial address setup command */
|
||||
#define CMD_CONFIGURE 0x0002 /* configure command */
|
||||
#define CMD_MCSETUP 0x0003 /* MC setup command */
|
||||
#define CMD_XMIT 0x0004 /* transmit command */
|
||||
#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
|
||||
#define CMD_DUMP 0x0006 /* dump command */
|
||||
#define CMD_DIAGNOSE 0x0007 /* diagnose command */
|
||||
|
||||
/*
|
||||
* Action command bits
|
||||
*/
|
||||
#define CMD_LAST 0x8000 /* indicates last command in the CBL */
|
||||
#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
|
||||
#define CMD_INT 0x2000 /* generate interrupt after execution */
|
||||
|
||||
/*
|
||||
* NOP - command
|
||||
*/
|
||||
struct nop_cmd_struct
|
||||
{
|
||||
u16 cmd_status; /* status of this command */
|
||||
u16 cmd_cmd; /* the command itself (+bits) */
|
||||
u16 cmd_link; /* offsetpointer to next command */
|
||||
};
|
||||
|
||||
/*
|
||||
* IA Setup command
|
||||
*/
|
||||
struct iasetup_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u8 iaddr[6];
|
||||
};
|
||||
|
||||
/*
|
||||
* Configure command
|
||||
*/
|
||||
struct configure_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u8 byte_cnt; /* size of the config-cmd */
|
||||
u8 fifo; /* fifo/recv monitor */
|
||||
u8 sav_bf; /* save bad frames (bit7=1)*/
|
||||
u8 adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
|
||||
u8 priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
|
||||
u8 ifs; /* inter frame spacing */
|
||||
u8 time_low; /* slot time low */
|
||||
u8 time_high; /* slot time high(0-2) and max. retries(4-7) */
|
||||
u8 promisc; /* promisc-mode(0) , et al (1-7) */
|
||||
u8 carr_coll; /* carrier(0-3)/collision(4-7) stuff */
|
||||
u8 fram_len; /* minimal frame len */
|
||||
u8 dummy; /* dummy */
|
||||
};
|
||||
|
||||
/*
|
||||
* Multicast Setup command
|
||||
*/
|
||||
struct mcsetup_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u16 mc_cnt; /* number of bytes in the MC-List */
|
||||
u8 mc_list[0][6]; /* pointer to 6 bytes entries */
|
||||
};
|
||||
|
||||
/*
|
||||
* DUMP command
|
||||
*/
|
||||
struct dump_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u16 dump_offset; /* pointeroffset to DUMP space */
|
||||
};
|
||||
|
||||
/*
|
||||
* transmit command
|
||||
*/
|
||||
struct transmit_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u16 tbd_offset; /* pointeroffset to TBD */
|
||||
u8 dest[6]; /* destination address of the frame */
|
||||
u16 length; /* user defined: 802.3 length / Ether type */
|
||||
};
|
||||
|
||||
#define TCMD_ERRMASK 0x0fa0
|
||||
#define TCMD_MAXCOLLMASK 0x000f
|
||||
#define TCMD_MAXCOLL 0x0020
|
||||
#define TCMD_HEARTBEAT 0x0040
|
||||
#define TCMD_DEFERRED 0x0080
|
||||
#define TCMD_UNDERRUN 0x0100
|
||||
#define TCMD_LOSTCTS 0x0200
|
||||
#define TCMD_NOCARRIER 0x0400
|
||||
#define TCMD_LATECOLL 0x0800
|
||||
|
||||
struct tdr_cmd_struct
|
||||
{
|
||||
u16 cmd_status;
|
||||
u16 cmd_cmd;
|
||||
u16 cmd_link;
|
||||
u16 status;
|
||||
};
|
||||
|
||||
#define TDR_LNK_OK 0x8000 /* No link problem identified */
|
||||
#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
|
||||
#define TDR_ET_OPN 0x2000 /* open, no correct termination */
|
||||
#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
|
||||
#define TDR_TIMEMASK 0x07ff /* mask for the time field */
|
||||
|
||||
/*
|
||||
* Transmit Buffer Descriptor (TBD)
|
||||
*/
|
||||
struct tbd_struct
|
||||
{
|
||||
u16 size; /* size + EOF-Flag(15) */
|
||||
u16 next; /* pointeroffset to next TBD */
|
||||
u32 buffer; /* pointer to buffer */
|
||||
};
|
||||
|
||||
#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
|
||||
|
||||
|
||||
|
||||
|
@ -1,928 +0,0 @@
|
||||
/* znet.c: An Zenith Z-Note ethernet driver for linux. */
|
||||
|
||||
/*
|
||||
Written by Donald Becker.
|
||||
|
||||
The author may be reached as becker@scyld.com.
|
||||
This driver is based on the Linux skeleton driver. The copyright of the
|
||||
skeleton driver is held by the United States Government, as represented
|
||||
by DIRNSA, and it is released under the GPL.
|
||||
|
||||
Thanks to Mike Hollick for alpha testing and suggestions.
|
||||
|
||||
References:
|
||||
The Crynwr packet driver.
|
||||
|
||||
"82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
|
||||
Intel Microcommunications Databook, Vol. 1, 1990.
|
||||
As usual with Intel, the documentation is incomplete and inaccurate.
|
||||
I had to read the Crynwr packet driver to figure out how to actually
|
||||
use the i82593, and guess at what register bits matched the loosely
|
||||
related i82586.
|
||||
|
||||
Theory of Operation
|
||||
|
||||
The i82593 used in the Zenith Z-Note series operates using two(!) slave
|
||||
DMA channels, one interrupt, and one 8-bit I/O port.
|
||||
|
||||
While there several ways to configure '593 DMA system, I chose the one
|
||||
that seemed commensurate with the highest system performance in the face
|
||||
of moderate interrupt latency: Both DMA channels are configured as
|
||||
recirculating ring buffers, with one channel (#0) dedicated to Rx and
|
||||
the other channel (#1) to Tx and configuration. (Note that this is
|
||||
different than the Crynwr driver, where the Tx DMA channel is initialized
|
||||
before each operation. That approach simplifies operation and Tx error
|
||||
recovery, but requires additional I/O in normal operation and precludes
|
||||
transmit buffer chaining.)
|
||||
|
||||
Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
|
||||
a reasonable ring size for Rx, while simplifying DMA buffer allocation --
|
||||
DMA buffers must not cross a 128K boundary. (In truth the size selection
|
||||
was influenced by my lack of '593 documentation. I thus was constrained
|
||||
to use the Crynwr '593 initialization table, which sets the Rx ring size
|
||||
to 8K.)
|
||||
|
||||
Despite my usual low opinion about Intel-designed parts, I must admit
|
||||
that the bulk data handling of the i82593 is a good design for
|
||||
an integrated system, like a laptop, where using two slave DMA channels
|
||||
doesn't pose a problem. I still take issue with using only a single I/O
|
||||
port. In the same controlled environment there are essentially no
|
||||
limitations on I/O space, and using multiple locations would eliminate
|
||||
the need for multiple operations when looking at status registers,
|
||||
setting the Rx ring boundary, or switching to promiscuous mode.
|
||||
|
||||
I also question Zenith's selection of the '593: one of the advertised
|
||||
advantages of earlier Intel parts was that if you figured out the magic
|
||||
initialization incantation you could use the same part on many different
|
||||
network types. Zenith's use of the "FriendlyNet" (sic) connector rather
|
||||
than an on-board transceiver leads me to believe that they were planning
|
||||
to take advantage of this. But, uhmmm, the '593 omits all but ethernet
|
||||
functionality from the serial subsystem.
|
||||
*/
|
||||
|
||||
/* 10/2002
|
||||
|
||||
o Resurected for Linux 2.5+ by Marc Zyngier <maz@wild-wind.fr.eu.org> :
|
||||
|
||||
- Removed strange DMA snooping in znet_sent_packet, which lead to
|
||||
TX buffer corruption on my laptop.
|
||||
- Use init_etherdev stuff.
|
||||
- Use kmalloc-ed DMA buffers.
|
||||
- Use as few global variables as possible.
|
||||
- Use proper resources management.
|
||||
- Use wireless/i82593.h as much as possible (structure, constants)
|
||||
- Compiles as module or build-in.
|
||||
- Now survives unplugging/replugging cable.
|
||||
|
||||
Some code was taken from wavelan_cs.
|
||||
|
||||
Tested on a vintage Zenith Z-Note 433Lnp+. Probably broken on
|
||||
anything else. Testers (and detailed bug reports) are welcome :-).
|
||||
|
||||
o TODO :
|
||||
|
||||
- Properly handle multicast
|
||||
- Understand why some traffic patterns add a 1s latency...
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include <linux/i82593.h>
|
||||
|
||||
static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n";
|
||||
|
||||
#ifndef ZNET_DEBUG
|
||||
#define ZNET_DEBUG 1
|
||||
#endif
|
||||
static unsigned int znet_debug = ZNET_DEBUG;
|
||||
module_param (znet_debug, int, 0);
|
||||
MODULE_PARM_DESC (znet_debug, "ZNet debug level");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* The DMA modes we need aren't in <dma.h>. */
|
||||
#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
|
||||
#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
|
||||
#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
|
||||
#define RX_BUF_SIZE 8192
|
||||
#define TX_BUF_SIZE 8192
|
||||
#define DMA_BUF_SIZE (RX_BUF_SIZE + 16) /* 8k + 16 bytes for trailers */
|
||||
|
||||
#define TX_TIMEOUT (HZ/10)
|
||||
|
||||
struct znet_private {
|
||||
int rx_dma, tx_dma;
|
||||
spinlock_t lock;
|
||||
short sia_base, sia_size, io_size;
|
||||
struct i82593_conf_block i593_init;
|
||||
/* The starting, current, and end pointers for the packet buffers. */
|
||||
ushort *rx_start, *rx_cur, *rx_end;
|
||||
ushort *tx_start, *tx_cur, *tx_end;
|
||||
ushort tx_buf_len; /* Tx buffer length, in words. */
|
||||
};
|
||||
|
||||
/* Only one can be built-in;-> */
|
||||
static struct net_device *znet_dev;
|
||||
|
||||
#define NETIDBLK_MAGIC "NETIDBLK"
|
||||
#define NETIDBLK_MAGIC_SIZE 8
|
||||
|
||||
struct netidblk {
|
||||
char magic[NETIDBLK_MAGIC_SIZE]; /* The magic number (string) "NETIDBLK" */
|
||||
unsigned char netid[8]; /* The physical station address */
|
||||
char nettype, globalopt;
|
||||
char vendor[8]; /* The machine vendor and product name. */
|
||||
char product[8];
|
||||
char irq1, irq2; /* Interrupts, only one is currently used. */
|
||||
char dma1, dma2;
|
||||
short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
|
||||
short iobase1, iosize1;
|
||||
short iobase2, iosize2; /* Second iobase unused. */
|
||||
char driver_options; /* Misc. bits */
|
||||
char pad;
|
||||
};
|
||||
|
||||
static int znet_open(struct net_device *dev);
|
||||
static netdev_tx_t znet_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
static irqreturn_t znet_interrupt(int irq, void *dev_id);
|
||||
static void znet_rx(struct net_device *dev);
|
||||
static int znet_close(struct net_device *dev);
|
||||
static void hardware_init(struct net_device *dev);
|
||||
static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
|
||||
static void znet_tx_timeout (struct net_device *dev);
|
||||
|
||||
/* Request needed resources */
|
||||
static int znet_request_resources (struct net_device *dev)
|
||||
{
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
|
||||
if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev))
|
||||
goto failed;
|
||||
if (request_dma (znet->rx_dma, "ZNet rx"))
|
||||
goto free_irq;
|
||||
if (request_dma (znet->tx_dma, "ZNet tx"))
|
||||
goto free_rx_dma;
|
||||
if (!request_region (znet->sia_base, znet->sia_size, "ZNet SIA"))
|
||||
goto free_tx_dma;
|
||||
if (!request_region (dev->base_addr, znet->io_size, "ZNet I/O"))
|
||||
goto free_sia;
|
||||
|
||||
return 0; /* Happy ! */
|
||||
|
||||
free_sia:
|
||||
release_region (znet->sia_base, znet->sia_size);
|
||||
free_tx_dma:
|
||||
free_dma (znet->tx_dma);
|
||||
free_rx_dma:
|
||||
free_dma (znet->rx_dma);
|
||||
free_irq:
|
||||
free_irq (dev->irq, dev);
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void znet_release_resources (struct net_device *dev)
|
||||
{
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
|
||||
release_region (znet->sia_base, znet->sia_size);
|
||||
release_region (dev->base_addr, znet->io_size);
|
||||
free_dma (znet->tx_dma);
|
||||
free_dma (znet->rx_dma);
|
||||
free_irq (dev->irq, dev);
|
||||
}
|
||||
|
||||
/* Keep the magical SIA stuff in a single function... */
|
||||
static void znet_transceiver_power (struct net_device *dev, int on)
|
||||
{
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
unsigned char v;
|
||||
|
||||
/* Turn on/off the 82501 SIA, using zenith-specific magic. */
|
||||
/* Select LAN control register */
|
||||
outb(0x10, znet->sia_base);
|
||||
|
||||
if (on)
|
||||
v = inb(znet->sia_base + 1) | 0x84;
|
||||
else
|
||||
v = inb(znet->sia_base + 1) & ~0x84;
|
||||
|
||||
outb(v, znet->sia_base+1); /* Turn on/off LAN power (bit 2). */
|
||||
}
|
||||
|
||||
/* Init the i82593, with current promisc/mcast configuration.
|
||||
Also used from hardware_init. */
|
||||
static void znet_set_multicast_list (struct net_device *dev)
|
||||
{
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
short ioaddr = dev->base_addr;
|
||||
struct i82593_conf_block *cfblk = &znet->i593_init;
|
||||
|
||||
memset(cfblk, 0x00, sizeof(struct i82593_conf_block));
|
||||
|
||||
/* The configuration block. What an undocumented nightmare.
|
||||
The first set of values are those suggested (without explanation)
|
||||
for ethernet in the Intel 82586 databook. The rest appear to be
|
||||
completely undocumented, except for cryptic notes in the Crynwr
|
||||
packet driver. This driver uses the Crynwr values verbatim. */
|
||||
|
||||
/* maz : Rewritten to take advantage of the wanvelan includes.
|
||||
At least we have names, not just blind values */
|
||||
|
||||
/* Byte 0 */
|
||||
cfblk->fifo_limit = 10; /* = 16 B rx and 80 B tx fifo thresholds */
|
||||
cfblk->forgnesi = 0; /* 0=82C501, 1=AMD7992B compatibility */
|
||||
cfblk->fifo_32 = 1;
|
||||
cfblk->d6mod = 0; /* Run in i82593 advanced mode */
|
||||
cfblk->throttle_enb = 1;
|
||||
|
||||
/* Byte 1 */
|
||||
cfblk->throttle = 8; /* Continuous w/interrupts, 128-clock DMA. */
|
||||
cfblk->cntrxint = 0; /* enable continuous mode receive interrupts */
|
||||
cfblk->contin = 1; /* enable continuous mode */
|
||||
|
||||
/* Byte 2 */
|
||||
cfblk->addr_len = ETH_ALEN;
|
||||
cfblk->acloc = 1; /* Disable source addr insertion by i82593 */
|
||||
cfblk->preamb_len = 2; /* 8 bytes preamble */
|
||||
cfblk->loopback = 0; /* Loopback off */
|
||||
|
||||
/* Byte 3 */
|
||||
cfblk->lin_prio = 0; /* Default priorities & backoff methods. */
|
||||
cfblk->tbofstop = 0;
|
||||
cfblk->exp_prio = 0;
|
||||
cfblk->bof_met = 0;
|
||||
|
||||
/* Byte 4 */
|
||||
cfblk->ifrm_spc = 6; /* 96 bit times interframe spacing */
|
||||
|
||||
/* Byte 5 */
|
||||
cfblk->slottim_low = 0; /* 512 bit times slot time (low) */
|
||||
|
||||
/* Byte 6 */
|
||||
cfblk->slottim_hi = 2; /* 512 bit times slot time (high) */
|
||||
cfblk->max_retr = 15; /* 15 collisions retries */
|
||||
|
||||
/* Byte 7 */
|
||||
cfblk->prmisc = ((dev->flags & IFF_PROMISC) ? 1 : 0); /* Promiscuous mode */
|
||||
cfblk->bc_dis = 0; /* Enable broadcast reception */
|
||||
cfblk->crs_1 = 0; /* Don't transmit without carrier sense */
|
||||
cfblk->nocrc_ins = 0; /* i82593 generates CRC */
|
||||
cfblk->crc_1632 = 0; /* 32-bit Autodin-II CRC */
|
||||
cfblk->crs_cdt = 0; /* CD not to be interpreted as CS */
|
||||
|
||||
/* Byte 8 */
|
||||
cfblk->cs_filter = 0; /* CS is recognized immediately */
|
||||
cfblk->crs_src = 0; /* External carrier sense */
|
||||
cfblk->cd_filter = 0; /* CD is recognized immediately */
|
||||
|
||||
/* Byte 9 */
|
||||
cfblk->min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length */
|
||||
|
||||
/* Byte A */
|
||||
cfblk->lng_typ = 1; /* Type/length checks OFF */
|
||||
cfblk->lng_fld = 1; /* Disable 802.3 length field check */
|
||||
cfblk->rxcrc_xf = 1; /* Don't transfer CRC to memory */
|
||||
cfblk->artx = 1; /* Disable automatic retransmission */
|
||||
cfblk->sarec = 1; /* Disable source addr trig of CD */
|
||||
cfblk->tx_jabber = 0; /* Disable jabber jam sequence */
|
||||
cfblk->hash_1 = 1; /* Use bits 0-5 in mc address hash */
|
||||
cfblk->lbpkpol = 0; /* Loopback pin active high */
|
||||
|
||||
/* Byte B */
|
||||
cfblk->fdx = 0; /* Disable full duplex operation */
|
||||
|
||||
/* Byte C */
|
||||
cfblk->dummy_6 = 0x3f; /* all ones, Default multicast addresses & backoff. */
|
||||
cfblk->mult_ia = 0; /* No multiple individual addresses */
|
||||
cfblk->dis_bof = 0; /* Disable the backoff algorithm ?! */
|
||||
|
||||
/* Byte D */
|
||||
cfblk->dummy_1 = 1; /* set to 1 */
|
||||
cfblk->tx_ifs_retrig = 3; /* Hmm... Disabled */
|
||||
cfblk->mc_all = (!netdev_mc_empty(dev) ||
|
||||
(dev->flags & IFF_ALLMULTI)); /* multicast all mode */
|
||||
cfblk->rcv_mon = 0; /* Monitor mode disabled */
|
||||
cfblk->frag_acpt = 0; /* Do not accept fragments */
|
||||
cfblk->tstrttrs = 0; /* No start transmission threshold */
|
||||
|
||||
/* Byte E */
|
||||
cfblk->fretx = 1; /* FIFO automatic retransmission */
|
||||
cfblk->runt_eop = 0; /* drop "runt" packets */
|
||||
cfblk->hw_sw_pin = 0; /* ?? */
|
||||
cfblk->big_endn = 0; /* Big Endian ? no... */
|
||||
cfblk->syncrqs = 1; /* Synchronous DRQ deassertion... */
|
||||
cfblk->sttlen = 1; /* 6 byte status registers */
|
||||
cfblk->rx_eop = 0; /* Signal EOP on packet reception */
|
||||
cfblk->tx_eop = 0; /* Signal EOP on packet transmission */
|
||||
|
||||
/* Byte F */
|
||||
cfblk->rbuf_size = RX_BUF_SIZE >> 12; /* Set receive buffer size */
|
||||
cfblk->rcvstop = 1; /* Enable Receive Stop Register */
|
||||
|
||||
if (znet_debug > 2) {
|
||||
int i;
|
||||
unsigned char *c;
|
||||
|
||||
for (i = 0, c = (char *) cfblk; i < sizeof (*cfblk); i++)
|
||||
printk ("%02X ", c[i]);
|
||||
printk ("\n");
|
||||
}
|
||||
|
||||
*znet->tx_cur++ = sizeof(struct i82593_conf_block);
|
||||
memcpy(znet->tx_cur, cfblk, sizeof(struct i82593_conf_block));
|
||||
znet->tx_cur += sizeof(struct i82593_conf_block)/2;
|
||||
outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
|
||||
|
||||
/* XXX FIXME maz : Add multicast addresses here, so having a
|
||||
* multicast address configured isn't equal to IFF_ALLMULTI */
|
||||
}
|
||||
|
||||
static const struct net_device_ops znet_netdev_ops = {
|
||||
.ndo_open = znet_open,
|
||||
.ndo_stop = znet_close,
|
||||
.ndo_start_xmit = znet_send_packet,
|
||||
.ndo_set_rx_mode = znet_set_multicast_list,
|
||||
.ndo_tx_timeout = znet_tx_timeout,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
|
||||
BIOS area. We just scan for the signature, and pull the vital parameters
|
||||
out of the structure. */
|
||||
|
||||
static int __init znet_probe (void)
|
||||
{
|
||||
int i;
|
||||
struct netidblk *netinfo;
|
||||
struct znet_private *znet;
|
||||
struct net_device *dev;
|
||||
char *p;
|
||||
char *plast = phys_to_virt(0x100000 - NETIDBLK_MAGIC_SIZE);
|
||||
int err = -ENOMEM;
|
||||
|
||||
/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
|
||||
for(p = (char *)phys_to_virt(0xf0000); p <= plast; p++)
|
||||
if (*p == 'N' &&
|
||||
strncmp(p, NETIDBLK_MAGIC, NETIDBLK_MAGIC_SIZE) == 0)
|
||||
break;
|
||||
|
||||
if (p > plast) {
|
||||
if (znet_debug > 1)
|
||||
printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct znet_private));
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
znet = netdev_priv(dev);
|
||||
|
||||
netinfo = (struct netidblk *)p;
|
||||
dev->base_addr = netinfo->iobase1;
|
||||
dev->irq = netinfo->irq1;
|
||||
|
||||
/* The station address is in the "netidblk" at 0x0f0000. */
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = netinfo->netid[i];
|
||||
|
||||
printk(KERN_INFO "%s: ZNET at %#3lx, %pM"
|
||||
", using IRQ %d DMA %d and %d.\n",
|
||||
dev->name, dev->base_addr, dev->dev_addr,
|
||||
dev->irq, netinfo->dma1, netinfo->dma2);
|
||||
|
||||
if (znet_debug > 1) {
|
||||
printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
|
||||
dev->name, netinfo->vendor,
|
||||
netinfo->irq1, netinfo->irq2,
|
||||
netinfo->dma1, netinfo->dma2);
|
||||
printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
|
||||
dev->name, netinfo->iobase1, netinfo->iosize1,
|
||||
netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
|
||||
}
|
||||
|
||||
if (znet_debug > 0)
|
||||
printk(KERN_INFO "%s", version);
|
||||
|
||||
znet->rx_dma = netinfo->dma1;
|
||||
znet->tx_dma = netinfo->dma2;
|
||||
spin_lock_init(&znet->lock);
|
||||
znet->sia_base = 0xe6; /* Magic address for the 82501 SIA */
|
||||
znet->sia_size = 2;
|
||||
/* maz: Despite the '593 being advertised above as using a
|
||||
* single 8bits I/O port, this driver does many 16bits
|
||||
* access. So set io_size accordingly */
|
||||
znet->io_size = 2;
|
||||
|
||||
if (!(znet->rx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
|
||||
goto free_dev;
|
||||
if (!(znet->tx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
|
||||
goto free_rx;
|
||||
|
||||
if (!dma_page_eq (znet->rx_start, znet->rx_start + (RX_BUF_SIZE/2-1)) ||
|
||||
!dma_page_eq (znet->tx_start, znet->tx_start + (TX_BUF_SIZE/2-1))) {
|
||||
printk (KERN_WARNING "tx/rx crossing DMA frontiers, giving up\n");
|
||||
goto free_tx;
|
||||
}
|
||||
|
||||
znet->rx_end = znet->rx_start + RX_BUF_SIZE/2;
|
||||
znet->tx_buf_len = TX_BUF_SIZE/2;
|
||||
znet->tx_end = znet->tx_start + znet->tx_buf_len;
|
||||
|
||||
/* The ZNET-specific entries in the device structure. */
|
||||
dev->netdev_ops = &znet_netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto free_tx;
|
||||
znet_dev = dev;
|
||||
return 0;
|
||||
|
||||
free_tx:
|
||||
kfree(znet->tx_start);
|
||||
free_rx:
|
||||
kfree(znet->rx_start);
|
||||
free_dev:
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int znet_open(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
if (znet_debug > 2)
|
||||
printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
|
||||
|
||||
/* These should never fail. You can't add devices to a sealed box! */
|
||||
if (znet_request_resources (dev)) {
|
||||
printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
znet_transceiver_power (dev, 1);
|
||||
|
||||
/* According to the Crynwr driver we should wait 50 msec. for the
|
||||
LAN clock to stabilize. My experiments indicates that the '593 can
|
||||
be initialized immediately. The delay is probably needed for the
|
||||
DC-to-DC converter to come up to full voltage, and for the oscillator
|
||||
to be spot-on at 20Mhz before transmitting.
|
||||
Until this proves to be a problem we rely on the higher layers for the
|
||||
delay and save allocating a timer entry. */
|
||||
|
||||
/* maz : Well, I'm getting every time the following message
|
||||
* without the delay on a 486@33. This machine is much too
|
||||
* fast... :-) So maybe the Crynwr driver wasn't wrong after
|
||||
* all, even if the message is completly harmless on my
|
||||
* setup. */
|
||||
mdelay (50);
|
||||
|
||||
/* This follows the packet driver's lead, and checks for success. */
|
||||
if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
|
||||
printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
|
||||
dev->name);
|
||||
|
||||
hardware_init(dev);
|
||||
netif_start_queue (dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void znet_tx_timeout (struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
ushort event, tx_status, rx_offset, state;
|
||||
|
||||
outb (CR0_STATUS_0, ioaddr);
|
||||
event = inb (ioaddr);
|
||||
outb (CR0_STATUS_1, ioaddr);
|
||||
tx_status = inw (ioaddr);
|
||||
outb (CR0_STATUS_2, ioaddr);
|
||||
rx_offset = inw (ioaddr);
|
||||
outb (CR0_STATUS_3, ioaddr);
|
||||
state = inb (ioaddr);
|
||||
printk (KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
|
||||
" resetting.\n", dev->name, event, tx_status, rx_offset, state);
|
||||
if (tx_status == TX_LOST_CRS)
|
||||
printk (KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
|
||||
dev->name);
|
||||
outb (OP0_RESET, ioaddr);
|
||||
hardware_init (dev);
|
||||
netif_wake_queue (dev);
|
||||
}
|
||||
|
||||
static netdev_tx_t znet_send_packet(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
short length = skb->len;
|
||||
|
||||
if (znet_debug > 4)
|
||||
printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name);
|
||||
|
||||
if (length < ETH_ZLEN) {
|
||||
if (skb_padto(skb, ETH_ZLEN))
|
||||
return NETDEV_TX_OK;
|
||||
length = ETH_ZLEN;
|
||||
}
|
||||
|
||||
netif_stop_queue (dev);
|
||||
|
||||
/* Check that the part hasn't reset itself, probably from suspend. */
|
||||
outb(CR0_STATUS_0, ioaddr);
|
||||
if (inw(ioaddr) == 0x0010 &&
|
||||
inw(ioaddr) == 0x0000 &&
|
||||
inw(ioaddr) == 0x0010) {
|
||||
if (znet_debug > 1)
|
||||
printk (KERN_WARNING "%s : waking up\n", dev->name);
|
||||
hardware_init(dev);
|
||||
znet_transceiver_power (dev, 1);
|
||||
}
|
||||
|
||||
if (1) {
|
||||
unsigned char *buf = (void *)skb->data;
|
||||
ushort *tx_link = znet->tx_cur - 1;
|
||||
ushort rnd_len = (length + 1)>>1;
|
||||
|
||||
dev->stats.tx_bytes+=length;
|
||||
|
||||
if (znet->tx_cur >= znet->tx_end)
|
||||
znet->tx_cur = znet->tx_start;
|
||||
*znet->tx_cur++ = length;
|
||||
if (znet->tx_cur + rnd_len + 1 > znet->tx_end) {
|
||||
int semi_cnt = (znet->tx_end - znet->tx_cur)<<1; /* Cvrt to byte cnt. */
|
||||
memcpy(znet->tx_cur, buf, semi_cnt);
|
||||
rnd_len -= semi_cnt>>1;
|
||||
memcpy(znet->tx_start, buf + semi_cnt, length - semi_cnt);
|
||||
znet->tx_cur = znet->tx_start + rnd_len;
|
||||
} else {
|
||||
memcpy(znet->tx_cur, buf, skb->len);
|
||||
znet->tx_cur += rnd_len;
|
||||
}
|
||||
*znet->tx_cur++ = 0;
|
||||
|
||||
spin_lock_irqsave(&znet->lock, flags);
|
||||
{
|
||||
*tx_link = OP0_TRANSMIT | CR0_CHNL;
|
||||
/* Is this always safe to do? */
|
||||
outb(OP0_TRANSMIT | CR0_CHNL, ioaddr);
|
||||
}
|
||||
spin_unlock_irqrestore (&znet->lock, flags);
|
||||
|
||||
netif_start_queue (dev);
|
||||
|
||||
if (znet_debug > 4)
|
||||
printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* The ZNET interrupt handler. */
|
||||
static irqreturn_t znet_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
int ioaddr;
|
||||
int boguscnt = 20;
|
||||
int handled = 0;
|
||||
|
||||
spin_lock (&znet->lock);
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
|
||||
outb(CR0_STATUS_0, ioaddr);
|
||||
do {
|
||||
ushort status = inb(ioaddr);
|
||||
if (znet_debug > 5) {
|
||||
ushort result, rx_ptr, running;
|
||||
outb(CR0_STATUS_1, ioaddr);
|
||||
result = inw(ioaddr);
|
||||
outb(CR0_STATUS_2, ioaddr);
|
||||
rx_ptr = inw(ioaddr);
|
||||
outb(CR0_STATUS_3, ioaddr);
|
||||
running = inb(ioaddr);
|
||||
printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
|
||||
dev->name, status, result, rx_ptr, running, boguscnt);
|
||||
}
|
||||
if ((status & SR0_INTERRUPT) == 0)
|
||||
break;
|
||||
|
||||
handled = 1;
|
||||
|
||||
if ((status & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
|
||||
(status & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
|
||||
(status & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) {
|
||||
int tx_status;
|
||||
outb(CR0_STATUS_1, ioaddr);
|
||||
tx_status = inw(ioaddr);
|
||||
/* It's undocumented, but tx_status seems to match the i82586. */
|
||||
if (tx_status & TX_OK) {
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.collisions += tx_status & TX_NCOL_MASK;
|
||||
} else {
|
||||
if (tx_status & (TX_LOST_CTS | TX_LOST_CRS))
|
||||
dev->stats.tx_carrier_errors++;
|
||||
if (tx_status & TX_UND_RUN)
|
||||
dev->stats.tx_fifo_errors++;
|
||||
if (!(tx_status & TX_HRT_BEAT))
|
||||
dev->stats.tx_heartbeat_errors++;
|
||||
if (tx_status & TX_MAX_COL)
|
||||
dev->stats.tx_aborted_errors++;
|
||||
/* ...and the catch-all. */
|
||||
if ((tx_status | (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) != (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL))
|
||||
dev->stats.tx_errors++;
|
||||
|
||||
/* Transceiver may be stuck if cable
|
||||
* was removed while emitting a
|
||||
* packet. Flip it off, then on to
|
||||
* reset it. This is very empirical,
|
||||
* but it seems to work. */
|
||||
|
||||
znet_transceiver_power (dev, 0);
|
||||
znet_transceiver_power (dev, 1);
|
||||
}
|
||||
netif_wake_queue (dev);
|
||||
}
|
||||
|
||||
if ((status & SR0_RECEPTION) ||
|
||||
(status & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) {
|
||||
znet_rx(dev);
|
||||
}
|
||||
/* Clear the interrupts we've handled. */
|
||||
outb(CR0_INT_ACK, ioaddr);
|
||||
} while (boguscnt--);
|
||||
|
||||
spin_unlock (&znet->lock);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static void znet_rx(struct net_device *dev)
|
||||
{
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
int boguscount = 1;
|
||||
short next_frame_end_offset = 0; /* Offset of next frame start. */
|
||||
short *cur_frame_end;
|
||||
short cur_frame_end_offset;
|
||||
|
||||
outb(CR0_STATUS_2, ioaddr);
|
||||
cur_frame_end_offset = inw(ioaddr);
|
||||
|
||||
if (cur_frame_end_offset == znet->rx_cur - znet->rx_start) {
|
||||
printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
|
||||
dev->name, cur_frame_end_offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use same method as the Crynwr driver: construct a forward list in
|
||||
the same area of the backwards links we now have. This allows us to
|
||||
pass packets to the upper layers in the order they were received --
|
||||
important for fast-path sequential operations. */
|
||||
while (znet->rx_start + cur_frame_end_offset != znet->rx_cur &&
|
||||
++boguscount < 5) {
|
||||
unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
|
||||
int count, status;
|
||||
|
||||
if (cur_frame_end_offset < 4) {
|
||||
/* Oh no, we have a special case: the frame trailer wraps around
|
||||
the end of the ring buffer. We've saved space at the end of
|
||||
the ring buffer for just this problem. */
|
||||
memcpy(znet->rx_end, znet->rx_start, 8);
|
||||
cur_frame_end_offset += (RX_BUF_SIZE/2);
|
||||
}
|
||||
cur_frame_end = znet->rx_start + cur_frame_end_offset - 4;
|
||||
|
||||
lo_status = *cur_frame_end++;
|
||||
hi_status = *cur_frame_end++;
|
||||
status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
|
||||
lo_cnt = *cur_frame_end++;
|
||||
hi_cnt = *cur_frame_end++;
|
||||
count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
|
||||
|
||||
if (znet_debug > 5)
|
||||
printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
|
||||
" count %#x status %04x.\n",
|
||||
cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
|
||||
count, status);
|
||||
cur_frame_end[-4] = status;
|
||||
cur_frame_end[-3] = next_frame_end_offset;
|
||||
cur_frame_end[-2] = count;
|
||||
next_frame_end_offset = cur_frame_end_offset;
|
||||
cur_frame_end_offset -= ((count + 1)>>1) + 3;
|
||||
if (cur_frame_end_offset < 0)
|
||||
cur_frame_end_offset += RX_BUF_SIZE/2;
|
||||
}
|
||||
|
||||
/* Now step forward through the list. */
|
||||
do {
|
||||
ushort *this_rfp_ptr = znet->rx_start + next_frame_end_offset;
|
||||
int status = this_rfp_ptr[-4];
|
||||
int pkt_len = this_rfp_ptr[-2];
|
||||
|
||||
if (znet_debug > 5)
|
||||
printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
|
||||
" next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
|
||||
this_rfp_ptr[-3]<<1);
|
||||
/* Once again we must assume that the i82586 docs apply. */
|
||||
if ( ! (status & RX_RCV_OK)) { /* There was an error. */
|
||||
dev->stats.rx_errors++;
|
||||
if (status & RX_CRC_ERR) dev->stats.rx_crc_errors++;
|
||||
if (status & RX_ALG_ERR) dev->stats.rx_frame_errors++;
|
||||
#if 0
|
||||
if (status & 0x0200) dev->stats.rx_over_errors++; /* Wrong. */
|
||||
if (status & 0x0100) dev->stats.rx_fifo_errors++;
|
||||
#else
|
||||
/* maz : Wild guess... */
|
||||
if (status & RX_OVRRUN) dev->stats.rx_over_errors++;
|
||||
#endif
|
||||
if (status & RX_SRT_FRM) dev->stats.rx_length_errors++;
|
||||
} else if (pkt_len > 1536) {
|
||||
dev->stats.rx_length_errors++;
|
||||
} else {
|
||||
/* Malloc up new buffer. */
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = netdev_alloc_skb(dev, pkt_len);
|
||||
if (skb == NULL) {
|
||||
if (znet_debug)
|
||||
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) {
|
||||
int semi_cnt = (znet->rx_end - znet->rx_cur)<<1;
|
||||
memcpy(skb_put(skb,semi_cnt), znet->rx_cur, semi_cnt);
|
||||
memcpy(skb_put(skb,pkt_len-semi_cnt), znet->rx_start,
|
||||
pkt_len - semi_cnt);
|
||||
} else {
|
||||
memcpy(skb_put(skb,pkt_len), znet->rx_cur, pkt_len);
|
||||
if (znet_debug > 6) {
|
||||
unsigned int *packet = (unsigned int *) skb->data;
|
||||
printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
|
||||
packet[1], packet[2], packet[3]);
|
||||
}
|
||||
}
|
||||
skb->protocol=eth_type_trans(skb,dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
}
|
||||
znet->rx_cur = this_rfp_ptr;
|
||||
if (znet->rx_cur >= znet->rx_end)
|
||||
znet->rx_cur -= RX_BUF_SIZE/2;
|
||||
update_stop_hit(ioaddr, (znet->rx_cur - znet->rx_start)<<1);
|
||||
next_frame_end_offset = this_rfp_ptr[-3];
|
||||
if (next_frame_end_offset == 0) /* Read all the frames? */
|
||||
break; /* Done for now */
|
||||
this_rfp_ptr = znet->rx_start + next_frame_end_offset;
|
||||
} while (--boguscount);
|
||||
|
||||
/* If any worth-while packets have been received, dev_rint()
|
||||
has done a mark_bh(INET_BH) for us and will work on them
|
||||
when we get to the bottom-half routine. */
|
||||
}
|
||||
|
||||
/* The inverse routine to znet_open(). */
|
||||
static int znet_close(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
netif_stop_queue (dev);
|
||||
|
||||
outb(OP0_RESET, ioaddr); /* CMD0_RESET */
|
||||
|
||||
if (znet_debug > 1)
|
||||
printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
|
||||
/* Turn off transceiver power. */
|
||||
znet_transceiver_power (dev, 0);
|
||||
|
||||
znet_release_resources (dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_dma(struct net_device *dev)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
unsigned char stat = inb (ioaddr);
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE;
|
||||
unsigned addr = inb(dma_port);
|
||||
short residue;
|
||||
|
||||
addr |= inb(dma_port) << 8;
|
||||
residue = get_dma_residue(znet->tx_dma);
|
||||
|
||||
if (znet_debug > 1) {
|
||||
flags=claim_dma_lock();
|
||||
printk(KERN_DEBUG "Stat:%02x Addr: %04x cnt:%3x\n",
|
||||
stat, addr<<1, residue);
|
||||
release_dma_lock(flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the hardware. We have to do this when the board is open()ed
|
||||
or when we come out of suspend mode. */
|
||||
static void hardware_init(struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
short ioaddr = dev->base_addr;
|
||||
struct znet_private *znet = netdev_priv(dev);
|
||||
|
||||
znet->rx_cur = znet->rx_start;
|
||||
znet->tx_cur = znet->tx_start;
|
||||
|
||||
/* Reset the chip, and start it up. */
|
||||
outb(OP0_RESET, ioaddr);
|
||||
|
||||
flags=claim_dma_lock();
|
||||
disable_dma(znet->rx_dma); /* reset by an interrupting task. */
|
||||
clear_dma_ff(znet->rx_dma);
|
||||
set_dma_mode(znet->rx_dma, DMA_RX_MODE);
|
||||
set_dma_addr(znet->rx_dma, isa_virt_to_bus(znet->rx_start));
|
||||
set_dma_count(znet->rx_dma, RX_BUF_SIZE);
|
||||
enable_dma(znet->rx_dma);
|
||||
/* Now set up the Tx channel. */
|
||||
disable_dma(znet->tx_dma);
|
||||
clear_dma_ff(znet->tx_dma);
|
||||
set_dma_mode(znet->tx_dma, DMA_TX_MODE);
|
||||
set_dma_addr(znet->tx_dma, isa_virt_to_bus(znet->tx_start));
|
||||
set_dma_count(znet->tx_dma, znet->tx_buf_len<<1);
|
||||
enable_dma(znet->tx_dma);
|
||||
release_dma_lock(flags);
|
||||
|
||||
if (znet_debug > 1)
|
||||
printk(KERN_DEBUG "%s: Initializing the i82593, rx buf %p tx buf %p\n",
|
||||
dev->name, znet->rx_start,znet->tx_start);
|
||||
/* Do an empty configure command, just like the Crynwr driver. This
|
||||
resets to chip to its default values. */
|
||||
*znet->tx_cur++ = 0;
|
||||
*znet->tx_cur++ = 0;
|
||||
show_dma(dev);
|
||||
outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
|
||||
|
||||
znet_set_multicast_list (dev);
|
||||
|
||||
*znet->tx_cur++ = 6;
|
||||
memcpy(znet->tx_cur, dev->dev_addr, 6);
|
||||
znet->tx_cur += 3;
|
||||
show_dma(dev);
|
||||
outb(OP0_IA_SETUP | CR0_CHNL, ioaddr);
|
||||
show_dma(dev);
|
||||
|
||||
update_stop_hit(ioaddr, 8192);
|
||||
if (znet_debug > 1) printk(KERN_DEBUG "enabling Rx.\n");
|
||||
outb(OP0_RCV_ENABLE, ioaddr);
|
||||
netif_start_queue (dev);
|
||||
}
|
||||
|
||||
static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
|
||||
{
|
||||
outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, ioaddr);
|
||||
if (znet_debug > 5)
|
||||
printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
|
||||
(rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE);
|
||||
outb((rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE, ioaddr);
|
||||
outb(OP1_SWIT_TO_PORT_0, ioaddr);
|
||||
}
|
||||
|
||||
static __exit void znet_cleanup (void)
|
||||
{
|
||||
if (znet_dev) {
|
||||
struct znet_private *znet = netdev_priv(znet_dev);
|
||||
|
||||
unregister_netdev (znet_dev);
|
||||
kfree (znet->rx_start);
|
||||
kfree (znet->tx_start);
|
||||
free_netdev (znet_dev);
|
||||
}
|
||||
}
|
||||
|
||||
module_init (znet_probe);
|
||||
module_exit (znet_cleanup);
|
@ -1,33 +0,0 @@
|
||||
#
|
||||
# Racal-Interlan device configuration
|
||||
#
|
||||
|
||||
config NET_VENDOR_RACAL
|
||||
bool "Racal-Interlan (Micom) NI devices"
|
||||
default y
|
||||
depends on ISA
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, such
|
||||
as the NI5010, NI5210 or NI6210, 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 NI cards. If you say Y, you will be asked for
|
||||
your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_RACAL
|
||||
|
||||
config NI5010
|
||||
tristate "NI5010 support (EXPERIMENTAL)"
|
||||
depends on ISA && EXPERIMENTAL && BROKEN_ON_SMP
|
||||
---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>. Note that this is still
|
||||
experimental code.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called ni5010.
|
||||
|
||||
endif # NET_VENDOR_RACAL
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
# Makefile for the Racal-Interlan network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_NI5010) += ni5010.o
|
@ -1,771 +0,0 @@
|
||||
/* ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard.
|
||||
*
|
||||
* Copyright 1996,1997,2006 Jan-Pascal van Best and Andreas Mohr.
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* The authors may be reached as:
|
||||
* janpascal@vanbest.org andi@lisas.de
|
||||
*
|
||||
* Sources:
|
||||
* Donald Becker's "skeleton.c"
|
||||
* Crynwr ni5010 packet driver
|
||||
*
|
||||
* Changes:
|
||||
* v0.0: First test version
|
||||
* v0.1: First working version
|
||||
* v0.2:
|
||||
* v0.3->v0.90: Now demand setting io and irq when loading as module
|
||||
* 970430 v0.91: modified for Linux 2.1.14
|
||||
* v0.92: Implemented Andreas' (better) NI5010 probe
|
||||
* 970503 v0.93: Fixed auto-irq failure on warm reboot (JB)
|
||||
* 970623 v1.00: First kernel version (AM)
|
||||
* 970814 v1.01: Added detection of onboard receive buffer size (AM)
|
||||
* 060611 v1.02: slight cleanup: email addresses, driver modernization.
|
||||
* Bugs:
|
||||
* - not SMP-safe (no locking of I/O accesses)
|
||||
* - Note that you have to patch ifconfig for the new /proc/net/dev
|
||||
* format. It gives incorrect stats otherwise.
|
||||
*
|
||||
* To do:
|
||||
* Fix all bugs :-)
|
||||
* Move some stuff to chipset_init()
|
||||
* Handle xmt errors other than collisions
|
||||
* Complete merge with Andreas' driver
|
||||
* Implement ring buffers (Is this useful? You can't squeeze
|
||||
* too many packet in a 2k buffer!)
|
||||
* Implement DMA (Again, is this useful? Some docs say DMA is
|
||||
* slower than programmed I/O)
|
||||
*
|
||||
* Compile with:
|
||||
* gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ \
|
||||
* -DMODULE -c ni5010.c
|
||||
*
|
||||
* Insert with e.g.:
|
||||
* insmod ni5010.ko io=0x300 irq=5
|
||||
*/
|
||||
|
||||
#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/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include "ni5010.h"
|
||||
|
||||
static const char boardname[] = "NI5010";
|
||||
static char version[] __initdata =
|
||||
"ni5010.c: v1.02 20060611 Jan-Pascal van Best and Andreas Mohr\n";
|
||||
|
||||
/* bufsize_rcv == 0 means autoprobing */
|
||||
static unsigned int bufsize_rcv;
|
||||
|
||||
#define JUMPERED_INTERRUPTS /* IRQ line jumpered on board */
|
||||
#undef JUMPERED_DMA /* No DMA used */
|
||||
#undef FULL_IODETECT /* Only detect in portlist */
|
||||
|
||||
#ifndef FULL_IODETECT
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int ports[] __initdata =
|
||||
{ 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0 };
|
||||
#endif
|
||||
|
||||
/* Use 0 for production, 1 for verification, >2 for debug */
|
||||
#ifndef NI5010_DEBUG
|
||||
#define NI5010_DEBUG 0
|
||||
#endif
|
||||
|
||||
/* Information that needs to be kept for each board. */
|
||||
struct ni5010_local {
|
||||
int o_pkt_size;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/* Index to functions, as function prototypes. */
|
||||
|
||||
static int ni5010_probe1(struct net_device *dev, int ioaddr);
|
||||
static int ni5010_open(struct net_device *dev);
|
||||
static int ni5010_send_packet(struct sk_buff *skb, struct net_device *dev);
|
||||
static irqreturn_t ni5010_interrupt(int irq, void *dev_id);
|
||||
static void ni5010_rx(struct net_device *dev);
|
||||
static void ni5010_timeout(struct net_device *dev);
|
||||
static int ni5010_close(struct net_device *dev);
|
||||
static void ni5010_set_multicast_list(struct net_device *dev);
|
||||
static void reset_receiver(struct net_device *dev);
|
||||
|
||||
static int process_xmt_interrupt(struct net_device *dev);
|
||||
#define tx_done(dev) 1
|
||||
static void hardware_send_packet(struct net_device *dev, char *buf, int length, int pad);
|
||||
static void chipset_init(struct net_device *dev, int startp);
|
||||
static void dump_packet(void *buf, int len);
|
||||
static void ni5010_show_registers(struct net_device *dev);
|
||||
|
||||
static int io;
|
||||
static int irq;
|
||||
|
||||
struct net_device * __init ni5010_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_etherdev(sizeof(struct ni5010_local));
|
||||
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);
|
||||
io = dev->base_addr;
|
||||
irq = dev->irq;
|
||||
}
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name));
|
||||
|
||||
if (io > 0x1ff) { /* Check a single specified location. */
|
||||
err = ni5010_probe1(dev, io);
|
||||
} else if (io != 0) { /* Don't probe at all. */
|
||||
err = -ENXIO;
|
||||
} else {
|
||||
#ifdef FULL_IODETECT
|
||||
for (io=0x200; io<0x400 && ni5010_probe1(dev, io) ; io+=0x20)
|
||||
;
|
||||
if (io == 0x400)
|
||||
err = -ENODEV;
|
||||
|
||||
#else
|
||||
for (port = ports; *port && ni5010_probe1(dev, *port); port++)
|
||||
;
|
||||
if (!*port)
|
||||
err = -ENODEV;
|
||||
#endif /* FULL_IODETECT */
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out1;
|
||||
return dev;
|
||||
out1:
|
||||
release_region(dev->base_addr, NI5010_IO_EXTENT);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static inline int rd_port(int ioaddr)
|
||||
{
|
||||
inb(IE_RBUF);
|
||||
return inb(IE_SAPROM);
|
||||
}
|
||||
|
||||
static void __init trigger_irq(int ioaddr)
|
||||
{
|
||||
outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */
|
||||
outb(0x00, IE_RESET); /* Board reset */
|
||||
outb(0x00, EDLC_XMASK); /* Disable all Xmt interrupts */
|
||||
outb(0x00, EDLC_RMASK); /* Disable all Rcv interrupt */
|
||||
outb(0xff, EDLC_XCLR); /* Clear all pending Xmt interrupts */
|
||||
outb(0xff, EDLC_RCLR); /* Clear all pending Rcv interrupts */
|
||||
/*
|
||||
* Transmit packet mode: Ignore parity, Power xcvr,
|
||||
* Enable loopback
|
||||
*/
|
||||
outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE);
|
||||
outb(RMD_BROADCAST, EDLC_RMODE); /* Receive normal&broadcast */
|
||||
outb(XM_ALL, EDLC_XMASK); /* Enable all Xmt interrupts */
|
||||
udelay(50); /* FIXME: Necessary? */
|
||||
outb(MM_EN_XMT|MM_MUX, IE_MMODE); /* Start transmission */
|
||||
}
|
||||
|
||||
static const struct net_device_ops ni5010_netdev_ops = {
|
||||
.ndo_open = ni5010_open,
|
||||
.ndo_stop = ni5010_close,
|
||||
.ndo_start_xmit = ni5010_send_packet,
|
||||
.ndo_set_rx_mode = ni5010_set_multicast_list,
|
||||
.ndo_tx_timeout = ni5010_timeout,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_change_mtu = eth_change_mtu,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the real probe routine. Linux has a history of friendly device
|
||||
* probes on the ISA bus. A good device probes avoids doing writes, and
|
||||
* verifies that the correct device exists and functions.
|
||||
*/
|
||||
|
||||
static int __init ni5010_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
static unsigned version_printed;
|
||||
struct ni5010_local *lp;
|
||||
int i;
|
||||
unsigned int data = 0;
|
||||
int boguscount = 40;
|
||||
int err = -ENODEV;
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
dev->irq = irq;
|
||||
|
||||
if (!request_region(ioaddr, NI5010_IO_EXTENT, boardname))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* This is no "official" probe method, I've rather tested which
|
||||
* probe works best with my seven NI5010 cards
|
||||
* (they have very different serial numbers)
|
||||
* Suggestions or failure reports are very, very welcome !
|
||||
* But I think it is a relatively good probe method
|
||||
* since it doesn't use any "outb"
|
||||
* It should be nearly 100% reliable !
|
||||
* well-known WARNING: this probe method (like many others)
|
||||
* will hang the system if a NE2000 card region is probed !
|
||||
*
|
||||
* - Andreas
|
||||
*/
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_probe1(%#3x)\n",
|
||||
dev->name, ioaddr));
|
||||
|
||||
if (inb(ioaddr+0) == 0xff)
|
||||
goto out;
|
||||
|
||||
while ( (rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr) &
|
||||
rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr)) != 0xff)
|
||||
{
|
||||
if (boguscount-- == 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #1 passed!\n", dev->name));
|
||||
|
||||
for (i=0; i<32; i++)
|
||||
if ( (data = rd_port(ioaddr)) != 0xff) break;
|
||||
if (data==0xff)
|
||||
goto out;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #2 passed!\n", dev->name));
|
||||
|
||||
if ((data != SA_ADDR0) || (rd_port(ioaddr) != SA_ADDR1) ||
|
||||
(rd_port(ioaddr) != SA_ADDR2))
|
||||
goto out;
|
||||
|
||||
for (i=0; i<4; i++)
|
||||
rd_port(ioaddr);
|
||||
|
||||
if ( (rd_port(ioaddr) != NI5010_MAGICVAL1) ||
|
||||
(rd_port(ioaddr) != NI5010_MAGICVAL2) )
|
||||
goto out;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #3 passed!\n", dev->name));
|
||||
|
||||
if (NI5010_DEBUG && version_printed++ == 0)
|
||||
printk(KERN_INFO "%s", version);
|
||||
|
||||
printk("NI5010 ethercard probe at 0x%x: ", ioaddr);
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
|
||||
for (i=0; i<6; i++) {
|
||||
outw(i, IE_GP);
|
||||
dev->dev_addr[i] = inb(IE_SAPROM);
|
||||
}
|
||||
printk("%pM ", dev->dev_addr);
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name));
|
||||
|
||||
#ifdef JUMPERED_INTERRUPTS
|
||||
if (dev->irq == 0xff)
|
||||
;
|
||||
else if (dev->irq < 2) {
|
||||
unsigned long irq_mask;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #5 passed!\n", dev->name));
|
||||
|
||||
irq_mask = probe_irq_on();
|
||||
trigger_irq(ioaddr);
|
||||
mdelay(20);
|
||||
dev->irq = probe_irq_off(irq_mask);
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #6 passed!\n", dev->name));
|
||||
|
||||
if (dev->irq == 0) {
|
||||
err = -EAGAIN;
|
||||
printk(KERN_WARNING "%s: no IRQ found!\n", dev->name);
|
||||
goto out;
|
||||
}
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #7 passed!\n", dev->name));
|
||||
} else if (dev->irq == 2) {
|
||||
dev->irq = 9;
|
||||
}
|
||||
#endif /* JUMPERED_INTERRUPTS */
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name));
|
||||
|
||||
/* DMA is not supported (yet?), so no use detecting it */
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
spin_lock_init(&lp->lock);
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: I/O #10 passed!\n", dev->name));
|
||||
|
||||
/* get the size of the onboard receive buffer
|
||||
* higher addresses than bufsize are wrapped into real buffer
|
||||
* i.e. data for offs. 0x801 is written to 0x1 with a 2K onboard buffer
|
||||
*/
|
||||
if (!bufsize_rcv) {
|
||||
outb(1, IE_MMODE); /* Put Rcv buffer on system bus */
|
||||
outw(0, IE_GP); /* Point GP at start of packet */
|
||||
outb(0, IE_RBUF); /* set buffer byte 0 to 0 */
|
||||
for (i = 1; i < 0xff; i++) {
|
||||
outw(i << 8, IE_GP); /* Point GP at packet size to be tested */
|
||||
outb(i, IE_RBUF);
|
||||
outw(0x0, IE_GP); /* Point GP at start of packet */
|
||||
data = inb(IE_RBUF);
|
||||
if (data == i) break;
|
||||
}
|
||||
bufsize_rcv = i << 8;
|
||||
outw(0, IE_GP); /* Point GP at start of packet */
|
||||
outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */
|
||||
}
|
||||
printk("-> bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE);
|
||||
|
||||
dev->netdev_ops = &ni5010_netdev_ops;
|
||||
dev->watchdog_timeo = HZ/20;
|
||||
|
||||
dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */
|
||||
|
||||
/* Shut up the ni5010 */
|
||||
outb(0, EDLC_RMASK); /* Mask all receive interrupts */
|
||||
outb(0, EDLC_XMASK); /* Mask all xmit interrupts */
|
||||
outb(0xff, EDLC_RCLR); /* Kill all pending rcv interrupts */
|
||||
outb(0xff, EDLC_XCLR); /* Kill all pending xmt interrupts */
|
||||
|
||||
printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq);
|
||||
if (dev->dma)
|
||||
printk(" & DMA %d", dev->dma);
|
||||
printk(".\n");
|
||||
return 0;
|
||||
out:
|
||||
release_region(dev->base_addr, NI5010_IO_EXTENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 a non-reboot way to recover if something goes wrong.
|
||||
*/
|
||||
|
||||
static int ni5010_open(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
int i;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name));
|
||||
|
||||
if (request_irq(dev->irq, ni5010_interrupt, 0, boardname, dev)) {
|
||||
printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq);
|
||||
return -EAGAIN;
|
||||
}
|
||||
PRINTK3((KERN_DEBUG "%s: passed open() #1\n", dev->name));
|
||||
/*
|
||||
* Always allocate the DMA channel after the IRQ,
|
||||
* and clean up on failure.
|
||||
*/
|
||||
#ifdef JUMPERED_DMA
|
||||
if (request_dma(dev->dma, cardname)) {
|
||||
printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma);
|
||||
free_irq(dev->irq, NULL);
|
||||
return -EAGAIN;
|
||||
}
|
||||
#endif /* JUMPERED_DMA */
|
||||
|
||||
PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name));
|
||||
/* Reset the hardware here. Don't forget to set the station address. */
|
||||
|
||||
outb(RS_RESET, EDLC_RESET); /* Hold up EDLC_RESET while configing board */
|
||||
outb(0, IE_RESET); /* Hardware reset of ni5010 board */
|
||||
outb(XMD_LBC, EDLC_XMODE); /* Only loopback xmits */
|
||||
|
||||
PRINTK3((KERN_DEBUG "%s: passed open() #3\n", dev->name));
|
||||
/* Set the station address */
|
||||
for(i = 0;i < 6; i++) {
|
||||
outb(dev->dev_addr[i], EDLC_ADDR + i);
|
||||
}
|
||||
|
||||
PRINTK3((KERN_DEBUG "%s: Initialising ni5010\n", dev->name));
|
||||
outb(0, EDLC_XMASK); /* No xmit interrupts for now */
|
||||
outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE);
|
||||
/* Normal packet xmit mode */
|
||||
outb(0xff, EDLC_XCLR); /* Clear all pending xmit interrupts */
|
||||
outb(RMD_BROADCAST, EDLC_RMODE);
|
||||
/* Receive broadcast and normal packets */
|
||||
reset_receiver(dev); /* Ready ni5010 for receiving packets */
|
||||
|
||||
outb(0, EDLC_RESET); /* Un-reset the ni5010 */
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
if (NI5010_DEBUG) ni5010_show_registers(dev);
|
||||
|
||||
PRINTK((KERN_DEBUG "%s: open successful\n", dev->name));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_receiver(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
PRINTK3((KERN_DEBUG "%s: resetting receiver\n", dev->name));
|
||||
outw(0, IE_GP); /* Receive packet at start of buffer */
|
||||
outb(0xff, EDLC_RCLR); /* Clear all pending rcv interrupts */
|
||||
outb(0, IE_MMODE); /* Put EDLC to rcv buffer */
|
||||
outb(MM_EN_RCV, IE_MMODE); /* Enable rcv */
|
||||
outb(0xff, EDLC_RMASK); /* Enable all rcv interrupts */
|
||||
}
|
||||
|
||||
static void ni5010_timeout(struct net_device *dev)
|
||||
{
|
||||
printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
|
||||
tx_done(dev) ? "IRQ conflict" : "network cable problem");
|
||||
/* Try to restart the adaptor. */
|
||||
/* FIXME: Give it a real kick here */
|
||||
chipset_init(dev, 1);
|
||||
dev->trans_start = jiffies; /* prevent tx timeout */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static int ni5010_send_packet(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_send_packet\n", dev->name));
|
||||
|
||||
/*
|
||||
* Block sending
|
||||
*/
|
||||
|
||||
netif_stop_queue(dev);
|
||||
hardware_send_packet(dev, (unsigned char *)skb->data, skb->len, length-skb->len);
|
||||
dev_kfree_skb (skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The typical workload of the driver:
|
||||
* Handle the network interface interrupts.
|
||||
*/
|
||||
static irqreturn_t ni5010_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct ni5010_local *lp;
|
||||
int ioaddr, status;
|
||||
int xmit_was_error = 0;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_interrupt\n", dev->name));
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
status = inb(IE_ISTAT);
|
||||
PRINTK3((KERN_DEBUG "%s: IE_ISTAT = %#02x\n", dev->name, status));
|
||||
|
||||
if ((status & IS_R_INT) == 0) ni5010_rx(dev);
|
||||
|
||||
if ((status & IS_X_INT) == 0) {
|
||||
xmit_was_error = process_xmt_interrupt(dev);
|
||||
}
|
||||
|
||||
if ((status & IS_DMA_INT) == 0) {
|
||||
PRINTK((KERN_DEBUG "%s: DMA complete (?)\n", dev->name));
|
||||
outb(0, IE_DMA_RST); /* Reset DMA int */
|
||||
}
|
||||
|
||||
if (!xmit_was_error)
|
||||
reset_receiver(dev);
|
||||
spin_unlock(&lp->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static void dump_packet(void *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_DEBUG "Packet length = %#4x\n", len);
|
||||
for (i = 0; i < len; i++){
|
||||
if (i % 16 == 0) printk(KERN_DEBUG "%#4.4x", i);
|
||||
if (i % 2 == 0) printk(" ");
|
||||
printk("%2.2x", ((unsigned char *)buf)[i]);
|
||||
if (i % 16 == 15) printk("\n");
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/* We have a good packet, get it out of the buffer. */
|
||||
static void ni5010_rx(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
unsigned char rcv_stat;
|
||||
struct sk_buff *skb;
|
||||
int i_pkt_size;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_rx()\n", dev->name));
|
||||
|
||||
rcv_stat = inb(EDLC_RSTAT);
|
||||
PRINTK3((KERN_DEBUG "%s: EDLC_RSTAT = %#2x\n", dev->name, rcv_stat));
|
||||
|
||||
if ( (rcv_stat & RS_VALID_BITS) != RS_PKT_OK) {
|
||||
PRINTK((KERN_INFO "%s: receive error.\n", dev->name));
|
||||
dev->stats.rx_errors++;
|
||||
if (rcv_stat & RS_RUNT) dev->stats.rx_length_errors++;
|
||||
if (rcv_stat & RS_ALIGN) dev->stats.rx_frame_errors++;
|
||||
if (rcv_stat & RS_CRC_ERR) dev->stats.rx_crc_errors++;
|
||||
if (rcv_stat & RS_OFLW) dev->stats.rx_fifo_errors++;
|
||||
outb(0xff, EDLC_RCLR); /* Clear the interrupt */
|
||||
return;
|
||||
}
|
||||
|
||||
outb(0xff, EDLC_RCLR); /* Clear the interrupt */
|
||||
|
||||
i_pkt_size = inw(IE_RCNT);
|
||||
if (i_pkt_size > ETH_FRAME_LEN || i_pkt_size < 10 ) {
|
||||
PRINTK((KERN_DEBUG "%s: Packet size error, packet size = %#4.4x\n",
|
||||
dev->name, i_pkt_size));
|
||||
dev->stats.rx_errors++;
|
||||
dev->stats.rx_length_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Malloc up new buffer. */
|
||||
skb = netdev_alloc_skb(dev, i_pkt_size + 3);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
skb_reserve(skb, 2);
|
||||
|
||||
/* Read packet into buffer */
|
||||
outb(MM_MUX, IE_MMODE); /* Rcv buffer to system bus */
|
||||
outw(0, IE_GP); /* Seek to beginning of packet */
|
||||
insb(IE_RBUF, skb_put(skb, i_pkt_size), i_pkt_size);
|
||||
|
||||
if (NI5010_DEBUG >= 4)
|
||||
dump_packet(skb->data, skb->len);
|
||||
|
||||
skb->protocol = eth_type_trans(skb,dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += i_pkt_size;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: Received packet, size=%#4.4x\n",
|
||||
dev->name, i_pkt_size));
|
||||
}
|
||||
|
||||
static int process_xmt_interrupt(struct net_device *dev)
|
||||
{
|
||||
struct ni5010_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
int xmit_stat;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering process_xmt_interrupt\n", dev->name));
|
||||
|
||||
xmit_stat = inb(EDLC_XSTAT);
|
||||
PRINTK3((KERN_DEBUG "%s: EDLC_XSTAT = %2.2x\n", dev->name, xmit_stat));
|
||||
|
||||
outb(0, EDLC_XMASK); /* Disable xmit IRQ's */
|
||||
outb(0xff, EDLC_XCLR); /* Clear all pending xmit IRQ's */
|
||||
|
||||
if (xmit_stat & XS_COLL){
|
||||
PRINTK((KERN_DEBUG "%s: collision detected, retransmitting\n",
|
||||
dev->name));
|
||||
outw(NI5010_BUFSIZE - lp->o_pkt_size, IE_GP);
|
||||
/* outb(0, IE_MMODE); */ /* xmt buf on sysbus FIXME: needed ? */
|
||||
outb(MM_EN_XMT | MM_MUX, IE_MMODE);
|
||||
outb(XM_ALL, EDLC_XMASK); /* Enable xmt IRQ's */
|
||||
dev->stats.collisions++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: handle other xmt error conditions */
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += lp->o_pkt_size;
|
||||
netif_wake_queue(dev);
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: sent packet, size=%#4.4x\n",
|
||||
dev->name, lp->o_pkt_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The inverse routine to ni5010_open(). */
|
||||
static int ni5010_close(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name));
|
||||
#ifdef JUMPERED_INTERRUPTS
|
||||
free_irq(dev->irq, NULL);
|
||||
#endif
|
||||
/* Put card in held-RESET state */
|
||||
outb(0, IE_MMODE);
|
||||
outb(RS_RESET, EDLC_RESET);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname));
|
||||
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 ni5010_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
short ioaddr = dev->base_addr;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering set_multicast_list\n", dev->name));
|
||||
|
||||
if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI ||
|
||||
!netdev_mc_empty(dev)) {
|
||||
outb(RMD_PROMISC, EDLC_RMODE); /* Enable promiscuous mode */
|
||||
PRINTK((KERN_DEBUG "%s: Entering promiscuous mode\n", dev->name));
|
||||
} else {
|
||||
PRINTK((KERN_DEBUG "%s: Entering broadcast mode\n", dev->name));
|
||||
outb(RMD_BROADCAST, EDLC_RMODE); /* Disable promiscuous mode, use normal mode */
|
||||
}
|
||||
}
|
||||
|
||||
static void hardware_send_packet(struct net_device *dev, char *buf, int length, int pad)
|
||||
{
|
||||
struct ni5010_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
unsigned long flags;
|
||||
unsigned int buf_offs;
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: entering hardware_send_packet\n", dev->name));
|
||||
|
||||
if (length > ETH_FRAME_LEN) {
|
||||
PRINTK((KERN_WARNING "%s: packet too large, not possible\n",
|
||||
dev->name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (NI5010_DEBUG) ni5010_show_registers(dev);
|
||||
|
||||
if (inb(IE_ISTAT) & IS_EN_XMT) {
|
||||
PRINTK((KERN_WARNING "%s: sending packet while already transmitting, not possible\n",
|
||||
dev->name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (NI5010_DEBUG > 3) dump_packet(buf, length);
|
||||
|
||||
buf_offs = NI5010_BUFSIZE - length - pad;
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
lp->o_pkt_size = length + pad;
|
||||
|
||||
outb(0, EDLC_RMASK); /* Mask all receive interrupts */
|
||||
outb(0, IE_MMODE); /* Put Xmit buffer on system bus */
|
||||
outb(0xff, EDLC_RCLR); /* Clear out pending rcv interrupts */
|
||||
|
||||
outw(buf_offs, IE_GP); /* Point GP at start of packet */
|
||||
outsb(IE_XBUF, buf, length); /* Put data in buffer */
|
||||
while(pad--)
|
||||
outb(0, IE_XBUF);
|
||||
|
||||
outw(buf_offs, IE_GP); /* Rewrite where packet starts */
|
||||
|
||||
/* should work without that outb() (Crynwr used it) */
|
||||
/*outb(MM_MUX, IE_MMODE);*/ /* Xmt buffer to EDLC bus */
|
||||
outb(MM_EN_XMT | MM_MUX, IE_MMODE); /* Begin transmission */
|
||||
outb(XM_ALL, EDLC_XMASK); /* Cause interrupt after completion or fail */
|
||||
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
netif_wake_queue(dev);
|
||||
|
||||
if (NI5010_DEBUG) ni5010_show_registers(dev);
|
||||
}
|
||||
|
||||
static void chipset_init(struct net_device *dev, int startp)
|
||||
{
|
||||
/* FIXME: Move some stuff here */
|
||||
PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name));
|
||||
}
|
||||
|
||||
static void ni5010_show_registers(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
PRINTK3((KERN_DEBUG "%s: XSTAT %#2.2x\n", dev->name, inb(EDLC_XSTAT)));
|
||||
PRINTK3((KERN_DEBUG "%s: XMASK %#2.2x\n", dev->name, inb(EDLC_XMASK)));
|
||||
PRINTK3((KERN_DEBUG "%s: RSTAT %#2.2x\n", dev->name, inb(EDLC_RSTAT)));
|
||||
PRINTK3((KERN_DEBUG "%s: RMASK %#2.2x\n", dev->name, inb(EDLC_RMASK)));
|
||||
PRINTK3((KERN_DEBUG "%s: RMODE %#2.2x\n", dev->name, inb(EDLC_RMODE)));
|
||||
PRINTK3((KERN_DEBUG "%s: XMODE %#2.2x\n", dev->name, inb(EDLC_XMODE)));
|
||||
PRINTK3((KERN_DEBUG "%s: ISTAT %#2.2x\n", dev->name, inb(IE_ISTAT)));
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static struct net_device *dev_ni5010;
|
||||
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(io, "ni5010 I/O base address");
|
||||
MODULE_PARM_DESC(irq, "ni5010 IRQ number");
|
||||
|
||||
static int __init ni5010_init_module(void)
|
||||
{
|
||||
PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname));
|
||||
/*
|
||||
if(io <= 0 || irq == 0){
|
||||
printk(KERN_WARNING "%s: Autoprobing not allowed for modules.\n", boardname);
|
||||
printk(KERN_WARNING "%s: Set symbols 'io' and 'irq'\n", boardname);
|
||||
return -EINVAL;
|
||||
}
|
||||
*/
|
||||
if (io <= 0){
|
||||
printk(KERN_WARNING "%s: Autoprobing for modules is hazardous, trying anyway..\n", boardname);
|
||||
}
|
||||
|
||||
PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io));
|
||||
dev_ni5010 = ni5010_probe(-1);
|
||||
if (IS_ERR(dev_ni5010))
|
||||
return PTR_ERR(dev_ni5010);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ni5010_cleanup_module(void)
|
||||
{
|
||||
PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname));
|
||||
unregister_netdev(dev_ni5010);
|
||||
release_region(dev_ni5010->base_addr, NI5010_IO_EXTENT);
|
||||
free_netdev(dev_ni5010);
|
||||
}
|
||||
module_init(ni5010_init_module);
|
||||
module_exit(ni5010_cleanup_module);
|
||||
#endif /* MODULE */
|
||||
MODULE_LICENSE("GPL");
|
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Racal-Interlan ni5010 Ethernet definitions
|
||||
*
|
||||
* This is an extension to the Linux operating system, and is covered by the
|
||||
* same GNU General Public License that covers that work.
|
||||
*
|
||||
* copyrights (c) 1996 by Jan-Pascal van Best (jvbest@wi.leidenuniv.nl)
|
||||
*
|
||||
* I have done a look in the following sources:
|
||||
* crynwr-packet-driver by Russ Nelson
|
||||
*/
|
||||
|
||||
#define NI5010_BUFSIZE 2048 /* number of bytes in a buffer */
|
||||
|
||||
#define NI5010_MAGICVAL0 0x00 /* magic-values for ni5010 card */
|
||||
#define NI5010_MAGICVAL1 0x55
|
||||
#define NI5010_MAGICVAL2 0xAA
|
||||
|
||||
#define SA_ADDR0 0x02
|
||||
#define SA_ADDR1 0x07
|
||||
#define SA_ADDR2 0x01
|
||||
|
||||
/* The number of low I/O ports used by the ni5010 ethercard. */
|
||||
#define NI5010_IO_EXTENT 32
|
||||
|
||||
#define PRINTK(x) if (NI5010_DEBUG) printk x
|
||||
#define PRINTK2(x) if (NI5010_DEBUG>=2) printk x
|
||||
#define PRINTK3(x) if (NI5010_DEBUG>=3) printk x
|
||||
|
||||
/* The various IE command registers */
|
||||
#define EDLC_XSTAT (ioaddr + 0x00) /* EDLC transmit csr */
|
||||
#define EDLC_XCLR (ioaddr + 0x00) /* EDLC transmit "Clear IRQ" */
|
||||
#define EDLC_XMASK (ioaddr + 0x01) /* EDLC transmit "IRQ Masks" */
|
||||
#define EDLC_RSTAT (ioaddr + 0x02) /* EDLC receive csr */
|
||||
#define EDLC_RCLR (ioaddr + 0x02) /* EDLC receive "Clear IRQ" */
|
||||
#define EDLC_RMASK (ioaddr + 0x03) /* EDLC receive "IRQ Masks" */
|
||||
#define EDLC_XMODE (ioaddr + 0x04) /* EDLC transmit Mode */
|
||||
#define EDLC_RMODE (ioaddr + 0x05) /* EDLC receive Mode */
|
||||
#define EDLC_RESET (ioaddr + 0x06) /* EDLC RESET register */
|
||||
#define EDLC_TDR1 (ioaddr + 0x07) /* "Time Domain Reflectometry" reg1 */
|
||||
#define EDLC_ADDR (ioaddr + 0x08) /* EDLC station address, 6 bytes */
|
||||
/* 0x0E doesn't exist for r/w */
|
||||
#define EDLC_TDR2 (ioaddr + 0x0f) /* "Time Domain Reflectometry" reg2 */
|
||||
#define IE_GP (ioaddr + 0x10) /* GP pointer (word register) */
|
||||
/* 0x11 is 2nd byte of GP Pointer */
|
||||
#define IE_RCNT (ioaddr + 0x10) /* Count of bytes in rcv'd packet */
|
||||
/* 0x11 is 2nd byte of "Byte Count" */
|
||||
#define IE_MMODE (ioaddr + 0x12) /* Memory Mode register */
|
||||
#define IE_DMA_RST (ioaddr + 0x13) /* IE DMA Reset. write only */
|
||||
#define IE_ISTAT (ioaddr + 0x13) /* IE Interrupt Status. read only */
|
||||
#define IE_RBUF (ioaddr + 0x14) /* IE Receive Buffer port */
|
||||
#define IE_XBUF (ioaddr + 0x15) /* IE Transmit Buffer port */
|
||||
#define IE_SAPROM (ioaddr + 0x16) /* window on station addr prom */
|
||||
#define IE_RESET (ioaddr + 0x17) /* any write causes Board Reset */
|
||||
|
||||
/* bits in EDLC_XSTAT, interrupt clear on write, status when read */
|
||||
#define XS_TPOK 0x80 /* transmit packet successful */
|
||||
#define XS_CS 0x40 /* carrier sense */
|
||||
#define XS_RCVD 0x20 /* transmitted packet received */
|
||||
#define XS_SHORT 0x10 /* transmission media is shorted */
|
||||
#define XS_UFLW 0x08 /* underflow. iff failed board */
|
||||
#define XS_COLL 0x04 /* collision occurred */
|
||||
#define XS_16COLL 0x02 /* 16th collision occurred */
|
||||
#define XS_PERR 0x01 /* parity error */
|
||||
|
||||
#define XS_CLR_UFLW 0x08 /* clear underflow */
|
||||
#define XS_CLR_COLL 0x04 /* clear collision */
|
||||
#define XS_CLR_16COLL 0x02 /* clear 16th collision */
|
||||
#define XS_CLR_PERR 0x01 /* clear parity error */
|
||||
|
||||
/* bits in EDLC_XMASK, mask/enable transmit interrupts. register is r/w */
|
||||
#define XM_TPOK 0x80 /* =1 to enable Xmt Pkt OK interrupts */
|
||||
#define XM_RCVD 0x20 /* =1 to enable Xmt Pkt Rcvd ints */
|
||||
#define XM_UFLW 0x08 /* =1 to enable Xmt Underflow ints */
|
||||
#define XM_COLL 0x04 /* =1 to enable Xmt Collision ints */
|
||||
#define XM_COLL16 0x02 /* =1 to enable Xmt 16th Coll ints */
|
||||
#define XM_PERR 0x01 /* =1 to enable Xmt Parity Error ints */
|
||||
/* note: always clear this bit */
|
||||
#define XM_ALL (XM_TPOK | XM_RCVD | XM_UFLW | XM_COLL | XM_COLL16)
|
||||
|
||||
/* bits in EDLC_RSTAT, interrupt clear on write, status when read */
|
||||
#define RS_PKT_OK 0x80 /* received good packet */
|
||||
#define RS_RST_PKT 0x10 /* RESET packet received */
|
||||
#define RS_RUNT 0x08 /* Runt Pkt rcvd. Len < 64 Bytes */
|
||||
#define RS_ALIGN 0x04 /* Alignment error. not 8 bit aligned */
|
||||
#define RS_CRC_ERR 0x02 /* Bad CRC on rcvd pkt */
|
||||
#define RS_OFLW 0x01 /* overflow for rcv FIFO */
|
||||
#define RS_VALID_BITS ( RS_PKT_OK | RS_RST_PKT | RS_RUNT | RS_ALIGN | RS_CRC_ERR | RS_OFLW )
|
||||
/* all valid RSTAT bits */
|
||||
|
||||
#define RS_CLR_PKT_OK 0x80 /* clear rcvd packet interrupt */
|
||||
#define RS_CLR_RST_PKT 0x10 /* clear RESET packet received */
|
||||
#define RS_CLR_RUNT 0x08 /* clear Runt Pckt received */
|
||||
#define RS_CLR_ALIGN 0x04 /* clear Alignment error */
|
||||
#define RS_CLR_CRC_ERR 0x02 /* clear CRC error */
|
||||
#define RS_CLR_OFLW 0x01 /* clear rcv FIFO Overflow */
|
||||
|
||||
/* bits in EDLC_RMASK, mask/enable receive interrupts. register is r/w */
|
||||
#define RM_PKT_OK 0x80 /* =1 to enable rcvd good packet ints */
|
||||
#define RM_RST_PKT 0x10 /* =1 to enable RESET packet ints */
|
||||
#define RM_RUNT 0x08 /* =1 to enable Runt Pkt rcvd ints */
|
||||
#define RM_ALIGN 0x04 /* =1 to enable Alignment error ints */
|
||||
#define RM_CRC_ERR 0x02 /* =1 to enable Bad CRC error ints */
|
||||
#define RM_OFLW 0x01 /* =1 to enable overflow error ints */
|
||||
|
||||
/* bits in EDLC_RMODE, set Receive Packet mode. register is r/w */
|
||||
#define RMD_TEST 0x80 /* =1 for Chip testing. normally 0 */
|
||||
#define RMD_ADD_SIZ 0x10 /* =1 5-byte addr match. normally 0 */
|
||||
#define RMD_EN_RUNT 0x08 /* =1 enable runt rcv. normally 0 */
|
||||
#define RMD_EN_RST 0x04 /* =1 to rcv RESET pkt. normally 0 */
|
||||
|
||||
#define RMD_PROMISC 0x03 /* receive *all* packets. unusual */
|
||||
#define RMD_MULTICAST 0x02 /* receive multicasts too. unusual */
|
||||
#define RMD_BROADCAST 0x01 /* receive broadcasts & normal. usual */
|
||||
#define RMD_NO_PACKETS 0x00 /* don't receive any packets. unusual */
|
||||
|
||||
/* bits in EDLC_XMODE, set Transmit Packet mode. register is r/w */
|
||||
#define XMD_COLL_CNT 0xf0 /* coll's since success. read-only */
|
||||
#define XMD_IG_PAR 0x08 /* =1 to ignore parity. ALWAYS set */
|
||||
#define XMD_T_MODE 0x04 /* =1 to power xcvr. ALWAYS set this */
|
||||
#define XMD_LBC 0x02 /* =1 for loopbakc. normally set */
|
||||
#define XMD_DIS_C 0x01 /* =1 disables contention. normally 0 */
|
||||
|
||||
/* bits in EDLC_RESET, write only */
|
||||
#define RS_RESET 0x80 /* =1 to hold EDLC in reset state */
|
||||
|
||||
/* bits in IE_MMODE, write only */
|
||||
#define MM_EN_DMA 0x80 /* =1 begin DMA xfer, Cplt clrs it */
|
||||
#define MM_EN_RCV 0x40 /* =1 allows Pkt rcv. clr'd by rcv */
|
||||
#define MM_EN_XMT 0x20 /* =1 begin Xmt pkt. Cplt clrs it */
|
||||
#define MM_BUS_PAGE 0x18 /* =00 ALWAYS. Used when MUX=1 */
|
||||
#define MM_NET_PAGE 0x06 /* =00 ALWAYS. Used when MUX=0 */
|
||||
#define MM_MUX 0x01 /* =1 means Rcv Buff on system bus */
|
||||
/* =0 means Xmt Buff on system bus */
|
||||
|
||||
/* bits in IE_ISTAT, read only */
|
||||
#define IS_TDIAG 0x80 /* =1 if Diagnostic problem */
|
||||
#define IS_EN_RCV 0x20 /* =1 until frame is rcv'd cplt */
|
||||
#define IS_EN_XMT 0x10 /* =1 until frame is xmt'd cplt */
|
||||
#define IS_EN_DMA 0x08 /* =1 until DMA is cplt or aborted */
|
||||
#define IS_DMA_INT 0x04 /* =0 iff DMA done interrupt. */
|
||||
#define IS_R_INT 0x02 /* =0 iff unmasked Rcv interrupt */
|
||||
#define IS_X_INT 0x01 /* =0 iff unmasked Xmt interrupt */
|
||||
|
@ -26,17 +26,6 @@ config ARM_ETHER3
|
||||
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 SEEQ8005
|
||||
tristate "SEEQ8005 support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
---help---
|
||||
This is a driver for the SEEQ 8005 network (Ethernet) card. If this
|
||||
is for you, 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 seeq8005.
|
||||
|
||||
config SGISEEQ
|
||||
tristate "SGI Seeq ethernet controller support"
|
||||
depends on SGI_HAS_SEEQ
|
||||
|
@ -3,5 +3,4 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARM_ETHER3) += ether3.o
|
||||
obj-$(CONFIG_SEEQ8005) += seeq8005.o
|
||||
obj-$(CONFIG_SGISEEQ) += sgiseeq.o
|
||||
|
@ -1,749 +0,0 @@
|
||||
/* seeq8005.c: A network driver for linux. */
|
||||
/*
|
||||
Based on skeleton.c,
|
||||
Written 1993-94 by Donald Becker.
|
||||
See the skeleton.c file for further copyright information.
|
||||
|
||||
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 hamish@zot.apana.org.au
|
||||
|
||||
This file is a network device driver for the SEEQ 8005 chipset and
|
||||
the Linux operating system.
|
||||
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"seeq8005.c:v1.00 8/07/95 Hamish Coleman (hamish@zot.apana.org.au)\n";
|
||||
|
||||
/*
|
||||
Sources:
|
||||
SEEQ 8005 databook
|
||||
|
||||
Version history:
|
||||
1.00 Public release. cosmetic changes (no warnings now)
|
||||
0.68 Turning per- packet,interrupt debug messages off - testing for release.
|
||||
0.67 timing problems/bad buffer reads seem to be fixed now
|
||||
0.63 *!@$ protocol=eth_type_trans -- now packets flow
|
||||
0.56 Send working
|
||||
0.48 Receive working
|
||||
*/
|
||||
|
||||
#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/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include "seeq8005.h"
|
||||
|
||||
/* First, a few definitions that the brave might change. */
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int seeq8005_portlist[] __initdata =
|
||||
{ 0x300, 0x320, 0x340, 0x360, 0};
|
||||
|
||||
/* use 0 for production, 1 for verification, >2 for debug */
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 1
|
||||
#endif
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
|
||||
/* Information that need to be kept for each board. */
|
||||
struct net_local {
|
||||
unsigned short receive_ptr; /* What address in packet memory do we expect a recv_pkt_header? */
|
||||
long open_time; /* Useless example local info. */
|
||||
};
|
||||
|
||||
/* The station (ethernet) address prefix, used for IDing the board. */
|
||||
#define SA_ADDR0 0x00
|
||||
#define SA_ADDR1 0x80
|
||||
#define SA_ADDR2 0x4b
|
||||
|
||||
/* Index to functions, as function prototypes. */
|
||||
|
||||
static int seeq8005_probe1(struct net_device *dev, int ioaddr);
|
||||
static int seeq8005_open(struct net_device *dev);
|
||||
static void seeq8005_timeout(struct net_device *dev);
|
||||
static netdev_tx_t seeq8005_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
static irqreturn_t seeq8005_interrupt(int irq, void *dev_id);
|
||||
static void seeq8005_rx(struct net_device *dev);
|
||||
static int seeq8005_close(struct net_device *dev);
|
||||
static void set_multicast_list(struct net_device *dev);
|
||||
|
||||
/* Example routines you must write ;->. */
|
||||
#define tx_done(dev) (inw(SEEQ_STATUS) & SEEQSTAT_TX_ON)
|
||||
static void hardware_send_packet(struct net_device *dev, char *buf, int length);
|
||||
extern void seeq8005_init(struct net_device *dev, int startp);
|
||||
static inline void wait_for_buffer(struct net_device *dev);
|
||||
|
||||
|
||||
/* Check for a network adaptor of this type, and return '0' iff one exists.
|
||||
If dev->base_addr == 0, probe all likely locations.
|
||||
If dev->base_addr == 1, always return failure.
|
||||
*/
|
||||
|
||||
static int io = 0x320;
|
||||
static int irq = 10;
|
||||
|
||||
struct net_device * __init seeq8005_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
|
||||
unsigned *port;
|
||||
int err = 0;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (unit >= 0) {
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
io = dev->base_addr;
|
||||
irq = dev->irq;
|
||||
}
|
||||
|
||||
if (io > 0x1ff) { /* Check a single specified location. */
|
||||
err = seeq8005_probe1(dev, io);
|
||||
} else if (io != 0) { /* Don't probe at all. */
|
||||
err = -ENXIO;
|
||||
} else {
|
||||
for (port = seeq8005_portlist; *port; port++) {
|
||||
if (seeq8005_probe1(dev, *port) == 0)
|
||||
break;
|
||||
}
|
||||
if (!*port)
|
||||
err = -ENODEV;
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
goto out1;
|
||||
return dev;
|
||||
out1:
|
||||
release_region(dev->base_addr, SEEQ8005_IO_EXTENT);
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static const struct net_device_ops seeq8005_netdev_ops = {
|
||||
.ndo_open = seeq8005_open,
|
||||
.ndo_stop = seeq8005_close,
|
||||
.ndo_start_xmit = seeq8005_send_packet,
|
||||
.ndo_tx_timeout = seeq8005_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 probe routine. Linux has a history of friendly device
|
||||
probes on the ISA bus. A good device probes avoids doing writes, and
|
||||
verifies that the correct device exists and functions. */
|
||||
|
||||
static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
static unsigned version_printed;
|
||||
int i,j;
|
||||
unsigned char SA_prom[32];
|
||||
int old_cfg1;
|
||||
int old_cfg2;
|
||||
int old_stat;
|
||||
int old_dmaar;
|
||||
int old_rear;
|
||||
int retval;
|
||||
|
||||
if (!request_region(ioaddr, SEEQ8005_IO_EXTENT, "seeq8005"))
|
||||
return -ENODEV;
|
||||
|
||||
if (net_debug>1)
|
||||
printk("seeq8005: probing at 0x%x\n",ioaddr);
|
||||
|
||||
old_stat = inw(SEEQ_STATUS); /* read status register */
|
||||
if (old_stat == 0xffff) {
|
||||
retval = -ENODEV;
|
||||
goto out; /* assume that 0xffff == no device */
|
||||
}
|
||||
if ( (old_stat & 0x1800) != 0x1800 ) { /* assume that unused bits are 1, as my manual says */
|
||||
if (net_debug>1) {
|
||||
printk("seeq8005: reserved stat bits != 0x1800\n");
|
||||
printk(" == 0x%04x\n",old_stat);
|
||||
}
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_rear = inw(SEEQ_REA);
|
||||
if (old_rear == 0xffff) {
|
||||
outw(0,SEEQ_REA);
|
||||
if (inw(SEEQ_REA) == 0xffff) { /* assume that 0xffff == no device */
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
} else if ((old_rear & 0xff00) != 0xff00) { /* assume that unused bits are 1 */
|
||||
if (net_debug>1) {
|
||||
printk("seeq8005: unused rear bits != 0xff00\n");
|
||||
printk(" == 0x%04x\n",old_rear);
|
||||
}
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_cfg2 = inw(SEEQ_CFG2); /* read CFG2 register */
|
||||
old_cfg1 = inw(SEEQ_CFG1);
|
||||
old_dmaar = inw(SEEQ_DMAAR);
|
||||
|
||||
if (net_debug>4) {
|
||||
printk("seeq8005: stat = 0x%04x\n",old_stat);
|
||||
printk("seeq8005: cfg1 = 0x%04x\n",old_cfg1);
|
||||
printk("seeq8005: cfg2 = 0x%04x\n",old_cfg2);
|
||||
printk("seeq8005: raer = 0x%04x\n",old_rear);
|
||||
printk("seeq8005: dmaar= 0x%04x\n",old_dmaar);
|
||||
}
|
||||
|
||||
outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); /* setup for reading PROM */
|
||||
outw( 0, SEEQ_DMAAR); /* set starting PROM address */
|
||||
outw( SEEQCFG1_BUFFER_PROM, SEEQ_CFG1); /* set buffer to look at PROM */
|
||||
|
||||
|
||||
j=0;
|
||||
for(i=0; i <32; i++) {
|
||||
j+= SA_prom[i] = inw(SEEQ_BUFFER) & 0xff;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* untested because I only have the one card */
|
||||
if ( (j&0xff) != 0 ) { /* checksum appears to be 8bit = 0 */
|
||||
if (net_debug>1) { /* check this before deciding that we have a card */
|
||||
printk("seeq8005: prom sum error\n");
|
||||
}
|
||||
outw( old_stat, SEEQ_STATUS);
|
||||
outw( old_dmaar, SEEQ_DMAAR);
|
||||
outw( old_cfg1, SEEQ_CFG1);
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
outw( SEEQCFG2_RESET, SEEQ_CFG2); /* reset the card */
|
||||
udelay(5);
|
||||
outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
|
||||
if (net_debug) {
|
||||
printk("seeq8005: prom sum = 0x%08x\n",j);
|
||||
for(j=0; j<32; j+=16) {
|
||||
printk("seeq8005: prom %02x: ",j);
|
||||
for(i=0;i<16;i++) {
|
||||
printk("%02x ",SA_prom[j|i]);
|
||||
}
|
||||
printk(" ");
|
||||
for(i=0;i<16;i++) {
|
||||
if ((SA_prom[j|i]>31)&&(SA_prom[j|i]<127)) {
|
||||
printk("%c", SA_prom[j|i]);
|
||||
} else {
|
||||
printk(" ");
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* testing the packet buffer memory doesn't work yet
|
||||
* but all other buffer accesses do
|
||||
* - fixing is not a priority
|
||||
*/
|
||||
if (net_debug>1) { /* test packet buffer memory */
|
||||
printk("seeq8005: testing packet buffer ... ");
|
||||
outw( SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
|
||||
outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
outw( 0 , SEEQ_DMAAR);
|
||||
for(i=0;i<32768;i++) {
|
||||
outw(0x5a5a, SEEQ_BUFFER);
|
||||
}
|
||||
j=jiffies+HZ;
|
||||
while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && time_before(jiffies, j) )
|
||||
mb();
|
||||
outw( 0 , SEEQ_DMAAR);
|
||||
while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, j+HZ))
|
||||
mb();
|
||||
if ( (inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
|
||||
outw( SEEQCMD_WINDOW_INT_ACK | (inw(SEEQ_STATUS)& SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
j=0;
|
||||
for(i=0;i<32768;i++) {
|
||||
if (inw(SEEQ_BUFFER) != 0x5a5a)
|
||||
j++;
|
||||
}
|
||||
if (j) {
|
||||
printk("%i\n",j);
|
||||
} else {
|
||||
printk("ok.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (net_debug && version_printed++ == 0)
|
||||
printk(version);
|
||||
|
||||
printk("%s: %s found at %#3x, ", dev->name, "seeq8005", ioaddr);
|
||||
|
||||
/* Fill in the 'dev' fields. */
|
||||
dev->base_addr = ioaddr;
|
||||
dev->irq = irq;
|
||||
|
||||
/* Retrieve and print the ethernet address. */
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = SA_prom[i+6];
|
||||
printk("%pM", dev->dev_addr);
|
||||
|
||||
if (dev->irq == 0xff)
|
||||
; /* Do nothing: a user-level program will set it. */
|
||||
else if (dev->irq < 2) { /* "Auto-IRQ" */
|
||||
unsigned long cookie = probe_irq_on();
|
||||
|
||||
outw( SEEQCMD_RX_INT_EN | SEEQCMD_SET_RX_ON | SEEQCMD_SET_RX_OFF, SEEQ_CMD );
|
||||
|
||||
dev->irq = probe_irq_off(cookie);
|
||||
|
||||
if (net_debug >= 2)
|
||||
printk(" autoirq is %d\n", 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 0
|
||||
{
|
||||
int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev);
|
||||
if (irqval) {
|
||||
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
|
||||
dev->irq, irqval);
|
||||
retval = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
dev->netdev_ops = &seeq8005_netdev_ops;
|
||||
dev->watchdog_timeo = HZ/20;
|
||||
dev->flags &= ~IFF_MULTICAST;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
release_region(ioaddr, SEEQ8005_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* 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 seeq8005_open(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
|
||||
{
|
||||
int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev);
|
||||
if (irqval) {
|
||||
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
|
||||
dev->irq, irqval);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the hardware here. Don't forget to set the station address. */
|
||||
seeq8005_init(dev, 1);
|
||||
|
||||
lp->open_time = jiffies;
|
||||
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void seeq8005_timeout(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
|
||||
tx_done(dev) ? "IRQ conflict" : "network cable problem");
|
||||
/* Try to restart the adaptor. */
|
||||
seeq8005_init(dev, 1);
|
||||
dev->trans_start = jiffies; /* prevent tx timeout */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static netdev_tx_t seeq8005_send_packet(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
short length = skb->len;
|
||||
unsigned char *buf;
|
||||
|
||||
if (length < ETH_ZLEN) {
|
||||
if (skb_padto(skb, ETH_ZLEN))
|
||||
return NETDEV_TX_OK;
|
||||
length = ETH_ZLEN;
|
||||
}
|
||||
buf = skb->data;
|
||||
|
||||
/* Block a timer-based transmit from overlapping */
|
||||
netif_stop_queue(dev);
|
||||
|
||||
hardware_send_packet(dev, buf, length);
|
||||
dev->stats.tx_bytes += length;
|
||||
dev_kfree_skb (skb);
|
||||
/* You might need to clean up and record Tx statistics here. */
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait_for_buffer
|
||||
*
|
||||
* This routine waits for the SEEQ chip to assert that the FIFO is ready
|
||||
* by checking for a window interrupt, and then clearing it. This has to
|
||||
* occur in the interrupt handler!
|
||||
*/
|
||||
inline void wait_for_buffer(struct net_device * dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
unsigned long tmp;
|
||||
int status;
|
||||
|
||||
tmp = jiffies + HZ;
|
||||
while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, tmp))
|
||||
cpu_relax();
|
||||
|
||||
if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
|
||||
outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
}
|
||||
|
||||
/* The typical workload of the driver:
|
||||
Handle the network interface interrupts. */
|
||||
static irqreturn_t seeq8005_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct net_local *lp;
|
||||
int ioaddr, status, boguscount = 0;
|
||||
int handled = 0;
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
status = inw(SEEQ_STATUS);
|
||||
do {
|
||||
if (net_debug >2) {
|
||||
printk("%s: int, status=0x%04x\n",dev->name,status);
|
||||
}
|
||||
|
||||
if (status & SEEQSTAT_WINDOW_INT) {
|
||||
handled = 1;
|
||||
outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
if (net_debug) {
|
||||
printk("%s: window int!\n",dev->name);
|
||||
}
|
||||
}
|
||||
if (status & SEEQSTAT_TX_INT) {
|
||||
handled = 1;
|
||||
outw( SEEQCMD_TX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
dev->stats.tx_packets++;
|
||||
netif_wake_queue(dev); /* Inform upper layers. */
|
||||
}
|
||||
if (status & SEEQSTAT_RX_INT) {
|
||||
handled = 1;
|
||||
/* Got a packet(s). */
|
||||
seeq8005_rx(dev);
|
||||
}
|
||||
status = inw(SEEQ_STATUS);
|
||||
} while ( (++boguscount < 10) && (status & SEEQSTAT_ANY_INT)) ;
|
||||
|
||||
if(net_debug>2) {
|
||||
printk("%s: eoi\n",dev->name);
|
||||
}
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
/* We have a good packet(s), get it/them out of the buffers. */
|
||||
static void seeq8005_rx(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int boguscount = 10;
|
||||
int pkt_hdr;
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
do {
|
||||
int next_packet;
|
||||
int pkt_len;
|
||||
int i;
|
||||
int status;
|
||||
|
||||
status = inw(SEEQ_STATUS);
|
||||
outw( lp->receive_ptr, SEEQ_DMAAR);
|
||||
outw(SEEQCMD_FIFO_READ | SEEQCMD_RX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
wait_for_buffer(dev);
|
||||
next_packet = ntohs(inw(SEEQ_BUFFER));
|
||||
pkt_hdr = inw(SEEQ_BUFFER);
|
||||
|
||||
if (net_debug>2) {
|
||||
printk("%s: 0x%04x recv next=0x%04x, hdr=0x%04x\n",dev->name,lp->receive_ptr,next_packet,pkt_hdr);
|
||||
}
|
||||
|
||||
if ((next_packet == 0) || ((pkt_hdr & SEEQPKTH_CHAIN)==0)) { /* Read all the frames? */
|
||||
return; /* Done for now */
|
||||
}
|
||||
|
||||
if ((pkt_hdr & SEEQPKTS_DONE)==0)
|
||||
break;
|
||||
|
||||
if (next_packet < lp->receive_ptr) {
|
||||
pkt_len = (next_packet + 0x10000 - ((DEFAULT_TEA+1)<<8)) - lp->receive_ptr - 4;
|
||||
} else {
|
||||
pkt_len = next_packet - lp->receive_ptr - 4;
|
||||
}
|
||||
|
||||
if (next_packet < ((DEFAULT_TEA+1)<<8)) { /* is the next_packet address sane? */
|
||||
printk("%s: recv packet ring corrupt, resetting board\n",dev->name);
|
||||
seeq8005_init(dev,1);
|
||||
return;
|
||||
}
|
||||
|
||||
lp->receive_ptr = next_packet;
|
||||
|
||||
if (net_debug>2) {
|
||||
printk("%s: recv len=0x%04x\n",dev->name,pkt_len);
|
||||
}
|
||||
|
||||
if (pkt_hdr & SEEQPKTS_ANY_ERROR) { /* There was an error. */
|
||||
dev->stats.rx_errors++;
|
||||
if (pkt_hdr & SEEQPKTS_SHORT) dev->stats.rx_frame_errors++;
|
||||
if (pkt_hdr & SEEQPKTS_DRIB) dev->stats.rx_frame_errors++;
|
||||
if (pkt_hdr & SEEQPKTS_OVERSIZE) dev->stats.rx_over_errors++;
|
||||
if (pkt_hdr & SEEQPKTS_CRC_ERR) dev->stats.rx_crc_errors++;
|
||||
/* skip over this packet */
|
||||
outw( SEEQCMD_FIFO_WRITE | SEEQCMD_DMA_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
outw( (lp->receive_ptr & 0xff00)>>8, SEEQ_REA);
|
||||
} else {
|
||||
/* Malloc up new buffer. */
|
||||
struct sk_buff *skb;
|
||||
unsigned char *buf;
|
||||
|
||||
skb = netdev_alloc_skb(dev, pkt_len);
|
||||
if (skb == NULL) {
|
||||
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
break;
|
||||
}
|
||||
skb_reserve(skb, 2); /* align data on 16 byte */
|
||||
buf = skb_put(skb,pkt_len);
|
||||
|
||||
insw(SEEQ_BUFFER, buf, (pkt_len + 1) >> 1);
|
||||
|
||||
if (net_debug>2) {
|
||||
char * p = buf;
|
||||
printk("%s: recv ",dev->name);
|
||||
for(i=0;i<14;i++) {
|
||||
printk("%02x ",*(p++)&0xff);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
skb->protocol=eth_type_trans(skb,dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
}
|
||||
} while ((--boguscount) && (pkt_hdr & SEEQPKTH_CHAIN));
|
||||
|
||||
/* If any worth-while packets have been received, netif_rx()
|
||||
has done a mark_bh(NET_BH) for us and will work on them
|
||||
when we get to the bottom-half routine. */
|
||||
}
|
||||
|
||||
/* The inverse routine to net_open(). */
|
||||
static int seeq8005_close(struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
|
||||
lp->open_time = 0;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/* Flush the Tx and disable Rx here. */
|
||||
outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
/* Update the statistics here. */
|
||||
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* I _could_ do up to 6 addresses here, but won't (yet?)
|
||||
*/
|
||||
|
||||
#if 0
|
||||
int ioaddr = dev->base_addr;
|
||||
/*
|
||||
* hmm, not even sure if my matching works _anyway_ - seem to be receiving
|
||||
* _everything_ . . .
|
||||
*/
|
||||
|
||||
if (num_addrs) { /* Enable promiscuous mode */
|
||||
outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_ALL, SEEQ_CFG1);
|
||||
dev->flags|=IFF_PROMISC;
|
||||
} else { /* Disable promiscuous mode, use normal mode */
|
||||
outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_BROAD, SEEQ_CFG1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void seeq8005_init(struct net_device *dev, int startp)
|
||||
{
|
||||
struct net_local *lp = netdev_priv(dev);
|
||||
int ioaddr = dev->base_addr;
|
||||
int i;
|
||||
|
||||
outw(SEEQCFG2_RESET, SEEQ_CFG2); /* reset device */
|
||||
udelay(5);
|
||||
|
||||
outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
outw( 0, SEEQ_DMAAR); /* load start address into both low and high byte */
|
||||
/* wait_for_buffer(dev); */ /* I think that you only need a wait for memory buffer */
|
||||
outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
|
||||
|
||||
for(i=0;i<6;i++) { /* set Station address */
|
||||
outb(dev->dev_addr[i], SEEQ_BUFFER);
|
||||
udelay(2);
|
||||
}
|
||||
|
||||
outw( SEEQCFG1_BUFFER_TEA, SEEQ_CFG1); /* set xmit end area pointer to 16K */
|
||||
outb( DEFAULT_TEA, SEEQ_BUFFER); /* this gives us 16K of send buffer and 48K of recv buffer */
|
||||
|
||||
lp->receive_ptr = (DEFAULT_TEA+1)<<8; /* so we can find our packet_header */
|
||||
outw( lp->receive_ptr, SEEQ_RPR); /* Receive Pointer Register is set to recv buffer memory */
|
||||
|
||||
outw( 0x00ff, SEEQ_REA); /* Receive Area End */
|
||||
|
||||
if (net_debug>4) {
|
||||
printk("%s: SA0 = ",dev->name);
|
||||
|
||||
outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
|
||||
outw( 0, SEEQ_DMAAR);
|
||||
outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
|
||||
|
||||
for(i=0;i<6;i++) {
|
||||
printk("%02x ",inb(SEEQ_BUFFER));
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
outw( SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD | SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
|
||||
outw( SEEQCFG2_AUTO_REA | SEEQCFG2_CTRLO, SEEQ_CFG2);
|
||||
outw( SEEQCMD_SET_RX_ON | SEEQCMD_TX_INT_EN | SEEQCMD_RX_INT_EN, SEEQ_CMD);
|
||||
|
||||
if (net_debug>4) {
|
||||
int old_cfg1;
|
||||
old_cfg1 = inw(SEEQ_CFG1);
|
||||
printk("%s: stat = 0x%04x\n",dev->name,inw(SEEQ_STATUS));
|
||||
printk("%s: cfg1 = 0x%04x\n",dev->name,old_cfg1);
|
||||
printk("%s: cfg2 = 0x%04x\n",dev->name,inw(SEEQ_CFG2));
|
||||
printk("%s: raer = 0x%04x\n",dev->name,inw(SEEQ_REA));
|
||||
printk("%s: dmaar= 0x%04x\n",dev->name,inw(SEEQ_DMAAR));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hardware_send_packet(struct net_device * dev, char *buf, int length)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
int status = inw(SEEQ_STATUS);
|
||||
int transmit_ptr = 0;
|
||||
unsigned long tmp;
|
||||
|
||||
if (net_debug>4) {
|
||||
printk("%s: send 0x%04x\n",dev->name,length);
|
||||
}
|
||||
|
||||
/* Set FIFO to writemode and set packet-buffer address */
|
||||
outw( SEEQCMD_FIFO_WRITE | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
outw( transmit_ptr, SEEQ_DMAAR);
|
||||
|
||||
/* output SEEQ Packet header barfage */
|
||||
outw( htons(length + 4), SEEQ_BUFFER);
|
||||
outw( SEEQPKTH_XMIT | SEEQPKTH_DATA_FOLLOWS | SEEQPKTH_XMIT_INT_EN, SEEQ_BUFFER );
|
||||
|
||||
/* blat the buffer */
|
||||
outsw( SEEQ_BUFFER, buf, (length +1) >> 1);
|
||||
/* paranoia !! */
|
||||
outw( 0, SEEQ_BUFFER);
|
||||
outw( 0, SEEQ_BUFFER);
|
||||
|
||||
/* set address of start of transmit chain */
|
||||
outw( transmit_ptr, SEEQ_TPR);
|
||||
|
||||
/* drain FIFO */
|
||||
tmp = jiffies;
|
||||
while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && time_before(jiffies, tmp + HZ))
|
||||
mb();
|
||||
|
||||
/* doit ! */
|
||||
outw( SEEQCMD_WINDOW_INT_ACK | SEEQCMD_SET_TX_ON | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
static struct net_device *dev_seeq;
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(io, int, 0);
|
||||
module_param(irq, int, 0);
|
||||
MODULE_PARM_DESC(io, "SEEQ 8005 I/O base address");
|
||||
MODULE_PARM_DESC(irq, "SEEQ 8005 IRQ number");
|
||||
|
||||
int __init init_module(void)
|
||||
{
|
||||
dev_seeq = seeq8005_probe(-1);
|
||||
return PTR_RET(dev_seeq);
|
||||
}
|
||||
|
||||
void __exit cleanup_module(void)
|
||||
{
|
||||
unregister_netdev(dev_seeq);
|
||||
release_region(dev_seeq->base_addr, SEEQ8005_IO_EXTENT);
|
||||
free_netdev(dev_seeq);
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* defines, etc for the seeq8005
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is distributed under GPL.
|
||||
*
|
||||
* This style and layout of this file is also copied
|
||||
* from many of the other linux network device drivers.
|
||||
*/
|
||||
|
||||
/* The number of low I/O ports used by the ethercard. */
|
||||
#define SEEQ8005_IO_EXTENT 16
|
||||
|
||||
#define SEEQ_B (ioaddr)
|
||||
|
||||
#define SEEQ_CMD (SEEQ_B) /* Write only */
|
||||
#define SEEQ_STATUS (SEEQ_B) /* Read only */
|
||||
#define SEEQ_CFG1 (SEEQ_B + 2)
|
||||
#define SEEQ_CFG2 (SEEQ_B + 4)
|
||||
#define SEEQ_REA (SEEQ_B + 6) /* Receive End Area Register */
|
||||
#define SEEQ_RPR (SEEQ_B + 10) /* Receive Pointer Register */
|
||||
#define SEEQ_TPR (SEEQ_B + 12) /* Transmit Pointer Register */
|
||||
#define SEEQ_DMAAR (SEEQ_B + 14) /* DMA Address Register */
|
||||
#define SEEQ_BUFFER (SEEQ_B + 8) /* Buffer Window Register */
|
||||
|
||||
#define DEFAULT_TEA (0x3f)
|
||||
|
||||
#define SEEQCMD_DMA_INT_EN (0x0001) /* DMA Interrupt Enable */
|
||||
#define SEEQCMD_RX_INT_EN (0x0002) /* Receive Interrupt Enable */
|
||||
#define SEEQCMD_TX_INT_EN (0x0004) /* Transmit Interrupt Enable */
|
||||
#define SEEQCMD_WINDOW_INT_EN (0x0008) /* What the hell is this for?? */
|
||||
#define SEEQCMD_INT_MASK (0x000f)
|
||||
|
||||
#define SEEQCMD_DMA_INT_ACK (0x0010) /* DMA ack */
|
||||
#define SEEQCMD_RX_INT_ACK (0x0020)
|
||||
#define SEEQCMD_TX_INT_ACK (0x0040)
|
||||
#define SEEQCMD_WINDOW_INT_ACK (0x0080)
|
||||
#define SEEQCMD_ACK_ALL (0x00f0)
|
||||
|
||||
#define SEEQCMD_SET_DMA_ON (0x0100) /* Enables DMA Request logic */
|
||||
#define SEEQCMD_SET_RX_ON (0x0200) /* Enables Packet RX */
|
||||
#define SEEQCMD_SET_TX_ON (0x0400) /* Starts TX run */
|
||||
#define SEEQCMD_SET_DMA_OFF (0x0800)
|
||||
#define SEEQCMD_SET_RX_OFF (0x1000)
|
||||
#define SEEQCMD_SET_TX_OFF (0x2000)
|
||||
#define SEEQCMD_SET_ALL_OFF (0x3800) /* set all logic off */
|
||||
|
||||
#define SEEQCMD_FIFO_READ (0x4000) /* Set FIFO to read mode (read from Buffer) */
|
||||
#define SEEQCMD_FIFO_WRITE (0x8000) /* Set FIFO to write mode */
|
||||
|
||||
#define SEEQSTAT_DMA_INT_EN (0x0001) /* Status of interrupt enable */
|
||||
#define SEEQSTAT_RX_INT_EN (0x0002)
|
||||
#define SEEQSTAT_TX_INT_EN (0x0004)
|
||||
#define SEEQSTAT_WINDOW_INT_EN (0x0008)
|
||||
|
||||
#define SEEQSTAT_DMA_INT (0x0010) /* Interrupt flagged */
|
||||
#define SEEQSTAT_RX_INT (0x0020)
|
||||
#define SEEQSTAT_TX_INT (0x0040)
|
||||
#define SEEQSTAT_WINDOW_INT (0x0080)
|
||||
#define SEEQSTAT_ANY_INT (0x00f0)
|
||||
|
||||
#define SEEQSTAT_DMA_ON (0x0100) /* DMA logic on */
|
||||
#define SEEQSTAT_RX_ON (0x0200) /* Packet RX on */
|
||||
#define SEEQSTAT_TX_ON (0x0400) /* TX running */
|
||||
|
||||
#define SEEQSTAT_FIFO_FULL (0x2000)
|
||||
#define SEEQSTAT_FIFO_EMPTY (0x4000)
|
||||
#define SEEQSTAT_FIFO_DIR (0x8000) /* 1=read, 0=write */
|
||||
|
||||
#define SEEQCFG1_BUFFER_MASK (0x000f) /* define what maps into the BUFFER register */
|
||||
#define SEEQCFG1_BUFFER_MAC0 (0x0000) /* MAC station addresses 0-5 */
|
||||
#define SEEQCFG1_BUFFER_MAC1 (0x0001)
|
||||
#define SEEQCFG1_BUFFER_MAC2 (0x0002)
|
||||
#define SEEQCFG1_BUFFER_MAC3 (0x0003)
|
||||
#define SEEQCFG1_BUFFER_MAC4 (0x0004)
|
||||
#define SEEQCFG1_BUFFER_MAC5 (0x0005)
|
||||
#define SEEQCFG1_BUFFER_PROM (0x0006) /* The Address/CFG PROM */
|
||||
#define SEEQCFG1_BUFFER_TEA (0x0007) /* Transmit end area */
|
||||
#define SEEQCFG1_BUFFER_BUFFER (0x0008) /* Packet buffer memory */
|
||||
#define SEEQCFG1_BUFFER_INT_VEC (0x0009) /* Interrupt Vector */
|
||||
|
||||
#define SEEQCFG1_DMA_INTVL_MASK (0x0030)
|
||||
#define SEEQCFG1_DMA_CONT (0x0000)
|
||||
#define SEEQCFG1_DMA_800ns (0x0010)
|
||||
#define SEEQCFG1_DMA_1600ns (0x0020)
|
||||
#define SEEQCFG1_DMA_3200ns (0x0030)
|
||||
|
||||
#define SEEQCFG1_DMA_LEN_MASK (0x00c0)
|
||||
#define SEEQCFG1_DMA_LEN1 (0x0000)
|
||||
#define SEEQCFG1_DMA_LEN2 (0x0040)
|
||||
#define SEEQCFG1_DMA_LEN4 (0x0080)
|
||||
#define SEEQCFG1_DMA_LEN8 (0x00c0)
|
||||
|
||||
#define SEEQCFG1_MAC_MASK (0x3f00) /* Dis/enable bits for MAC addresses */
|
||||
#define SEEQCFG1_MAC0_EN (0x0100)
|
||||
#define SEEQCFG1_MAC1_EN (0x0200)
|
||||
#define SEEQCFG1_MAC2_EN (0x0400)
|
||||
#define SEEQCFG1_MAC3_EN (0x0800)
|
||||
#define SEEQCFG1_MAC4_EN (0x1000)
|
||||
#define SEEQCFG1_MAC5_EN (0x2000)
|
||||
|
||||
#define SEEQCFG1_MATCH_MASK (0xc000) /* Packet matching logic cfg bits */
|
||||
#define SEEQCFG1_MATCH_SPECIFIC (0x0000) /* only matching MAC addresses */
|
||||
#define SEEQCFG1_MATCH_BROAD (0x4000) /* matching and broadcast addresses */
|
||||
#define SEEQCFG1_MATCH_MULTI (0x8000) /* matching, broadcast and multicast */
|
||||
#define SEEQCFG1_MATCH_ALL (0xc000) /* Promiscuous mode */
|
||||
|
||||
#define SEEQCFG1_DEFAULT (SEEQCFG1_BUFFER_BUFFER | SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD)
|
||||
|
||||
#define SEEQCFG2_BYTE_SWAP (0x0001) /* 0=Intel byte-order */
|
||||
#define SEEQCFG2_AUTO_REA (0x0002) /* if set, Receive End Area will be updated when reading from Buffer */
|
||||
|
||||
#define SEEQCFG2_CRC_ERR_EN (0x0008) /* enables receiving of packets with CRC errors */
|
||||
#define SEEQCFG2_DRIBBLE_EN (0x0010) /* enables receiving of non-aligned packets */
|
||||
#define SEEQCFG2_SHORT_EN (0x0020) /* enables receiving of short packets */
|
||||
|
||||
#define SEEQCFG2_SLOTSEL (0x0040) /* 0= standard IEEE802.3, 1= smaller,faster, non-standard */
|
||||
#define SEEQCFG2_NO_PREAM (0x0080) /* 1= user supplies Xmit preamble bytes */
|
||||
#define SEEQCFG2_ADDR_LEN (0x0100) /* 1= 2byte addresses */
|
||||
#define SEEQCFG2_REC_CRC (0x0200) /* 0= received packets will have CRC stripped from them */
|
||||
#define SEEQCFG2_XMIT_NO_CRC (0x0400) /* don't xmit CRC with each packet (user supplies it) */
|
||||
#define SEEQCFG2_LOOPBACK (0x0800)
|
||||
#define SEEQCFG2_CTRLO (0x1000)
|
||||
#define SEEQCFG2_RESET (0x8000) /* software Hard-reset bit */
|
||||
|
||||
struct seeq_pkt_hdr {
|
||||
unsigned short next; /* address of next packet header */
|
||||
unsigned char babble_int:1, /* enable int on >1514 byte packet */
|
||||
coll_int:1, /* enable int on collision */
|
||||
coll_16_int:1, /* enable int on >15 collision */
|
||||
xmit_int:1, /* enable int on success (or xmit with <15 collision) */
|
||||
unused:1,
|
||||
data_follows:1, /* if not set, process this as a header and pointer only */
|
||||
chain_cont:1, /* if set, more headers in chain only cmd bit valid in recv header */
|
||||
xmit_recv:1; /* if set, a xmit packet, else a receive packet.*/
|
||||
unsigned char status;
|
||||
};
|
||||
|
||||
#define SEEQPKTH_BAB_INT_EN (0x01) /* xmit only */
|
||||
#define SEEQPKTH_COL_INT_EN (0x02) /* xmit only */
|
||||
#define SEEQPKTH_COL16_INT_EN (0x04) /* xmit only */
|
||||
#define SEEQPKTH_XMIT_INT_EN (0x08) /* xmit only */
|
||||
#define SEEQPKTH_DATA_FOLLOWS (0x20) /* supposedly in xmit only */
|
||||
#define SEEQPKTH_CHAIN (0x40) /* more headers follow */
|
||||
#define SEEQPKTH_XMIT (0x80)
|
||||
|
||||
#define SEEQPKTS_BABBLE (0x0100) /* xmit only */
|
||||
#define SEEQPKTS_OVERSIZE (0x0100) /* recv only */
|
||||
#define SEEQPKTS_COLLISION (0x0200) /* xmit only */
|
||||
#define SEEQPKTS_CRC_ERR (0x0200) /* recv only */
|
||||
#define SEEQPKTS_COLL16 (0x0400) /* xmit only */
|
||||
#define SEEQPKTS_DRIB (0x0400) /* recv only */
|
||||
#define SEEQPKTS_SHORT (0x0800) /* recv only */
|
||||
#define SEEQPKTS_DONE (0x8000)
|
||||
#define SEEQPKTS_ANY_ERROR (0x0f00)
|
Loading…
Reference in New Issue
Block a user