* Re: [PATCH net-next v7 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-04-22 16:41 UTC (permalink / raw)
To: john.fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
On Sat, Mar 28, 2026, at 11:17 AM, Chuck Lever wrote:
> I'd like to encourage in-kernel kTLS consumers (i.e., NFS and
> NVMe/TCP) to coalesce on the use of read_sock. When I suggested
> this to Hannes, he reported a number of nagging performance
> scalability issues with read_sock. This series is an attempt to
> run these issues down and get them fixed before we convert the
> above sock_recvmsg consumers over to read_sock.
>
> Batch async decryption and its submit/deliver scaffolding were
> dropped from this series because async_capable is always false
> for TLS 1.3, which NFS and NVMe/TCP both require. Async crypto
> support for TLS 1.3 is a prerequisite for revisiting that work.
>
> ---
> Changes since v6:
> - Rebased on net-next, v5's 1/6 was merged upstream
>
> Changes since v5:
> - Patch 6: Set released = true when sk_flush_backlog() returns
> true, so tls_strp_msg_load() knows the socket lock was
> released (Sabrina)
> - Patch 6: Drop Fixes tag; submit bug fix separately via net
> if warranted (Sabrina)
> - Patch 6: Note redundant flush on cold path in commit message
> (Sabrina)
>
> Changes since v4:
> - Drop batch async decryption and submit/deliver restructure:
> async_capable is always false for TLS 1.3, so the new code
> was unreachable for NFS and NVMe/TCP
> - Purge async_hold directly in tls_decrypt_async_wait() and drop
> the tls_decrypt_async_drain() wrapper
> - Merge tls_strp_check_rcv_quiet() into tls_strp_check_rcv() with
> a bool wake parameter; fix lost wakeup on the recvmsg exit path
>
> Changes since v3:
> - Clarify why tls_decrypt_async_drain() is separate from _wait()
> - Fold tls_err_abort() into tls_rx_one_record(), drop tls_rx_decrypt_record()
> - Move backlog flush into tls_rx_rec_wait() so all RX paths benefit
>
> Changes since v2:
> - Fix short read self tests
>
> Changes since v1:
> - Add C11 reference
> - Extend data_ready reduction to recvmsg and splice
> - Restructure read_sock and recvmsg using shared helpers
>
> ---
> Chuck Lever (5):
> tls: Abort the connection on decrypt failure
> tls: Fix dangling skb pointer in tls_sw_read_sock()
> tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
> tls: Suppress spurious saved_data_ready on all receive paths
> tls: Flush backlog before waiting for a new record
>
> net/tls/tls.h | 4 ++--
> net/tls/tls_main.c | 2 +-
> net/tls/tls_strp.c | 42 +++++++++++++++++++++++++++++++-----------
> net/tls/tls_sw.c | 50 ++++++++++++++++++++++++++++++--------------------
> 4 files changed, 64 insertions(+), 34 deletions(-)
> ---
> base-commit: ced629dc8e5c51ff2b5d847adeeb1035cd655d58
> change-id: 20260317-tls-read-sock-a0022c9df265
I see that this series is currently not in v7.1. What is left to do?
--
Chuck Lever
^ permalink raw reply
* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Paul Fertser @ 2026-04-22 16:44 UTC (permalink / raw)
To: Michael Bommarito
Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <20260422160342.1975093-1-michael.bommarito@gmail.com>
Hello Michael,
On Wed, Apr 22, 2026 at 12:03:36PM -0400, Michael Bommarito wrote:
> NC-SI treats the management controller as privileged, but the Linux
...
> The threat model here is a compromised BMC or management-channel MITM
> on the NC-SI link.
The subject of the cover letter and the quoted fragment suggest that
you have a wrong impression of where NC-SI links exist and what they
carry, let me try to clarify.
On motherboards with BMC (the management controller) there often is a
way for the BMC (dedicated SoC these days) to talk to the
host-controlled NIC via NC-SI which is basically RMII (normally used
to talk to Ethernet PHY but here it's used to talk to a whole big NIC)
on hardware level plus special kind of frames sent in-band for
(partial) control and monitoring of the NIC. And regular frames are
transmitted over the same set of signals, there's no dedicated channel
for any kind of management inside NC-SI.
The code your patches modify always runs only on the BMC itself, the
packets parsed are generated by a NIC directly.
So if anything, the threat model here is compromised NIC
firmware. MITMing sounds unlikely as that would require tricky
hardware modifications and if you can do that it's easier to put a
modified NIC instead.
The idea to not trust anything coming from a NIC too much is good in
general but please take the correct context into account when
reasoning about the patches.
^ permalink raw reply
* Re: [PATCH iwl-net v1] igc: set tx buffer type for SMD frames
From: Simon Horman @ 2026-04-22 16:46 UTC (permalink / raw)
To: Kohei Enju
Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Choong Yong Liang, Vladimir Oltean,
Vinicius Costa Gomes, Chwee-Lin Choong
In-Reply-To: <20260417193223.291093-1-kohei@enjuk.jp>
On Sat, Apr 18, 2026 at 04:31:47AM +0900, Kohei Enju wrote:
> Sashiko pointed out that igc_fpe_init_smd_frame() initializes
> igc_tx_buffer fields for an SMD skb, but does not set the buffer type:
> https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp
>
> Since igc_tx_buffer entries are reused, a stale XDP or XSK type can
> remain and make TX completion use the wrong cleanup path.
>
> Set the buffer type to IGC_TX_BUFFER_TYPE_SKB.
>
> Fixes: 5422570c0010 ("igc: add support for frame preemption verification")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Maciej Fijalkowski @ 2026-04-22 16:58 UTC (permalink / raw)
To: Jason Xing
Cc: Alexander Lobakin, davem, edumazet, kuba, pabeni, bjorn,
magnus.karlsson, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>
On Thu, Apr 23, 2026 at 12:37:07AM +0800, Jason Xing wrote:
> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
> >
> > From: Jason Xing <kerneljasonxing@gmail.com>
> > Date: Wed, 22 Apr 2026 11:36:50 +0800
> >
> > > From: Jason Xing <kernelxing@tencent.com>
> > >
> > > In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> > > descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> > > uintptr_t cast:
> > >
> > > skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> > >
> > > On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> > > the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> > > mode the chunk offset is encoded in bits 48-63 of the descriptor
> > > address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> > > lost entirely. The completion queue then returns a truncated address to
> > > userspace, making buffer recycling impossible.
> >
> > What if we relax the restriction a bit? For example, refuse to configure
>
> As to the bug itself, yes, It only affects the unaligned mode.
>
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
>
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;
> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
>
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)
Abstracting from discussion, that is not a patch we would want against
-net. We should not forbid 32bit on stable kernels, maybe there is someone
in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs
patch's content looks weird ;)
>
> Thanks,
> Jason
>
> > an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
> > under CONFIG_32_BIT like it was done in Page Pool:
> >
> > skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> >
> > #ifdef CONFIG_32BIT
> > if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
> > // WARN_ONCE or whatever + error path
> > #endif
> >
> > I never used XSk on a 32-bit arch, but back when I was working on 32-bit
> > MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
> > Sure, just for fun, now that we have cheap AArch64 and other 64-bit
> > embedded chips, 32-bit embedded networking SoCs are almost dead, but
> > OTOH, as you can see, other subsystems like PP still try to support 32 bit.
> > Especially given that this issue applies to only to the skb XSk path,
> > not native in-driver implementations.
> >
> > >
> > > Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> > > strictly stop supporting it at compile time.
> > >
> > > Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> > > Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> > > Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > ---
> > > net/xdp/Kconfig | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> > > index 71af2febe72a..819aa5795f50 100644
> > > --- a/net/xdp/Kconfig
> > > +++ b/net/xdp/Kconfig
> > > @@ -1,7 +1,7 @@
> > > # SPDX-License-Identifier: GPL-2.0-only
> > > config XDP_SOCKETS
> > > bool "XDP sockets"
> > > - depends on BPF_SYSCALL
> > > + depends on BPF_SYSCALL && 64BIT
> > > default n
> > > help
> > > XDP sockets allows a channel between XDP programs and
> >
> > Thanks,
> > Olek
^ permalink raw reply
* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Alexander Lobakin @ 2026-04-22 17:00 UTC (permalink / raw)
To: Jason Xing
Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>
From: Jason Xing <kerneljasonxing@gmail.com>
Date: Thu, 23 Apr 2026 00:37:07 +0800
> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
>>
>> From: Jason Xing <kerneljasonxing@gmail.com>
>> Date: Wed, 22 Apr 2026 11:36:50 +0800
>>
>>> From: Jason Xing <kernelxing@tencent.com>
>>>
>>> In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
>>> descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
>>> uintptr_t cast:
>>>
>>> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>>
>>> On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
>>> the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
>>> mode the chunk offset is encoded in bits 48-63 of the descriptor
>>> address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
>>> lost entirely. The completion queue then returns a truncated address to
>>> userspace, making buffer recycling impossible.
>>
>> What if we relax the restriction a bit? For example, refuse to configure
>
> As to the bug itself, yes, It only affects the unaligned mode.
>
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
>
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;
I'm fine with this one, as well as with the check I proposed. This one
would happen during the configuration so it won't affect the hotpath,
my check is just inspired by what Page Pool does when
`sizeof(dma_addr_t) > sizeof(unsigned long)` (rare case but perfectly
valid on some architectures).
> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
>
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)
Up to you and other reviewers, but I really do think cutting off 32-bit
arches entirely due to a bug which can happen only under certain
conditions (skb XSk mode + unaligned mode) is too aggressive.
>
> Thanks,
> Jason
>
>> an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
>> under CONFIG_32_BIT like it was done in Page Pool:
>>
>> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>
>> #ifdef CONFIG_32BIT
>> if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
>> // WARN_ONCE or whatever + error path
>> #endif
>>
>> I never used XSk on a 32-bit arch, but back when I was working on 32-bit
>> MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
>> Sure, just for fun, now that we have cheap AArch64 and other 64-bit
>> embedded chips, 32-bit embedded networking SoCs are almost dead, but
>> OTOH, as you can see, other subsystems like PP still try to support 32 bit.
>> Especially given that this issue applies to only to the skb XSk path,
>> not native in-driver implementations.
>>
>>>
>>> Since we hear no one is using AF_XDP on 32-bit arch, we decided to
>>> strictly stop supporting it at compile time.
Thanks,
Olek
^ permalink raw reply
* Re: [PATCH v2] mptcp: do not drop partial packets
From: Matthieu Baerts @ 2026-04-22 17:03 UTC (permalink / raw)
To: Shardul Bankar, pabeni, martineau
Cc: geliang, davem, edumazet, kuba, horms, netdev, mptcp,
linux-kernel, janak, kalpan.jani, Shardul Bankar
In-Reply-To: <20260422143931.43281-1-shardul.b@mpiricsoftware.com>
Hi Shardul,
On 22/04/2026 16:39, Shardul Bankar wrote:
> When a packet arrives with map_seq < ack_seq < end_seq, the beginning
> of the packet has already been acknowledged but the end contains new
> data. Currently the entire packet is dropped as "old data," forcing
> the sender to retransmit.
>
> Instead, skip the already-acked bytes by adjusting the skb offset and
> enqueue only the new portion. Update bytes_received and ack_seq to
> reflect the new data consumed.
Thank you for the v2.
I didn't review it (yet), but just to let you know that there are some
rules on Netdev [1] (that usually also applied on MPTCP side), and an
important one is:
- don't repost your patches within one 24h period
Each version generates a lot of emails that are sent and need to be
triaged. With the high volume, it is then harder for us to follow, plus
a lot of shared resources are used, etc.
One last thing, because this patch is not an urgent fix, do you mind
sending new versions only the to MPTCP ML: to a restricted number of
people for the first versions, there is enough traffic on Netdev.
[1] https://docs.kernel.org/process/maintainer-netdev.html
> A previous attempt at this fix (commit 1d2ce718811a ("mptcp: do not
> drop partial packets"), reverted in commit bf39160c4218 ("Revert
Note: these two commits should not be mentioned here, they have only
been applied to the MPTCP tree, but not upstreamed. Instead, please use
lore links, e.g.
https://lore.kernel.org/c9b426a4e163aa3c4fe8b80c79f1a610f47ae7d8.1763075056.git.pabeni@redhat.com
> "mptcp: do not drop partial packets"")) also added a zero-window
> check and changed rcv_wnd_sent initialization, which caused test
> regressions. This version addresses only the partial packet handling
> without modifying receive window accounting.
>
> Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.")
> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600
> Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
Checkpatch is complaining that the author of the patch is not the same
as the one who sent the patch; You probably need to run this command to
fix it:
git commit --amend --reset-author
(when the patch is sent, `git format-patch` will add a second From: tag)
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply
* Re: [PATCH net v3 1/2] bnge: fix initial HWRM sequence
From: Simon Horman @ 2026-04-22 17:06 UTC (permalink / raw)
To: vikas.gupta
Cc: 'Simon Horman', davem, edumazet, kuba, pabeni,
andrew+netdev, netdev, linux-kernel, vsrama-krishna.nemani,
bhargava.marreddy, rajashekar.hudumula, ajit.khaparde,
dharmender.garg, rahul-rg.gupta
In-Reply-To: <20260418023438.1597876-2-vikas.gupta@broadcom.com>
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
bnge: fix initial HWRM sequence
Firmware may not advertize correct resources if backing store is not
enabled before resource information is queried.
Fix the initial sequence of HWRMs so that driver gets capabilities
and resource information correctly.
> diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> index 1c14c5fe8d613..68b74eb2c3a29 100644
> --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
[ ... ]
> @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
>
> bnge_hwrm_fw_set_time(bd);
>
> - rc = bnge_hwrm_func_drv_rgtr(bd);
> + /* Get the resources and configuration from firmware */
> + rc = bnge_func_qcaps(bd);
> if (rc) {
> - dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> + dev_err(bd->dev, "Failed querying caps rc: %d\n", rc);
> return rc;
> }
>
> rc = bnge_alloc_ctx_mem(bd);
> if (rc) {
> dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc);
> - goto err_func_unrgtr;
> + goto err_free_ctx_mem;
> }
>
> - /* Get the resources and configuration from firmware */
> - rc = bnge_func_qcaps(bd);
> + rc = bnge_hwrm_func_drv_rgtr(bd);
> if (rc) {
> - dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc);
> - rc = -ENODEV;
> + dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> + goto err_free_ctx_mem;
Does this regression leave the hardware with active DMA pointers to freed
memory?
If bnge_alloc_ctx_mem() allocates host DMA memory and passes the addresses
to the firmware via bnge_hwrm_func_backing_store(), and then
bnge_hwrm_func_drv_rgtr() fails, the code jumps directly to err_free_ctx_mem.
> + }
> +
> + rc = bnge_func_qrcaps_qcfg(bd);
> + if (rc) {
> + dev_err(bd->dev, "Failed querying resources rc: %d\n", rc);
> goto err_func_unrgtr;
> }
>
> @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
> return 0;
>
> err_func_unrgtr:
> - bnge_fw_unregister_dev(bd);
> + bnge_hwrm_func_drv_unrgtr(bd);
> +err_free_ctx_mem:
> + bnge_free_ctx_mem(bd);
Since the err_func_unrgtr label is bypassed, is the firmware ever told to
drop the backing store configuration before the host frees the memory in
bnge_free_ctx_mem()?
In the pre-patch sequence, the driver was registered first, so any failure
subsequently triggered a driver unregister, which implicitly told the
firmware to drop the backing store configuration. With the new order, does
the firmware maintain active DMA pointers to the freed host memory?
^ permalink raw reply
* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Michael Bommarito @ 2026-04-22 17:06 UTC (permalink / raw)
To: Paul Fertser
Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <aej66UTWzQ74dven@home.paul.comp>
On Wed, Apr 22, 2026 at 12:44 PM Paul Fertser <fercerpav@gmail.com> wrote:
> The code your patches modify always runs only on the BMC itself, the
> packets parsed are generated by a NIC directly.
>
> So if anything, the threat model here is compromised NIC
> firmware. MITMing sounds unlikely as that would require tricky
> hardware modifications and if you can do that it's easier to put a
> modified NIC instead.
Thanks for the correction, I definitely got that backwards!
Should I submit a v2 with the corrected notes or wait for any other
feedback / review on patch substance (assuming you think it's actually
worth merging)?
Thanks,
Mike Bommarito
^ permalink raw reply
* Re: [PATCH] net: dsa: realtek: rtl8365mb: add support for RTL8367SB
From: Breno Leitao @ 2026-04-22 17:12 UTC (permalink / raw)
To: Mieczyslaw Nalewaj; +Cc: netdev@vger.kernel.org
In-Reply-To: <f5ac420f-0087-4af0-bb43-b9a5b6228fbd@yahoo.com>
On Wed, Apr 22, 2026 at 05:58:30PM +0200, Mieczyslaw Nalewaj wrote:
> Add chip info entry for the Realtek RTL8367SB switch. This device has
> chip ID 0x6367 and version 0x0010. It exposes two external interfaces:
> port 6 supports SGMII and HSGMII, while port 7 supports MII, TMII,
> RMII and RGMII. Use the existing 8365MB-VC jam table for initialization.
This should target net-next, which is currently closed. Additionally, it
appears the maintainers were not properly copied on this patch?!
https://lore.kernel.org/all/20260412142250.131bf997@kernel.org/
pw-bot: cr
^ permalink raw reply
* Re: [net-next v38] mctp pcc: Implement MCTP over PCC Transport
From: Adam Young @ 2026-04-22 17:17 UTC (permalink / raw)
To: Jakub Kicinski, admiyo
Cc: jk, matt, andrew+netdev, davem, edumazet, pabeni, netdev,
linux-kernel, sudeep.holla, Jonathan.Cameron, lihuisong
In-Reply-To: <20260410032441.1844450-1-kuba@kernel.org>
On 4/9/26 23:24, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> mctp pcc: Implement MCTP over PCC Transport
>
> This commit implements a network driver for the Management Component
> Transport Protocol (MCTP) over the Platform Communication Channel (PCC).
>
> Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
> Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/14_Platform_Communications_Channel/Platform_Comm_Channel.html
>
>> diff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/drivers/net/mctp/mctp-pcc.c
> [ ... ]
>
>> +static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> + // The message must at least have the PCC command indicating it is an MCTP
>> + // message followed by the MCTP header, or we have a malformed message.
>> + if (pcc_header.length < sizeof(pcc_header.command) + sizeof(struct mctp_hdr)) {
>> + dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> + return;
>> + }
>> +
>> + size = pcc_header.length - sizeof(pcc_header.command) + sizeof(pcc_header);
> Can this size calculation wrap around? Because pcc_header.length is read from
> shared memory, a maliciously large value (e.g. 0xFFFFFFF8) could wrap around
> the unsigned 32-bit addition, resulting in a small positive size.
>
> This small positive size would then bypass the check below, and eventually
> fail in skb_pull(), leaving the malformed skb to be passed to the network
> stack.
This seems legit. The logic to check the size should ensure that the
value pulled out of the shared buffer is the size of the buffer, minus
the header.
>
>> + // If the reported size is larger than the shared memory, something is wrong
>> + // and the best we can do is treat it as corrupted data.
>> + if (size > inbox->chan->shmem_size) {
>> + dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> + return;
>> + }
> [ ... ]
>
>> +static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)
>> +{
> [ ... ]
>
>> + pcc_header = skb_push(skb, sizeof(*pcc_header));
>> + pcc_header->signature = PCC_SIGNATURE | mpnd->outbox.index;
>> + pcc_header->flags = PCC_CMD_COMPLETION_NOTIFY;
>> + memcpy(&pcc_header->command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);
>> + pcc_header->length = len + MCTP_SIGNATURE_LENGTH;
> Are these assignments writing native endian values into shared memory?
>
> The ACPI Platform Communications Channel specification defines the shared
> memory region as strictly little-endian. Writing without cpu_to_le32() will
> cause big endian architectures to write byte-swapped metadata, resulting in
> dropped packets.
This looks legit, and it reflects some of the early version of my patch
where I was doing similar transforms.
The target for this code is only ARM little endian systems, but I think
it is more correct to handle the wider cases in case that decision changes.
>
>> +
>> + rc = mbox_send_message(mpnd->outbox.chan->mchan, skb);
>> + if (rc < 0) {
> [ ... ]
>
>> +static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> + if (skb->len > outbox->chan->shmem_size) {
>> + dev_dstats_tx_dropped(mctp_pcc_ndev->ndev);
>> + return;
>> + }
> If the skb is oversized, this skips the copy and returns, but does it abort
> the transmission?
>
> Since mctp_pcc_tx() already submitted the message and tx_prepare() returns
> void, the hardware doorbell will still be rung. The remote endpoint would
> process the unmodified shared memory, replaying the previous packet's data.
This looks legit. Once we are in tx_prepare the message is going to be
sent. All checks like this need to be done in tx
>
>> + memcpy_toio(outbox->chan->shmem, skb->data, skb->len);
> Is it possible to read out of bounds here if the skb is fragmented?
>
> The skb->data pointer only points to the linear portion of the packet, while
> skb->len represents the total packet length including page fragments.
> skb_cow_head() does not linearize the packet, so a call to skb_linearize()
> might be needed before copying.
Legit.
While we don't have fragmentation enabled right now, it could very well
be enabled in the future, and this should be addressed.
>
>> +}
>> +
>> +static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int r)
>> +{
>> + struct mctp_pcc_ndev *mctp_pcc_ndev;
>> + struct sk_buff *skb = mssg;
>> +
>> + mctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);
>> + dev_dstats_tx_add(mctp_pcc_ndev->ndev, skb->len);
> Could this lead to sequence counter corruption and deadlock?
>
> mctp_pcc_tx() runs in process or softirq context and calls
> dev_dstats_tx_dropped(). mctp_pcc_tx_done() runs in the controller's hardirq
> context and calls dev_dstats_tx_add().
>
> The generic dev_dstats_* macros use u64_stats_update_begin(), which does not
> disable interrupts. If a hardware interrupt fires while the softirq path is
> inside u64_stats_update_begin(), the hardirq path will re-enter the lock.
I think this is legit. mctp_pcc_tx_done is triggered by the interupt
handler (it is the registered call back). While I do prefer to register
the packet as being sent at this point, I think that latest it can be
done is after the packet is written to the shared region in
mctp_pcc_tx_prepare.
>
>> + dev_consume_skb_any(skb);
>> + netif_wake_queue(mctp_pcc_ndev->ndev);
>> +}
> [ ... ]
>
>> +static int mctp_pcc_ndo_stop(struct net_device *ndev)
>> +{
>> + struct mctp_pcc_ndev *mctp_pcc_ndev;
>> + unsigned int count, idx;
>> + struct mbox_chan *chan;
>> + struct sk_buff *skb;
>> +
>> + mctp_pcc_ndev = netdev_priv(ndev);
>> + chan = mctp_pcc_ndev->outbox.chan->mchan;
>> +
>> + scoped_guard(spinlock_irqsave, &chan->lock) {
>> + chan->active_req = NULL;
> Does this bypass the mailbox API and cause a memory leak?
>
> By explicitly setting active_req to NULL without freeing the in-flight skb,
> the skb is leaked.
>
> Additionally, when the hardware completion interrupt fires, mbox_chan_txdone()
> will pass NULL as the mssg parameter to mctp_pcc_tx_done(). Since
> mctp_pcc_tx_done() dereferences skb->len, this will result in a NULL pointer
> dereference.
Legit. I was thinking that the packet pointed to by chan->active_req
was still in the ring buffer, but it has already been removed.
>
>> + while (chan->msg_count > 0) {
>> + count = chan->msg_count;
>> + idx = chan->msg_free;
>> + if (idx >= count)
>> + idx -= count;
>> + else
>> + idx += MBOX_TX_QUEUE_LEN - count;
> Is this index calculation accurate?
>
> Since msg_free is incremented and msg_count is decremented in each iteration
> below, the calculated idx jumps by 2. This skips alternate skbs, leaking
> them, and reads uninitialized memory, which is then passed to
> dev_consume_skb_any(), causing a use-after-free or double free.
I don't think this is legit. I've written code to test this algorithm,
and it works as written. This logic comes from the
drivers/mailbox/mailbox.c function msg_submit.
idx does not jump by two each iteration.
>
>> + skb = chan->msg_data[idx];
>> + dev_dstats_tx_dropped(ndev);
>> + dev_consume_skb_any(skb);
>> + chan->msg_count--;
>> + chan->msg_free++;
>> + }
>> + }
> [ ... ]
>
>> +static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)
>> +{
>> + struct mctp_pcc_lookup_context context = {0};
>> + struct mctp_pcc_ndev *mctp_pcc_ndev;
> [ ... ]
>
>> + status = acpi_walk_resources(dev_handle, "_CRS", lookup_pcct_indices,
>> + &context);
>> + if (!ACPI_SUCCESS(status)) {
>> + dev_err(dev, "FAILED to lookup PCC indexes from CRS\n");
>> + return -EINVAL;
>> + }
> What happens if the _CRS table contains fewer than two PCC_DWORD_TYPE
> resources?
>
> If context.index is not verified to be at least 2 here, context.inbox_index
> and context.outbox_index might remain uninitialized or at their default zero
> values. This could cause the driver to blindly request PCC channel 0, which
> may belong to another hardware subsystem.
If that is the case then things are truely corrupted on the system. So,
yeah, this is legit, but a sign of things much worse happening.
It is easy enough to check that we get 2 values in the context from walk
resources.
>
>> +
>> + snprintf(name, sizeof(name), "mctppcc%d", context.inbox_index);
>> + ndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,
>> + mctp_pcc_setup);
^ permalink raw reply
* Re: [PATCH iwl-net v1] ice: fix UAF/NULL deref when VSI rebuild and XDP attach race
From: Simon Horman @ 2026-04-22 18:00 UTC (permalink / raw)
To: Kohei Enju
Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Wojciech Drewek, Jacob Keller, Larysa Zaremba,
Maciej Fijalkowski
In-Reply-To: <20260418090137.411506-1-kohei@enjuk.jp>
On Sat, Apr 18, 2026 at 09:01:15AM +0000, Kohei Enju wrote:
> ice_xdp_setup_prog() unconditionally hot-swaps xdp_prog when
> ICE_VSI_REBUILD_PENDING is set. In the attach path, this can publish a
> new rx_ring->xdp_prog before rx_ring->xdp_ring becomes valid while the
> rebuild is pending. As a result, ice_clean_rx_irq() may dereference
> rx_ring->xdp_ring too early.
>
> With high-volume RX packets, running these commands in parallel
> triggered a KASAN splat [1].
> # ethtool --reset $DEV irq dma filter offload
> # ip link set dev $DEV xdp {obj $OBJ sec xdp,off}
>
> Fix this by rejecting XDP attach while rebuild is pending.
> Keep XDP detach allowed in this window. Detach clears rx_ring->xdp_prog,
> so the RX path will not attempt to access rx_ring->xdp_ring.
>
> [1]
> BUG: KASAN: slab-use-after-free in ice_napi_poll+0x3921/0x41a0
> Read of size 2 at addr ffff88812475b880 by task ksoftirqd/1/23
> [...]
> Call Trace:
> <TASK>
> ice_napi_poll+0x3921/0x41a0
> __napi_poll+0x98/0x520
> net_rx_action+0x8f2/0xfa0
> handle_softirqs+0x1cb/0x7f0
> [...]
> </TASK>
>
> Allocated by task 7246:
> ice_prepare_xdp_rings+0x3de/0x12d0
> ice_xdp+0x61c/0xef0
> dev_xdp_install+0x3c4/0x840
> dev_xdp_attach+0x50a/0x10a0
> dev_change_xdp_fd+0x175/0x210
> [...]
>
> Freed by task 7251:
> __rcu_free_sheaf_prepare+0x5f/0x230
> rcu_free_sheaf+0x1a/0xf0
> rcu_core+0x567/0x1d80
> handle_softirqs+0x1cb/0x7f0
>
> Fixes: 2504b8405768 ("ice: protect XDP configuration with a mutex")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>
Reviewed-by: Simon Horman <horms@kernel.org>
Sashiko has provided some feedback on this patch.
However, I believe the issues it raises are not introduced
by this patch and should not block progress of it.
I'd like to ask if you could take a look over that feedback
and see if any follow-up is appropriate.
Thanks!
^ permalink raw reply
* [PATCH net v2 02/15] drivers: net: 3com: 3c515: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The 3c515 was written by Donald Becker between 1997-1998. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c515.c | 1566 ------------------------------------
drivers/net/ethernet/3com/Kconfig | 11 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 1578 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
deleted file mode 100644
index 2227c83a4862..000000000000
--- a/drivers/net/ethernet/3com/3c515.c
+++ /dev/null
@@ -1,1566 +0,0 @@
-/*
- Written 1997-1998 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
-
- 2000/2/2- Added support for kernel-level ISAPnP
- by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
- Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
-
- 2001/11/17 - Added ethtool support (jgarzik)
-
- 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
-
-*/
-
-#define DRV_NAME "3c515"
-
-#define CORKSCREW 1
-
-/* "Knobs" that adjust features and parameters. */
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
- Setting to > 1512 effectively disables this feature. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
-/* Allow the use of fragment bus master transfers instead of only
- programmed-I/O for Vortex cards. Full-bus-master transfers are always
- enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
- the feature may be turned on using 'options'. */
-#define VORTEX_BUS_MASTER
-
-/* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 16
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
-
-#include <linux/module.h>
-#include <linux/isapnp.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define NEW_MULTICAST
-#include <linux/delay.h>
-
-#define MAX_UNITS 8
-
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
-MODULE_LICENSE("GPL");
-
-/* "Knobs" for adjusting internal parameters. */
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define DRIVER_DEBUG 1
-/* Some values here only for performance evaluation and path-coverage
- debugging. */
-static int rx_nocopy, rx_copy, queued_packet;
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
- limited cases. */
-#define WAIT_TX_AVAIL 200
-
-/* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */
-
-/* The size here is somewhat misleading: the Corkscrew also uses the ISA
- aliased registers at <base>+0x400.
- */
-#define CORKSCREW_TOTAL_SIZE 0x20
-
-#ifdef DRIVER_DEBUG
-static int corkscrew_debug = DRIVER_DEBUG;
-#else
-static int corkscrew_debug = 1;
-#endif
-
-#define CORKSCREW_ID 10
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
-3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout,
-it's not practical to integrate this driver with the other EtherLink drivers.
-
-II. Board-specific settings
-
-The Corkscrew has an EEPROM for configuration, but no special settings are
-needed for Linux.
-
-III. Driver operation
-
-The 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
-PCI cards, with the bus master interface extensively modified to work with
-the ISA bus.
-
-The card is capable of full-bus-master transfers with separate
-lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
-DEC Tulip and Intel Speedo3.
-
-This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
-receive buffer. This scheme allocates full-sized skbuffs as receive
-buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is
-chosen to trade-off the memory wasted by passing the full-sized skbuff to
-the queue layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the netif
-layer. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-IV. Notes
-
-Thanks to Terry Murphy of 3Com for providing documentation and a development
-board.
-
-The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
-project names. I use these names to eliminate confusion -- 3Com product
-numbers and names are very similar and often confused.
-
-The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
-This driver only supports ethernet frames because of the recent MTU limit
-of 1.5K, but the changes to support 4.5K are minimal.
-*/
-
-/* Operational definitions.
- These are not used by other compilation units and thus are not
- exported in a ".h" file.
-
- First the windows. There are eight register windows, with the command
- and status registers available in each.
- */
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-/* The top five bits written to EL3_CMD are a command, the lower
- 11 bits are the parameter, if applicable.
- Note that 11 parameters bits was fine for ethernet, but the new chips
- can handle FDDI length frames (~4500 octets) and now parameters count
- 32-bit 'Dwords' rather than octets. */
-
-enum corkscrew_cmd {
- TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
- RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
- UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
- DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
- TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
- AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
- SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
- SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
- StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
- StatsDisable = 22 << 11, StopCoax = 23 << 11,
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
- RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-/* Bits in the general status register. */
-enum corkscrew_status {
- IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
- TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
- IntReq = 0x0040, StatsFull = 0x0080,
- DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
- DMAInProgress = 1 << 11, /* DMA controller is still busy. */
- CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
- On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
-enum Window1 {
- TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
- RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
- TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
-};
-enum Window0 {
- Wn0IRQ = 0x08,
-#if defined(CORKSCREW)
- Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
- Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
-#else
- Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
- Wn0EepromData = 12, /* Window 0: EEPROM results register. */
-#endif
-};
-enum Win0_EEPROM_bits {
- EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
- EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
- EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
-};
-
-/* EEPROM locations. */
-enum eeprom_offset {
- PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
- EtherLink3ID = 7,
-};
-
-enum Window3 { /* Window 3: MAC/config bits. */
- Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
-};
-enum wn3_config {
- Ram_size = 7,
- Ram_width = 8,
- Ram_speed = 0x30,
- Rom_size = 0xc0,
- Ram_split_shift = 16,
- Ram_split = 3 << Ram_split_shift,
- Xcvr_shift = 20,
- Xcvr = 7 << Xcvr_shift,
- Autoselect = 0x1000000,
-};
-
-enum Window4 {
- Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
-};
-enum Win4_Media_bits {
- Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
- Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
- Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
- Media_LnkBeat = 0x0800,
-};
-enum Window7 { /* Window 7: Bus Master control. */
- Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
-};
-
-/* Boomerang-style bus master control registers. Note ISA aliases! */
-enum MasterCtrl {
- PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
- 0x40c,
- TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
-};
-
-/* The Rx and Tx descriptor lists.
- Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
- alignment contraint on tx_ring[] and rx_ring[]. */
-struct boom_rx_desc {
- u32 next;
- s32 status;
- u32 addr;
- s32 length;
-};
-
-/* Values for the Rx status entry. */
-enum rx_desc_status {
- RxDComplete = 0x00008000, RxDError = 0x4000,
- /* See boomerang_rx() for actual error bits */
-};
-
-struct boom_tx_desc {
- u32 next;
- s32 status;
- u32 addr;
- s32 length;
-};
-
-struct corkscrew_private {
- const char *product_name;
- struct list_head list;
- struct net_device *our_dev;
- /* The Rx and Tx rings are here to keep them quad-word-aligned. */
- struct boom_rx_desc rx_ring[RX_RING_SIZE];
- struct boom_tx_desc tx_ring[TX_RING_SIZE];
- /* The addresses of transmit- and receive-in-place skbuffs. */
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
- struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
- struct timer_list timer; /* Media selection timer. */
- int capabilities ; /* Adapter capabilities word. */
- int options; /* User-settable misc. driver options. */
- int last_rx_packets; /* For media autoselection. */
- unsigned int available_media:8, /* From Wn3_Options */
- media_override:3, /* Passed-in media type. */
- default_media:3, /* Read from the EEPROM. */
- full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
- full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
- tx_full:1;
- spinlock_t lock;
- struct device *dev;
-};
-
-/* The action to take with a media selection timer tick.
- Note that we deviate from the 3Com order by checking 10base2 before AUI.
- */
-enum xcvr_types {
- XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
- XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
-};
-
-static struct media_table {
- char *name;
- unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
- mask:8, /* The transceiver-present bit in Wn3_Config. */
- next:8; /* The media type to try next. */
- short wait; /* Time before we check media status. */
-} media_tbl[] = {
- { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
- { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
- { "undefined", 0, 0x80, XCVR_10baseT, 10000},
- { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
- { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
- { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
- { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
- { "undefined", 0, 0x01, XCVR_10baseT, 10000},
- { "Default", 0, 0xFF, XCVR_10baseT, 10000},
-};
-
-#ifdef __ISAPNP__
-static struct isapnp_device_id corkscrew_isapnp_adapters[] = {
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
- (long) "3Com Fast EtherLink ISA" },
- { } /* terminate list */
-};
-
-MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
-
-static int nopnp;
-#endif /* __ISAPNP__ */
-
-static struct net_device *corkscrew_scan(int unit);
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
- struct pnp_dev *idev, int card_number);
-static int corkscrew_open(struct net_device *dev);
-static void corkscrew_timer(struct timer_list *t);
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int corkscrew_rx(struct net_device *dev);
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue);
-static int boomerang_rx(struct net_device *dev);
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
-static int corkscrew_close(struct net_device *dev);
-static void update_stats(int addr, struct net_device *dev);
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-/*
- Unfortunately maximizing the shared code between the integrated and
- module version of the driver results in a complicated set of initialization
- procedures.
- init_module() -- modules / tc59x_init() -- built-in
- The wrappers for corkscrew_scan()
- corkscrew_scan() The common routine that scans for PCI and EISA cards
- corkscrew_found_device() Allocate a device structure when we find a card.
- Different versions exist for modules and built-in.
- corkscrew_probe1() Fill in the device structure -- this is separated
- so that the modules code can put it in dev->init.
-*/
-/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
-/* Note: this is the only limit on the number of cards supported!! */
-static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
-
-#ifdef MODULE
-static int debug = -1;
-
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
-MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
-MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
-
-/* A list of all installed Vortex devices, for removing the driver module. */
-/* we will need locking (and refcounting) if we ever use it for more */
-static LIST_HEAD(root_corkscrew_dev);
-
-static int corkscrew_init_module(void)
-{
- int found = 0;
- if (debug >= 0)
- corkscrew_debug = debug;
- while (corkscrew_scan(-1))
- found++;
- return found ? 0 : -ENODEV;
-}
-module_init(corkscrew_init_module);
-
-#else
-struct net_device *tc515_probe(int unit)
-{
- struct net_device *dev = corkscrew_scan(unit);
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- return dev;
-}
-#endif /* not MODULE */
-
-static int check_device(unsigned ioaddr)
-{
- int timer;
-
- if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
- return 0;
- /* Check the resource configuration for a matching ioaddr. */
- if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
- release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
- return 0;
- }
- /* Verify by reading the device ID from the EEPROM. */
- outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 us. for the read to take place. */
- for (timer = 4; timer >= 0; timer--) {
- udelay(162);
- if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
- break;
- }
- if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
- release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
- return 0;
- }
- return 1;
-}
-
-static void cleanup_card(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- list_del_init(&vp->list);
- if (dev->dma)
- free_dma(dev->dma);
- outw(TotalReset, dev->base_addr + EL3_CMD);
- release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
- if (vp->dev)
- pnp_device_detach(to_pnp_dev(vp->dev));
-}
-
-static struct net_device *corkscrew_scan(int unit)
-{
- struct net_device *dev;
- static int cards_found = 0;
- static int ioaddr;
- int err;
-#ifdef __ISAPNP__
- short i;
- static int pnp_cards;
-#endif
-
- dev = alloc_etherdev(sizeof(struct corkscrew_private));
- if (!dev)
- return ERR_PTR(-ENOMEM);
-
- if (unit >= 0) {
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- }
-
-#ifdef __ISAPNP__
- if(nopnp == 1)
- goto no_pnp;
- for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
- struct pnp_dev *idev = NULL;
- int irq;
- while((idev = pnp_find_dev(NULL,
- corkscrew_isapnp_adapters[i].vendor,
- corkscrew_isapnp_adapters[i].function,
- idev))) {
-
- if (pnp_device_attach(idev) < 0)
- continue;
- if (pnp_activate_dev(idev) < 0) {
- pr_warn("pnp activate failed (out of resources?)\n");
- pnp_device_detach(idev);
- continue;
- }
- if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
- pnp_device_detach(idev);
- continue;
- }
- ioaddr = pnp_port_start(idev, 0);
- irq = pnp_irq(idev, 0);
- if (!check_device(ioaddr)) {
- pnp_device_detach(idev);
- continue;
- }
- if(corkscrew_debug)
- pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
- (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
- pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
- inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
- /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
- SET_NETDEV_DEV(dev, &idev->dev);
- pnp_cards++;
- err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
- if (!err)
- return dev;
- cleanup_card(dev);
- }
- }
-no_pnp:
-#endif /* __ISAPNP__ */
-
- /* Check all locations on the ISA bus -- evil! */
- for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
- if (!check_device(ioaddr))
- continue;
-
- pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
- inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
- err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
- if (!err)
- return dev;
- cleanup_card(dev);
- }
- free_netdev(dev);
- return NULL;
-}
-
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = corkscrew_open,
- .ndo_stop = corkscrew_close,
- .ndo_start_xmit = corkscrew_start_xmit,
- .ndo_tx_timeout = corkscrew_timeout,
- .ndo_get_stats = corkscrew_get_stats,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
- struct pnp_dev *idev, int card_number)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
- __be16 addr[ETH_ALEN / 2];
- int i;
- int irq;
-
-#ifdef __ISAPNP__
- if (idev) {
- irq = pnp_irq(idev, 0);
- vp->dev = &idev->dev;
- } else {
- irq = inw(ioaddr + 0x2002) & 15;
- }
-#else
- irq = inw(ioaddr + 0x2002) & 15;
-#endif
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
- dev->dma = inw(ioaddr + 0x2000) & 7;
- vp->product_name = "3c515";
- vp->options = dev->mem_start;
- vp->our_dev = dev;
-
- if (!vp->options) {
- if (card_number >= MAX_UNITS)
- vp->options = -1;
- else
- vp->options = options[card_number];
- }
-
- if (vp->options >= 0) {
- vp->media_override = vp->options & 7;
- if (vp->media_override == 2)
- vp->media_override = 0;
- vp->full_duplex = (vp->options & 8) ? 1 : 0;
- vp->bus_master = (vp->options & 16) ? 1 : 0;
- } else {
- vp->media_override = 7;
- vp->full_duplex = 0;
- vp->bus_master = 0;
- }
-#ifdef MODULE
- list_add(&vp->list, &root_corkscrew_dev);
-#endif
-
- pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
-
- spin_lock_init(&vp->lock);
-
- timer_setup(&vp->timer, corkscrew_timer, 0);
-
- /* Read the station address from the EEPROM. */
- EL3WINDOW(0);
- for (i = 0; i < 0x18; i++) {
- int timer;
- outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 us. for the read to take place. */
- for (timer = 4; timer >= 0; timer--) {
- udelay(162);
- if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
- break;
- }
- eeprom[i] = inw(ioaddr + Wn0EepromData);
- checksum ^= eeprom[i];
- if (i < 3)
- addr[i] = htons(eeprom[i]);
- }
- eth_hw_addr_set(dev, (u8 *)addr);
- checksum = (checksum ^ (checksum >> 8)) & 0xff;
- if (checksum != 0x00)
- pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
- pr_cont(" %pM", dev->dev_addr);
- if (eeprom[16] == 0x11c7) { /* Corkscrew */
- if (request_dma(dev->dma, "3c515")) {
- pr_cont(", DMA %d allocation failed", dev->dma);
- dev->dma = 0;
- } else
- pr_cont(", DMA %d", dev->dma);
- }
- pr_cont(", IRQ %d\n", dev->irq);
- /* Tell them about an invalid IRQ. */
- if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
- pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n");
-
- {
- static const char * const ram_split[] = {
- "5:3", "3:1", "1:1", "3:5"
- };
- __u32 config;
- EL3WINDOW(3);
- vp->available_media = inw(ioaddr + Wn3_Options);
- config = inl(ioaddr + Wn3_Config);
- if (corkscrew_debug > 1)
- pr_info(" Internal config register is %4.4x, transceivers %#x.\n",
- config, inw(ioaddr + Wn3_Options));
- pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
- 8 << config & Ram_size,
- config & Ram_width ? "word" : "byte",
- ram_split[(config & Ram_split) >> Ram_split_shift],
- config & Autoselect ? "autoselect/" : "",
- media_tbl[(config & Xcvr) >> Xcvr_shift].name);
- vp->default_media = (config & Xcvr) >> Xcvr_shift;
- vp->autoselect = config & Autoselect ? 1 : 0;
- dev->if_port = vp->default_media;
- }
- if (vp->media_override != 7) {
- pr_info(" Media override to transceiver type %d (%s).\n",
- vp->media_override,
- media_tbl[vp->media_override].name);
- dev->if_port = vp->media_override;
- }
-
- vp->capabilities = eeprom[16];
- vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
- /* Rx is broken at 10mbps, so we always disable it. */
- /* vp->full_bus_master_rx = 0; */
- vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
-
- /* The 3c51x-specific entries in the device structure. */
- dev->netdev_ops = &netdev_ops;
- dev->watchdog_timeo = (400 * HZ) / 1000;
- dev->ethtool_ops = &netdev_ethtool_ops;
-
- return register_netdev(dev);
-}
-
-
-static int corkscrew_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct corkscrew_private *vp = netdev_priv(dev);
- bool armtimer = false;
- __u32 config;
- int i;
-
- /* Before initializing select the active media port. */
- EL3WINDOW(3);
- if (vp->full_duplex)
- outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
- config = inl(ioaddr + Wn3_Config);
-
- if (vp->media_override != 7) {
- if (corkscrew_debug > 1)
- pr_info("%s: Media override to transceiver %d (%s).\n",
- dev->name, vp->media_override,
- media_tbl[vp->media_override].name);
- dev->if_port = vp->media_override;
- } else if (vp->autoselect) {
- /* Find first available media type, starting with 100baseTx. */
- dev->if_port = 4;
- while (!(vp->available_media & media_tbl[dev->if_port].mask))
- dev->if_port = media_tbl[dev->if_port].next;
-
- if (corkscrew_debug > 1)
- pr_debug("%s: Initial media type %s.\n",
- dev->name, media_tbl[dev->if_port].name);
- armtimer = true;
- } else
- dev->if_port = vp->default_media;
-
- config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
- outl(config, ioaddr + Wn3_Config);
-
- if (corkscrew_debug > 1) {
- pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
- dev->name, config);
- }
-
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
-
- outw(RxReset, ioaddr + EL3_CMD);
- /* Wait a few ticks for the RxReset command to complete. */
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
-
- outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
- /* Use the now-standard shared IRQ implementation. */
- if (vp->capabilities == 0x11c7) {
- /* Corkscrew: Cannot share ISA resources. */
- if (dev->irq == 0 ||
- dev->dma == 0 ||
- request_irq(dev->irq, corkscrew_interrupt, 0,
- vp->product_name, dev))
- return -EAGAIN;
- enable_dma(dev->dma);
- set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
- vp->product_name, dev)) {
- return -EAGAIN;
- }
-
- if (armtimer)
- mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
-
- if (corkscrew_debug > 1) {
- EL3WINDOW(4);
- pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
- dev->name, dev->irq, inw(ioaddr + Wn4_Media));
- }
-
- /* Set the station address and mask in window 2 each time opened. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
- for (; i < 12; i += 2)
- outw(0, ioaddr + i);
-
- if (dev->if_port == 3)
- /* Start the thinnet transceiver. We should really wait 50ms... */
- outw(StartCoax, ioaddr + EL3_CMD);
- EL3WINDOW(4);
- outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
- media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 10; i++)
- inb(ioaddr + i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
- /* New: On the Vortex we must also clear the BadSSD counter. */
- EL3WINDOW(4);
- inb(ioaddr + 12);
- /* ..and on the Boomerang we enable the extra statistics bits. */
- outw(0x0040, ioaddr + Wn4_NetDiag);
-
- /* Switch to register set 7 for normal use. */
- EL3WINDOW(7);
-
- if (vp->full_bus_master_rx) { /* Boomerang bus master. */
- vp->cur_rx = vp->dirty_rx = 0;
- if (corkscrew_debug > 2)
- pr_debug("%s: Filling in the Rx ring.\n", dev->name);
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- if (i < (RX_RING_SIZE - 1))
- vp->rx_ring[i].next =
- isa_virt_to_bus(&vp->rx_ring[i + 1]);
- else
- vp->rx_ring[i].next = 0;
- vp->rx_ring[i].status = 0; /* Clear complete bit. */
- vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
- skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
- vp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break; /* Bad news! */
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
- }
- if (i != 0)
- vp->rx_ring[i - 1].next =
- isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
- outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
- }
- if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
- vp->cur_tx = vp->dirty_tx = 0;
- outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
- /* Clear the Tx ring. */
- for (i = 0; i < TX_RING_SIZE; i++)
- vp->tx_skbuff[i] = NULL;
- outl(0, ioaddr + DownListPtr);
- }
- /* Set receiver mode: presumably accept b-case and phys addr only. */
- set_rx_mode(dev);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-
- netif_start_queue(dev);
-
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
- (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
- (vp->full_bus_master_rx ? UpComplete : RxComplete) |
- (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
- /* Ack all pending events, and set active indicator mask. */
- outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
- ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
- | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
- ioaddr + EL3_CMD);
-
- return 0;
-}
-
-static void corkscrew_timer(struct timer_list *t)
-{
-#ifdef AUTOMEDIA
- struct corkscrew_private *vp = timer_container_of(vp, t, timer);
- struct net_device *dev = vp->our_dev;
- int ioaddr = dev->base_addr;
- unsigned long flags;
- int ok = 0;
-
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection timer tick happened, %s.\n",
- dev->name, media_tbl[dev->if_port].name);
-
- spin_lock_irqsave(&vp->lock, flags);
-
- {
- int old_window = inw(ioaddr + EL3_CMD) >> 13;
- int media_status;
- EL3WINDOW(4);
- media_status = inw(ioaddr + Wn4_Media);
- switch (dev->if_port) {
- case 0:
- case 4:
- case 5: /* 10baseT, 100baseTX, 100baseFX */
- if (media_status & Media_LnkBeat) {
- ok = 1;
- if (corkscrew_debug > 1)
- pr_debug("%s: Media %s has link beat, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
- } else if (corkscrew_debug > 1)
- pr_debug("%s: Media %s is has no link beat, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
-
- break;
- default: /* Other media types handled by Tx timeouts. */
- if (corkscrew_debug > 1)
- pr_debug("%s: Media %s is has no indication, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
- ok = 1;
- }
- if (!ok) {
- __u32 config;
-
- do {
- dev->if_port =
- media_tbl[dev->if_port].next;
- }
- while (!(vp->available_media & media_tbl[dev->if_port].mask));
-
- if (dev->if_port == 8) { /* Go back to default. */
- dev->if_port = vp->default_media;
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection failing, using default %s port.\n",
- dev->name,
- media_tbl[dev->if_port].name);
- } else {
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection failed, now trying %s port.\n",
- dev->name,
- media_tbl[dev->if_port].name);
- vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
- add_timer(&vp->timer);
- }
- outw((media_status & ~(Media_10TP | Media_SQE)) |
- media_tbl[dev->if_port].media_bits,
- ioaddr + Wn4_Media);
-
- EL3WINDOW(3);
- config = inl(ioaddr + Wn3_Config);
- config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
- outl(config, ioaddr + Wn3_Config);
-
- outw(dev->if_port == 3 ? StartCoax : StopCoax,
- ioaddr + EL3_CMD);
- }
- EL3WINDOW(old_window);
- }
-
- spin_unlock_irqrestore(&vp->lock, flags);
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection timer finished, %s.\n",
- dev->name, media_tbl[dev->if_port].name);
-
-#endif /* AUTOMEDIA */
-}
-
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue)
-{
- int i;
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
-
- pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n",
- dev->name, inb(ioaddr + TxStatus),
- inw(ioaddr + EL3_STATUS));
- /* Slight code bloat to be user friendly. */
- if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
- pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n",
- dev->name);
-#ifndef final_version
- pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n",
- vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
- vp->cur_tx);
- pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
- &vp->tx_ring[0]);
- for (i = 0; i < TX_RING_SIZE; i++) {
- pr_debug(" %d: %p length %8.8x status %8.8x\n", i,
- &vp->tx_ring[i],
- vp->tx_ring[i].length, vp->tx_ring[i].status);
- }
-#endif
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_trans_update(dev); /* prevent tx timeout */
- dev->stats.tx_errors++;
- dev->stats.tx_dropped++;
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
-
- /* Block a timer-based transmit from overlapping. */
-
- netif_stop_queue(dev);
-
- if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */
- /* Calculate the next Tx descriptor entry. */
- int entry = vp->cur_tx % TX_RING_SIZE;
- struct boom_tx_desc *prev_entry;
- unsigned long flags;
- int i;
-
- if (vp->tx_full) /* No room to transmit with */
- return NETDEV_TX_BUSY;
- if (vp->cur_tx != 0)
- prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
- else
- prev_entry = NULL;
- if (corkscrew_debug > 3)
- pr_debug("%s: Trying to send a packet, Tx index %d.\n",
- dev->name, vp->cur_tx);
- /* vp->tx_full = 1; */
- vp->tx_skbuff[entry] = skb;
- vp->tx_ring[entry].next = 0;
- vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
- vp->tx_ring[entry].length = skb->len | 0x80000000;
- vp->tx_ring[entry].status = skb->len | 0x80000000;
-
- spin_lock_irqsave(&vp->lock, flags);
- outw(DownStall, ioaddr + EL3_CMD);
- /* Wait for the stall to complete. */
- for (i = 20; i >= 0; i--)
- if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
- break;
- if (prev_entry)
- prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
- if (inl(ioaddr + DownListPtr) == 0) {
- outl(isa_virt_to_bus(&vp->tx_ring[entry]),
- ioaddr + DownListPtr);
- queued_packet++;
- }
- outw(DownUnstall, ioaddr + EL3_CMD);
- spin_unlock_irqrestore(&vp->lock, flags);
-
- vp->cur_tx++;
- if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
- vp->tx_full = 1;
- else { /* Clear previous interrupt enable. */
- if (prev_entry)
- prev_entry->status &= ~0x80000000;
- netif_wake_queue(dev);
- }
- return NETDEV_TX_OK;
- }
- /* Put out the doubleword header... */
- outl(skb->len, ioaddr + TX_FIFO);
- dev->stats.tx_bytes += skb->len;
-#ifdef VORTEX_BUS_MASTER
- if (vp->bus_master) {
- /* Set the bus-master controller to transfer the packet. */
- outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
- outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
- vp->tx_skb = skb;
- outw(StartDMADown, ioaddr + EL3_CMD);
- /* queue will be woken at the DMADone interrupt. */
- } else {
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb(skb);
- if (inw(ioaddr + TxFree) > 1536) {
- netif_wake_queue(dev);
- } else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + (1536 >> 2),
- ioaddr + EL3_CMD);
- }
-#else
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb(skb);
- if (inw(ioaddr + TxFree) > 1536) {
- netif_wake_queue(dev);
- } else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
-#endif /* bus master */
-
-
- /* Clear the Tx status stack. */
- {
- short tx_status;
- int i = 4;
-
- while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
- if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
- if (corkscrew_debug > 2)
- pr_debug("%s: Tx error, status %2.2x.\n",
- dev->name, tx_status);
- if (tx_status & 0x04)
- dev->stats.tx_fifo_errors++;
- if (tx_status & 0x38)
- dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) {
- int j;
- outw(TxReset, ioaddr + EL3_CMD);
- for (j = 20; j >= 0; j--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- }
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
- }
- }
- return NETDEV_TX_OK;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
-{
- /* Use the now-standard shared IRQ implementation. */
- struct net_device *dev = dev_id;
- struct corkscrew_private *lp = netdev_priv(dev);
- int ioaddr, status;
- int latency;
- int i = max_interrupt_work;
-
- ioaddr = dev->base_addr;
- latency = inb(ioaddr + Timer);
-
- spin_lock(&lp->lock);
-
- status = inw(ioaddr + EL3_STATUS);
-
- if (corkscrew_debug > 4)
- pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
- dev->name, status, latency);
- if ((status & 0xE000) != 0xE000) {
- static int donedidthis;
- /* Some interrupt controllers store a bogus interrupt from boot-time.
- Ignore a single early interrupt, but don't hang the machine for
- other interrupt problems. */
- if (donedidthis++ > 100) {
- pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
- dev->name, status, netif_running(dev));
- free_irq(dev->irq, dev);
- dev->irq = -1;
- }
- }
-
- do {
- if (corkscrew_debug > 5)
- pr_debug("%s: In interrupt loop, status %4.4x.\n",
- dev->name, status);
- if (status & RxComplete)
- corkscrew_rx(dev);
-
- if (status & TxAvailable) {
- if (corkscrew_debug > 5)
- pr_debug(" TX room bit was handled.\n");
- /* There's room in the FIFO for a full-sized packet. */
- outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
- }
- if (status & DownComplete) {
- unsigned int dirty_tx = lp->dirty_tx;
-
- while (lp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
- break; /* It still hasn't been processed. */
- if (lp->tx_skbuff[entry]) {
- dev_consume_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = NULL;
- }
- dirty_tx++;
- }
- lp->dirty_tx = dirty_tx;
- outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
- if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
- lp->tx_full = 0;
- netif_wake_queue(dev);
- }
- }
-#ifdef VORTEX_BUS_MASTER
- if (status & DMADone) {
- outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
- dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */
- netif_wake_queue(dev);
- }
-#endif
- if (status & UpComplete) {
- boomerang_rx(dev);
- outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
- }
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts at once. */
- if (status & RxEarly) { /* Rx early is unused. */
- corkscrew_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & StatsFull) { /* Empty statistics. */
- static int DoneDidThat;
- if (corkscrew_debug > 4)
- pr_debug("%s: Updating stats.\n", dev->name);
- update_stats(ioaddr, dev);
- /* DEBUG HACK: Disable statistics as an interrupt source. */
- /* This occurs when we have the wrong media type! */
- if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
- int win, reg;
- pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
- dev->name);
- for (win = 0; win < 8; win++) {
- EL3WINDOW(win);
- pr_notice("Vortex window %d:", win);
- for (reg = 0; reg < 16; reg++)
- pr_cont(" %2.2x", inb(ioaddr + reg));
- pr_cont("\n");
- }
- EL3WINDOW(7);
- outw(SetIntrEnb | TxAvailable |
- RxComplete | AdapterFailure |
- UpComplete | DownComplete |
- TxComplete, ioaddr + EL3_CMD);
- DoneDidThat++;
- }
- }
- if (status & AdapterFailure) {
- /* Adapter failure requires Rx reset and reinit. */
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure,
- ioaddr + EL3_CMD);
- }
- }
-
- if (--i < 0) {
- pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
- dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
- /* Disable all pending interrupts. */
- outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
- outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-
- } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
-
- spin_unlock(&lp->lock);
-
- if (corkscrew_debug > 4)
- pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
- return IRQ_HANDLED;
-}
-
-static int corkscrew_rx(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int i;
- short rx_status;
-
- if (corkscrew_debug > 5)
- pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
- while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
- if (rx_status & 0x4000) { /* Error, update stats. */
- unsigned char rx_error = inb(ioaddr + RxErrors);
- if (corkscrew_debug > 2)
- pr_debug(" Rx error: status %2.2x.\n",
- rx_error);
- dev->stats.rx_errors++;
- if (rx_error & 0x01)
- dev->stats.rx_over_errors++;
- if (rx_error & 0x02)
- dev->stats.rx_length_errors++;
- if (rx_error & 0x04)
- dev->stats.rx_frame_errors++;
- if (rx_error & 0x08)
- dev->stats.rx_crc_errors++;
- if (rx_error & 0x10)
- dev->stats.rx_length_errors++;
- } else {
- /* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
- if (corkscrew_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- /* 'skb_put()' points to the start of sk_buff data area. */
- insl(ioaddr + RX_FIFO,
- skb_put(skb, pkt_len),
- (pkt_len + 3) >> 2);
- outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- /* Wait a limited time to go to next packet. */
- for (i = 200; i >= 0; i--)
- if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- continue;
- } else if (corkscrew_debug)
- pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
- }
- outw(RxDiscard, ioaddr + EL3_CMD);
- dev->stats.rx_dropped++;
- /* Wait a limited time to skip this packet. */
- for (i = 200; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- }
- return 0;
-}
-
-static int boomerang_rx(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int entry = vp->cur_rx % RX_RING_SIZE;
- int ioaddr = dev->base_addr;
- int rx_status;
-
- if (corkscrew_debug > 5)
- pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
- while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
- if (rx_status & RxDError) { /* Error, update stats. */
- unsigned char rx_error = rx_status >> 16;
- if (corkscrew_debug > 2)
- pr_debug(" Rx error: status %2.2x.\n",
- rx_error);
- dev->stats.rx_errors++;
- if (rx_error & 0x01)
- dev->stats.rx_over_errors++;
- if (rx_error & 0x02)
- dev->stats.rx_length_errors++;
- if (rx_error & 0x04)
- dev->stats.rx_frame_errors++;
- if (rx_error & 0x08)
- dev->stats.rx_crc_errors++;
- if (rx_error & 0x10)
- dev->stats.rx_length_errors++;
- } else {
- /* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
- struct sk_buff *skb;
-
- dev->stats.rx_bytes += pkt_len;
- if (corkscrew_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
-
- /* Check if the packet is long enough to just accept without
- copying to a properly sized skbuff. */
- if (pkt_len < rx_copybreak &&
- (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- /* 'skb_put()' points to the start of sk_buff data area. */
- skb_put_data(skb,
- isa_bus_to_virt(vp->rx_ring[entry].addr),
- pkt_len);
- rx_copy++;
- } else {
- void *temp;
- /* Pass up the skbuff already on the Rx ring. */
- skb = vp->rx_skbuff[entry];
- vp->rx_skbuff[entry] = NULL;
- temp = skb_put(skb, pkt_len);
- /* Remove this checking code for final release. */
- if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
- pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
- dev->name,
- isa_bus_to_virt(vp->rx_ring[entry].addr),
- skb->head, temp);
- rx_nocopy++;
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- }
- entry = (++vp->cur_rx) % RX_RING_SIZE;
- }
- /* Refill the Rx ring buffers. */
- for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
- struct sk_buff *skb;
- entry = vp->dirty_rx % RX_RING_SIZE;
- if (vp->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
- if (skb == NULL)
- break; /* Bad news! */
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
- vp->rx_skbuff[entry] = skb;
- }
- vp->rx_ring[entry].status = 0; /* Clear complete bit. */
- }
- return 0;
-}
-
-static int corkscrew_close(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- int i;
-
- netif_stop_queue(dev);
-
- if (corkscrew_debug > 1) {
- pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
- dev->name, inw(ioaddr + EL3_STATUS),
- inb(ioaddr + TxStatus));
- pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
- dev->name, rx_nocopy, rx_copy, queued_packet);
- }
-
- timer_delete_sync(&vp->timer);
-
- /* Turn off statistics ASAP. We update lp->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
-
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- if (dev->if_port == XCVR_10base2)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
-
- free_irq(dev->irq, dev);
-
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
- update_stats(ioaddr, dev);
- if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
- outl(0, ioaddr + UpListPtr);
- for (i = 0; i < RX_RING_SIZE; i++)
- if (vp->rx_skbuff[i]) {
- dev_kfree_skb(vp->rx_skbuff[i]);
- vp->rx_skbuff[i] = NULL;
- }
- }
- if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
- outl(0, ioaddr + DownListPtr);
- for (i = 0; i < TX_RING_SIZE; i++)
- if (vp->tx_skbuff[i]) {
- dev_kfree_skb(vp->tx_skbuff[i]);
- vp->tx_skbuff[i] = NULL;
- }
- }
-
- return 0;
-}
-
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- unsigned long flags;
-
- if (netif_running(dev)) {
- spin_lock_irqsave(&vp->lock, flags);
- update_stats(dev->base_addr, dev);
- spin_unlock_irqrestore(&vp->lock, flags);
- }
- return &dev->stats;
-}
-
-/* Update statistics.
- Unlike with the EL3 we need not worry about interrupts changing
- the window setting from underneath us, but we must still guard
- against a race condition with a StatsUpdate interrupt updating the
- table. This is done by checking that the ASM (!) code generated uses
- atomic updates with '+='.
- */
-static void update_stats(int ioaddr, struct net_device *dev)
-{
- /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
- /* Switch to the stats window, and read everything. */
- EL3WINDOW(6);
- dev->stats.tx_carrier_errors += inb(ioaddr + 0);
- dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
- /* Multiple collisions. */ inb(ioaddr + 2);
- dev->stats.collisions += inb(ioaddr + 3);
- dev->stats.tx_window_errors += inb(ioaddr + 4);
- dev->stats.rx_fifo_errors += inb(ioaddr + 5);
- dev->stats.tx_packets += inb(ioaddr + 6);
- dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
- /* Rx packets */ inb(ioaddr + 7);
- /* Must read to clear */
- /* Tx deferrals */ inb(ioaddr + 8);
- /* Don't bother with register 9, an extension of registers 6&7.
- If we do use the 6&7 values the atomic update assumption above
- is invalid. */
- inw(ioaddr + 10); /* Total Rx and Tx octets. */
- inw(ioaddr + 12);
- /* New: On the Vortex we must also clear the BadSSD counter. */
- EL3WINDOW(4);
- inb(ioaddr + 12);
-
- /* We change back to window 7 (not 1) with the Vortex. */
- EL3WINDOW(7);
-}
-
-/* This new version of set_rx_mode() supports v1.4 kernels.
- The Vortex chip has no documented multicast filter, so the only
- multicast setting is to receive all multicast frames. At least
- the chip has a very clean way to set the mode, unlike many others. */
-static void set_rx_mode(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- unsigned short new_mode;
-
- if (dev->flags & IFF_PROMISC) {
- if (corkscrew_debug > 3)
- pr_debug("%s: Setting promiscuous mode.\n",
- dev->name);
- new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
- } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
- new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
- } else
- new_mode = SetRxFilter | RxStation | RxBroadcast;
-
- outw(new_mode, ioaddr + EL3_CMD);
-}
-
-static void netdev_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx",
- dev->base_addr);
-}
-
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
- return corkscrew_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
- corkscrew_debug = level;
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
- .get_drvinfo = netdev_get_drvinfo,
- .get_msglevel = netdev_get_msglevel,
- .set_msglevel = netdev_set_msglevel,
-};
-
-#ifdef MODULE
-static void __exit corkscrew_exit_module(void)
-{
- while (!list_empty(&root_corkscrew_dev)) {
- struct net_device *dev;
- struct corkscrew_private *vp;
-
- vp = list_entry(root_corkscrew_dev.next,
- struct corkscrew_private, list);
- dev = vp->our_dev;
- unregister_netdev(dev);
- cleanup_card(dev);
- free_netdev(dev);
- }
-}
-module_exit(corkscrew_exit_module);
-#endif /* MODULE */
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index c05a1b63c1c9..3fd3202d9776 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,17 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-config 3C515
- tristate "3c515 ISA \"Fast EtherLink\""
- depends on ISA && ISA_DMA_API && !PPC32
- select NETDEV_LEGACY_INIT
- help
- If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
- network card, say Y here.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c515.
-
config PCMCIA_3C574
tristate "3Com 3c574 PCMCIA support"
depends on PCMCIA && HAS_IOPORT
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f7623fa2d441..babfd93d5d53 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
# Makefile for the 3Com Ethernet device drivers
#
-obj-$(CONFIG_3C515) += 3c515.o
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The 3c509 was written by Donald Becker between 1993-2000. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Delete Documentation as well
---
.../device_drivers/ethernet/3com/3c509.rst | 249 ----
drivers/net/ethernet/3com/3c509.c | 1448 --------------------
drivers/net/ethernet/3com/Kconfig | 14 -
drivers/net/ethernet/3com/Makefile | 1 -
4 files changed, 1712 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst b/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
deleted file mode 100644
index 47f706bacdd9..000000000000
--- a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
+++ /dev/null
@@ -1,249 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=============================================================================
-Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
-=============================================================================
-
-This file contains the instructions and caveats for v1.18c and higher versions
-of the 3c509 driver. You should not use the driver without reading this file.
-
-release 1.0
-
-28 February 2002
-
-Current maintainer (corrections to):
- David Ruggiero <jdr@farfalle.com>
-
-Introduction
-============
-
-The following are notes and information on using the 3Com EtherLink III series
-ethercards in Linux. These cards are commonly known by the most widely-used
-card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
-be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
-(aka "Vortex" or "Boomerang") series. Kernel support for the 3c509 family is
-provided by the module 3c509.c, which has code to support all of the following
-models:
-
- - 3c509 (original ISA card)
- - 3c509B (later revision of the ISA card; supports full-duplex)
- - 3c589 (PCMCIA)
- - 3c589B (later revision of the 3c589; supports full-duplex)
- - 3c579 (EISA)
-
-Large portions of this documentation were heavily borrowed from the guide
-written the original author of the 3c509 driver, Donald Becker. The master
-copy of that document, which contains notes on older versions of the driver,
-currently resides on Scyld web server: http://www.scyld.com/.
-
-
-Special Driver Features
-=======================
-
-Overriding card settings
-
-The driver allows boot- or load-time overriding of the card's detected IOADDR,
-IRQ, and transceiver settings, although this capability shouldn't generally be
-needed except to enable full-duplex mode (see below). An example of the syntax
-for LILO parameters for doing this::
-
- ether=10,0x310,3,0x3c509,eth0
-
-This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
-transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
-with other card types when overriding the I/O address. When the driver is
-loaded as a module, only the IRQ may be overridden. For example,
-setting two cards to IRQ10 and IRQ11 is done by using the irq module
-option::
-
- options 3c509 irq=10,11
-
-
-Full-duplex mode
-================
-
-The v1.18c driver added support for the 3c509B's full-duplex capabilities.
-In order to enable and successfully use full-duplex mode, three conditions
-must be met:
-
-(a) You must have a Etherlink III card model whose hardware supports full-
-duplex operations. Currently, the only members of the 3c509 family that are
-positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
-(PCMCIA) cards. Cards without the "B" model designation do *not* support
-full-duplex mode; these include the original 3c509 (no "B"), the original
-3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
-
-(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
-connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
-AUI and 10base2 network cabling is physically incapable of full-duplex
-operation.
-
-(c) Most importantly, your 3c509B must be connected to a link partner that is
-itself full-duplex capable. This is almost certainly one of two things: a full-
-duplex-capable Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
-another system that's connected directly to the 3c509B via a crossover cable.
-
-Full-duplex mode can be enabled using 'ethtool'.
-
-.. warning::
-
- Extremely important caution concerning full-duplex mode
-
- Understand that the 3c509B's hardware's full-duplex support is much more
- limited than that provide by more modern network interface cards. Although
- at the physical layer of the network it fully supports full-duplex operation,
- the card was designed before the current Ethernet auto-negotiation (N-way)
- spec was written. This means that the 3c509B family ***cannot and will not
- auto-negotiate a full-duplex connection with its link partner under any
- circumstances, no matter how it is initialized***. If the full-duplex mode
- of the 3c509B is enabled, its link partner will very likely need to be
- independently _forced_ into full-duplex mode as well; otherwise various nasty
- failures will occur - at the very least, you'll see massive numbers of packet
- collisions. This is one of very rare circumstances where disabling auto-
- negotiation and forcing the duplex mode of a network interface card or switch
- would ever be necessary or desirable.
-
-
-Available Transceiver Types
-===========================
-
-For versions of the driver v1.18c and above, the available transceiver types are:
-
-== =========================================================================
-0 transceiver type from EEPROM config (normally 10baseT); force half-duplex
-1 AUI (thick-net / DB15 connector)
-2 (undefined)
-3 10base2 (thin-net == coax / BNC connector)
-4 10baseT (RJ-45 connector); force half-duplex mode
-8 transceiver type and duplex mode taken from card's EEPROM config settings
-12 10baseT (RJ-45 connector); force full-duplex mode
-== =========================================================================
-
-Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
-that the new transceiver codes 8 and 12 are the *only* ones that will enable
-full-duplex mode, no matter what the card's detected EEPROM settings might be.
-This insured that merely upgrading the driver from an earlier version would
-never automatically enable full-duplex mode in an existing installation;
-it must always be explicitly enabled via one of these code in order to be
-activated.
-
-The transceiver type can be changed using 'ethtool'.
-
-
-Interpretation of error messages and common problems
-----------------------------------------------------
-
-Error Messages
-^^^^^^^^^^^^^^
-
-eth0: Infinite loop in interrupt, status 2011.
-These are "mostly harmless" message indicating that the driver had too much
-work during that interrupt cycle. With a status of 0x2011 you are receiving
-packets faster than they can be removed from the card. This should be rare
-or impossible in normal operation. Possible causes of this error report are:
-
- - a "green" mode enabled that slows the processor down when there is no
- keyboard activity.
-
- - some other device or device driver hogging the bus or disabling interrupts.
- Check /proc/interrupts for excessive interrupt counts. The timer tick
- interrupt should always be incrementing faster than the others.
-
-No received packets
-^^^^^^^^^^^^^^^^^^^
-
-If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
-receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
-have an interrupt line problem. Check /proc/interrupts to verify that the
-card is actually generating interrupts. If the interrupt count is not
-increasing you likely have a physical conflict with two devices trying to
-use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
-or IRQ5, and the easiest solution is to move the 3c509 to a different
-interrupt line. If the device is receiving packets but 'ping' doesn't work,
-you have a routing problem.
-
-Tx Carrier Errors Reported in /proc/net/dev
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
-field in /proc/net/dev increments as quickly as the Tx packet count, you
-likely have an unterminated network or the incorrect media transceiver selected.
-
-3c509B card is not detected on machines with an ISA PnP BIOS.
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-While the updated driver works with most PnP BIOS programs, it does not work
-with all. This can be fixed by disabling PnP support using the 3Com-supplied
-setup program.
-
-3c509 card is not detected on overclocked machines
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Increase the delay time in id_read_eeprom() from the current value, 500,
-to an absurdly high value, such as 5000.
-
-
-Decoding Status and Error Messages
-----------------------------------
-
-
-The bits in the main status register are:
-
-===== ======================================
-value description
-===== ======================================
-0x01 Interrupt latch
-0x02 Tx overrun, or Rx underrun
-0x04 Tx complete
-0x08 Tx FIFO room available
-0x10 A complete Rx packet has arrived
-0x20 A Rx packet has started to arrive
-0x40 The driver has requested an interrupt
-0x80 Statistics counter nearly full
-===== ======================================
-
-The bits in the transmit (Tx) status word are:
-
-===== ============================================
-value description
-===== ============================================
-0x02 Out-of-window collision.
-0x04 Status stack overflow (normally impossible).
-0x08 16 collisions.
-0x10 Tx underrun (not enough PCI bus bandwidth).
-0x20 Tx jabber.
-0x40 Tx interrupt requested.
-0x80 Status is valid (this should always be set).
-===== ============================================
-
-
-When a transmit error occurs the driver produces a status message such as::
-
- eth0: Transmit error, Tx status register 82
-
-The two values typically seen here are:
-
-0x82
-^^^^
-
-Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network.
-
-0x88
-^^^^
-
-16 collisions. This typically occurs when the network is exceptionally busy
-or when another host doesn't correctly back off after a collision. If this
-error is mixed with 0x82 errors it is the result of a host incorrectly set
-to full duplex (see above).
-
-Both of these errors are the result of network problems that should be
-corrected. They do not represent driver malfunction.
-
-
-Revision history (this file)
-============================
-
-28Feb02 v1.0 DR New; major portions based on Becker original 3c509 docs
-
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
deleted file mode 100644
index fb68339e1511..000000000000
--- a/drivers/net/ethernet/3com/3c509.c
+++ /dev/null
@@ -1,1448 +0,0 @@
-/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
-/*
- Written 1993-2000 by Donald Becker.
-
- Copyright 1994-2000 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency. This software may be used and
- distributed according to the terms of the GNU General Public License,
- incorporated herein by reference.
-
- This driver is for the 3Com EtherLinkIII series.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
- Known limitations:
- Because of the way 3c509 ISA detection works it's difficult to predict
- a priori which of several ISA-mode cards will be detected first.
-
- This driver does not use predictive interrupt mode, resulting in higher
- packet latency but lower overhead. If interrupts are disabled for an
- unusually long time it could also result in missed packets, but in
- practice this rarely happens.
-
-
- FIXES:
- Alan Cox: Removed the 'Unexpected interrupt' bug.
- Michael Meskes: Upgraded to Donald Becker's version 1.07.
- Alan Cox: Increased the eeprom delay. Regardless of
- what the docs say some people definitely
- get problems with lower (but in card spec)
- delays
- v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
- other cleanups. -djb
- Andrea Arcangeli: Upgraded to Donald Becker's version 1.12.
- Rick Payne: Fixed SMP race condition
- v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
- v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
- v1.15 1/31/98 Faster recovery for Tx errors. -djb
- v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
- v1.18 12Mar2001 Andrew Morton
- - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
- - Reviewed against 1.18 from scyld.com
- v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
- - ethtool support
- v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
- - Power Management support
- v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
- - Full duplex support
- v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
- - Additional ethtool features
- v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
- - Increase *read_eeprom udelay to workaround oops with 2 cards.
- v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
- - Introduce driver model for EISA cards.
- v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org>
- - convert to isa_driver and pnp_driver and some cleanups
-*/
-
-#define DRV_NAME "3c509"
-
-/* A few values that may be tweaked. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (400*HZ/1000)
-
-#include <linux/module.h>
-#include <linux/isa.h>
-#include <linux/pnp.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/pm.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h> /* for udelay() */
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/device.h>
-#include <linux/eisa.h>
-#include <linux/bitops.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#ifdef EL3_DEBUG
-static int el3_debug = EL3_DEBUG;
-#else
-static int el3_debug = 2;
-#endif
-
-/* Used to do a global count of all the cards in the system. Must be
- * a global variable so that the eisa probe routines can increment
- * it */
-static int el3_cards = 0;
-#define EL3_MAX_CARDS 8
-
-/* To minimize the size of the driver source I only define operating
- constants if they are used several times. You'll need the manual
- anyway if you want to understand driver details. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-#define EEPROM_READ 0x80
-
-#define EL3_IO_EXTENT 16
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-
-/* The top five bits written to EL3_CMD are a command, the lower
- 11 bits are the parameter, if applicable. */
-enum c509cmd {
- TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
- RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
- TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
- FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
- SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
- SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
- StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
- PowerDown = 28<<11, PowerAuto = 29<<11};
-
-enum c509status {
- IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
- TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
- IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
- RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
-
-/* Register window 1 offsets, the window used in normal operation. */
-#define TX_FIFO 0x00
-#define RX_FIFO 0x00
-#define RX_STATUS 0x08
-#define TX_STATUS 0x0B
-#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
-
-#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */
-#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */
-#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
-#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
-#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
-#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */
-#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */
-
-/*
- * Must be a power of two (we use a binary and in the
- * circular queue)
- */
-#define SKB_QUEUE_SIZE 64
-
-enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
-
-struct el3_private {
- spinlock_t lock;
- /* skb send-queue */
- int head, size;
- struct sk_buff *queue[SKB_QUEUE_SIZE];
- enum el3_cardtype type;
-};
-static int id_port;
-static int current_tag;
-static struct net_device *el3_devs[EL3_MAX_CARDS];
-
-/* Parameters that may be passed into the module. */
-static int debug = -1;
-static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
-#ifdef CONFIG_PNP
-static int nopnp;
-#endif
-
-static int el3_common_init(struct net_device *dev);
-static void el3_common_remove(struct net_device *dev);
-static ushort id_read_eeprom(int index);
-static ushort read_eeprom(int ioaddr, int index);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev);
-static int el3_close(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue);
-static void el3_down(struct net_device *dev);
-static void el3_up(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-#ifdef CONFIG_PM
-static int el3_suspend(struct device *, pm_message_t);
-static int el3_resume(struct device *);
-#else
-#define el3_suspend NULL
-#define el3_resume NULL
-#endif
-
-
-/* generic device remove for all device types */
-static int el3_device_remove (struct device *device);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void el3_poll_controller(struct net_device *dev);
-#endif
-
-/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
-static int el3_isa_id_sequence(__be16 *phys_addr)
-{
- short lrs_state = 0xff;
- int i;
-
- /* ISA boards are detected by sending the ID sequence to the
- ID_PORT. We find cards past the first by setting the 'current_tag'
- on cards as they are found. Cards with their tag set will not
- respond to subsequent ID sequences. */
-
- outb(0x00, id_port);
- outb(0x00, id_port);
- for (i = 0; i < 255; i++) {
- outb(lrs_state, id_port);
- lrs_state <<= 1;
- lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
- }
- /* For the first probe, clear all board's tag registers. */
- if (current_tag == 0)
- outb(0xd0, id_port);
- else /* Otherwise kill off already-found boards. */
- outb(0xd8, id_port);
- if (id_read_eeprom(7) != 0x6d50)
- return 1;
- /* Read in EEPROM data, which does contention-select.
- Only the lowest address board will stay "on-line".
- 3Com got the byte order backwards. */
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(id_read_eeprom(i));
-#ifdef CONFIG_PNP
- if (!nopnp) {
- /* The ISA PnP 3c509 cards respond to the ID sequence too.
- This check is needed in order not to register them twice. */
- for (i = 0; i < el3_cards; i++) {
- struct el3_private *lp = netdev_priv(el3_devs[i]);
- if (lp->type == EL3_PNP &&
- ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) {
- if (el3_debug > 3)
- pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
- phys_addr[0] & 0xff, phys_addr[0] >> 8,
- phys_addr[1] & 0xff, phys_addr[1] >> 8,
- phys_addr[2] & 0xff, phys_addr[2] >> 8);
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
- return 2;
- }
- }
- }
-#endif /* CONFIG_PNP */
- return 0;
-
-}
-
-static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
- int irq, int if_port, enum el3_cardtype type)
-{
- struct el3_private *lp = netdev_priv(dev);
-
- eth_hw_addr_set(dev, (u8 *)phys_addr);
- dev->base_addr = ioaddr;
- dev->irq = irq;
- dev->if_port = if_port;
- lp->type = type;
-}
-
-static int el3_isa_match(struct device *pdev, unsigned int ndev)
-{
- struct net_device *dev;
- int ioaddr, isa_irq, if_port, err;
- unsigned int iobase;
- __be16 phys_addr[3];
-
- while ((err = el3_isa_id_sequence(phys_addr)) == 2)
- ; /* Skip to next card when PnP card found */
- if (err == 1)
- return 0;
-
- iobase = id_read_eeprom(8);
- if_port = iobase >> 14;
- ioaddr = 0x200 + ((iobase & 0x1f) << 4);
- if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
- isa_irq = irq[el3_cards];
- else
- isa_irq = id_read_eeprom(9) >> 12;
-
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev)
- return -ENOMEM;
-
- SET_NETDEV_DEV(dev, pdev);
-
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
- free_netdev(dev);
- return 0;
- }
-
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
-
- /* Activate the adaptor at the EEPROM location. */
- outb((ioaddr >> 4) | 0xe0, id_port);
-
- EL3WINDOW(0);
- if (inw(ioaddr) != 0x6d50) {
- free_netdev(dev);
- return 0;
- }
-
- /* Free the interrupt so that some other card can use it. */
- outw(0x0f00, ioaddr + WN0_IRQ);
-
- el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
- dev_set_drvdata(pdev, dev);
- if (el3_common_init(dev)) {
- free_netdev(dev);
- return 0;
- }
-
- el3_devs[el3_cards++] = dev;
- return 1;
-}
-
-static void el3_isa_remove(struct device *pdev,
- unsigned int ndev)
-{
- el3_device_remove(pdev);
- dev_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_isa_suspend(struct device *dev, unsigned int n,
- pm_message_t state)
-{
- current_tag = 0;
- return el3_suspend(dev, state);
-}
-
-static int el3_isa_resume(struct device *dev, unsigned int n)
-{
- struct net_device *ndev = dev_get_drvdata(dev);
- int ioaddr = ndev->base_addr, err;
- __be16 phys_addr[3];
-
- while ((err = el3_isa_id_sequence(phys_addr)) == 2)
- ; /* Skip to next card when PnP card found */
- if (err == 1)
- return 0;
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
- /* Enable the card */
- outb((ioaddr >> 4) | 0xe0, id_port);
- EL3WINDOW(0);
- if (inw(ioaddr) != 0x6d50)
- return 1;
- /* Free the interrupt so that some other card can use it. */
- outw(0x0f00, ioaddr + WN0_IRQ);
- return el3_resume(dev);
-}
-#endif
-
-static struct isa_driver el3_isa_driver = {
- .match = el3_isa_match,
- .remove = el3_isa_remove,
-#ifdef CONFIG_PM
- .suspend = el3_isa_suspend,
- .resume = el3_isa_resume,
-#endif
- .driver = {
- .name = "3c509"
- },
-};
-static int isa_registered;
-
-#ifdef CONFIG_PNP
-static const struct pnp_device_id el3_pnp_ids[] = {
- { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
- { .id = "TCM5091" }, /* 3Com Etherlink III */
- { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
- { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
- { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
- { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
- { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
- { .id = "" }
-};
-MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
-
-static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
-{
- short i;
- int ioaddr, irq, if_port;
- __be16 phys_addr[3];
- struct net_device *dev = NULL;
- int err;
-
- ioaddr = pnp_port_start(pdev, 0);
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
- return -EBUSY;
- irq = pnp_irq(pdev, 0);
- EL3WINDOW(0);
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(read_eeprom(ioaddr, i));
- if_port = read_eeprom(ioaddr, 8) >> 14;
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev) {
- release_region(ioaddr, EL3_IO_EXTENT);
- return -ENOMEM;
- }
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
- pnp_set_drvdata(pdev, dev);
- err = el3_common_init(dev);
-
- if (err) {
- pnp_set_drvdata(pdev, NULL);
- free_netdev(dev);
- return err;
- }
-
- el3_devs[el3_cards++] = dev;
- return 0;
-}
-
-static void el3_pnp_remove(struct pnp_dev *pdev)
-{
- el3_common_remove(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
-{
- return el3_suspend(&pdev->dev, state);
-}
-
-static int el3_pnp_resume(struct pnp_dev *pdev)
-{
- return el3_resume(&pdev->dev);
-}
-#endif
-
-static struct pnp_driver el3_pnp_driver = {
- .name = "3c509",
- .id_table = el3_pnp_ids,
- .probe = el3_pnp_probe,
- .remove = el3_pnp_remove,
-#ifdef CONFIG_PM
- .suspend = el3_pnp_suspend,
- .resume = el3_pnp_resume,
-#endif
-};
-static int pnp_registered;
-#endif /* CONFIG_PNP */
-
-#ifdef CONFIG_EISA
-static const struct eisa_device_id el3_eisa_ids[] = {
- { "TCM5090" },
- { "TCM5091" },
- { "TCM5092" },
- { "TCM5093" },
- { "TCM5094" },
- { "TCM5095" },
- { "TCM5098" },
- { "" }
-};
-MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
-
-static int el3_eisa_probe (struct device *device);
-
-static struct eisa_driver el3_eisa_driver = {
- .id_table = el3_eisa_ids,
- .driver = {
- .name = "3c579",
- .probe = el3_eisa_probe,
- .remove = el3_device_remove,
- .suspend = el3_suspend,
- .resume = el3_resume,
- }
-};
-static int eisa_registered;
-#endif
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = el3_open,
- .ndo_stop = el3_close,
- .ndo_start_xmit = el3_start_xmit,
- .ndo_get_stats = el3_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_tx_timeout = el3_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = el3_poll_controller,
-#endif
-};
-
-static int el3_common_init(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int err;
- static const char * const if_names[] = {
- "10baseT", "AUI", "undefined", "BNC"
- };
-
- spin_lock_init(&lp->lock);
-
- if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
- dev->if_port = (dev->mem_start & 0x0f);
- } else { /* xcvr codes 0/8 */
- /* use eeprom value, but save user's full-duplex selection */
- dev->if_port |= (dev->mem_start & 0x08);
- }
-
- /* The EL3-specific entries in the device structure. */
- dev->netdev_ops = &netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
- dev->ethtool_ops = ðtool_ops;
-
- err = register_netdev(dev);
- if (err) {
- pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
- dev->base_addr, dev->irq);
- release_region(dev->base_addr, EL3_IO_EXTENT);
- return err;
- }
-
- pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
- dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
- dev->dev_addr, dev->irq);
-
- return 0;
-
-}
-
-static void el3_common_remove (struct net_device *dev)
-{
- unregister_netdev (dev);
- release_region(dev->base_addr, EL3_IO_EXTENT);
- free_netdev (dev);
-}
-
-#ifdef CONFIG_EISA
-static int el3_eisa_probe(struct device *device)
-{
- short i;
- int ioaddr, irq, if_port;
- __be16 phys_addr[3];
- struct net_device *dev = NULL;
- struct eisa_device *edev;
- int err;
-
- /* Yeepee, The driver framework is calling us ! */
- edev = to_eisa_device (device);
- ioaddr = edev->base_addr;
-
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
- return -EBUSY;
-
- /* Change the register set to the configuration window 0. */
- outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
-
- irq = inw(ioaddr + WN0_IRQ) >> 12;
- if_port = inw(ioaddr + 6)>>14;
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(read_eeprom(ioaddr, i));
-
- /* Restore the "Product ID" to the EEPROM read register. */
- read_eeprom(ioaddr, 3);
-
- dev = alloc_etherdev(sizeof (struct el3_private));
- if (dev == NULL) {
- release_region(ioaddr, EL3_IO_EXTENT);
- return -ENOMEM;
- }
-
- SET_NETDEV_DEV(dev, device);
-
- el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
- eisa_set_drvdata (edev, dev);
- err = el3_common_init(dev);
-
- if (err) {
- eisa_set_drvdata (edev, NULL);
- free_netdev(dev);
- return err;
- }
-
- el3_devs[el3_cards++] = dev;
- return 0;
-}
-#endif
-
-/* This remove works for all device types.
- *
- * The net dev must be stored in the driver data field */
-static int el3_device_remove(struct device *device)
-{
- struct net_device *dev;
-
- dev = dev_get_drvdata(device);
-
- el3_common_remove (dev);
- return 0;
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
- Assume that we are in register window zero.
- */
-static ushort read_eeprom(int ioaddr, int index)
-{
- outw(EEPROM_READ + index, ioaddr + 10);
- /* Pause for at least 162 us. for the read to take place.
- Some chips seem to require much longer */
- mdelay(2);
- return inw(ioaddr + 12);
-}
-
-/* Read a word from the EEPROM when in the ISA ID probe state. */
-static ushort id_read_eeprom(int index)
-{
- int bit, word = 0;
-
- /* Issue read command, and pause for at least 162 us. for it to complete.
- Assume extra-fast 16Mhz bus. */
- outb(EEPROM_READ + index, id_port);
-
- /* Pause for at least 162 us. for the read to take place. */
- /* Some chips seem to require much longer */
- mdelay(4);
-
- for (bit = 15; bit >= 0; bit--)
- word = (word << 1) + (inb(id_port) & 0x01);
-
- if (el3_debug > 3)
- pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
-
- return word;
-}
-
-
-static int
-el3_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int i;
-
- outw(TxReset, ioaddr + EL3_CMD);
- outw(RxReset, ioaddr + EL3_CMD);
- outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
- i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
- if (i)
- return i;
-
- EL3WINDOW(0);
- if (el3_debug > 3)
- pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
- dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
-
- el3_up(dev);
-
- if (el3_debug > 3)
- pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n",
- dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
-
- return 0;
-}
-
-static void
-el3_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
- int ioaddr = dev->base_addr;
-
- /* Transmitter timeout, serious problems. */
- pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
- dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
- inw(ioaddr + TX_FREE));
- dev->stats.tx_errors++;
- netif_trans_update(dev); /* prevent tx timeout */
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
-}
-
-
-static netdev_tx_t
-el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- unsigned long flags;
-
- netif_stop_queue (dev);
-
- dev->stats.tx_bytes += skb->len;
-
- if (el3_debug > 4) {
- pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
- dev->name, skb->len, inw(ioaddr + EL3_STATUS));
- }
- /*
- * We lock the driver against other processors. Note
- * we don't need to lock versus the IRQ as we suspended
- * that. This means that we lose the ability to take
- * an RX during a TX upload. That sucks a bit with SMP
- * on an original 3c509 (2K buffer)
- *
- * Using disable_irq stops us crapping on other
- * time sensitive devices.
- */
-
- spin_lock_irqsave(&lp->lock, flags);
-
- /* Put out the doubleword header... */
- outw(skb->len, ioaddr + TX_FIFO);
- outw(0x00, ioaddr + TX_FIFO);
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-
- if (inw(ioaddr + TX_FREE) > 1536)
- netif_start_queue(dev);
- else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
-
- spin_unlock_irqrestore(&lp->lock, flags);
-
- dev_consume_skb_any (skb);
-
- /* Clear the Tx status stack. */
- {
- short tx_status;
- int i = 4;
-
- while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
- if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
- if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
- }
- return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t
-el3_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct el3_private *lp;
- int ioaddr, status;
- int i = max_interrupt_work;
-
- lp = netdev_priv(dev);
- spin_lock(&lp->lock);
-
- ioaddr = dev->base_addr;
-
- if (el3_debug > 4) {
- status = inw(ioaddr + EL3_STATUS);
- pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
- }
-
- while ((status = inw(ioaddr + EL3_STATUS)) &
- (IntLatch | RxComplete | StatsFull)) {
-
- if (status & RxComplete)
- el3_rx(dev);
-
- if (status & TxAvailable) {
- if (el3_debug > 5)
- pr_debug(" TX room bit was handled.\n");
- /* There's room in the FIFO for a full-sized packet. */
- outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- netif_wake_queue (dev);
- }
- if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
- /* Handle all uncommon interrupts. */
- if (status & StatsFull) /* Empty statistics. */
- update_stats(dev);
- if (status & RxEarly) { /* Rx early is unused. */
- el3_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & TxComplete) { /* Really Tx error. */
- short tx_status;
- int i = 4;
-
- while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
- if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
- if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
- }
- if (status & AdapterFailure) {
- /* Adapter failure requires Rx reset and reinit. */
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- outw(SetRxFilter | RxStation | RxBroadcast
- | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
- | (dev->flags & IFF_PROMISC ? RxProm : 0),
- ioaddr + EL3_CMD);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
- }
-
- if (--i < 0) {
- pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
- dev->name, status);
- /* Clear all interrupts. */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
- }
-
- if (el3_debug > 4) {
- pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
- inw(ioaddr + EL3_STATUS));
- }
- spin_unlock(&lp->lock);
- return IRQ_HANDLED;
-}
-
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void el3_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- el3_interrupt(dev->irq, dev);
- enable_irq(dev->irq);
-}
-#endif
-
-static struct net_device_stats *
-el3_get_stats(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
-
- /*
- * This is fast enough not to bother with disable IRQ
- * stuff.
- */
-
- spin_lock_irqsave(&lp->lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->lock, flags);
- return &dev->stats;
-}
-
-/* Update statistics. We change to register window 6, so this should be run
- single-threaded if the device is active. This is expected to be a rare
- operation, and it's simpler for the rest of the driver to assume that
- window 1 is always valid rather than use a special window-state variable.
- */
-static void update_stats(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- if (el3_debug > 5)
- pr_debug(" Updating the statistics.\n");
- /* Turn off statistics updates while reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- /* Switch to the stats window, and read everything. */
- EL3WINDOW(6);
- dev->stats.tx_carrier_errors += inb(ioaddr + 0);
- dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
- /* Multiple collisions. */ inb(ioaddr + 2);
- dev->stats.collisions += inb(ioaddr + 3);
- dev->stats.tx_window_errors += inb(ioaddr + 4);
- dev->stats.rx_fifo_errors += inb(ioaddr + 5);
- dev->stats.tx_packets += inb(ioaddr + 6);
- /* Rx packets */ inb(ioaddr + 7);
- /* Tx deferrals */ inb(ioaddr + 8);
- inw(ioaddr + 10); /* Total Rx and Tx octets. */
- inw(ioaddr + 12);
-
- /* Back to window 1, and turn statistics back on. */
- EL3WINDOW(1);
- outw(StatsEnable, ioaddr + EL3_CMD);
-}
-
-static int
-el3_rx(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- short rx_status;
-
- if (el3_debug > 5)
- pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
- while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
- if (rx_status & 0x4000) { /* Error, update stats. */
- short error = rx_status & 0x3800;
-
- outw(RxDiscard, ioaddr + EL3_CMD);
- dev->stats.rx_errors++;
- switch (error) {
- case 0x0000: dev->stats.rx_over_errors++; break;
- case 0x0800: dev->stats.rx_length_errors++; break;
- case 0x1000: dev->stats.rx_frame_errors++; break;
- case 0x1800: dev->stats.rx_length_errors++; break;
- case 0x2000: dev->stats.rx_frame_errors++; break;
- case 0x2800: dev->stats.rx_crc_errors++; break;
- }
- } else {
- short pkt_len = rx_status & 0x7ff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5);
- if (el3_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte */
-
- /* 'skb->data' points to the start of sk_buff data area. */
- insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
- (pkt_len + 3) >> 2);
-
- outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
- skb->protocol = eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_bytes += pkt_len;
- dev->stats.rx_packets++;
- continue;
- }
- outw(RxDiscard, ioaddr + EL3_CMD);
- dev->stats.rx_dropped++;
- if (el3_debug)
- pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, pkt_len);
- }
- inw(ioaddr + EL3_STATUS); /* Delay. */
- while (inw(ioaddr + EL3_STATUS) & 0x1000)
- pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
- inw(ioaddr + EL3_STATUS) );
- }
-
- return 0;
-}
-
-/*
- * Set or clear the multicast filter for this adaptor.
- */
-static void
-set_multicast_list(struct net_device *dev)
-{
- unsigned long flags;
- struct el3_private *lp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- int mc_count = netdev_mc_count(dev);
-
- if (el3_debug > 1) {
- static int old;
- if (old != mc_count) {
- old = mc_count;
- pr_debug("%s: Setting Rx mode to %d addresses.\n",
- dev->name, mc_count);
- }
- }
- spin_lock_irqsave(&lp->lock, flags);
- if (dev->flags&IFF_PROMISC) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
- ioaddr + EL3_CMD);
- }
- else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
- }
- else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int
-el3_close(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct el3_private *lp = netdev_priv(dev);
-
- if (el3_debug > 2)
- pr_debug("%s: Shutting down ethercard.\n", dev->name);
-
- el3_down(dev);
-
- free_irq(dev->irq, dev);
- /* Switching back to window 0 disables the IRQ. */
- EL3WINDOW(0);
- if (lp->type != EL3_EISA) {
- /* But we explicitly zero the IRQ line select anyway. Don't do
- * it on EISA cards, it prevents the module from getting an
- * IRQ after unload+reload... */
- outw(0x0f00, ioaddr + WN0_IRQ);
- }
-
- return 0;
-}
-
-static int
-el3_link_ok(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- u16 tmp;
-
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_MEDIA);
- EL3WINDOW(1);
- return tmp & (1<<11);
-}
-
-static void
-el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd)
-{
- u16 tmp;
- int ioaddr = dev->base_addr;
- u32 supported;
-
- EL3WINDOW(0);
- /* obtain current transceiver via WN4_MEDIA? */
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- switch (tmp >> 14) {
- case 0:
- cmd->base.port = PORT_TP;
- break;
- case 1:
- cmd->base.port = PORT_AUI;
- break;
- case 3:
- cmd->base.port = PORT_BNC;
- break;
- default:
- break;
- }
-
- cmd->base.duplex = DUPLEX_HALF;
- supported = 0;
- tmp = inw(ioaddr + WN0_CONF_CTRL);
- if (tmp & (1<<13))
- supported |= SUPPORTED_AUI;
- if (tmp & (1<<12))
- supported |= SUPPORTED_BNC;
- if (tmp & (1<<9)) {
- supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full; /* hmm... */
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_NETDIAG);
- if (tmp & FD_ENABLE)
- cmd->base.duplex = DUPLEX_FULL;
- }
-
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
- supported);
- cmd->base.speed = SPEED_10;
- EL3WINDOW(1);
-}
-
-static int
-el3_netdev_set_ecmd(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- u16 tmp;
- int ioaddr = dev->base_addr;
-
- if (cmd->base.speed != SPEED_10)
- return -EINVAL;
- if ((cmd->base.duplex != DUPLEX_HALF) &&
- (cmd->base.duplex != DUPLEX_FULL))
- return -EINVAL;
-
- /* change XCVR type */
- EL3WINDOW(0);
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- switch (cmd->base.port) {
- case PORT_TP:
- tmp &= ~(3<<14);
- dev->if_port = 0;
- break;
- case PORT_AUI:
- tmp |= (1<<14);
- dev->if_port = 1;
- break;
- case PORT_BNC:
- tmp |= (3<<14);
- dev->if_port = 3;
- break;
- default:
- return -EINVAL;
- }
-
- outw(tmp, ioaddr + WN0_ADDR_CONF);
- if (dev->if_port == 3) {
- /* fire up the DC-DC convertor if BNC gets enabled */
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- if (tmp & (3 << 14)) {
- outw(StartCoax, ioaddr + EL3_CMD);
- udelay(800);
- } else
- return -EIO;
- }
-
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_NETDIAG);
- if (cmd->base.duplex == DUPLEX_FULL)
- tmp |= FD_ENABLE;
- else
- tmp &= ~FD_ENABLE;
- outw(tmp, ioaddr + WN4_NETDIAG);
- EL3WINDOW(1);
-
- return 0;
-}
-
-static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-}
-
-static int el3_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
-
- spin_lock_irq(&lp->lock);
- el3_netdev_get_ecmd(dev, cmd);
- spin_unlock_irq(&lp->lock);
- return 0;
-}
-
-static int el3_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
- int ret;
-
- spin_lock_irq(&lp->lock);
- ret = el3_netdev_set_ecmd(dev, cmd);
- spin_unlock_irq(&lp->lock);
- return ret;
-}
-
-static u32 el3_get_link(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- u32 ret;
-
- spin_lock_irq(&lp->lock);
- ret = el3_link_ok(dev);
- spin_unlock_irq(&lp->lock);
- return ret;
-}
-
-static u32 el3_get_msglevel(struct net_device *dev)
-{
- return el3_debug;
-}
-
-static void el3_set_msglevel(struct net_device *dev, u32 v)
-{
- el3_debug = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .get_drvinfo = el3_get_drvinfo,
- .get_link = el3_get_link,
- .get_msglevel = el3_get_msglevel,
- .set_msglevel = el3_set_msglevel,
- .get_link_ksettings = el3_get_link_ksettings,
- .set_link_ksettings = el3_set_link_ksettings,
-};
-
-static void
-el3_down(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- netif_stop_queue(dev);
-
- /* Turn off statistics ASAP. We update lp->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
-
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- if (dev->if_port == 3)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
- /* Disable link beat and jabber, if_port may change here next open(). */
- EL3WINDOW(4);
- outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
- }
-
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
- update_stats(dev);
-}
-
-static void
-el3_up(struct net_device *dev)
-{
- int i, sw_info, net_diag;
- int ioaddr = dev->base_addr;
-
- /* Activating the board required and does no harm otherwise */
- outw(0x0001, ioaddr + 4);
-
- /* Set the IRQ line. */
- outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
-
- /* Set the station address in window 2 each time opened. */
- EL3WINDOW(2);
-
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
-
- if ((dev->if_port & 0x03) == 3) /* BNC interface */
- /* Start the thinnet transceiver. We should really wait 50ms...*/
- outw(StartCoax, ioaddr + EL3_CMD);
- else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
- /* Combine secondary sw_info word (the adapter level) and primary
- sw_info word (duplex setting plus other useless bits) */
- EL3WINDOW(0);
- sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
- (read_eeprom(ioaddr, 0x0d) & 0xBff0);
-
- EL3WINDOW(4);
- net_diag = inw(ioaddr + WN4_NETDIAG);
- net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
- pr_info("%s: ", dev->name);
- switch (dev->if_port & 0x0c) {
- case 12:
- /* force full-duplex mode if 3c5x9b */
- if (sw_info & 0x000f) {
- pr_cont("Forcing 3c5x9b full-duplex mode");
- break;
- }
- fallthrough;
- case 8:
- /* set full-duplex mode based on eeprom config setting */
- if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
- pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
- break;
- }
- fallthrough;
- default:
- /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
- pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
- net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
- }
-
- outw(net_diag, ioaddr + WN4_NETDIAG);
- pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
- if (el3_debug > 3)
- pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
- /* Enable link beat and jabber check. */
- outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
- }
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 9; i++)
- inb(ioaddr + i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
-
- /* Switch to register set 1 for normal use. */
- EL3WINDOW(1);
-
- /* Accept b-case and phys addr only. */
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
- /* Ack all pending events, and set active indicator mask. */
- outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
- ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
- ioaddr + EL3_CMD);
-
- netif_start_queue(dev);
-}
-
-/* Power Management support functions */
-#ifdef CONFIG_PM
-
-static int
-el3_suspend(struct device *pdev, pm_message_t state)
-{
- unsigned long flags;
- struct net_device *dev;
- struct el3_private *lp;
- int ioaddr;
-
- dev = dev_get_drvdata(pdev);
- lp = netdev_priv(dev);
- ioaddr = dev->base_addr;
-
- spin_lock_irqsave(&lp->lock, flags);
-
- if (netif_running(dev))
- netif_device_detach(dev);
-
- el3_down(dev);
- outw(PowerDown, ioaddr + EL3_CMD);
-
- spin_unlock_irqrestore(&lp->lock, flags);
- return 0;
-}
-
-static int
-el3_resume(struct device *pdev)
-{
- unsigned long flags;
- struct net_device *dev;
- struct el3_private *lp;
- int ioaddr;
-
- dev = dev_get_drvdata(pdev);
- lp = netdev_priv(dev);
- ioaddr = dev->base_addr;
-
- spin_lock_irqsave(&lp->lock, flags);
-
- outw(PowerUp, ioaddr + EL3_CMD);
- EL3WINDOW(0);
- el3_up(dev);
-
- if (netif_running(dev))
- netif_device_attach(dev);
-
- spin_unlock_irqrestore(&lp->lock, flags);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-module_param(debug,int, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-#ifdef CONFIG_PNP
-module_param(nopnp, int, 0);
-MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
-#endif /* CONFIG_PNP */
-MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
-MODULE_LICENSE("GPL");
-
-static int __init el3_init_module(void)
-{
- int ret = 0;
-
- if (debug >= 0)
- el3_debug = debug;
-
-#ifdef CONFIG_PNP
- if (!nopnp) {
- ret = pnp_register_driver(&el3_pnp_driver);
- if (!ret)
- pnp_registered = 1;
- }
-#endif
- /* Select an open I/O location at 0x1*0 to do ISA contention select. */
- /* Start with 0x110 to avoid some sound cards.*/
- for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
- if (!request_region(id_port, 1, "3c509-control"))
- continue;
- outb(0x00, id_port);
- outb(0xff, id_port);
- if (inb(id_port) & 0x01)
- break;
- else
- release_region(id_port, 1);
- }
- if (id_port >= 0x200) {
- id_port = 0;
- pr_err("No I/O port available for 3c509 activation.\n");
- } else {
- ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
- if (!ret)
- isa_registered = 1;
- }
-#ifdef CONFIG_EISA
- ret = eisa_driver_register(&el3_eisa_driver);
- if (!ret)
- eisa_registered = 1;
-#endif
-
-#ifdef CONFIG_PNP
- if (pnp_registered)
- ret = 0;
-#endif
- if (isa_registered)
- ret = 0;
-#ifdef CONFIG_EISA
- if (eisa_registered)
- ret = 0;
-#endif
- return ret;
-}
-
-static void __exit el3_cleanup_module(void)
-{
-#ifdef CONFIG_PNP
- if (pnp_registered)
- pnp_unregister_driver(&el3_pnp_driver);
-#endif
- if (isa_registered)
- isa_unregister_driver(&el3_isa_driver);
- if (id_port)
- release_region(id_port, 1);
-#ifdef CONFIG_EISA
- if (eisa_registered)
- eisa_driver_unregister(&el3_eisa_driver);
-#endif
-}
-
-module_init (el3_init_module);
-module_exit (el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 1fbab79e2be4..c05a1b63c1c9 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,20 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-config EL3
- tristate "3c509/3c579 \"EtherLink III\" support"
- depends on (ISA || EISA)
- help
- If you have a network (Ethernet) card belonging to the 3Com
- EtherLinkIII series, say Y here.
-
- If your card is not working you may need to use the DOS
- setup disk to disable Plug & Play mode, and to select the default
- media type.
-
- To compile this driver as a module, choose M here. The module
- will be called 3c509.
-
config 3C515
tristate "3c515 ISA \"Fast EtherLink\""
depends on ISA && ISA_DMA_API && !PPC32
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f8b73babc510..f7623fa2d441 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
# Makefile for the 3Com Ethernet device drivers
#
-obj-$(CONFIG_EL3) += 3c509.o
obj-$(CONFIG_3C515) += 3c515.o
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
These old drivers have not been much of a Maintenance burden until
recently. Now there are more newbies using AI and fuzzers finding
issues, resulting in more work for Maintainers. Fixing these old
drivers make little sense, if it is not clear they have users.
These mostly ISA and PCMCIA Ethernet devices, mostly from the last
century, a couple from 2001 or 2002. It seems unlikely they are still
used. However, remove them one patch at a time so they can be brought
back if somebody still has the hardware, runs modern kernels and wants
to take up the roll of driver Maintainer.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
Changes in v2:
- Drop removal of 3c59x and xirc2ps, MVME147
- Remove some documents which belong to the drivers being removed.
- Link to v1: https://lore.kernel.org/r/20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch
---
Andrew Lunn (15):
drivers: net: 3com: 3c509: Remove this driver
drivers: net: 3com: 3c515: Remove this driver
drivers: net: 3com: 3c574: Remove this driver
drivers: net: 3com: 3c589: Remove this driver
drivers: net: amd: lance: Remove this driver
drivers: net: amd: nmclan: Remove this driver
drivers: net: smsc: smc9194: Remove this driver
drivers: net: smsc: smc91c92: Remove this driver
drivers: net: cirrus: cs89x0: Remove this driver
drivers: net: cirrus: mac89x0: Remove this driver
drivers: net: fujitsu: fmvj18x: Remove this driver
drivers: net: 8390: AX88190: Remove this driver
drivers: net: 8390: pcnet: Remove this driver
drivers: net: 8390: ultra: Remove this driver
drivers: net: 8390: wd80x3: Remove this driver
.../device_drivers/ethernet/3com/3c509.rst | 249 ---
.../device_drivers/ethernet/cirrus/cs89x0.rst | 647 ------
.../device_drivers/ethernet/smsc/smc9.rst | 48 -
drivers/net/ethernet/3com/3c509.c | 1448 --------------
drivers/net/ethernet/3com/3c515.c | 1566 ---------------
drivers/net/ethernet/3com/3c574_cs.c | 1164 -----------
drivers/net/ethernet/3com/3c589_cs.c | 974 ---------
drivers/net/ethernet/3com/Kconfig | 45 -
drivers/net/ethernet/3com/Makefile | 4 -
drivers/net/ethernet/8390/Kconfig | 52 -
drivers/net/ethernet/8390/Makefile | 4 -
drivers/net/ethernet/8390/axnet_cs.c | 1707 ----------------
drivers/net/ethernet/8390/pcnet_cs.c | 1717 ----------------
drivers/net/ethernet/8390/smc-ultra.c | 630 ------
drivers/net/ethernet/8390/wd.c | 575 ------
drivers/net/ethernet/Kconfig | 1 -
drivers/net/ethernet/amd/Kconfig | 21 -
drivers/net/ethernet/amd/Makefile | 2 -
drivers/net/ethernet/amd/lance.c | 1317 -------------
drivers/net/ethernet/amd/nmclan_cs.c | 1508 --------------
drivers/net/ethernet/cirrus/Kconfig | 40 -
drivers/net/ethernet/cirrus/Makefile | 2 -
drivers/net/ethernet/cirrus/cs89x0.c | 1915 ------------------
drivers/net/ethernet/cirrus/cs89x0.h | 461 -----
drivers/net/ethernet/cirrus/mac89x0.c | 577 ------
drivers/net/ethernet/fujitsu/Kconfig | 30 -
drivers/net/ethernet/fujitsu/Makefile | 6 -
drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 1176 -----------
drivers/net/ethernet/smsc/Kconfig | 27 -
drivers/net/ethernet/smsc/Makefile | 2 -
drivers/net/ethernet/smsc/smc9194.c | 1535 ---------------
drivers/net/ethernet/smsc/smc91c92_cs.c | 2059 --------------------
32 files changed, 21509 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260421-v7-0-0-net-next-driver-removal-v1-47c88c987b34
Best regards,
--
Andrew Lunn <andrew@lunn.ch>
^ permalink raw reply
* [PATCH net v2 04/15] drivers: net: 3com: 3c589: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The 3c589 was written by David A. Hinds 2001. It is an PCMCIA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c589_cs.c | 974 -----------------------------------
drivers/net/ethernet/3com/Kconfig | 10 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 985 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
deleted file mode 100644
index ea49be43b8c3..000000000000
--- a/drivers/net/ethernet/3com/3c589_cs.c
+++ /dev/null
@@ -1,974 +0,0 @@
-/* ======================================================================
- *
- * A PCMCIA ethernet driver for the 3com 3c589 card.
- *
- * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
- *
- * 3c589_cs.c 1.162 2001/10/13 00:08:50
- *
- * The network driver code is based on Donald Becker's 3c589 code:
- *
- * Written 1994 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency. This software may be used and
- * distributed according to the terms of the GNU General Public License,
- * incorporated herein by reference.
- * Donald Becker may be reached at becker@scyld.com
- *
- * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- * ======================================================================
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME "3c589_cs"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-#include <linux/jiffies.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-
-/* To minimize the size of the driver source I only define operating
- * constants if they are used several times. You'll need the manual
- * if you want to understand driver details.
- */
-
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_TIMER 0x0a
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-#define EEPROM_READ 0x0080
-#define EEPROM_BUSY 0x8000
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-/* The top five bits written to EL3_CMD are a command, the lower
- * 11 bits are the parameter, if applicable.
- */
-
-enum c509cmd {
- TotalReset = 0<<11,
- SelectWindow = 1<<11,
- StartCoax = 2<<11,
- RxDisable = 3<<11,
- RxEnable = 4<<11,
- RxReset = 5<<11,
- RxDiscard = 8<<11,
- TxEnable = 9<<11,
- TxDisable = 10<<11,
- TxReset = 11<<11,
- FakeIntr = 12<<11,
- AckIntr = 13<<11,
- SetIntrEnb = 14<<11,
- SetStatusEnb = 15<<11,
- SetRxFilter = 16<<11,
- SetRxThreshold = 17<<11,
- SetTxThreshold = 18<<11,
- SetTxStart = 19<<11,
- StatsEnable = 21<<11,
- StatsDisable = 22<<11,
- StopCoax = 23<<11
-};
-
-enum c509status {
- IntLatch = 0x0001,
- AdapterFailure = 0x0002,
- TxComplete = 0x0004,
- TxAvailable = 0x0008,
- RxComplete = 0x0010,
- RxEarly = 0x0020,
- IntReq = 0x0040,
- StatsFull = 0x0080,
- CmdBusy = 0x1000
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
- RxStation = 1,
- RxMulticast = 2,
- RxBroadcast = 4,
- RxProm = 8
-};
-
-/* Register window 1 offsets, the window used in normal operation. */
-#define TX_FIFO 0x00
-#define RX_FIFO 0x00
-#define RX_STATUS 0x08
-#define TX_STATUS 0x0B
-#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
-
-#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
-#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
-#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
-#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */
-
-/* Time in jiffies before concluding Tx hung */
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-struct el3_private {
- struct pcmcia_device *p_dev;
- /* For transceiver monitoring */
- struct timer_list media;
- u16 media_status;
- u16 fast_poll;
- unsigned long last_irq;
- spinlock_t lock;
-};
-
-static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* Special hook for setting if_port when module is loaded */
-INT_MODULE_PARM(if_port, 0);
-
-
-/*====================================================================*/
-
-static int tc589_config(struct pcmcia_device *link);
-static void tc589_release(struct pcmcia_device *link);
-
-static u16 read_eeprom(unsigned int ioaddr, int index);
-static void tc589_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static int el3_config(struct net_device *dev, struct ifmap *map);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev);
-static int el3_close(struct net_device *dev);
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static void set_rx_mode(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-static void tc589_detach(struct pcmcia_device *p_dev);
-
-static const struct net_device_ops el3_netdev_ops = {
- .ndo_open = el3_open,
- .ndo_stop = el3_close,
- .ndo_start_xmit = el3_start_xmit,
- .ndo_tx_timeout = el3_tx_timeout,
- .ndo_set_config = el3_config,
- .ndo_get_stats = el3_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int tc589_probe(struct pcmcia_device *link)
-{
- struct el3_private *lp;
- struct net_device *dev;
- int ret;
-
- dev_dbg(&link->dev, "3c589_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- link->priv = dev;
- lp->p_dev = link;
-
- spin_lock_init(&lp->lock);
- link->resource[0]->end = 16;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
-
- link->config_flags |= CONF_ENABLE_IRQ;
- link->config_index = 1;
-
- dev->netdev_ops = &el3_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- dev->ethtool_ops = &netdev_ethtool_ops;
-
- ret = tc589_config(link);
- if (ret)
- goto err_free_netdev;
-
- return 0;
-
-err_free_netdev:
- free_netdev(dev);
- return ret;
-}
-
-static void tc589_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "3c589_detach\n");
-
- unregister_netdev(dev);
-
- tc589_release(link);
-
- free_netdev(dev);
-} /* tc589_detach */
-
-static int tc589_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- int ret, i, j, multi = 0, fifo;
- __be16 addr[ETH_ALEN / 2];
- unsigned int ioaddr;
- static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
- u8 *buf;
- size_t len;
-
- dev_dbg(&link->dev, "3c589_config\n");
-
- /* Is this a 3c562? */
- if (link->manf_id != MANFID_3COM)
- dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
- multi = (link->card_id == PRODID_3COM_3C562);
-
- link->io_lines = 16;
-
- /* For the 3c562, the base address must be xx00-xx7f */
- for (i = j = 0; j < 0x400; j += 0x10) {
- if (multi && (j & 0x80))
- continue;
- link->resource[0]->start = j ^ 0x300;
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0)
- goto failed;
-
- ret = pcmcia_request_irq(link, el3_interrupt);
- if (ret)
- goto failed;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
- ioaddr = dev->base_addr;
- EL3WINDOW(0);
-
- /* The 3c589 has an extra EEPROM for configuration info, including
- * the hardware address. The 3c562 puts the address in the CIS.
- */
- len = pcmcia_get_tuple(link, 0x88, &buf);
- if (buf && len >= 6) {
- for (i = 0; i < 3; i++)
- addr[i] = htons(le16_to_cpu(buf[i*2]));
- kfree(buf);
- } else {
- kfree(buf); /* 0 < len < 6 */
- for (i = 0; i < 3; i++)
- addr[i] = htons(read_eeprom(ioaddr, i));
- if (addr[0] == htons(0x6060)) {
- dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
- dev->base_addr, dev->base_addr+15);
- goto failed;
- }
- }
- eth_hw_addr_set(dev, (u8 *)addr);
-
- /* The address and resource configuration register aren't loaded from
- * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
- */
-
- outw(0x3f00, ioaddr + 8);
- fifo = inl(ioaddr);
-
- /* The if_port symbol can be set when the module is loaded */
- if ((if_port >= 0) && (if_port <= 3))
- dev->if_port = if_port;
- else
- dev_err(&link->dev, "invalid if_port requested\n");
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- dev_err(&link->dev, "register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
- (multi ? "562" : "589"), dev->base_addr, dev->irq,
- dev->dev_addr);
- netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
- (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
- if_names[dev->if_port]);
- return 0;
-
-failed:
- tc589_release(link);
- return -ENODEV;
-} /* tc589_config */
-
-static void tc589_release(struct pcmcia_device *link)
-{
- pcmcia_disable_device(link);
-}
-
-static int tc589_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int tc589_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- tc589_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-/*====================================================================*/
-
-/* Use this for commands that may take time to finish */
-
-static void tc589_wait_for_completion(struct net_device *dev, int cmd)
-{
- int i = 100;
- outw(cmd, dev->base_addr + EL3_CMD);
- while (--i > 0)
- if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
- break;
- if (i == 0)
- netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
- * Assume that we are in register window zero.
- */
-
-static u16 read_eeprom(unsigned int ioaddr, int index)
-{
- int i;
- outw(EEPROM_READ + index, ioaddr + 10);
- /* Reading the eeprom takes 162 us */
- for (i = 1620; i >= 0; i--)
- if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
- break;
- return inw(ioaddr + 12);
-}
-
-/* Set transceiver type, perhaps to something other than what the user
- * specified in dev->if_port.
- */
-
-static void tc589_set_xcvr(struct net_device *dev, int if_port)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- EL3WINDOW(0);
- switch (if_port) {
- case 0:
- case 1:
- outw(0, ioaddr + 6);
- break;
- case 2:
- outw(3<<14, ioaddr + 6);
- break;
- case 3:
- outw(1<<14, ioaddr + 6);
- break;
- }
- /* On PCMCIA, this just turns on the LED */
- outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
- /* 10baseT interface, enable link beat and jabber check. */
- EL3WINDOW(4);
- outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
- EL3WINDOW(1);
- if (if_port == 2)
- lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
- else
- lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
-}
-
-static void dump_status(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- EL3WINDOW(1);
- netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
- inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
- inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
- EL3WINDOW(4);
- netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
- inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
- inw(ioaddr+0x0a));
- EL3WINDOW(1);
-}
-
-/* Reset and restore all of the 3c589 registers. */
-static void tc589_reset(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- EL3WINDOW(0);
- outw(0x0001, ioaddr + 4); /* Activate board. */
- outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
-
- /* Set the station address in window 2. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
-
- tc589_set_xcvr(dev, dev->if_port);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 9; i++)
- inb(ioaddr+i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
-
- /* Switch to register set 1 for normal use. */
- EL3WINDOW(1);
-
- set_rx_mode(dev);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
- /* Ack all pending events, and set active indicator mask. */
- outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
- ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
- | AdapterFailure, ioaddr + EL3_CMD);
-}
-
-static void netdev_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- snprintf(info->bus_info, sizeof(info->bus_info),
- "PCMCIA 0x%lx", dev->base_addr);
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
- .get_drvinfo = netdev_get_drvinfo,
-};
-
-static int el3_config(struct net_device *dev, struct ifmap *map)
-{
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (map->port <= 3) {
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- tc589_set_xcvr(dev, dev->if_port);
- } else {
- return -EINVAL;
- }
- }
- return 0;
-}
-
-static int el3_open(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
- netif_start_queue(dev);
-
- tc589_reset(dev);
- timer_setup(&lp->media, media_check, 0);
- mod_timer(&lp->media, jiffies + HZ);
-
- dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
- dev->name, inw(dev->base_addr + EL3_STATUS));
-
- return 0;
-}
-
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- unsigned int ioaddr = dev->base_addr;
-
- netdev_warn(dev, "Transmit timed out!\n");
- dump_status(dev);
- dev->stats.tx_errors++;
- netif_trans_update(dev); /* prevent tx timeout */
- /* Issue TX_RESET and TX_START commands. */
- tc589_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
-}
-
-static void pop_tx_status(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- /* Clear the Tx status stack. */
- for (i = 32; i > 0; i--) {
- u_char tx_status = inb(ioaddr + TX_STATUS);
- if (!(tx_status & 0x84))
- break;
- /* reset transmitter on jabber error or underrun */
- if (tx_status & 0x30)
- tc589_wait_for_completion(dev, TxReset);
- if (tx_status & 0x38) {
- netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->stats.tx_aborted_errors++;
- }
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
-}
-
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct el3_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
- (long)skb->len, inw(ioaddr + EL3_STATUS));
-
- spin_lock_irqsave(&priv->lock, flags);
-
- dev->stats.tx_bytes += skb->len;
-
- /* Put out the doubleword header... */
- outw(skb->len, ioaddr + TX_FIFO);
- outw(0x00, ioaddr + TX_FIFO);
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-
- if (inw(ioaddr + TX_FREE) <= 1536) {
- netif_stop_queue(dev);
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
- }
-
- pop_tx_status(dev);
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t el3_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr;
- __u16 status;
- int i = 0, handled = 1;
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
-
- ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
-
- spin_lock(&lp->lock);
- while ((status = inw(ioaddr + EL3_STATUS)) &
- (IntLatch | RxComplete | StatsFull)) {
- if ((status & 0xe000) != 0x2000) {
- netdev_dbg(dev, "interrupt from dead card\n");
- handled = 0;
- break;
- }
- if (status & RxComplete)
- el3_rx(dev);
- if (status & TxAvailable) {
- netdev_dbg(dev, " TX room bit was handled.\n");
- /* There's room in the FIFO for a full-sized packet. */
- outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
- }
- if (status & TxComplete)
- pop_tx_status(dev);
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts. */
- if (status & StatsFull) /* Empty statistics. */
- update_stats(dev);
- if (status & RxEarly) {
- /* Rx early is unused. */
- el3_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & AdapterFailure) {
- u16 fifo_diag;
- EL3WINDOW(4);
- fifo_diag = inw(ioaddr + 4);
- EL3WINDOW(1);
- netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
- fifo_diag);
- if (fifo_diag & 0x0400) {
- /* Tx overrun */
- tc589_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- if (fifo_diag & 0x2000) {
- /* Rx underrun */
- tc589_wait_for_completion(dev, RxReset);
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD);
- }
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
- }
- if (++i > 10) {
- netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
- status);
- /* Clear all interrupts */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
- }
- lp->last_irq = jiffies;
- spin_unlock(&lp->lock);
- netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
- inw(ioaddr + EL3_STATUS));
- return IRQ_RETVAL(handled);
-}
-
-static void media_check(struct timer_list *t)
-{
- struct el3_private *lp = timer_container_of(lp, t, media);
- struct net_device *dev = lp->p_dev->priv;
- unsigned int ioaddr = dev->base_addr;
- u16 media, errs;
- unsigned long flags;
-
- if (!netif_device_present(dev))
- goto reschedule;
-
- /* Check for pending interrupt with expired latency timer: with
- * this, we can limp along even if the interrupt is blocked
- */
- if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
- (inb(ioaddr + EL3_TIMER) == 0xff)) {
- if (!lp->fast_poll)
- netdev_warn(dev, "interrupt(s) dropped!\n");
-
- local_irq_save(flags);
- el3_interrupt(dev->irq, dev);
- local_irq_restore(flags);
-
- lp->fast_poll = HZ;
- }
- if (lp->fast_poll) {
- lp->fast_poll--;
- lp->media.expires = jiffies + HZ/100;
- add_timer(&lp->media);
- return;
- }
-
- /* lp->lock guards the EL3 window. Window should always be 1 except
- * when the lock is held
- */
-
- spin_lock_irqsave(&lp->lock, flags);
- EL3WINDOW(4);
- media = inw(ioaddr+WN4_MEDIA) & 0xc810;
-
- /* Ignore collisions unless we've had no irq's recently */
- if (time_before(jiffies, lp->last_irq + HZ)) {
- media &= ~0x0010;
- } else {
- /* Try harder to detect carrier errors */
- EL3WINDOW(6);
- outw(StatsDisable, ioaddr + EL3_CMD);
- errs = inb(ioaddr + 0);
- outw(StatsEnable, ioaddr + EL3_CMD);
- dev->stats.tx_carrier_errors += errs;
- if (errs || (lp->media_status & 0x0010))
- media |= 0x0010;
- }
-
- if (media != lp->media_status) {
- if ((media & lp->media_status & 0x8000) &&
- ((lp->media_status ^ media) & 0x0800))
- netdev_info(dev, "%s link beat\n",
- (lp->media_status & 0x0800 ? "lost" : "found"));
- else if ((media & lp->media_status & 0x4000) &&
- ((lp->media_status ^ media) & 0x0010))
- netdev_info(dev, "coax cable %s\n",
- (lp->media_status & 0x0010 ? "ok" : "problem"));
- if (dev->if_port == 0) {
- if (media & 0x8000) {
- if (media & 0x0800)
- netdev_info(dev, "flipped to 10baseT\n");
- else
- tc589_set_xcvr(dev, 2);
- } else if (media & 0x4000) {
- if (media & 0x0010)
- tc589_set_xcvr(dev, 1);
- else
- netdev_info(dev, "flipped to 10base2\n");
- }
- }
- lp->media_status = media;
- }
-
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->lock, flags);
-
-reschedule:
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
-}
-
-static struct net_device_stats *el3_get_stats(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
- struct pcmcia_device *link = lp->p_dev;
-
- if (pcmcia_dev_present(link)) {
- spin_lock_irqsave(&lp->lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->lock, flags);
- }
- return &dev->stats;
-}
-
-/* Update statistics. We change to register window 6, so this should be run
-* single-threaded if the device is active. This is expected to be a rare
-* operation, and it's simpler for the rest of the driver to assume that
-* window 1 is always valid rather than use a special window-state variable.
-*
-* Caller must hold the lock for this
-*/
-
-static void update_stats(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "updating the statistics.\n");
- /* Turn off statistics updates while reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- /* Switch to the stats window, and read everything. */
- EL3WINDOW(6);
- dev->stats.tx_carrier_errors += inb(ioaddr + 0);
- dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
- /* Multiple collisions. */
- inb(ioaddr + 2);
- dev->stats.collisions += inb(ioaddr + 3);
- dev->stats.tx_window_errors += inb(ioaddr + 4);
- dev->stats.rx_fifo_errors += inb(ioaddr + 5);
- dev->stats.tx_packets += inb(ioaddr + 6);
- /* Rx packets */
- inb(ioaddr + 7);
- /* Tx deferrals */
- inb(ioaddr + 8);
- /* Rx octets */
- inw(ioaddr + 10);
- /* Tx octets */
- inw(ioaddr + 12);
-
- /* Back to window 1, and turn statistics back on. */
- EL3WINDOW(1);
- outw(StatsEnable, ioaddr + EL3_CMD);
-}
-
-static int el3_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int worklimit = 32;
- short rx_status;
-
- netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
- while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
- worklimit > 0) {
- worklimit--;
- if (rx_status & 0x4000) { /* Error, update stats. */
- short error = rx_status & 0x3800;
- dev->stats.rx_errors++;
- switch (error) {
- case 0x0000:
- dev->stats.rx_over_errors++;
- break;
- case 0x0800:
- dev->stats.rx_length_errors++;
- break;
- case 0x1000:
- dev->stats.rx_frame_errors++;
- break;
- case 0x1800:
- dev->stats.rx_length_errors++;
- break;
- case 0x2000:
- dev->stats.rx_frame_errors++;
- break;
- case 0x2800:
- dev->stats.rx_crc_errors++;
- break;
- }
- } else {
- short pkt_len = rx_status & 0x7ff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5);
-
- netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2);
- insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
- (pkt_len+3)>>2);
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- } else {
- netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
- pkt_len);
- dev->stats.rx_dropped++;
- }
- }
- /* Pop the top of the Rx FIFO */
- tc589_wait_for_completion(dev, RxDiscard);
- }
- if (worklimit == 0)
- netdev_warn(dev, "too much work in el3_rx!\n");
- return 0;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u16 opts = SetRxFilter | RxStation | RxBroadcast;
-
- if (dev->flags & IFF_PROMISC)
- opts |= RxMulticast | RxProm;
- else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
- opts |= RxMulticast;
- outw(opts, ioaddr + EL3_CMD);
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct el3_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- set_rx_mode(dev);
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int el3_close(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
- if (pcmcia_dev_present(link)) {
- /* Turn off statistics ASAP. We update dev->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
-
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- if (dev->if_port == 2)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 1) {
- /* Disable link beat and jabber */
- EL3WINDOW(4);
- outw(0, ioaddr + WN4_MEDIA);
- }
-
- /* Switching back to window 0 disables the IRQ. */
- EL3WINDOW(0);
- /* But we explicitly zero the IRQ line select anyway. */
- outw(0x0f00, ioaddr + WN0_IRQ);
-
- /* Check if the card still exists */
- if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
- update_stats(dev);
- }
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&lp->media);
-
- return 0;
-}
-
-static const struct pcmcia_device_id tc589_ids[] = {
- PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
- PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
- PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
- PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, tc589_ids);
-
-static struct pcmcia_driver tc589_driver = {
- .owner = THIS_MODULE,
- .name = "3c589_cs",
- .probe = tc589_probe,
- .remove = tc589_detach,
- .id_table = tc589_ids,
- .suspend = tc589_suspend,
- .resume = tc589_resume,
-};
-module_pcmcia_driver(tc589_driver);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 294403ad7141..399cb6c56198 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-config PCMCIA_3C589
- tristate "3Com 3c589 PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- help
- Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
- (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called 3c589_cs. If unsure, say N.
-
config VORTEX
tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
depends on (PCI || EISA) && HAS_IOPORT_MAP
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index 45fb9af9b5c7..5c4d07f1d456 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,6 +3,5 @@
# Makefile for the 3Com Ethernet device drivers
#
-obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 03/15] drivers: net: 3com: 3c574: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The 3c574 was written by Donald Becker between 19973-1998. It is an
PCMCIA device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c574_cs.c | 1164 ----------------------------------
drivers/net/ethernet/3com/Kconfig | 10 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 1175 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
deleted file mode 100644
index 1f2070497a75..000000000000
--- a/drivers/net/ethernet/3com/3c574_cs.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
-
- Written 1993-1998 by
- Donald Becker, becker@scyld.com, (driver core) and
- David Hinds, dahinds@users.sourceforge.net (from his PC card code).
- Locking fixes (C) Copyright 2003 Red Hat Inc
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-
- This driver derives from Donald Becker's 3c509 core, which has the
- following copyright:
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
-
-
-*/
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
-Adapter.
-
-II. Board-specific settings
-
-None -- PC cards are autoconfigured.
-
-III. Driver operation
-
-The 3c574 uses a Boomerang-style interface, without the bus-master capability.
-See the Boomerang driver and documentation for most details.
-
-IV. Notes and chip documentation.
-
-Two added registers are used to enhance PIO performance, RunnerRdCtrl and
-RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the
-count of word (16 bits) reads or writes the driver is about to do to the Rx
-or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card
-translation latency by buffering the I/O operations with an 8 word FIFO.
-Note: No other chip accesses are permitted when this buffer is used.
-
-A second enhancement is that both attribute and common memory space
-0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster
-with *some* PCcard bridges) may be used instead of I/O operations.
-This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
-
-Some slow PC card bridges work better if they never see a WAIT signal.
-This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
-Only do this after testing that it is reliable and improves performance.
-
-The upper five bits of RunnerRdCtrl are used to window into PCcard
-configuration space registers. Window 0 is the regular Boomerang/Odie
-register set, 1-5 are various PC card control registers, and 16-31 are
-the (reversed!) CIS table.
-
-A final note: writing the InternalConfig register in window 3 with an
-invalid ramWidth is Very Bad.
-
-V. References
-
-http://www.scyld.com/expert/NWay.html
-http://www.national.com/opf/DP/DP83840A.html
-
-Thanks to Terry Murphy of 3Com for providing development information for
-earlier 3Com products.
-
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-#include <linux/mii.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-INT_MODULE_PARM(max_interrupt_work, 32);
-
-/* Force full duplex modes? */
-INT_MODULE_PARM(full_duplex, 0);
-
-/* Autodetect link polarity reversal? */
-INT_MODULE_PARM(auto_polarity, 1);
-
-
-/*====================================================================*/
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT ((800*HZ)/1000)
-
-/* To minimize the size of the driver source and make the driver more
- readable not all constants are symbolically defined.
- You'll need the manual if you want to understand driver details anyway. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-/* The top five bits written to EL3_CMD are a command, the lower
- 11 bits are the parameter, if applicable. */
-enum el3_cmds {
- TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
- RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
- TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
- FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
- SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
- SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
- StatsDisable = 22<<11, StopCoax = 23<<11,
-};
-
-enum elxl_status {
- IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
- TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
- IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
- RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-enum Window0 {
- Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
- IntrStatus=0x0E, /* Valid in all windows. */
-};
-/* These assumes the larger EEPROM. */
-enum Win0_EEPROM_cmds {
- EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
- EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
- EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
- On the "Odie" this window is always mapped at offsets 0x10-0x1f.
- Except for TxFree, which is overlapped by RunnerWrCtrl. */
-enum Window1 {
- TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
- RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
- TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
- RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
-};
-
-enum Window3 { /* Window 3: MAC/config bits. */
- Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
-};
-enum wn3_config {
- Ram_size = 7,
- Ram_width = 8,
- Ram_speed = 0x30,
- Rom_size = 0xc0,
- Ram_split_shift = 16,
- Ram_split = 3 << Ram_split_shift,
- Xcvr_shift = 20,
- Xcvr = 7 << Xcvr_shift,
- Autoselect = 0x1000000,
-};
-
-enum Window4 { /* Window 4: Xcvr/media bits. */
- Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
-};
-
-#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
-
-struct el3_private {
- struct pcmcia_device *p_dev;
- u16 advertising, partner; /* NWay media advertisement */
- unsigned char phys; /* MII device address */
- unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
- /* for transceiver monitoring */
- struct timer_list media;
- unsigned short media_status;
- unsigned short fast_poll;
- unsigned long last_irq;
- spinlock_t window_lock; /* Guards the Window selection */
-};
-
-/* Set iff a MII transceiver on any interface requires mdio preamble.
- This only set with the original DP83840 on older 3c905 boards, so the extra
- code size of a per-interface flag is not worthwhile. */
-static char mii_preamble_required = 0;
-
-/* Index of functions. */
-
-static int tc574_config(struct pcmcia_device *link);
-static void tc574_release(struct pcmcia_device *link);
-
-static void mdio_sync(unsigned int ioaddr, int bits);
-static int mdio_read(unsigned int ioaddr, int phy_id, int location);
-static void mdio_write(unsigned int ioaddr, int phy_id, int location,
- int value);
-static unsigned short read_eeprom(unsigned int ioaddr, int index);
-static void tc574_wait_for_completion(struct net_device *dev, int cmd);
-
-static void tc574_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev, int worklimit);
-static int el3_close(struct net_device *dev);
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-
-static void tc574_detach(struct pcmcia_device *p_dev);
-
-/*
- tc574_attach() creates an "instance" of the driver, allocating
- local data structures for one device. The device is registered
- with Card Services.
-*/
-static const struct net_device_ops el3_netdev_ops = {
- .ndo_open = el3_open,
- .ndo_stop = el3_close,
- .ndo_start_xmit = el3_start_xmit,
- .ndo_tx_timeout = el3_tx_timeout,
- .ndo_get_stats = el3_get_stats,
- .ndo_eth_ioctl = el3_ioctl,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int tc574_probe(struct pcmcia_device *link)
-{
- struct el3_private *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "3c574_attach()\n");
-
- /* Create the PC card device object. */
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- link->priv = dev;
- lp->p_dev = link;
-
- spin_lock_init(&lp->window_lock);
- link->resource[0]->end = 32;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
- link->config_flags |= CONF_ENABLE_IRQ;
- link->config_index = 1;
-
- dev->netdev_ops = &el3_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- return tc574_config(link);
-}
-
-static void tc574_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "3c574_detach()\n");
-
- unregister_netdev(dev);
-
- tc574_release(link);
-
- free_netdev(dev);
-} /* tc574_detach */
-
-static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
-
-static int tc574_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct el3_private *lp = netdev_priv(dev);
- int ret, i, j;
- __be16 addr[ETH_ALEN / 2];
- unsigned int ioaddr;
- char *cardname;
- __u32 config;
- u8 *buf;
- size_t len;
-
- dev_dbg(&link->dev, "3c574_config()\n");
-
- link->io_lines = 16;
-
- for (i = j = 0; j < 0x400; j += 0x20) {
- link->resource[0]->start = j ^ 0x300;
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0)
- goto failed;
-
- ret = pcmcia_request_irq(link, el3_interrupt);
- if (ret)
- goto failed;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- ioaddr = dev->base_addr;
-
- /* The 3c574 normally uses an EEPROM for configuration info, including
- the hardware address. The future products may include a modem chip
- and put the address in the CIS. */
-
- len = pcmcia_get_tuple(link, 0x88, &buf);
- if (buf && len >= 6) {
- for (i = 0; i < 3; i++)
- addr[i] = htons(le16_to_cpu(buf[i * 2]));
- kfree(buf);
- } else {
- kfree(buf); /* 0 < len < 6 */
- EL3WINDOW(0);
- for (i = 0; i < 3; i++)
- addr[i] = htons(read_eeprom(ioaddr, i + 10));
- if (addr[0] == htons(0x6060)) {
- pr_notice("IO port conflict at 0x%03lx-0x%03lx\n",
- dev->base_addr, dev->base_addr+15);
- goto failed;
- }
- }
- eth_hw_addr_set(dev, (u8 *)addr);
- if (link->prod_id[1])
- cardname = link->prod_id[1];
- else
- cardname = "3Com 3c574";
-
- {
- u_char mcr;
- outw(2<<11, ioaddr + RunnerRdCtrl);
- mcr = inb(ioaddr + 2);
- outw(0<<11, ioaddr + RunnerRdCtrl);
- pr_info(" ASIC rev %d,", mcr>>3);
- EL3WINDOW(3);
- config = inl(ioaddr + Wn3_Config);
- lp->default_media = (config & Xcvr) >> Xcvr_shift;
- lp->autoselect = config & Autoselect ? 1 : 0;
- }
-
- timer_setup(&lp->media, media_check, 0);
-
- {
- int phy;
-
- /* Roadrunner only: Turn on the MII transceiver */
- outw(0x8040, ioaddr + Wn3_Options);
- mdelay(1);
- outw(0xc040, ioaddr + Wn3_Options);
- tc574_wait_for_completion(dev, TxReset);
- tc574_wait_for_completion(dev, RxReset);
- mdelay(1);
- outw(0x8040, ioaddr + Wn3_Options);
-
- EL3WINDOW(4);
- for (phy = 1; phy <= 32; phy++) {
- int mii_status;
- mdio_sync(ioaddr, 32);
- mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
- if (mii_status != 0xffff) {
- lp->phys = phy & 0x1f;
- dev_dbg(&link->dev, " MII transceiver at "
- "index %d, status %x.\n",
- phy, mii_status);
- if ((mii_status & 0x0040) == 0)
- mii_preamble_required = 1;
- break;
- }
- }
- if (phy > 32) {
- pr_notice(" No MII transceivers found!\n");
- goto failed;
- }
- i = mdio_read(ioaddr, lp->phys, 16) | 0x40;
- mdio_write(ioaddr, lp->phys, 16, i);
- lp->advertising = mdio_read(ioaddr, lp->phys, 4);
- if (full_duplex) {
- /* Only advertise the FD media types. */
- lp->advertising &= ~0x02a0;
- mdio_write(ioaddr, lp->phys, 4, lp->advertising);
- }
- }
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n",
- cardname, dev->base_addr, dev->irq, dev->dev_addr);
- netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n",
- 8 << (config & Ram_size),
- ram_split[(config & Ram_split) >> Ram_split_shift],
- config & Autoselect ? "autoselect " : "");
-
- return 0;
-
-failed:
- tc574_release(link);
- return -ENODEV;
-
-} /* tc574_config */
-
-static void tc574_release(struct pcmcia_device *link)
-{
- pcmcia_disable_device(link);
-}
-
-static int tc574_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int tc574_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- tc574_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-static void dump_status(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- EL3WINDOW(1);
- netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n",
- inw(ioaddr+EL3_STATUS),
- inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
- inw(ioaddr+TxFree));
- EL3WINDOW(4);
- netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
- inw(ioaddr+0x04), inw(ioaddr+0x06),
- inw(ioaddr+0x08), inw(ioaddr+0x0a));
- EL3WINDOW(1);
-}
-
-/*
- Use this for commands that may take time to finish
-*/
-static void tc574_wait_for_completion(struct net_device *dev, int cmd)
-{
- int i = 1500;
- outw(cmd, dev->base_addr + EL3_CMD);
- while (--i > 0)
- if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
- if (i == 0)
- netdev_notice(dev, "command 0x%04x did not complete!\n", cmd);
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
- Assume that we are in register window zero.
- */
-static unsigned short read_eeprom(unsigned int ioaddr, int index)
-{
- int timer;
- outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 usec for the read to take place. */
- for (timer = 1620; timer >= 0; timer--) {
- if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
- break;
- }
- return inw(ioaddr + Wn0EepromData);
-}
-
-/* MII transceiver control section.
- Read and write the MII registers using software-generated serial
- MDIO protocol. See the MII specifications or DP83840A data sheet
- for details.
- The maxium data clock rate is 2.5 Mhz. The timing is easily met by the
- slow PC card interface. */
-
-#define MDIO_SHIFT_CLK 0x01
-#define MDIO_DIR_WRITE 0x04
-#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
-#define MDIO_DATA_READ 0x02
-#define MDIO_ENB_IN 0x00
-
-/* Generate the preamble required for initial synchronization and
- a few older transceivers. */
-static void mdio_sync(unsigned int ioaddr, int bits)
-{
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- /* Establish sync by sending at least 32 logic ones. */
- while (-- bits >= 0) {
- outw(MDIO_DATA_WRITE1, mdio_addr);
- outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- }
-}
-
-static int mdio_read(unsigned int ioaddr, int phy_id, int location)
-{
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- unsigned int retval = 0;
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the read command bits out. */
- for (i = 14; i >= 0; i--) {
- int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value)
-{
- int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
- int i;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- }
- /* Leave the interface idle. */
- for (i = 1; i >= 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- }
-}
-
-/* Reset and restore all of the 3c574 registers. */
-static void tc574_reset(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int i;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
-
- tc574_wait_for_completion(dev, TotalReset|0x10);
-
- spin_lock_irqsave(&lp->window_lock, flags);
- /* Clear any transactions in progress. */
- outw(0, ioaddr + RunnerWrCtrl);
- outw(0, ioaddr + RunnerRdCtrl);
-
- /* Set the station address and mask. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
- for (; i < 12; i+=2)
- outw(0, ioaddr + i);
-
- /* Reset config options */
- EL3WINDOW(3);
- outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
- outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
- ioaddr + Wn3_Config);
- /* Roadrunner only: Turn on the MII transceiver. */
- outw(0x8040, ioaddr + Wn3_Options);
- mdelay(1);
- outw(0xc040, ioaddr + Wn3_Options);
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- tc574_wait_for_completion(dev, TxReset);
- tc574_wait_for_completion(dev, RxReset);
- mdelay(1);
- spin_lock_irqsave(&lp->window_lock, flags);
- EL3WINDOW(3);
- outw(0x8040, ioaddr + Wn3_Options);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 10; i++)
- inb(ioaddr + i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
- EL3WINDOW(4);
- inb(ioaddr + 12);
- inb(ioaddr + 13);
-
- /* .. enable any extra statistics bits.. */
- outw(0x0040, ioaddr + Wn4_NetDiag);
-
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- /* .. re-sync MII and re-fill what NWay is advertising. */
- mdio_sync(ioaddr, 32);
- mdio_write(ioaddr, lp->phys, 4, lp->advertising);
- if (!auto_polarity) {
- /* works for TDK 78Q2120 series MII's */
- i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
- mdio_write(ioaddr, lp->phys, 16, i);
- }
-
- spin_lock_irqsave(&lp->window_lock, flags);
- /* Switch to register set 1 for normal use, just for TxFree. */
- set_rx_mode(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
- /* Ack all pending events, and set active indicator mask. */
- outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
- ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
- | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
-}
-
-static int el3_open(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
- netif_start_queue(dev);
-
- tc574_reset(dev);
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
-
- dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
- dev->name, inw(dev->base_addr + EL3_STATUS));
-
- return 0;
-}
-
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "Transmit timed out!\n");
- dump_status(dev);
- dev->stats.tx_errors++;
- netif_trans_update(dev); /* prevent tx timeout */
- /* Issue TX_RESET and TX_START commands. */
- tc574_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
-}
-
-static void pop_tx_status(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- /* Clear the Tx status stack. */
- for (i = 32; i > 0; i--) {
- u_char tx_status = inb(ioaddr + TxStatus);
- if (!(tx_status & 0x84))
- break;
- /* reset transmitter on jabber error or underrun */
- if (tx_status & 0x30)
- tc574_wait_for_completion(dev, TxReset);
- if (tx_status & 0x38) {
- pr_debug("%s: transmit error: status 0x%02x\n",
- dev->name, tx_status);
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->stats.tx_aborted_errors++;
- }
- outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
- }
-}
-
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
-
- pr_debug("%s: el3_start_xmit(length = %ld) called, "
- "status %4.4x.\n", dev->name, (long)skb->len,
- inw(ioaddr + EL3_STATUS));
-
- spin_lock_irqsave(&lp->window_lock, flags);
-
- dev->stats.tx_bytes += skb->len;
-
- /* Put out the doubleword header... */
- outw(skb->len, ioaddr + TX_FIFO);
- outw(0, ioaddr + TX_FIFO);
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
-
- /* TxFree appears only in Window 1, not offset 0x1c. */
- if (inw(ioaddr + TxFree) <= 1536) {
- netif_stop_queue(dev);
- /* Interrupt us when the FIFO has room for max-sized packet.
- The threshold is in units of dwords. */
- outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
- }
-
- pop_tx_status(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t el3_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr;
- unsigned status;
- int work_budget = max_interrupt_work;
- int handled = 0;
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
- ioaddr = dev->base_addr;
-
- pr_debug("%s: interrupt, status %4.4x.\n",
- dev->name, inw(ioaddr + EL3_STATUS));
-
- spin_lock(&lp->window_lock);
-
- while ((status = inw(ioaddr + EL3_STATUS)) &
- (IntLatch | RxComplete | RxEarly | StatsFull)) {
- if (!netif_device_present(dev) ||
- ((status & 0xe000) != 0x2000)) {
- pr_debug("%s: Interrupt from dead card\n", dev->name);
- break;
- }
-
- handled = 1;
-
- if (status & RxComplete)
- work_budget = el3_rx(dev, work_budget);
-
- if (status & TxAvailable) {
- pr_debug(" TX room bit was handled.\n");
- /* There's room in the FIFO for a full-sized packet. */
- outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
- }
-
- if (status & TxComplete)
- pop_tx_status(dev);
-
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts. */
- if (status & StatsFull)
- update_stats(dev);
- if (status & RxEarly) {
- work_budget = el3_rx(dev, work_budget);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & AdapterFailure) {
- u16 fifo_diag;
- EL3WINDOW(4);
- fifo_diag = inw(ioaddr + Wn4_FIFODiag);
- EL3WINDOW(1);
- netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n",
- fifo_diag);
- if (fifo_diag & 0x0400) {
- /* Tx overrun */
- tc574_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- if (fifo_diag & 0x2000) {
- /* Rx underrun */
- tc574_wait_for_completion(dev, RxReset);
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD);
- }
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
- }
-
- if (--work_budget < 0) {
- pr_debug("%s: Too much work in interrupt, "
- "status %4.4x.\n", dev->name, status);
- /* Clear all interrupts */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
- }
-
- pr_debug("%s: exiting interrupt, status %4.4x.\n",
- dev->name, inw(ioaddr + EL3_STATUS));
-
- spin_unlock(&lp->window_lock);
- return IRQ_RETVAL(handled);
-}
-
-/*
- This timer serves two purposes: to check for missed interrupts
- (and as a last resort, poll the NIC for events), and to monitor
- the MII, reporting changes in cable status.
-*/
-static void media_check(struct timer_list *t)
-{
- struct el3_private *lp = timer_container_of(lp, t, media);
- struct net_device *dev = lp->p_dev->priv;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
- unsigned short /* cable, */ media, partner;
-
- if (!netif_device_present(dev))
- goto reschedule;
-
- /* Check for pending interrupt with expired latency timer: with
- this, we can limp along even if the interrupt is blocked */
- if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {
- if (!lp->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
-
- local_irq_save(flags);
- el3_interrupt(dev->irq, dev);
- local_irq_restore(flags);
-
- lp->fast_poll = HZ;
- }
- if (lp->fast_poll) {
- lp->fast_poll--;
- lp->media.expires = jiffies + 2*HZ/100;
- add_timer(&lp->media);
- return;
- }
-
- spin_lock_irqsave(&lp->window_lock, flags);
- EL3WINDOW(4);
- media = mdio_read(ioaddr, lp->phys, 1);
- partner = mdio_read(ioaddr, lp->phys, 5);
- EL3WINDOW(1);
-
- if (media != lp->media_status) {
- if ((media ^ lp->media_status) & 0x0004)
- netdev_info(dev, "%s link beat\n",
- (lp->media_status & 0x0004) ? "lost" : "found");
- if ((media ^ lp->media_status) & 0x0020) {
- lp->partner = 0;
- if (lp->media_status & 0x0020) {
- netdev_info(dev, "autonegotiation restarted\n");
- } else if (partner) {
- partner &= lp->advertising;
- lp->partner = partner;
- netdev_info(dev, "autonegotiation complete: "
- "%dbaseT-%cD selected\n",
- (partner & 0x0180) ? 100 : 10,
- (partner & 0x0140) ? 'F' : 'H');
- } else {
- netdev_info(dev, "link partner did not autonegotiate\n");
- }
-
- EL3WINDOW(3);
- outb((partner & 0x0140 ? 0x20 : 0) |
- (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
- EL3WINDOW(1);
-
- }
- if (media & 0x0010)
- netdev_info(dev, "remote fault detected\n");
- if (media & 0x0002)
- netdev_info(dev, "jabber detected\n");
- lp->media_status = media;
- }
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
-reschedule:
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
-}
-
-static struct net_device_stats *el3_get_stats(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
-
- if (netif_device_present(dev)) {
- unsigned long flags;
- spin_lock_irqsave(&lp->window_lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- }
- return &dev->stats;
-}
-
-/* Update statistics.
- Surprisingly this need not be run single-threaded, but it effectively is.
- The counters clear when read, so the adds must merely be atomic.
- */
-static void update_stats(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u8 up;
-
- pr_debug("%s: updating the statistics.\n", dev->name);
-
- if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
- return;
-
- /* Unlike the 3c509 we need not turn off stats updates while reading. */
- /* Switch to the stats window, and read everything. */
- EL3WINDOW(6);
- dev->stats.tx_carrier_errors += inb(ioaddr + 0);
- dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
- /* Multiple collisions. */ inb(ioaddr + 2);
- dev->stats.collisions += inb(ioaddr + 3);
- dev->stats.tx_window_errors += inb(ioaddr + 4);
- dev->stats.rx_fifo_errors += inb(ioaddr + 5);
- dev->stats.tx_packets += inb(ioaddr + 6);
- up = inb(ioaddr + 9);
- dev->stats.tx_packets += (up&0x30) << 4;
- /* Rx packets */ inb(ioaddr + 7);
- /* Tx deferrals */ inb(ioaddr + 8);
- /* rx */ inw(ioaddr + 10);
- /* tx */ inw(ioaddr + 12);
-
- EL3WINDOW(4);
- /* BadSSD */ inb(ioaddr + 12);
- up = inb(ioaddr + 13);
-
- EL3WINDOW(1);
-}
-
-static int el3_rx(struct net_device *dev, int worklimit)
-{
- unsigned int ioaddr = dev->base_addr;
- short rx_status;
-
- pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
- dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
- while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
- worklimit > 0) {
- worklimit--;
- if (rx_status & 0x4000) { /* Error, update stats. */
- short error = rx_status & 0x3800;
- dev->stats.rx_errors++;
- switch (error) {
- case 0x0000: dev->stats.rx_over_errors++; break;
- case 0x0800: dev->stats.rx_length_errors++; break;
- case 0x1000: dev->stats.rx_frame_errors++; break;
- case 0x1800: dev->stats.rx_length_errors++; break;
- case 0x2000: dev->stats.rx_frame_errors++; break;
- case 0x2800: dev->stats.rx_crc_errors++; break;
- }
- } else {
- short pkt_len = rx_status & 0x7ff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5);
-
- pr_debug(" Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2);
- insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
- ((pkt_len+3)>>2));
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- } else {
- pr_debug("%s: couldn't allocate a sk_buff of"
- " size %d.\n", dev->name, pkt_len);
- dev->stats.rx_dropped++;
- }
- }
- tc574_wait_for_completion(dev, RxDiscard);
- }
-
- return worklimit;
-}
-
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- struct mii_ioctl_data *data = if_mii(rq);
- int phy = lp->phys & 0x1f;
-
- pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
- dev->name, rq->ifr_ifrn.ifrn_name, cmd,
- data->phy_id, data->reg_num, data->val_in, data->val_out);
-
- switch(cmd) {
- case SIOCGMIIPHY: /* Get the address of the PHY in use. */
- data->phy_id = phy;
- fallthrough;
- case SIOCGMIIREG: /* Read the specified MII register. */
- {
- int saved_window;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- saved_window = inw(ioaddr + EL3_CMD) >> 13;
- EL3WINDOW(4);
- data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f,
- data->reg_num & 0x1f);
- EL3WINDOW(saved_window);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- return 0;
- }
- case SIOCSMIIREG: /* Write the specified MII register */
- {
- int saved_window;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- saved_window = inw(ioaddr + EL3_CMD) >> 13;
- EL3WINDOW(4);
- mdio_write(ioaddr, data->phy_id & 0x1f,
- data->reg_num & 0x1f, data->val_in);
- EL3WINDOW(saved_window);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- return 0;
- }
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
- documented. Until it is we revert to receiving all multicast frames when
- any multicast reception is desired.
- Note: My other drivers emit a log message whenever promiscuous mode is
- entered to help detect password sniffers. This is less desirable on
- typical PC card machines, so we omit the message.
- */
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
-
- if (dev->flags & IFF_PROMISC)
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
- ioaddr + EL3_CMD);
- else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
- outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
- else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- set_rx_mode(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-}
-
-static int el3_close(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
- if (pcmcia_dev_present(link)) {
- unsigned long flags;
-
- /* Turn off statistics ASAP. We update lp->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
-
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- /* Note: Switching to window 0 may disable the IRQ. */
- EL3WINDOW(0);
- spin_lock_irqsave(&lp->window_lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- /* force interrupts off */
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
- }
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&lp->media);
-
- return 0;
-}
-
-static const struct pcmcia_device_id tc574_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, tc574_ids);
-
-static struct pcmcia_driver tc574_driver = {
- .owner = THIS_MODULE,
- .name = "3c574_cs",
- .probe = tc574_probe,
- .remove = tc574_detach,
- .id_table = tc574_ids,
- .suspend = tc574_suspend,
- .resume = tc574_resume,
-};
-module_pcmcia_driver(tc574_driver);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 3fd3202d9776..294403ad7141 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-config PCMCIA_3C574
- tristate "3Com 3c574 PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- help
- Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
- (PC-card) Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called 3c574_cs. If unsure, say N.
-
config PCMCIA_3C589
tristate "3Com 3c589 PCMCIA support"
depends on PCMCIA && HAS_IOPORT
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index babfd93d5d53..45fb9af9b5c7 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -4,6 +4,5 @@
#
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
-obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 05/15] drivers: net: amd: lance: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The lance was written by Donald Becker between 1993-1998. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/amd/Kconfig | 11 -
drivers/net/ethernet/amd/Makefile | 1 -
drivers/net/ethernet/amd/lance.c | 1317 -------------------------------------
3 files changed, 1329 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 45e8d698781c..c5abb81977dd 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -43,17 +43,6 @@ config AMD8111_ETH
To compile this driver as a module, choose M here. The module
will be called amd8111e.
-config LANCE
- tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
- depends on ISA && ISA_DMA_API && !ARM && !PPC32
- select NETDEV_LEGACY_INIT
- help
- If you have a network (Ethernet) card of this type, say Y here.
- Some LinkSys cards are of this type.
-
- To compile this driver as a module, choose M here: the module
- will be called lance. This is recommended.
-
config PCNET32
tristate "AMD PCnet32 PCI support"
depends on PCI && HAS_IOPORT
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index 2dcfb84731e1..f261501f7324 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_ARIADNE) += ariadne.o
obj-$(CONFIG_ATARILANCE) += atarilance.o
obj-$(CONFIG_DECLANCE) += declance.o
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
-obj-$(CONFIG_LANCE) += lance.o
obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c
deleted file mode 100644
index 98afd8cb0efb..000000000000
--- a/drivers/net/ethernet/amd/lance.c
+++ /dev/null
@@ -1,1317 +0,0 @@
-/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
-/*
- Written/copyright 1993-1998 by Donald Becker.
-
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
- with most other LANCE-based bus-master (NE2100/NE2500) ethercards.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
- Andrey V. Savochkin:
- - alignment problem with 1.3.* kernel and some minor changes.
- Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
- - added support for Linux/Alpha, but removed most of it, because
- it worked only for the PCI chip.
- - added hook for the 32bit lance driver
- - added PCnetPCI II (79C970A) to chip table
- Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
- - hopefully fix above so Linux/Alpha can use ISA cards too.
- 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
- v1.12 10/27/97 Module support -djb
- v1.14 2/3/98 Module support modified, made PCI support optional -djb
- v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed
- before unregister_netdev() which caused NULL pointer
- reference later in the chain (in rtnetlink_fill_ifinfo())
- -- Mika Kuoppala <miku@iki.fi>
-
- Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
- the 2.1 version of the old driver - Alan Cox
-
- Get rid of check_region, check kmalloc return in lance_probe1
- Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
-
- Reworked detection, added support for Racal InterLan EtherBlaster cards
- Vesselin Kostadinov <vesok at yahoo dot com > - 22/4/2004
-*/
-
-static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <linux/bitops.h>
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
-static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
-static int __init do_lance_probe(struct net_device *dev);
-
-
-static struct card {
- char id_offset14;
- char id_offset15;
-} cards[] = {
- { //"normal"
- .id_offset14 = 0x57,
- .id_offset15 = 0x57,
- },
- { //NI6510EB
- .id_offset14 = 0x52,
- .id_offset15 = 0x44,
- },
- { //Racal InterLan EtherBlaster
- .id_offset14 = 0x52,
- .id_offset15 = 0x49,
- },
-};
-#define NUM_CARDS 3
-
-#ifdef LANCE_DEBUG
-static int lance_debug = LANCE_DEBUG;
-#else
-static int lance_debug = 1;
-#endif
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the AMD 79C960, the "PCnet-ISA
-single-chip ethernet controller for ISA". This chip is used in a wide
-variety of boards from vendors such as Allied Telesis, HP, Kingston,
-and Boca. This driver is also intended to work with older AMD 7990
-designs, such as the NE1500 and NE2100, and newer 79C961. For convenience,
-I use the name LANCE to refer to all of the AMD chips, even though it properly
-refers only to the original 7990.
-
-II. Board-specific settings
-
-The driver is designed to work the boards that use the faster
-bus-master mode, rather than in shared memory mode. (Only older designs
-have on-board buffer memory needed to support the slower shared memory mode.)
-
-Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
-channel. This driver probes the likely base addresses:
-{0x300, 0x320, 0x340, 0x360}.
-After the board is found it generates a DMA-timeout interrupt and uses
-autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
-of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
-probed for by enabling each free DMA channel in turn and checking if
-initialization succeeds.
-
-The HP-J2405A board is an exception: with this board it is easy to read the
-EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
-_know_ the base address -- that field is for writing the EEPROM.)
-
-III. Driver operation
-
-IIIa. Ring buffers
-The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes
-the base and length of the data buffer, along with status bits. The length
-of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
-the buffer length (rather than being directly the buffer length) for
-implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to
-ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries
-needlessly uses extra space and reduces the chance that an upper layer will
-be able to reorder queued Tx packets based on priority. Decreasing the number
-of entries makes it more difficult to achieve back-to-back packet transmission
-and increases the chance that Rx ring will overflow. (Consider the worst case
-of receiving back-to-back minimum-sized packets.)
-
-The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
-statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
-avoid the administrative overhead. For the Rx side this avoids dynamically
-allocating full-sized buffers "just in case", at the expense of a
-memory-to-memory data copy for each packet received. For most systems this
-is a good tradeoff: the Rx buffer will always be in low memory, the copy
-is inexpensive, and it primes the cache for later packet processing. For Tx
-the buffers are only used when needed as low-memory bounce buffers.
-
-IIIB. 16M memory limitations.
-For the ISA bus master mode all structures used directly by the LANCE,
-the initialization block, Rx and Tx rings, and data buffers, must be
-accessible from the ISA bus, i.e. in the lower 16M of real memory.
-This is a problem for current Linux kernels on >16M machines. The network
-devices are initialized after memory initialization, and the kernel doles out
-memory from the top of memory downward. The current solution is to have a
-special network initialization routine that's called before memory
-initialization; this will eventually be generalized for all network devices.
-As mentioned before, low-memory "bounce-buffers" are used when needed.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-*/
-
-/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
- Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
- That translates to 4 and 4 (16 == 2^^4).
- This is a compile-time option for efficiency.
- */
-#ifndef LANCE_LOG_TX_BUFFERS
-#define LANCE_LOG_TX_BUFFERS 4
-#define LANCE_LOG_RX_BUFFERS 4
-#endif
-
-#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
-
-#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
-
-#define PKT_BUF_SZ 1544
-
-/* Offsets from base I/O address. */
-#define LANCE_DATA 0x10
-#define LANCE_ADDR 0x12
-#define LANCE_RESET 0x14
-#define LANCE_BUS_IF 0x16
-#define LANCE_TOTAL_SIZE 0x18
-
-#define TX_TIMEOUT (HZ/5)
-
-/* The LANCE Rx and Tx ring descriptors. */
-struct lance_rx_head {
- s32 base;
- s16 buf_length; /* This length is 2s complement (negative)! */
- s16 msg_length; /* This length is "normal". */
-};
-
-struct lance_tx_head {
- s32 base;
- s16 length; /* Length is 2s complement (negative)! */
- s16 misc;
-};
-
-/* The LANCE initialization block, described in databook. */
-struct lance_init_block {
- u16 mode; /* Pre-set mode (reg. 15) */
- u8 phys_addr[6]; /* Physical ethernet address */
- u32 filter[2]; /* Multicast filter (unused). */
- /* Receive and transmit ring base, along with extra bits. */
- u32 rx_ring; /* Tx and Rx ring base pointers */
- u32 tx_ring;
-};
-
-struct lance_private {
- /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */
- struct lance_rx_head rx_ring[RX_RING_SIZE];
- struct lance_tx_head tx_ring[TX_RING_SIZE];
- struct lance_init_block init_block;
- const char *name;
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
- /* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
- /* Tx low-memory "bounce buffer" address. */
- char (*tx_bounce_buffs)[PKT_BUF_SZ];
- int cur_rx, cur_tx; /* The next free ring entry */
- int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- int dma;
- unsigned char chip_version; /* See lance_chip_type. */
- spinlock_t devlock;
-};
-
-#define LANCE_MUST_PAD 0x00000001
-#define LANCE_ENABLE_AUTOSELECT 0x00000002
-#define LANCE_MUST_REINIT_RING 0x00000004
-#define LANCE_MUST_UNRESET 0x00000008
-#define LANCE_HAS_MISSED_FRAME 0x00000010
-
-/* A mapping from the chip ID number to the part number and features.
- These are from the datasheets -- in real life the '970 version
- reportedly has the same ID as the '965. */
-static struct lance_chip_type {
- int id_number;
- const char *name;
- int flags;
-} chip_table[] = {
- {0x0000, "LANCE 7990", /* Ancient lance chip. */
- LANCE_MUST_PAD + LANCE_MUST_UNRESET},
- {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
- it the PCnet32. */
- {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x0, "PCnet (unknown)",
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
-};
-
-enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6};
-
-
-/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
- Assume yes until we know the memory size. */
-static unsigned char lance_need_isa_bounce_buffers = 1;
-
-static int lance_open(struct net_device *dev);
-static void lance_init_ring(struct net_device *dev, gfp_t mode);
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int lance_rx(struct net_device *dev);
-static irqreturn_t lance_interrupt(int irq, void *dev_id);
-static int lance_close(struct net_device *dev);
-static struct net_device_stats *lance_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue);
-
-
-
-#ifdef MODULE
-#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */
-
-static struct net_device *dev_lance[MAX_CARDS];
-static int io[MAX_CARDS];
-static int dma[MAX_CARDS];
-static int irq[MAX_CARDS];
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-module_param_hw_array(dma, int, dma, NULL, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(lance_debug, int, 0);
-MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required");
-MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)");
-MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)");
-MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)");
-
-static int __init lance_init_module(void)
-{
- struct net_device *dev;
- int this_dev, found = 0;
-
- for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
- if (io[this_dev] == 0) {
- if (this_dev != 0) /* only complain once */
- break;
- printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
- return -EPERM;
- }
- dev = alloc_etherdev(0);
- if (!dev)
- break;
- dev->irq = irq[this_dev];
- dev->base_addr = io[this_dev];
- dev->dma = dma[this_dev];
- if (do_lance_probe(dev) == 0) {
- dev_lance[found++] = dev;
- continue;
- }
- free_netdev(dev);
- break;
- }
- if (found != 0)
- return 0;
- return -ENXIO;
-}
-module_init(lance_init_module);
-
-static void cleanup_card(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- if (dev->dma != 4)
- free_dma(dev->dma);
- release_region(dev->base_addr, LANCE_TOTAL_SIZE);
- kfree(lp->tx_bounce_buffs);
- kfree((void*)lp->rx_buffs);
- kfree(lp);
-}
-
-static void __exit lance_cleanup_module(void)
-{
- int this_dev;
-
- for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
- struct net_device *dev = dev_lance[this_dev];
- if (dev) {
- unregister_netdev(dev);
- cleanup_card(dev);
- free_netdev(dev);
- }
- }
-}
-module_exit(lance_cleanup_module);
-#endif /* MODULE */
-MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver");
-MODULE_LICENSE("GPL");
-
-
-/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
- board probes now that kmalloc() can allocate ISA DMA-able regions.
- This also allows the LANCE driver to be used as a module.
- */
-static int __init do_lance_probe(struct net_device *dev)
-{
- unsigned int *port;
- int result;
-
- if (high_memory <= phys_to_virt(16*1024*1024))
- lance_need_isa_bounce_buffers = 0;
-
- for (port = lance_portlist; *port; port++) {
- int ioaddr = *port;
- struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE,
- "lance-probe");
-
- if (r) {
- /* Detect the card with minimal I/O reads */
- char offset14 = inb(ioaddr + 14);
- int card;
- for (card = 0; card < NUM_CARDS; ++card)
- if (cards[card].id_offset14 == offset14)
- break;
- if (card < NUM_CARDS) {/*yes, the first byte matches*/
- char offset15 = inb(ioaddr + 15);
- for (card = 0; card < NUM_CARDS; ++card)
- if ((cards[card].id_offset14 == offset14) &&
- (cards[card].id_offset15 == offset15))
- break;
- }
- if (card < NUM_CARDS) { /*Signature OK*/
- result = lance_probe1(dev, ioaddr, 0, 0);
- if (!result) {
- struct lance_private *lp = dev->ml_priv;
- int ver = lp->chip_version;
-
- r->name = chip_table[ver].name;
- return 0;
- }
- }
- release_region(ioaddr, LANCE_TOTAL_SIZE);
- }
- }
- return -ENODEV;
-}
-
-#ifndef MODULE
-struct net_device * __init lance_probe(int unit)
-{
- struct net_device *dev = alloc_etherdev(0);
- int err;
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
-
- err = do_lance_probe(dev);
- if (err)
- goto out;
- return dev;
-out:
- free_netdev(dev);
- return ERR_PTR(err);
-}
-#endif
-
-static const struct net_device_ops lance_netdev_ops = {
- .ndo_open = lance_open,
- .ndo_start_xmit = lance_start_xmit,
- .ndo_stop = lance_close,
- .ndo_get_stats = lance_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_tx_timeout = lance_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
-{
- struct lance_private *lp;
- unsigned long dma_channels; /* Mark spuriously-busy DMA channels */
- int i, reset_val, lance_version;
- const char *chipname;
- /* Flags for specific chips or boards. */
- unsigned char hpJ2405A = 0; /* HP ISA adaptor */
- int hp_builtin = 0; /* HP on-board ethernet. */
- static int did_version; /* Already printed version info. */
- unsigned long flags;
- int err = -ENOMEM;
- void __iomem *bios;
- u8 addr[ETH_ALEN];
-
- /* First we look for special cases.
- Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
- There are two HP versions, check the BIOS for the configuration port.
- This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
- */
- bios = ioremap(0xf00f0, 0x14);
- if (!bios)
- return -ENOMEM;
- if (readw(bios + 0x12) == 0x5048) {
- static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
- int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99;
- /* We can have boards other than the built-in! Verify this is on-board. */
- if ((inb(hp_port) & 0xc0) == 0x80 &&
- ioaddr_table[inb(hp_port) & 3] == ioaddr)
- hp_builtin = hp_port;
- }
- iounmap(bios);
- /* We also recognize the HP Vectra on-board here, but check below. */
- hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 &&
- inb(ioaddr+2) == 0x09);
-
- /* Reset the LANCE. */
- reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
-
- /* The Un-Reset needed is only needed for the real NE2100, and will
- confuse the HP board. */
- if (!hpJ2405A)
- outw(reset_val, ioaddr+LANCE_RESET);
-
- outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
- if (inw(ioaddr+LANCE_DATA) != 0x0004)
- return -ENODEV;
-
- /* Get the version of the chip. */
- outw(88, ioaddr+LANCE_ADDR);
- if (inw(ioaddr+LANCE_ADDR) != 88) {
- lance_version = 0;
- } else { /* Good, it's a newer chip. */
- int chip_version = inw(ioaddr+LANCE_DATA);
- outw(89, ioaddr+LANCE_ADDR);
- chip_version |= inw(ioaddr+LANCE_DATA) << 16;
- if (lance_debug > 2)
- printk(" LANCE chip version is %#x.\n", chip_version);
- if ((chip_version & 0xfff) != 0x003)
- return -ENODEV;
- chip_version = (chip_version >> 12) & 0xffff;
- for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
- if (chip_table[lance_version].id_number == chip_version)
- break;
- }
- }
-
- /* We can't allocate private data from alloc_etherdev() because it must
- a ISA DMA-able region. */
- chipname = chip_table[lance_version].name;
- printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr);
-
- /* There is a 16 byte station address PROM at the base address.
- The first six bytes are the station address. */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + i);
- eth_hw_addr_set(dev, addr);
- printk("%pM", dev->dev_addr);
-
- dev->base_addr = ioaddr;
- /* Make certain the data structures used by the LANCE are aligned and DMAble. */
-
- lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL);
- if (!lp)
- return -ENOMEM;
- if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
- dev->ml_priv = lp;
- lp->name = chipname;
- lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ,
- GFP_DMA | GFP_KERNEL);
- if (!lp->rx_buffs)
- goto out_lp;
- if (lance_need_isa_bounce_buffers) {
- lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ,
- GFP_DMA | GFP_KERNEL);
- if (!lp->tx_bounce_buffs)
- goto out_rx;
- } else
- lp->tx_bounce_buffs = NULL;
-
- lp->chip_version = lance_version;
- spin_lock_init(&lp->devlock);
-
- lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
- for (i = 0; i < 6; i++)
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- lp->init_block.filter[0] = 0x00000000;
- lp->init_block.filter[1] = 0x00000000;
- lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
- lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-
- outw(0x0001, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
- outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
- outw(0x0002, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
- outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
- outw(0x0000, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
-
- if (irq) { /* Set iff PCI card. */
- dev->dma = 4; /* Native bus-master, no DMA channel needed. */
- dev->irq = irq;
- } else if (hp_builtin) {
- static const char dma_tbl[4] = {3, 5, 6, 0};
- static const char irq_tbl[4] = {3, 4, 5, 9};
- unsigned char port_val = inb(hp_builtin);
- dev->dma = dma_tbl[(port_val >> 4) & 3];
- dev->irq = irq_tbl[(port_val >> 2) & 3];
- printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
- } else if (hpJ2405A) {
- static const char dma_tbl[4] = {3, 5, 6, 7};
- static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
- short reset_val = inw(ioaddr+LANCE_RESET);
- dev->dma = dma_tbl[(reset_val >> 2) & 3];
- dev->irq = irq_tbl[(reset_val >> 4) & 7];
- printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
- } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */
- short bus_info;
- outw(8, ioaddr+LANCE_ADDR);
- bus_info = inw(ioaddr+LANCE_BUS_IF);
- dev->dma = bus_info & 0x07;
- dev->irq = (bus_info >> 4) & 0x0F;
- } else {
- /* The DMA channel may be passed in PARAM1. */
- if (dev->mem_start & 0x07)
- dev->dma = dev->mem_start & 0x07;
- }
-
- if (dev->dma == 0) {
- /* Read the DMA channel status register, so that we can avoid
- stuck DMA channels in the DMA detection below. */
- dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
- (inb(DMA2_STAT_REG) & 0xf0);
- }
- err = -ENODEV;
- if (dev->irq >= 2)
- printk(" assigned IRQ %d", dev->irq);
- else if (lance_version != 0) { /* 7990 boards need DMA detection first. */
- unsigned long irq_mask;
-
- /* To auto-IRQ we enable the initialization-done and DMA error
- interrupts. For ISA boards we get a DMA error, but VLB and PCI
- boards will work. */
- irq_mask = probe_irq_on();
-
- /* Trigger an initialization just for the interrupt. */
- outw(0x0041, ioaddr+LANCE_DATA);
-
- mdelay(20);
- dev->irq = probe_irq_off(irq_mask);
- if (dev->irq)
- printk(", probed IRQ %d", dev->irq);
- else {
- printk(", failed to detect IRQ line.\n");
- goto out_tx;
- }
-
- /* Check for the initialization done bit, 0x0100, which means
- that we don't need a DMA channel. */
- if (inw(ioaddr+LANCE_DATA) & 0x0100)
- dev->dma = 4;
- }
-
- if (dev->dma == 4) {
- printk(", no DMA needed.\n");
- } else if (dev->dma) {
- if (request_dma(dev->dma, chipname)) {
- printk("DMA %d allocation failed.\n", dev->dma);
- goto out_tx;
- } else
- printk(", assigned DMA %d.\n", dev->dma);
- } else { /* OK, we have to auto-DMA. */
- for (i = 0; i < 4; i++) {
- static const char dmas[] = { 5, 6, 7, 3 };
- int dma = dmas[i];
- int boguscnt;
-
- /* Don't enable a permanently busy DMA channel, or the machine
- will hang. */
- if (test_bit(dma, &dma_channels))
- continue;
- outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
- if (request_dma(dma, chipname))
- continue;
-
- flags=claim_dma_lock();
- set_dma_mode(dma, DMA_MODE_CASCADE);
- enable_dma(dma);
- release_dma_lock(flags);
-
- /* Trigger an initialization. */
- outw(0x0001, ioaddr+LANCE_DATA);
- for (boguscnt = 100; boguscnt > 0; --boguscnt)
- if (inw(ioaddr+LANCE_DATA) & 0x0900)
- break;
- if (inw(ioaddr+LANCE_DATA) & 0x0100) {
- dev->dma = dma;
- printk(", DMA %d.\n", dev->dma);
- break;
- } else {
- flags=claim_dma_lock();
- disable_dma(dma);
- release_dma_lock(flags);
- free_dma(dma);
- }
- }
- if (i == 4) { /* Failure: bail. */
- printk("DMA detection failed.\n");
- goto out_tx;
- }
- }
-
- if (lance_version == 0 && dev->irq == 0) {
- /* We may auto-IRQ now that we have a DMA channel. */
- /* Trigger an initialization just for the interrupt. */
- unsigned long irq_mask;
-
- irq_mask = probe_irq_on();
- outw(0x0041, ioaddr+LANCE_DATA);
-
- mdelay(40);
- dev->irq = probe_irq_off(irq_mask);
- if (dev->irq == 0) {
- printk(" Failed to detect the 7990 IRQ line.\n");
- goto out_dma;
- }
- printk(" Auto-IRQ detected IRQ%d.\n", dev->irq);
- }
-
- if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
- /* Turn on auto-select of media (10baseT or BNC) so that the user
- can watch the LEDs even if the board isn't opened. */
- outw(0x0002, ioaddr+LANCE_ADDR);
- /* Don't touch 10base2 power bit. */
- outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
-
- if (lance_debug > 0 && did_version++ == 0)
- printk(version);
-
- /* The LANCE-specific entries in the device structure. */
- dev->netdev_ops = &lance_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- err = register_netdev(dev);
- if (err)
- goto out_dma;
- return 0;
-out_dma:
- if (dev->dma != 4)
- free_dma(dev->dma);
-out_tx:
- kfree(lp->tx_bounce_buffs);
-out_rx:
- kfree((void*)lp->rx_buffs);
-out_lp:
- kfree(lp);
- return err;
-}
-
-
-static int
-lance_open(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int ioaddr = dev->base_addr;
- int i;
-
- if (dev->irq == 0 ||
- request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) {
- return -EAGAIN;
- }
-
- /* We used to allocate DMA here, but that was silly.
- DMA lines can't be shared! We now permanently allocate them. */
-
- /* Reset the LANCE */
- inw(ioaddr+LANCE_RESET);
-
- /* The DMA controller is used as a no-operation slave, "cascade mode". */
- if (dev->dma != 4) {
- unsigned long flags=claim_dma_lock();
- enable_dma(dev->dma);
- set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- release_dma_lock(flags);
- }
-
- /* Un-Reset the LANCE, needed only for the NE2100. */
- if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
- outw(0, ioaddr+LANCE_RESET);
-
- if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
- /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
- outw(0x0002, ioaddr+LANCE_ADDR);
- /* Only touch autoselect bit. */
- outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
-
- if (lance_debug > 1)
- printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
- dev->name, dev->irq, dev->dma,
- (u32) isa_virt_to_bus(lp->tx_ring),
- (u32) isa_virt_to_bus(lp->rx_ring),
- (u32) isa_virt_to_bus(&lp->init_block));
-
- lance_init_ring(dev, GFP_KERNEL);
- /* Re-initialize the LANCE, and start it when done. */
- outw(0x0001, ioaddr+LANCE_ADDR);
- outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
- outw(0x0002, ioaddr+LANCE_ADDR);
- outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-
- outw(0x0004, ioaddr+LANCE_ADDR);
- outw(0x0915, ioaddr+LANCE_DATA);
-
- outw(0x0000, ioaddr+LANCE_ADDR);
- outw(0x0001, ioaddr+LANCE_DATA);
-
- netif_start_queue (dev);
-
- i = 0;
- while (i++ < 100)
- if (inw(ioaddr+LANCE_DATA) & 0x0100)
- break;
- /*
- * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
- * reports that doing so triggers a bug in the '974.
- */
- outw(0x0042, ioaddr+LANCE_DATA);
-
- if (lance_debug > 2)
- printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
- dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
-
- return 0; /* Always succeed */
-}
-
-/* The LANCE has been halted for one reason or another (busmaster memory
- arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
- etc.). Modern LANCE variants always reload their ring-buffer
- configuration when restarted, so we must reinitialize our ring
- context before restarting. As part of this reinitialization,
- find all packets still on the Tx ring and pretend that they had been
- sent (in effect, drop the packets on the floor) - the higher-level
- protocols will time out and retransmit. It'd be better to shuffle
- these skbs to a temp list and then actually re-Tx them after
- restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
-*/
-
-static void
-lance_purge_ring(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int i;
-
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = lp->rx_skbuff[i];
- lp->rx_skbuff[i] = NULL;
- lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
- if (skb)
- dev_kfree_skb_any(skb);
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i]) {
- dev_kfree_skb_any(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = NULL;
- }
- }
-}
-
-
-/* Initialize the LANCE Rx and Tx rings. */
-static void
-lance_init_ring(struct net_device *dev, gfp_t gfp)
-{
- struct lance_private *lp = dev->ml_priv;
- int i;
-
- lp->cur_rx = lp->cur_tx = 0;
- lp->dirty_rx = lp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- void *rx_buff;
-
- skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
- lp->rx_skbuff[i] = skb;
- if (skb)
- rx_buff = skb->data;
- else
- rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
- if (!rx_buff)
- lp->rx_ring[i].base = 0;
- else
- lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000;
- lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
- }
- /* The Tx buffer address is filled in as needed, but we do need to clear
- the upper ownership bit. */
- for (i = 0; i < TX_RING_SIZE; i++) {
- lp->tx_skbuff[i] = NULL;
- lp->tx_ring[i].base = 0;
- }
-
- lp->init_block.mode = 0x0000;
- for (i = 0; i < 6; i++)
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- lp->init_block.filter[0] = 0x00000000;
- lp->init_block.filter[1] = 0x00000000;
- lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
- lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-}
-
-static void
-lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
-{
- struct lance_private *lp = dev->ml_priv;
-
- if (must_reinit ||
- (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
- lance_purge_ring(dev);
- lance_init_ring(dev, GFP_ATOMIC);
- }
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(csr0_bits, dev->base_addr + LANCE_DATA);
-}
-
-
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
- struct lance_private *lp = (struct lance_private *) dev->ml_priv;
- int ioaddr = dev->base_addr;
-
- outw (0, ioaddr + LANCE_ADDR);
- printk ("%s: transmit timed out, status %4.4x, resetting.\n",
- dev->name, inw (ioaddr + LANCE_DATA));
- outw (0x0004, ioaddr + LANCE_DATA);
- dev->stats.tx_errors++;
-#ifndef final_version
- if (lance_debug > 3) {
- int i;
- printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
- lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "",
- lp->cur_rx);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
- lp->rx_ring[i].msg_length);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- lp->tx_ring[i].base, -lp->tx_ring[i].length,
- lp->tx_ring[i].misc);
- printk ("\n");
- }
-#endif
- lance_restart (dev, 0x0043, 1);
-
- netif_trans_update(dev); /* prevent tx timeout */
- netif_wake_queue (dev);
-}
-
-
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int ioaddr = dev->base_addr;
- int entry;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->devlock, flags);
-
- if (lance_debug > 3) {
- outw(0x0000, ioaddr+LANCE_ADDR);
- printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
- inw(ioaddr+LANCE_DATA));
- outw(0x0000, ioaddr+LANCE_DATA);
- }
-
- /* Fill in a Tx ring entry */
-
- /* Mask to ring buffer boundary. */
- entry = lp->cur_tx & TX_RING_MOD_MASK;
-
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- /* The old LANCE chips doesn't automatically pad buffers to min. size. */
- if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
- if (skb->len < ETH_ZLEN) {
- if (skb_padto(skb, ETH_ZLEN))
- goto out;
- lp->tx_ring[entry].length = -ETH_ZLEN;
- }
- else
- lp->tx_ring[entry].length = -skb->len;
- } else
- lp->tx_ring[entry].length = -skb->len;
-
- lp->tx_ring[entry].misc = 0x0000;
-
- dev->stats.tx_bytes += skb->len;
-
- /* If any part of this buffer is >16M we must copy it to a low-memory
- buffer. */
- if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) {
- if (lance_debug > 5)
- printk("%s: bouncing a high-memory packet (%#x).\n",
- dev->name, (u32)isa_virt_to_bus(skb->data));
- skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
- lp->tx_ring[entry].base =
- ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
- dev_consume_skb_irq(skb);
- } else {
- lp->tx_skbuff[entry] = skb;
- lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
- }
- lp->cur_tx++;
-
- /* Trigger an immediate send poll. */
- outw(0x0000, ioaddr+LANCE_ADDR);
- outw(0x0048, ioaddr+LANCE_DATA);
-
- if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE)
- netif_stop_queue(dev);
-
-out:
- spin_unlock_irqrestore(&lp->devlock, flags);
- return NETDEV_TX_OK;
-}
-
-/* The LANCE interrupt handler. */
-static irqreturn_t lance_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct lance_private *lp;
- int csr0, ioaddr, boguscnt=10;
- int must_restart;
-
- ioaddr = dev->base_addr;
- lp = dev->ml_priv;
-
- spin_lock (&lp->devlock);
-
- outw(0x00, dev->base_addr + LANCE_ADDR);
- while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 &&
- --boguscnt >= 0) {
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
-
- must_restart = 0;
-
- if (lance_debug > 5)
- printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
- dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
-
- if (csr0 & 0x0400) /* Rx interrupt */
- lance_rx(dev);
-
- if (csr0 & 0x0200) { /* Tx-done interrupt */
- int dirty_tx = lp->dirty_tx;
-
- while (dirty_tx < lp->cur_tx) {
- int entry = dirty_tx & TX_RING_MOD_MASK;
- int status = lp->tx_ring[entry].base;
-
- if (status < 0)
- break; /* It still hasn't been Txed */
-
- lp->tx_ring[entry].base = 0;
-
- if (status & 0x40000000) {
- /* There was an major error, log it. */
- int err_status = lp->tx_ring[entry].misc;
- dev->stats.tx_errors++;
- if (err_status & 0x0400)
- dev->stats.tx_aborted_errors++;
- if (err_status & 0x0800)
- dev->stats.tx_carrier_errors++;
- if (err_status & 0x1000)
- dev->stats.tx_window_errors++;
- if (err_status & 0x4000) {
- /* Ackk! On FIFO errors the Tx unit is turned off! */
- dev->stats.tx_fifo_errors++;
- /* Remove this verbosity later! */
- printk("%s: Tx FIFO error! Status %4.4x.\n",
- dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
- }
- } else {
- if (status & 0x18000000)
- dev->stats.collisions++;
- dev->stats.tx_packets++;
- }
-
- /* We must free the original skb if it's not a data-only copy
- in the bounce buffer. */
- if (lp->tx_skbuff[entry]) {
- dev_consume_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = NULL;
- }
- dirty_tx++;
- }
-
-#ifndef final_version
- if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
- printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n",
- dirty_tx, lp->cur_tx,
- netif_queue_stopped(dev) ? "yes" : "no");
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- /* if the ring is no longer full, accept more packets */
- if (netif_queue_stopped(dev) &&
- dirty_tx > lp->cur_tx - TX_RING_SIZE + 2)
- netif_wake_queue (dev);
-
- lp->dirty_tx = dirty_tx;
- }
-
- /* Log misc errors. */
- if (csr0 & 0x4000)
- dev->stats.tx_errors++; /* Tx babble. */
- if (csr0 & 0x1000)
- dev->stats.rx_errors++; /* Missed a Rx frame. */
- if (csr0 & 0x0800) {
- printk("%s: Bus master arbitration failure, status %4.4x.\n",
- dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
- }
-
- if (must_restart) {
- /* stop the chip to clear the error condition, then restart */
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(0x0004, dev->base_addr + LANCE_DATA);
- lance_restart(dev, 0x0002, 0);
- }
- }
-
- /* Clear any other interrupt, and set interrupt enable. */
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(0x7940, dev->base_addr + LANCE_DATA);
-
- if (lance_debug > 4)
- printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
- dev->name, inw(ioaddr + LANCE_ADDR),
- inw(dev->base_addr + LANCE_DATA));
-
- spin_unlock (&lp->devlock);
- return IRQ_HANDLED;
-}
-
-static int
-lance_rx(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int entry = lp->cur_rx & RX_RING_MOD_MASK;
- int i;
-
- /* If we own the next entry, it's a new packet. Send it up. */
- while (lp->rx_ring[entry].base >= 0) {
- int status = lp->rx_ring[entry].base >> 24;
-
- if (status != 0x03) { /* There was an error. */
- /* There is a tricky error noted by John Murphy,
- <murf@perftech.com> to Russ Nelson: Even with full-sized
- buffers it's possible for a jabber packet to use two
- buffers, with only the last correctly noting the error. */
- if (status & 0x01) /* Only count a general error at the */
- dev->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x20)
- dev->stats.rx_frame_errors++;
- if (status & 0x10)
- dev->stats.rx_over_errors++;
- if (status & 0x08)
- dev->stats.rx_crc_errors++;
- if (status & 0x04)
- dev->stats.rx_fifo_errors++;
- lp->rx_ring[entry].base &= 0x03ffffff;
- }
- else
- {
- /* Malloc up new buffer, compatible with net3. */
- short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
- struct sk_buff *skb;
-
- if(pkt_len<60)
- {
- printk("%s: Runt packet!\n",dev->name);
- dev->stats.rx_errors++;
- }
- else
- {
- skb = dev_alloc_skb(pkt_len+2);
- if (!skb)
- {
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
- for (i=0; i < RX_RING_SIZE; i++)
- if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
- break;
-
- if (i > RX_RING_SIZE -2)
- {
- dev->stats.rx_dropped++;
- lp->rx_ring[entry].base |= 0x80000000;
- lp->cur_rx++;
- }
- break;
- }
- skb_reserve(skb,2); /* 16 byte align */
- skb_put(skb,pkt_len); /* Make room */
- skb_copy_to_linear_data(skb,
- (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)),
- pkt_len);
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- }
- }
- /* The docs say that the buffer length isn't touched, but Andrew Boyd
- of QNX reports that some revs of the 79C965 clear it. */
- lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
- lp->rx_ring[entry].base |= 0x80000000;
- entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
- }
-
- /* We should check that at least two ring entries are free. If not,
- we should free one and mark stats->rx_dropped++. */
-
- return 0;
-}
-
-static int
-lance_close(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct lance_private *lp = dev->ml_priv;
-
- netif_stop_queue (dev);
-
- if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
- outw(112, ioaddr+LANCE_ADDR);
- dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
- }
- outw(0, ioaddr+LANCE_ADDR);
-
- if (lance_debug > 1)
- printk("%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inw(ioaddr+LANCE_DATA));
-
- /* We stop the LANCE here -- it occasionally polls
- memory if we don't. */
- outw(0x0004, ioaddr+LANCE_DATA);
-
- if (dev->dma != 4)
- {
- unsigned long flags=claim_dma_lock();
- disable_dma(dev->dma);
- release_dma_lock(flags);
- }
- free_irq(dev->irq, dev);
-
- lance_purge_ring(dev);
-
- return 0;
-}
-
-static struct net_device_stats *lance_get_stats(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
-
- if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
- short ioaddr = dev->base_addr;
- short saved_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->devlock, flags);
- saved_addr = inw(ioaddr+LANCE_ADDR);
- outw(112, ioaddr+LANCE_ADDR);
- dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
- outw(saved_addr, ioaddr+LANCE_ADDR);
- spin_unlock_irqrestore(&lp->devlock, flags);
- }
-
- return &dev->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- short ioaddr = dev->base_addr;
-
- outw(0, ioaddr+LANCE_ADDR);
- outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
-
- if (dev->flags&IFF_PROMISC) {
- outw(15, ioaddr+LANCE_ADDR);
- outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
- } else {
- short multicast_table[4];
- int i;
- int num_addrs=netdev_mc_count(dev);
- if(dev->flags&IFF_ALLMULTI)
- num_addrs=1;
- /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
- memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
- for (i = 0; i < 4; i++) {
- outw(8 + i, ioaddr+LANCE_ADDR);
- outw(multicast_table[i], ioaddr+LANCE_DATA);
- }
- outw(15, ioaddr+LANCE_ADDR);
- outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
- }
-
- lance_restart(dev, 0x0142, 0); /* Resume normal operation */
-
-}
-
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 06/15] drivers: net: amd: nmclan: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The nmclan was written by Roger C Pao in 1995. It is an PCMCIA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/amd/Kconfig | 10 -
drivers/net/ethernet/amd/Makefile | 1 -
drivers/net/ethernet/amd/nmclan_cs.c | 1508 ----------------------------------
3 files changed, 1519 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index c5abb81977dd..e35991141a1a 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -109,16 +109,6 @@ config MVME147_NET
driver for this chip in your kernel.
To compile this driver as a module, choose M here.
-config PCMCIA_NMCLAN
- tristate "New Media PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- help
- Say Y here if you intend to attach a New Media Ethernet or LiveWire
- PCMCIA (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called nmclan_cs. If unsure, say N.
-
config SUN3LANCE
tristate "Sun3/Sun3x on-board LANCE support"
depends on (SUN3 || SUN3X)
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index f261501f7324..e485fae235a7 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_DECLANCE) += declance.o
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
-obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
obj-$(CONFIG_PCNET32) += pcnet32.o
obj-$(CONFIG_SUN3LANCE) += sun3lance.o
obj-$(CONFIG_SUNLANCE) += sunlance.o
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
deleted file mode 100644
index 37054a670407..000000000000
--- a/drivers/net/ethernet/amd/nmclan_cs.c
+++ /dev/null
@@ -1,1508 +0,0 @@
-/* ----------------------------------------------------------------------------
-Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
- nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
-
- The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
- Access Controller for Ethernet (MACE). It is essentially the Am2150
- PCMCIA Ethernet card contained in the Am2150 Demo Kit.
-
-Written by Roger C. Pao <rpao@paonet.org>
- Copyright 1995 Roger C. Pao
- Linux 2.5 cleanups Copyright Red Hat 2003
-
- This software may be used and distributed according to the terms of
- the GNU General Public License.
-
-Ported to Linux 1.3.* network driver environment by
- Matti Aarnio <mea@utu.fi>
-
-References
-
- Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
- Am79C940 (MACE) Data Sheet, 1994
- Am79C90 (C-LANCE) Data Sheet, 1994
- Linux PCMCIA Programmer's Guide v1.17
- /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
-
- Eric Mears, New Media Corporation
- Tom Pollard, New Media Corporation
- Dean Siasoyco, New Media Corporation
- Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
- Donald Becker <becker@scyld.com>
- David Hinds <dahinds@users.sourceforge.net>
-
- The Linux client driver is based on the 3c589_cs.c client driver by
- David Hinds.
-
- The Linux network driver outline is based on the 3c589_cs.c driver,
- the 8390.c driver, and the example skeleton.c kernel code, which are
- by Donald Becker.
-
- The Am2150 network driver hardware interface code is based on the
- OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
-
- Special thanks for testing and help in debugging this driver goes
- to Ken Lesniak.
-
--------------------------------------------------------------------------------
-Driver Notes and Issues
--------------------------------------------------------------------------------
-
-1. Developed on a Dell 320SLi
- PCMCIA Card Services 2.6.2
- Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
-
-2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
- 'insmod pcmcia_core.o io_speed=300'.
- This will avoid problems with fast systems which causes rx_framecnt
- to return random values.
-
-3. If hot extraction does not work for you, use 'ifconfig eth0 down'
- before extraction.
-
-4. There is a bad slow-down problem in this driver.
-
-5. Future: Multicast processing. In the meantime, do _not_ compile your
- kernel with multicast ip enabled.
-
--------------------------------------------------------------------------------
-History
--------------------------------------------------------------------------------
-Log: nmclan_cs.c,v
- * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
- * Fixed hang on card eject as we probe it
- * Cleaned up to use new style locking.
- *
- * Revision 0.16 1995/07/01 06:42:17 rpao
- * Bug fix: nmclan_reset() called CardServices incorrectly.
- *
- * Revision 0.15 1995/05/24 08:09:47 rpao
- * Re-implement MULTI_TX dev->tbusy handling.
- *
- * Revision 0.14 1995/05/23 03:19:30 rpao
- * Added, in nmclan_config(), "tuple.Attributes = 0;".
- * Modified MACE ID check to ignore chip revision level.
- * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
- *
- * Revision 0.13 1995/05/18 05:56:34 rpao
- * Statistics changes.
- * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
- * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup.
- *
- * Revision 0.12 1995/05/14 00:12:23 rpao
- * Statistics overhaul.
- *
-
-95/05/13 rpao V0.10a
- Bug fix: MACE statistics counters used wrong I/O ports.
- Bug fix: mace_interrupt() needed to allow statistics to be
- processed without RX or TX interrupts pending.
-95/05/11 rpao V0.10
- Multiple transmit request processing.
- Modified statistics to use MACE counters where possible.
-95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
- *Released
-95/05/10 rpao V0.08
- Bug fix: Make all non-exported functions private by using
- static keyword.
- Bug fix: Test IntrCnt _before_ reading MACE_IR.
-95/05/10 rpao V0.07 Statistics.
-95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
-
----------------------------------------------------------------------------- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME "nmclan_cs"
-
-/* ----------------------------------------------------------------------------
-Conditional Compilation Options
----------------------------------------------------------------------------- */
-
-#define MULTI_TX 0
-#define RESET_ON_TIMEOUT 1
-#define TX_INTERRUPTABLE 1
-#define RESET_XILINX 0
-
-/* ----------------------------------------------------------------------------
-Include Files
----------------------------------------------------------------------------- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-
-#include <pcmcia/cisreg.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/* ----------------------------------------------------------------------------
-Defines
----------------------------------------------------------------------------- */
-
-#define MACE_LADRF_LEN 8
- /* 8 bytes in Logical Address Filter */
-
-/* Loop Control Defines */
-#define MACE_MAX_IR_ITERATIONS 10
-#define MACE_MAX_RX_ITERATIONS 12
- /*
- TBD: Dean brought this up, and I assumed the hardware would
- handle it:
-
- If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
- non-zero when the isr exits. We may not get another interrupt
- to process the remaining packets for some time.
- */
-
-/*
-The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
-which manages the interface between the MACE and the PCMCIA bus. It
-also includes buffer management for the 32K x 8 SRAM to control up to
-four transmit and 12 receive frames at a time.
-*/
-#define AM2150_MAX_TX_FRAMES 4
-#define AM2150_MAX_RX_FRAMES 12
-
-/* Am2150 Ethernet Card I/O Mapping */
-#define AM2150_RCV 0x00
-#define AM2150_XMT 0x04
-#define AM2150_XMT_SKIP 0x09
-#define AM2150_RCV_NEXT 0x0A
-#define AM2150_RCV_FRAME_COUNT 0x0B
-#define AM2150_MACE_BANK 0x0C
-#define AM2150_MACE_BASE 0x10
-
-/* MACE Registers */
-#define MACE_RCVFIFO 0
-#define MACE_XMTFIFO 1
-#define MACE_XMTFC 2
-#define MACE_XMTFS 3
-#define MACE_XMTRC 4
-#define MACE_RCVFC 5
-#define MACE_RCVFS 6
-#define MACE_FIFOFC 7
-#define MACE_IR 8
-#define MACE_IMR 9
-#define MACE_PR 10
-#define MACE_BIUCC 11
-#define MACE_FIFOCC 12
-#define MACE_MACCC 13
-#define MACE_PLSCC 14
-#define MACE_PHYCC 15
-#define MACE_CHIPIDL 16
-#define MACE_CHIPIDH 17
-#define MACE_IAC 18
-/* Reserved */
-#define MACE_LADRF 20
-#define MACE_PADR 21
-/* Reserved */
-/* Reserved */
-#define MACE_MPC 24
-/* Reserved */
-#define MACE_RNTPC 26
-#define MACE_RCVCC 27
-/* Reserved */
-#define MACE_UTR 29
-#define MACE_RTR1 30
-#define MACE_RTR2 31
-
-/* MACE Bit Masks */
-#define MACE_XMTRC_EXDEF 0x80
-#define MACE_XMTRC_XMTRC 0x0F
-
-#define MACE_XMTFS_XMTSV 0x80
-#define MACE_XMTFS_UFLO 0x40
-#define MACE_XMTFS_LCOL 0x20
-#define MACE_XMTFS_MORE 0x10
-#define MACE_XMTFS_ONE 0x08
-#define MACE_XMTFS_DEFER 0x04
-#define MACE_XMTFS_LCAR 0x02
-#define MACE_XMTFS_RTRY 0x01
-
-#define MACE_RCVFS_RCVSTS 0xF000
-#define MACE_RCVFS_OFLO 0x8000
-#define MACE_RCVFS_CLSN 0x4000
-#define MACE_RCVFS_FRAM 0x2000
-#define MACE_RCVFS_FCS 0x1000
-
-#define MACE_FIFOFC_RCVFC 0xF0
-#define MACE_FIFOFC_XMTFC 0x0F
-
-#define MACE_IR_JAB 0x80
-#define MACE_IR_BABL 0x40
-#define MACE_IR_CERR 0x20
-#define MACE_IR_RCVCCO 0x10
-#define MACE_IR_RNTPCO 0x08
-#define MACE_IR_MPCO 0x04
-#define MACE_IR_RCVINT 0x02
-#define MACE_IR_XMTINT 0x01
-
-#define MACE_MACCC_PROM 0x80
-#define MACE_MACCC_DXMT2PD 0x40
-#define MACE_MACCC_EMBA 0x20
-#define MACE_MACCC_RESERVED 0x10
-#define MACE_MACCC_DRCVPA 0x08
-#define MACE_MACCC_DRCVBC 0x04
-#define MACE_MACCC_ENXMT 0x02
-#define MACE_MACCC_ENRCV 0x01
-
-#define MACE_PHYCC_LNKFL 0x80
-#define MACE_PHYCC_DLNKTST 0x40
-#define MACE_PHYCC_REVPOL 0x20
-#define MACE_PHYCC_DAPC 0x10
-#define MACE_PHYCC_LRT 0x08
-#define MACE_PHYCC_ASEL 0x04
-#define MACE_PHYCC_RWAKE 0x02
-#define MACE_PHYCC_AWAKE 0x01
-
-#define MACE_IAC_ADDRCHG 0x80
-#define MACE_IAC_PHYADDR 0x04
-#define MACE_IAC_LOGADDR 0x02
-
-#define MACE_UTR_RTRE 0x80
-#define MACE_UTR_RTRD 0x40
-#define MACE_UTR_RPA 0x20
-#define MACE_UTR_FCOLL 0x10
-#define MACE_UTR_RCVFCSE 0x08
-#define MACE_UTR_LOOP_INCL_MENDEC 0x06
-#define MACE_UTR_LOOP_NO_MENDEC 0x04
-#define MACE_UTR_LOOP_EXTERNAL 0x02
-#define MACE_UTR_LOOP_NONE 0x00
-#define MACE_UTR_RESERVED 0x01
-
-/* Switch MACE register bank (only 0 and 1 are valid) */
-#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
-
-#define MACE_IMR_DEFAULT \
- (0xFF - \
- ( \
- MACE_IR_CERR | \
- MACE_IR_RCVCCO | \
- MACE_IR_RNTPCO | \
- MACE_IR_MPCO | \
- MACE_IR_RCVINT | \
- MACE_IR_XMTINT \
- ) \
- )
-#undef MACE_IMR_DEFAULT
-#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
-
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* ----------------------------------------------------------------------------
-Type Definitions
----------------------------------------------------------------------------- */
-
-typedef struct _mace_statistics {
- /* MACE_XMTFS */
- int xmtsv;
- int uflo;
- int lcol;
- int more;
- int one;
- int defer;
- int lcar;
- int rtry;
-
- /* MACE_XMTRC */
- int exdef;
- int xmtrc;
-
- /* RFS1--Receive Status (RCVSTS) */
- int oflo;
- int clsn;
- int fram;
- int fcs;
-
- /* RFS2--Runt Packet Count (RNTPC) */
- int rfs_rntpc;
-
- /* RFS3--Receive Collision Count (RCVCC) */
- int rfs_rcvcc;
-
- /* MACE_IR */
- int jab;
- int babl;
- int cerr;
- int rcvcco;
- int rntpco;
- int mpco;
-
- /* MACE_MPC */
- int mpc;
-
- /* MACE_RNTPC */
- int rntpc;
-
- /* MACE_RCVCC */
- int rcvcc;
-} mace_statistics;
-
-typedef struct _mace_private {
- struct pcmcia_device *p_dev;
- mace_statistics mace_stats; /* MACE chip statistics counters */
-
- /* restore_multicast_list() state variables */
- int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
- int multicast_num_addrs;
-
- char tx_free_frames; /* Number of free transmit frame buffers */
- char tx_irq_disabled; /* MACE TX interrupt disabled */
-
- spinlock_t bank_lock; /* Must be held if you step off bank 0 */
-} mace_private;
-
-/* ----------------------------------------------------------------------------
-Private Global Variables
----------------------------------------------------------------------------- */
-
-static const char *if_names[]={
- "Auto", "10baseT", "BNC",
-};
-
-/* ----------------------------------------------------------------------------
-Parameters
- These are the parameters that can be set during loading with
- 'insmod'.
----------------------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("New Media PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
-INT_MODULE_PARM(if_port, 0);
-
-
-/* ----------------------------------------------------------------------------
-Function Prototypes
----------------------------------------------------------------------------- */
-
-static int nmclan_config(struct pcmcia_device *link);
-static void nmclan_release(struct pcmcia_device *link);
-
-static void nmclan_reset(struct net_device *dev);
-static int mace_config(struct net_device *dev, struct ifmap *map);
-static int mace_open(struct net_device *dev);
-static int mace_close(struct net_device *dev);
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static irqreturn_t mace_interrupt(int irq, void *dev_id);
-static struct net_device_stats *mace_get_stats(struct net_device *dev);
-static int mace_rx(struct net_device *dev, unsigned char RxCnt);
-static void restore_multicast_list(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-static void nmclan_detach(struct pcmcia_device *p_dev);
-
-static const struct net_device_ops mace_netdev_ops = {
- .ndo_open = mace_open,
- .ndo_stop = mace_close,
- .ndo_start_xmit = mace_start_xmit,
- .ndo_tx_timeout = mace_tx_timeout,
- .ndo_set_config = mace_config,
- .ndo_get_stats = mace_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int nmclan_probe(struct pcmcia_device *link)
-{
- mace_private *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "nmclan_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(mace_private));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- lp->p_dev = link;
- link->priv = dev;
-
- spin_lock_init(&lp->bank_lock);
- link->resource[0]->end = 32;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- link->config_flags |= CONF_ENABLE_IRQ;
- link->config_index = 1;
- link->config_regs = PRESENT_OPTION;
-
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
- dev->netdev_ops = &mace_netdev_ops;
- dev->ethtool_ops = &netdev_ethtool_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- return nmclan_config(link);
-} /* nmclan_attach */
-
-static void nmclan_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "nmclan_detach\n");
-
- unregister_netdev(dev);
-
- nmclan_release(link);
-
- free_netdev(dev);
-} /* nmclan_detach */
-
-/* ----------------------------------------------------------------------------
-mace_read
- Reads a MACE register. This is bank independent; however, the
- caller must ensure that this call is not interruptable. We are
- assuming that during normal operation, the MACE is always in
- bank 0.
----------------------------------------------------------------------------- */
-static int mace_read(mace_private *lp, unsigned int ioaddr, int reg)
-{
- int data = 0xFF;
- unsigned long flags;
-
- switch (reg >> 4) {
- case 0: /* register 0-15 */
- data = inb(ioaddr + AM2150_MACE_BASE + reg);
- break;
- case 1: /* register 16-31 */
- spin_lock_irqsave(&lp->bank_lock, flags);
- MACEBANK(1);
- data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
- MACEBANK(0);
- spin_unlock_irqrestore(&lp->bank_lock, flags);
- break;
- }
- return data & 0xFF;
-} /* mace_read */
-
-/* ----------------------------------------------------------------------------
-mace_write
- Writes to a MACE register. This is bank independent; however,
- the caller must ensure that this call is not interruptable. We
- are assuming that during normal operation, the MACE is always in
- bank 0.
----------------------------------------------------------------------------- */
-static void mace_write(mace_private *lp, unsigned int ioaddr, int reg,
- int data)
-{
- unsigned long flags;
-
- switch (reg >> 4) {
- case 0: /* register 0-15 */
- outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
- break;
- case 1: /* register 16-31 */
- spin_lock_irqsave(&lp->bank_lock, flags);
- MACEBANK(1);
- outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
- MACEBANK(0);
- spin_unlock_irqrestore(&lp->bank_lock, flags);
- break;
- }
-} /* mace_write */
-
-/* ----------------------------------------------------------------------------
-mace_init
- Resets the MACE chip.
----------------------------------------------------------------------------- */
-static int mace_init(mace_private *lp, unsigned int ioaddr,
- const char *enet_addr)
-{
- int i;
- int ct = 0;
-
- /* MACE Software reset */
- mace_write(lp, ioaddr, MACE_BIUCC, 1);
- while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
- /* Wait for reset bit to be cleared automatically after <= 200ns */;
- if(++ct > 500)
- {
- pr_err("reset failed, card removed?\n");
- return -1;
- }
- udelay(1);
- }
- mace_write(lp, ioaddr, MACE_BIUCC, 0);
-
- /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
- mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
-
- mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
- mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
-
- /*
- * Bit 2-1 PORTSEL[1-0] Port Select.
- * 00 AUI/10Base-2
- * 01 10Base-T
- * 10 DAI Port (reserved in Am2150)
- * 11 GPSI
- * For this card, only the first two are valid.
- * So, PLSCC should be set to
- * 0x00 for 10Base-2
- * 0x02 for 10Base-T
- * Or just set ASEL in PHYCC below!
- */
- switch (if_port) {
- case 1:
- mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
- break;
- case 2:
- mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
- break;
- default:
- mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
- /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
- and the MACE device will automatically select the operating media
- interface port. */
- break;
- }
-
- mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
- /* Poll ADDRCHG bit */
- ct = 0;
- while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
- {
- if(++ ct > 500)
- {
- pr_err("ADDRCHG timeout, card removed?\n");
- return -1;
- }
- }
- /* Set PADR register */
- for (i = 0; i < ETH_ALEN; i++)
- mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
-
- /* MAC Configuration Control Register should be written last */
- /* Let set_multicast_list set this. */
- /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
- mace_write(lp, ioaddr, MACE_MACCC, 0x00);
- return 0;
-} /* mace_init */
-
-static int nmclan_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- mace_private *lp = netdev_priv(dev);
- u8 *buf;
- size_t len;
- int i, ret;
- unsigned int ioaddr;
-
- dev_dbg(&link->dev, "nmclan_config\n");
-
- link->io_lines = 5;
- ret = pcmcia_request_io(link);
- if (ret)
- goto failed;
- ret = pcmcia_request_irq(link, mace_interrupt);
- if (ret)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- ioaddr = dev->base_addr;
-
- /* Read the ethernet address from the CIS. */
- len = pcmcia_get_tuple(link, 0x80, &buf);
- if (!buf || len < ETH_ALEN) {
- kfree(buf);
- goto failed;
- }
- eth_hw_addr_set(dev, buf);
- kfree(buf);
-
- /* Verify configuration by reading the MACE ID. */
- {
- char sig[2];
-
- sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
- sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
- if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
- dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n",
- sig[0], sig[1]);
- } else {
- pr_notice("mace id not found: %x %x should be 0x40 0x?9\n",
- sig[0], sig[1]);
- goto failed;
- }
- }
-
- if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
- goto failed;
-
- /* The if_port symbol can be set when the module is loaded */
- if (if_port <= 2)
- dev->if_port = if_port;
- else
- pr_notice("invalid if_port requested\n");
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- i = register_netdev(dev);
- if (i != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n",
- dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr);
- return 0;
-
-failed:
- nmclan_release(link);
- return -ENODEV;
-} /* nmclan_config */
-
-static void nmclan_release(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "nmclan_release\n");
- pcmcia_disable_device(link);
-}
-
-static int nmclan_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int nmclan_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- nmclan_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/* ----------------------------------------------------------------------------
-nmclan_reset
- Reset and restore all of the Xilinx and MACE registers.
----------------------------------------------------------------------------- */
-static void nmclan_reset(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
-#if RESET_XILINX
- struct pcmcia_device *link = &lp->link;
- u8 OrigCorValue;
-
- /* Save original COR value */
- pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue);
-
- /* Reset Xilinx */
- dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n",
- OrigCorValue);
- pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET);
- /* Need to wait for 20 ms for PCMCIA to finish reset. */
-
- /* Restore original COR configuration index */
- pcmcia_write_config_byte(link, CISREG_COR,
- (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK)));
- /* Xilinx is now completely reset along with the MACE chip. */
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
-#endif /* #if RESET_XILINX */
-
- /* Xilinx is now completely reset along with the MACE chip. */
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
- /* Reinitialize the MACE chip for operation. */
- mace_init(lp, dev->base_addr, dev->dev_addr);
- mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
-
- /* Restore the multicast list and enable TX and RX. */
- restore_multicast_list(dev);
-} /* nmclan_reset */
-
-/* ----------------------------------------------------------------------------
-mace_config
- [Someone tell me what this is supposed to do? Is if_port a defined
- standard? If so, there should be defines to indicate 1=10Base-T,
- 2=10Base-2, etc. including limited automatic detection.]
----------------------------------------------------------------------------- */
-static int mace_config(struct net_device *dev, struct ifmap *map)
-{
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (map->port <= 2) {
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- } else
- return -EINVAL;
- }
- return 0;
-} /* mace_config */
-
-/* ----------------------------------------------------------------------------
-mace_open
- Open device driver.
----------------------------------------------------------------------------- */
-static int mace_open(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
-
- MACEBANK(0);
-
- netif_start_queue(dev);
- nmclan_reset(dev);
-
- return 0; /* Always succeed */
-} /* mace_open */
-
-/* ----------------------------------------------------------------------------
-mace_close
- Closes device driver.
----------------------------------------------------------------------------- */
-static int mace_close(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
- /* Mask off all interrupts from the MACE chip. */
- outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-
- link->open--;
- netif_stop_queue(dev);
-
- return 0;
-} /* mace_close */
-
-static void netdev_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- snprintf(info->bus_info, sizeof(info->bus_info),
- "PCMCIA 0x%lx", dev->base_addr);
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
- .get_drvinfo = netdev_get_drvinfo,
-};
-
-/* ----------------------------------------------------------------------------
-mace_start_xmit
- This routine begins the packet transmit function. When completed,
- it will generate a transmit interrupt.
-
- According to /usr/src/linux/net/inet/dev.c, if _start_xmit
- returns 0, the "packet is now solely the responsibility of the
- driver." If _start_xmit returns non-zero, the "transmission
- failed, put skb back into a list."
----------------------------------------------------------------------------- */
-
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- netdev_notice(dev, "transmit timed out -- ");
-#if RESET_ON_TIMEOUT
- pr_cont("resetting card\n");
- pcmcia_reset_card(link->socket);
-#else /* #if RESET_ON_TIMEOUT */
- pr_cont("NOT resetting card\n");
-#endif /* #if RESET_ON_TIMEOUT */
- netif_trans_update(dev); /* prevent tx timeout */
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netif_stop_queue(dev);
-
- pr_debug("%s: mace_start_xmit(length = %ld) called.\n",
- dev->name, (long)skb->len);
-
-#if (!TX_INTERRUPTABLE)
- /* Disable MACE TX interrupts. */
- outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
- ioaddr + AM2150_MACE_BASE + MACE_IMR);
- lp->tx_irq_disabled=1;
-#endif /* #if (!TX_INTERRUPTABLE) */
-
- {
- /* This block must not be interrupted by another transmit request!
- mace_tx_timeout will take care of timer-based retransmissions from
- the upper layers. The interrupt handler is guaranteed never to
- service a transmit interrupt while we are in here.
- */
-
- dev->stats.tx_bytes += skb->len;
- lp->tx_free_frames--;
-
- /* WARNING: Write the _exact_ number of bytes written in the header! */
- /* Put out the word header [must be an outw()] . . . */
- outw(skb->len, ioaddr + AM2150_XMT);
- /* . . . and the packet [may be any combination of outw() and outb()] */
- outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
- if (skb->len & 1) {
- /* Odd byte transfer */
- outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
- }
-
-#if MULTI_TX
- if (lp->tx_free_frames > 0)
- netif_start_queue(dev);
-#endif /* #if MULTI_TX */
- }
-
-#if (!TX_INTERRUPTABLE)
- /* Re-enable MACE TX interrupts. */
- lp->tx_irq_disabled=0;
- outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-#endif /* #if (!TX_INTERRUPTABLE) */
-
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-} /* mace_start_xmit */
-
-/* ----------------------------------------------------------------------------
-mace_interrupt
- The interrupt handler.
----------------------------------------------------------------------------- */
-static irqreturn_t mace_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr;
- int status;
- int IntrCnt = MACE_MAX_IR_ITERATIONS;
-
- if (!dev) {
- pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n",
- irq);
- return IRQ_NONE;
- }
-
- ioaddr = dev->base_addr;
-
- if (lp->tx_irq_disabled) {
- const char *msg;
- if (lp->tx_irq_disabled)
- msg = "Interrupt with tx_irq_disabled";
- else
- msg = "Re-entering the interrupt handler";
- netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n",
- msg,
- inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
- inb(ioaddr + AM2150_MACE_BASE + MACE_IMR));
- /* WARNING: MACE_IR has been read! */
- return IRQ_NONE;
- }
-
- if (!netif_device_present(dev)) {
- netdev_dbg(dev, "interrupt from dead card\n");
- return IRQ_NONE;
- }
-
- do {
- /* WARNING: MACE_IR is a READ/CLEAR port! */
- status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
- if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS)
- return IRQ_NONE;
-
- pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
-
- if (status & MACE_IR_RCVINT) {
- mace_rx(dev, MACE_MAX_RX_ITERATIONS);
- }
-
- if (status & MACE_IR_XMTINT) {
- unsigned char fifofc;
- unsigned char xmtrc;
- unsigned char xmtfs;
-
- fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
- if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
- dev->stats.tx_errors++;
- outb(0xFF, ioaddr + AM2150_XMT_SKIP);
- }
-
- /* Transmit Retry Count (XMTRC, reg 4) */
- xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
- if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
- lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
-
- if (
- (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
- MACE_XMTFS_XMTSV /* Transmit Status Valid */
- ) {
- lp->mace_stats.xmtsv++;
-
- if (xmtfs & ~MACE_XMTFS_XMTSV) {
- if (xmtfs & MACE_XMTFS_UFLO) {
- /* Underflow. Indicates that the Transmit FIFO emptied before
- the end of frame was reached. */
- lp->mace_stats.uflo++;
- }
- if (xmtfs & MACE_XMTFS_LCOL) {
- /* Late Collision */
- lp->mace_stats.lcol++;
- }
- if (xmtfs & MACE_XMTFS_MORE) {
- /* MORE than one retry was needed */
- lp->mace_stats.more++;
- }
- if (xmtfs & MACE_XMTFS_ONE) {
- /* Exactly ONE retry occurred */
- lp->mace_stats.one++;
- }
- if (xmtfs & MACE_XMTFS_DEFER) {
- /* Transmission was defered */
- lp->mace_stats.defer++;
- }
- if (xmtfs & MACE_XMTFS_LCAR) {
- /* Loss of carrier */
- lp->mace_stats.lcar++;
- }
- if (xmtfs & MACE_XMTFS_RTRY) {
- /* Retry error: transmit aborted after 16 attempts */
- lp->mace_stats.rtry++;
- }
- } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
-
- } /* if (xmtfs & MACE_XMTFS_XMTSV) */
-
- dev->stats.tx_packets++;
- lp->tx_free_frames++;
- netif_wake_queue(dev);
- } /* if (status & MACE_IR_XMTINT) */
-
- if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
- if (status & MACE_IR_JAB) {
- /* Jabber Error. Excessive transmit duration (20-150ms). */
- lp->mace_stats.jab++;
- }
- if (status & MACE_IR_BABL) {
- /* Babble Error. >1518 bytes transmitted. */
- lp->mace_stats.babl++;
- }
- if (status & MACE_IR_CERR) {
- /* Collision Error. CERR indicates the absence of the
- Signal Quality Error Test message after a packet
- transmission. */
- lp->mace_stats.cerr++;
- }
- if (status & MACE_IR_RCVCCO) {
- /* Receive Collision Count Overflow; */
- lp->mace_stats.rcvcco++;
- }
- if (status & MACE_IR_RNTPCO) {
- /* Runt Packet Count Overflow */
- lp->mace_stats.rntpco++;
- }
- if (status & MACE_IR_MPCO) {
- /* Missed Packet Count Overflow */
- lp->mace_stats.mpco++;
- }
- } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
-
- } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
-
- return IRQ_HANDLED;
-} /* mace_interrupt */
-
-/* ----------------------------------------------------------------------------
-mace_rx
- Receives packets.
----------------------------------------------------------------------------- */
-static int mace_rx(struct net_device *dev, unsigned char RxCnt)
-{
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- unsigned char rx_framecnt;
- unsigned short rx_status;
-
- while (
- ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
- (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
- (RxCnt--)
- ) {
- rx_status = inw(ioaddr + AM2150_RCV);
-
- pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status"
- " 0x%X.\n", dev->name, rx_framecnt, rx_status);
-
- if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
- dev->stats.rx_errors++;
- if (rx_status & MACE_RCVFS_OFLO) {
- lp->mace_stats.oflo++;
- }
- if (rx_status & MACE_RCVFS_CLSN) {
- lp->mace_stats.clsn++;
- }
- if (rx_status & MACE_RCVFS_FRAM) {
- lp->mace_stats.fram++;
- }
- if (rx_status & MACE_RCVFS_FCS) {
- lp->mace_stats.fcs++;
- }
- } else {
- short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
- /* Auto Strip is off, always subtract 4 */
- struct sk_buff *skb;
-
- lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
- /* runt packet count */
- lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
- /* rcv collision count */
-
- pr_debug(" receiving packet size 0x%X rx_status"
- " 0x%X.\n", pkt_len, rx_status);
-
- skb = netdev_alloc_skb(dev, pkt_len + 2);
-
- if (skb) {
- skb_reserve(skb, 2);
- insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
- if (pkt_len & 1)
- *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
- skb->protocol = eth_type_trans(skb, dev);
-
- netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
- continue;
- } else {
- pr_debug("%s: couldn't allocate a sk_buff of size"
- " %d.\n", dev->name, pkt_len);
- dev->stats.rx_dropped++;
- }
- }
- outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
- } /* while */
-
- return 0;
-} /* mace_rx */
-
-/* ----------------------------------------------------------------------------
-pr_linux_stats
----------------------------------------------------------------------------- */
-static void pr_linux_stats(struct net_device_stats *pstats)
-{
- pr_debug("pr_linux_stats\n");
- pr_debug(" rx_packets=%-7ld tx_packets=%ld\n",
- (long)pstats->rx_packets, (long)pstats->tx_packets);
- pr_debug(" rx_errors=%-7ld tx_errors=%ld\n",
- (long)pstats->rx_errors, (long)pstats->tx_errors);
- pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n",
- (long)pstats->rx_dropped, (long)pstats->tx_dropped);
- pr_debug(" multicast=%-7ld collisions=%ld\n",
- (long)pstats->multicast, (long)pstats->collisions);
-
- pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n",
- (long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
- pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
- (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
- pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
- (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
-
- pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
- (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
- pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
- (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
- pr_debug(" tx_window_errors=%ld\n",
- (long)pstats->tx_window_errors);
-} /* pr_linux_stats */
-
-/* ----------------------------------------------------------------------------
-pr_mace_stats
----------------------------------------------------------------------------- */
-static void pr_mace_stats(mace_statistics *pstats)
-{
- pr_debug("pr_mace_stats\n");
-
- pr_debug(" xmtsv=%-7d uflo=%d\n",
- pstats->xmtsv, pstats->uflo);
- pr_debug(" lcol=%-7d more=%d\n",
- pstats->lcol, pstats->more);
- pr_debug(" one=%-7d defer=%d\n",
- pstats->one, pstats->defer);
- pr_debug(" lcar=%-7d rtry=%d\n",
- pstats->lcar, pstats->rtry);
-
- /* MACE_XMTRC */
- pr_debug(" exdef=%-7d xmtrc=%d\n",
- pstats->exdef, pstats->xmtrc);
-
- /* RFS1--Receive Status (RCVSTS) */
- pr_debug(" oflo=%-7d clsn=%d\n",
- pstats->oflo, pstats->clsn);
- pr_debug(" fram=%-7d fcs=%d\n",
- pstats->fram, pstats->fcs);
-
- /* RFS2--Runt Packet Count (RNTPC) */
- /* RFS3--Receive Collision Count (RCVCC) */
- pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n",
- pstats->rfs_rntpc, pstats->rfs_rcvcc);
-
- /* MACE_IR */
- pr_debug(" jab=%-7d babl=%d\n",
- pstats->jab, pstats->babl);
- pr_debug(" cerr=%-7d rcvcco=%d\n",
- pstats->cerr, pstats->rcvcco);
- pr_debug(" rntpco=%-7d mpco=%d\n",
- pstats->rntpco, pstats->mpco);
-
- /* MACE_MPC */
- pr_debug(" mpc=%d\n", pstats->mpc);
-
- /* MACE_RNTPC */
- pr_debug(" rntpc=%d\n", pstats->rntpc);
-
- /* MACE_RCVCC */
- pr_debug(" rcvcc=%d\n", pstats->rcvcc);
-
-} /* pr_mace_stats */
-
-/* ----------------------------------------------------------------------------
-update_stats
- Update statistics. We change to register window 1, so this
- should be run single-threaded if the device is active. This is
- expected to be a rare operation, and it's simpler for the rest
- of the driver to assume that window 0 is always valid rather
- than use a special window-state variable.
-
- oflo & uflo should _never_ occur since it would mean the Xilinx
- was not able to transfer data between the MACE FIFO and the
- card's SRAM fast enough. If this happens, something is
- seriously wrong with the hardware.
----------------------------------------------------------------------------- */
-static void update_stats(unsigned int ioaddr, struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
- lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
- lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
- lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
- /* At this point, mace_stats is fully updated for this call.
- We may now update the netdev stats. */
-
- /* The MACE has no equivalent for netdev stats field which are commented
- out. */
-
- /* dev->stats.multicast; */
- dev->stats.collisions =
- lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
- /* Collision: The MACE may retry sending a packet 15 times
- before giving up. The retry count is in XMTRC.
- Does each retry constitute a collision?
- If so, why doesn't the RCVCC record these collisions? */
-
- /* detailed rx_errors: */
- dev->stats.rx_length_errors =
- lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
- /* dev->stats.rx_over_errors */
- dev->stats.rx_crc_errors = lp->mace_stats.fcs;
- dev->stats.rx_frame_errors = lp->mace_stats.fram;
- dev->stats.rx_fifo_errors = lp->mace_stats.oflo;
- dev->stats.rx_missed_errors =
- lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
-
- /* detailed tx_errors */
- dev->stats.tx_aborted_errors = lp->mace_stats.rtry;
- dev->stats.tx_carrier_errors = lp->mace_stats.lcar;
- /* LCAR usually results from bad cabling. */
- dev->stats.tx_fifo_errors = lp->mace_stats.uflo;
- dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr;
- /* dev->stats.tx_window_errors; */
-} /* update_stats */
-
-/* ----------------------------------------------------------------------------
-mace_get_stats
- Gathers ethernet statistics from the MACE chip.
----------------------------------------------------------------------------- */
-static struct net_device_stats *mace_get_stats(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
- update_stats(dev->base_addr, dev);
-
- pr_debug("%s: updating the statistics.\n", dev->name);
- pr_linux_stats(&dev->stats);
- pr_mace_stats(&lp->mace_stats);
-
- return &dev->stats;
-} /* net_device_stats */
-
-/* ----------------------------------------------------------------------------
-updateCRC
- Modified from Am79C90 data sheet.
----------------------------------------------------------------------------- */
-
-#ifdef BROKEN_MULTICAST
-
-static void updateCRC(int *CRC, int bit)
-{
- static const int poly[]={
- 1,1,1,0, 1,1,0,1,
- 1,0,1,1, 1,0,0,0,
- 1,0,0,0, 0,0,1,1,
- 0,0,1,0, 0,0,0,0
- }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the
- CRC generator polynomial. */
-
- int j;
-
- /* shift CRC and control bit (CRC[32]) */
- for (j = 32; j > 0; j--)
- CRC[j] = CRC[j-1];
- CRC[0] = 0;
-
- /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
- if (bit ^ CRC[32])
- for (j = 0; j < 32; j++)
- CRC[j] ^= poly[j];
-} /* updateCRC */
-
-/* ----------------------------------------------------------------------------
-BuildLAF
- Build logical address filter.
- Modified from Am79C90 data sheet.
-
-Input
- ladrf: logical address filter (contents initialized to 0)
- adr: ethernet address
----------------------------------------------------------------------------- */
-static void BuildLAF(int *ladrf, int *adr)
-{
- int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
-
- int i, byte; /* temporary array indices */
- int hashcode; /* the output object */
-
- CRC[32]=0;
-
- for (byte = 0; byte < 6; byte++)
- for (i = 0; i < 8; i++)
- updateCRC(CRC, (adr[byte] >> i) & 1);
-
- hashcode = 0;
- for (i = 0; i < 6; i++)
- hashcode = (hashcode << 1) + CRC[i];
-
- byte = hashcode >> 3;
- ladrf[byte] |= (1 << (hashcode & 7));
-
-#ifdef PCMCIA_DEBUG
- if (0)
- printk(KERN_DEBUG " adr =%pM\n", adr);
- printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode);
- for (i = 0; i < 8; i++)
- pr_cont(" %02X", ladrf[i]);
- pr_cont("\n");
-#endif
-} /* BuildLAF */
-
-/* ----------------------------------------------------------------------------
-restore_multicast_list
- Restores the multicast filter for MACE chip to the last
- set_multicast_list() call.
-
-Input
- multicast_num_addrs
- multicast_ladrf[]
----------------------------------------------------------------------------- */
-static void restore_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- int num_addrs = lp->multicast_num_addrs;
- int *ladrf = lp->multicast_ladrf;
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- pr_debug("%s: restoring Rx mode to %d addresses.\n",
- dev->name, num_addrs);
-
- if (num_addrs > 0) {
-
- pr_debug("Attempt to restore multicast list detected.\n");
-
- mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
- /* Poll ADDRCHG bit */
- while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
- ;
- /* Set LADRF register */
- for (i = 0; i < MACE_LADRF_LEN; i++)
- mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
-
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
- } else if (num_addrs < 0) {
-
- /* Promiscuous mode: receive all packets */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC,
- MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
- );
-
- } else {
-
- /* Normal mode */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
- }
-} /* restore_multicast_list */
-
-/* ----------------------------------------------------------------------------
-set_multicast_list
- Set or clear the multicast filter for this adaptor.
-
-Input
- num_addrs == -1 Promiscuous mode, receive all packets
- num_addrs == 0 Normal mode, clear multicast list
- num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- best-effort filtering.
-Output
- multicast_num_addrs
- multicast_ladrf[]
----------------------------------------------------------------------------- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- int adr[ETH_ALEN] = {0}; /* Ethernet address */
- struct netdev_hw_addr *ha;
-
-#ifdef PCMCIA_DEBUG
- {
- static int old;
- if (netdev_mc_count(dev) != old) {
- old = netdev_mc_count(dev);
- pr_debug("%s: setting Rx mode to %d addresses.\n",
- dev->name, old);
- }
- }
-#endif
-
- /* Set multicast_num_addrs. */
- lp->multicast_num_addrs = netdev_mc_count(dev);
-
- /* Set multicast_ladrf. */
- if (num_addrs > 0) {
- /* Calculate multicast logical address filter */
- memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
- netdev_for_each_mc_addr(ha, dev) {
- memcpy(adr, ha->addr, ETH_ALEN);
- BuildLAF(lp->multicast_ladrf, adr);
- }
- }
-
- restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-#endif /* BROKEN_MULTICAST */
-
-static void restore_multicast_list(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
-
- pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name,
- lp->multicast_num_addrs);
-
- if (dev->flags & IFF_PROMISC) {
- /* Promiscuous mode: receive all packets */
- mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC,
- MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
- );
- } else {
- /* Normal mode */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
- }
-} /* restore_multicast_list */
-
-static void set_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
-#ifdef PCMCIA_DEBUG
- {
- static int old;
- if (netdev_mc_count(dev) != old) {
- old = netdev_mc_count(dev);
- pr_debug("%s: setting Rx mode to %d addresses.\n",
- dev->name, old);
- }
- }
-#endif
-
- lp->multicast_num_addrs = netdev_mc_count(dev);
- restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-static const struct pcmcia_device_id nmclan_ids[] = {
- PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, nmclan_ids);
-
-static struct pcmcia_driver nmclan_cs_driver = {
- .owner = THIS_MODULE,
- .name = "nmclan_cs",
- .probe = nmclan_probe,
- .remove = nmclan_detach,
- .id_table = nmclan_ids,
- .suspend = nmclan_suspend,
- .resume = nmclan_resume,
-};
-module_pcmcia_driver(nmclan_cs_driver);
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 07/15] drivers: net: smsc: smc9194: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The smc9194 was written by Erik Stahlman in 1996. It is an ISA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/smsc/Kconfig | 15 -
drivers/net/ethernet/smsc/Makefile | 1 -
drivers/net/ethernet/smsc/smc9194.c | 1535 -----------------------------------
3 files changed, 1551 deletions(-)
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index 13ce9086a9ca..d25bbcc98854 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -19,21 +19,6 @@ config NET_VENDOR_SMSC
if NET_VENDOR_SMSC
-config SMC9194
- tristate "SMC 9194 support"
- depends on ISA
- select CRC32
- select NETDEV_LEGACY_INIT
- help
- This is support for the SMC9xxx based Ethernet cards. Choose this
- option if you have a DELL laptop with the docking station, or
- another SMC9192/9194 based chipset. Say Y if you want it compiled
- into the kernel, and read the file
- <file:Documentation/networking/device_drivers/ethernet/smsc/smc9.rst>.
-
- To compile this driver as a module, choose M here. The module
- will be called smc9194.
-
config SMC91X
tristate "SMC 91C9x/91C1xxx support"
select CRC32
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index 1501fa364c13..afea0b94c2a4 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -3,7 +3,6 @@
# Makefile for the SMSC network device drivers.
#
-obj-$(CONFIG_SMC9194) += smc9194.o
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
obj-$(CONFIG_EPIC100) += epic100.o
diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
deleted file mode 100644
index e2e7b1c68563..000000000000
--- a/drivers/net/ethernet/smsc/smc9194.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*------------------------------------------------------------------------
- . smc9194.c
- . This is a driver for SMC's 9000 series of Ethernet cards.
- .
- . Copyright (C) 1996 by Erik Stahlman
- . This software may be used and distributed according to the terms
- . of the GNU General Public License, incorporated herein by reference.
- .
- . "Features" of the SMC chip:
- . 4608 byte packet memory. ( for the 91C92. Others have more )
- . EEPROM for configuration
- . AUI/TP selection ( mine has 10Base2/10BaseT select )
- .
- . Arguments:
- . io = for the base address
- . irq = for the IRQ
- . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
- .
- . author:
- . Erik Stahlman ( erik@vt.edu )
- . contributors:
- . Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- .
- . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
- .
- . Sources:
- . o SMC databook
- . o skeleton.c by Donald Becker ( becker@scyld.com )
- . o ( a LOT of advice from Becker as well )
- .
- . History:
- . 12/07/95 Erik Stahlman written, got receive/xmit handled
- . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-)
- . 01/06/96 Erik Stahlman cleaned up some, better testing, etc
- . 01/29/96 Erik Stahlman fixed autoirq, added multicast
- . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset
- . 2. got rid of post-decrementing bug -- UGH.
- . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more
- . descriptive error messages.
- . 02/15/96 Erik Stahlman Fixed typo that caused detection failure
- . 02/23/96 Erik Stahlman Modified it to fit into kernel tree
- . Added support to change hardware address
- . Cleared stats on opens
- . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13
- . Kludge for automatic IRQ detection
- . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 +
- . Fixed bug reported by Gardner Buchanan in
- . smc_enable, with outw instead of outb
- . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert
- . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory
- . allocation
- . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
- . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
- . 11/08/01 Matt Domsch Use common crc32 function
- ----------------------------------------------------------------------------*/
-
-static const char version[] =
- "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)";
-
-#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/crc32.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-
-#include <asm/io.h>
-
-#include "smc9194.h"
-
-#define DRV_NAME "smc9194"
-
-/*------------------------------------------------------------------------
- .
- . Configuration options, for the experienced user to change.
- .
- -------------------------------------------------------------------------*/
-
-/*
- . Do you want to use 32 bit xfers? This should work on all chips, as
- . the chipset is designed to accommodate them.
-*/
-#ifdef __sh__
-#undef USE_32_BIT
-#else
-#define USE_32_BIT 1
-#endif
-
-/*
- .the SMC9194 can be at any of the following port addresses. To change,
- .for a slightly different card, you can add it to the array. Keep in
- .mind that the array must end in zero.
-*/
-
-struct devlist {
- unsigned int port;
- unsigned int irq;
-};
-
-static struct devlist smc_devlist[] __initdata = {
- {.port = 0x200, .irq = 0},
- {.port = 0x220, .irq = 0},
- {.port = 0x240, .irq = 0},
- {.port = 0x260, .irq = 0},
- {.port = 0x280, .irq = 0},
- {.port = 0x2A0, .irq = 0},
- {.port = 0x2C0, .irq = 0},
- {.port = 0x2E0, .irq = 0},
- {.port = 0x300, .irq = 0},
- {.port = 0x320, .irq = 0},
- {.port = 0x340, .irq = 0},
- {.port = 0x360, .irq = 0},
- {.port = 0x380, .irq = 0},
- {.port = 0x3A0, .irq = 0},
- {.port = 0x3C0, .irq = 0},
- {.port = 0x3E0, .irq = 0},
- {.port = 0, .irq = 0},
-};
-/*
- . Wait time for memory to be free. This probably shouldn't be
- . tuned that much, as waiting for this means nothing else happens
- . in the system
-*/
-#define MEMORY_WAIT_TIME 16
-
-/*
- . DEBUGGING LEVELS
- .
- . 0 for normal operation
- . 1 for slightly more details
- . >2 for various levels of increasingly useless information
- . 2 for interrupt tracking, status flags
- . 3 for packet dumps, etc.
-*/
-#define SMC_DEBUG 0
-
-#if (SMC_DEBUG > 2 )
-#define PRINTK3(x) printk x
-#else
-#define PRINTK3(x)
-#endif
-
-#if SMC_DEBUG > 1
-#define PRINTK2(x) printk x
-#else
-#define PRINTK2(x)
-#endif
-
-#ifdef SMC_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x)
-#endif
-
-
-/*------------------------------------------------------------------------
- .
- . The internal workings of the driver. If you are changing anything
- . here with the SMC stuff, you should have the datasheet and known
- . what you are doing.
- .
- -------------------------------------------------------------------------*/
-#define CARDNAME "SMC9194"
-
-
-/* store this information for the driver.. */
-struct smc_local {
- /*
- If I have to wait until memory is available to send
- a packet, I will store the skbuff here, until I get the
- desired memory. Then, I'll send it out and free it.
- */
- struct sk_buff * saved_skb;
-
- /*
- . This keeps track of how many packets that I have
- . sent out. When an TX_EMPTY interrupt comes, I know
- . that all of these have been sent.
- */
- int packets_waiting;
-};
-
-
-/*-----------------------------------------------------------------
- .
- . The driver can be entered at any of the following entry points.
- .
- .------------------------------------------------------------------ */
-
-/*
- . This is called by register_netdev(). It is responsible for
- . checking the portlist for the SMC9000 series chipset. If it finds
- . one, then it will initialize the device, find the hardware information,
- . and sets up the appropriate device parameters.
- . NOTE: Interrupts are *OFF* when this procedure is called.
- .
- . NB:This shouldn't be static since it is referred to externally.
-*/
-struct net_device *smc_init(int unit);
-
-/*
- . The kernel calls this function when someone wants to use the device,
- . typically 'ifconfig ethX up'.
-*/
-static int smc_open(struct net_device *dev);
-
-/*
- . Our watchdog timed out. Called by the networking layer
-*/
-static void smc_timeout(struct net_device *dev, unsigned int txqueue);
-
-/*
- . This is called by the kernel in response to 'ifconfig ethX down'. It
- . is responsible for cleaning up everything that the open routine
- . does, and maybe putting the card into a powerdown state.
-*/
-static int smc_close(struct net_device *dev);
-
-/*
- . Finally, a call to set promiscuous mode ( for TCPDUMP and related
- . programs ) and multicast modes.
-*/
-static void smc_set_multicast_list(struct net_device *dev);
-
-
-/*---------------------------------------------------------------
- .
- . Interrupt level calls..
- .
- ----------------------------------------------------------------*/
-
-/*
- . Handles the actual interrupt
-*/
-static irqreturn_t smc_interrupt(int irq, void *);
-/*
- . This is a separate procedure to handle the receipt of a packet, to
- . leave the interrupt code looking slightly cleaner
-*/
-static inline void smc_rcv( struct net_device *dev );
-/*
- . This handles a TX interrupt, which is only called when an error
- . relating to a packet is sent.
-*/
-static inline void smc_tx( struct net_device * dev );
-
-/*
- ------------------------------------------------------------
- .
- . Internal routines
- .
- ------------------------------------------------------------
-*/
-
-/*
- . Test if a given location contains a chip, trying to cause as
- . little damage as possible if it's not a SMC chip.
-*/
-static int smc_probe(struct net_device *dev, int ioaddr);
-
-/*
- . A rather simple routine to print out a packet for debugging purposes.
-*/
-#if SMC_DEBUG > 2
-static void print_packet( byte *, int );
-#endif
-
-#define tx_done(dev) 1
-
-/* this is called to actually send the packet to the chip */
-static void smc_hardware_send_packet( struct net_device * dev );
-
-/* Since I am not sure if I will have enough room in the chip's ram
- . to store the packet, I call this routine, which either sends it
- . now, or generates an interrupt when the card is ready for the
- . packet */
-static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb,
- struct net_device *dev );
-
-/* this does a soft reset on the device */
-static void smc_reset( int ioaddr );
-
-/* Enable Interrupts, Receive, and Transmit */
-static void smc_enable( int ioaddr );
-
-/* this puts the device in an inactive state */
-static void smc_shutdown( int ioaddr );
-
-/* This routine will find the IRQ of the driver if one is not
- . specified in the input to the device. */
-static int smc_findirq( int ioaddr );
-
-/*
- . Function: smc_reset( int ioaddr )
- . Purpose:
- . This sets the SMC91xx chip to its normal state, hopefully from whatever
- . mess that any other DOS driver has put it in.
- .
- . Maybe I should reset more registers to defaults in here? SOFTRESET should
- . do that for me.
- .
- . Method:
- . 1. send a SOFT RESET
- . 2. wait for it to finish
- . 3. enable autorelease mode
- . 4. reset the memory management unit
- . 5. clear all interrupts
- .
-*/
-static void smc_reset( int ioaddr )
-{
- /* This resets the registers mostly to defaults, but doesn't
- affect EEPROM. That seems unnecessary */
- SMC_SELECT_BANK( 0 );
- outw( RCR_SOFTRESET, ioaddr + RCR );
-
- /* this should pause enough for the chip to be happy */
- SMC_DELAY( );
-
- /* Set the transmit and receive configuration registers to
- default values */
- outw( RCR_CLEAR, ioaddr + RCR );
- outw( TCR_CLEAR, ioaddr + TCR );
-
- /* set the control register to automatically
- release successfully transmitted packets, to make the best
- use out of our limited memory */
- SMC_SELECT_BANK( 1 );
- outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
-
- /* Reset the MMU */
- SMC_SELECT_BANK( 2 );
- outw( MC_RESET, ioaddr + MMU_CMD );
-
- /* Note: It doesn't seem that waiting for the MMU busy is needed here,
- but this is a place where future chipsets _COULD_ break. Be wary
- of issuing another MMU command right after this */
-
- outb( 0, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_enable
- . Purpose: let the chip talk to the outside work
- . Method:
- . 1. Enable the transmitter
- . 2. Enable the receiver
- . 3. Enable interrupts
-*/
-static void smc_enable( int ioaddr )
-{
- SMC_SELECT_BANK( 0 );
- /* see the header file for options in TCR/RCR NORMAL*/
- outw( TCR_NORMAL, ioaddr + TCR );
- outw( RCR_NORMAL, ioaddr + RCR );
-
- /* now, enable interrupts */
- SMC_SELECT_BANK( 2 );
- outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_shutdown
- . Purpose: closes down the SMC91xxx chip.
- . Method:
- . 1. zero the interrupt mask
- . 2. clear the enable receive flag
- . 3. clear the enable xmit flags
- .
- . TODO:
- . (1) maybe utilize power down mode.
- . Why not yet? Because while the chip will go into power down mode,
- . the manual says that it will wake up in response to any I/O requests
- . in the register space. Empirical results do not show this working.
-*/
-static void smc_shutdown( int ioaddr )
-{
- /* no more interrupts for me */
- SMC_SELECT_BANK( 2 );
- outb( 0, ioaddr + INT_MASK );
-
- /* and tell the card to stay away from that nasty outside world */
- SMC_SELECT_BANK( 0 );
- outb( RCR_CLEAR, ioaddr + RCR );
- outb( TCR_CLEAR, ioaddr + TCR );
-#if 0
- /* finally, shut the chip down */
- SMC_SELECT_BANK( 1 );
- outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL );
-#endif
-}
-
-
-/*
- . Function: smc_setmulticast( int ioaddr, struct net_device *dev )
- . Purpose:
- . This sets the internal hardware table to filter out unwanted multicast
- . packets before they take up memory.
- .
- . The SMC chip uses a hash table where the high 6 bits of the CRC of
- . address are the offset into the table. If that bit is 1, then the
- . multicast packet is accepted. Otherwise, it's dropped silently.
- .
- . To use the 6 bits as an offset into the table, the high 3 bits are the
- . number of the 8 bit register, while the low 3 bits are the bit within
- . that register.
- .
- . This routine is based very heavily on the one provided by Peter Cammaert.
-*/
-
-
-static void smc_setmulticast(int ioaddr, struct net_device *dev)
-{
- int i;
- unsigned char multicast_table[ 8 ];
- struct netdev_hw_addr *ha;
- /* table for flipping the order of 3 bits */
- unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
-
- /* start with a table of all zeros: reject all */
- memset( multicast_table, 0, sizeof( multicast_table ) );
-
- netdev_for_each_mc_addr(ha, dev) {
- int position;
-
- /* only use the low order bits */
- position = ether_crc_le(6, ha->addr) & 0x3f;
-
- /* do some messy swapping to put the bit in the right spot */
- multicast_table[invert3[position&7]] |=
- (1<<invert3[(position>>3)&7]);
-
- }
- /* now, the table can be loaded into the chipset */
- SMC_SELECT_BANK( 3 );
-
- for ( i = 0; i < 8 ; i++ ) {
- outb( multicast_table[i], ioaddr + MULTICAST1 + i );
- }
-}
-
-/*
- . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
- . Purpose:
- . Attempt to allocate memory for a packet, if chip-memory is not
- . available, then tell the card to generate an interrupt when it
- . is available.
- .
- . Algorithm:
- .
- . o if the saved_skb is not currently null, then drop this packet
- . on the floor. This should never happen, because of TBUSY.
- . o if the saved_skb is null, then replace it with the current packet,
- . o See if I can sending it now.
- . o (NO): Enable interrupts and let the interrupt handler deal with it.
- . o (YES):Send it now.
-*/
-static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smc_local *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- word length;
- unsigned short numPages;
- word time_out;
-
- netif_stop_queue(dev);
- /* Well, I want to send the packet.. but I don't know
- if I can send it right now... */
-
- if ( lp->saved_skb) {
- /* THIS SHOULD NEVER HAPPEN. */
- dev->stats.tx_aborted_errors++;
- printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
- return NETDEV_TX_BUSY;
- }
- lp->saved_skb = skb;
-
- length = skb->len;
-
- if (length < ETH_ZLEN) {
- if (skb_padto(skb, ETH_ZLEN)) {
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
- }
- length = ETH_ZLEN;
- }
-
- /*
- ** The MMU wants the number of pages to be the number of 256 bytes
- ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
- **
- ** Pkt size for allocating is data length +6 (for additional status words,
- ** length and ctl!) If odd size last byte is included in this header.
- */
- numPages = ((length & 0xfffe) + 6) / 256;
-
- if (numPages > 7 ) {
- printk(CARDNAME": Far too big packet error.\n");
- /* freeing the packet is a good thing here... but should
- . any packets of this size get down here? */
- dev_kfree_skb (skb);
- lp->saved_skb = NULL;
- /* this IS an error, but, i don't want the skb saved */
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
- }
- /* either way, a packet is waiting now */
- lp->packets_waiting++;
-
- /* now, try to allocate the memory */
- SMC_SELECT_BANK( 2 );
- outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
- /*
- . Performance Hack
- .
- . wait a short amount of time.. if I can send a packet now, I send
- . it now. Otherwise, I enable an interrupt and wait for one to be
- . available.
- .
- . I could have handled this a slightly different way, by checking to
- . see if any memory was available in the FREE MEMORY register. However,
- . either way, I need to generate an allocation, and the allocation works
- . no matter what, so I saw no point in checking free memory.
- */
- time_out = MEMORY_WAIT_TIME;
- do {
- word status;
-
- status = inb( ioaddr + INTERRUPT );
- if ( status & IM_ALLOC_INT ) {
- /* acknowledge the interrupt */
- outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
- break;
- }
- } while ( -- time_out );
-
- if ( !time_out ) {
- /* oh well, wait until the chip finds memory later */
- SMC_ENABLE_INT( IM_ALLOC_INT );
- PRINTK2((CARDNAME": memory allocation deferred.\n"));
- /* it's deferred, but I'll handle it later */
- return NETDEV_TX_OK;
- }
- /* or YES! I can send the packet now.. */
- smc_hardware_send_packet(dev);
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
-}
-
-/*
- . Function: smc_hardware_send_packet(struct net_device * )
- . Purpose:
- . This sends the actual packet to the SMC9xxx chip.
- .
- . Algorithm:
- . First, see if a saved_skb is available.
- . ( this should NOT be called if there is no 'saved_skb'
- . Now, find the packet number that the chip allocated
- . Point the data pointers at it in memory
- . Set the length word in the chip's memory
- . Dump the packet to chip memory
- . Check if a last byte is needed ( odd length packet )
- . if so, set the control flag right
- . Tell the card to send it
- . Enable the transmit interrupt, so I know if it failed
- . Free the kernel data if I actually sent it.
-*/
-static void smc_hardware_send_packet( struct net_device * dev )
-{
- struct smc_local *lp = netdev_priv(dev);
- byte packet_no;
- struct sk_buff * skb = lp->saved_skb;
- word length;
- unsigned int ioaddr;
- byte * buf;
-
- ioaddr = dev->base_addr;
-
- if ( !skb ) {
- PRINTK((CARDNAME": In XMIT with no packet to send\n"));
- return;
- }
- length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- buf = skb->data;
-
- /* If I get here, I _know_ there is a packet slot waiting for me */
- packet_no = inb( ioaddr + PNR_ARR + 1 );
- if ( packet_no & 0x80 ) {
- /* or isn't there? BAD CHIP! */
- netdev_dbg(dev, CARDNAME": Memory allocation failed.\n");
- dev_kfree_skb_any(skb);
- lp->saved_skb = NULL;
- netif_wake_queue(dev);
- return;
- }
-
- /* we have a packet address, so tell the card to use it */
- outb( packet_no, ioaddr + PNR_ARR );
-
- /* point to the beginning of the packet */
- outw( PTR_AUTOINC , ioaddr + POINTER );
-
- PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length));
-#if SMC_DEBUG > 2
- print_packet( buf, length );
-#endif
-
- /* send the packet length ( +6 for status, length and ctl byte )
- and the status word ( set to zeros ) */
-#ifdef USE_32_BIT
- outl( (length +6 ) << 16 , ioaddr + DATA_1 );
-#else
- outw( 0, ioaddr + DATA_1 );
- /* send the packet length ( +6 for status words, length, and ctl*/
- outb( (length+6) & 0xFF,ioaddr + DATA_1 );
- outb( (length+6) >> 8 , ioaddr + DATA_1 );
-#endif
-
- /* send the actual data
- . I _think_ it's faster to send the longs first, and then
- . mop up by sending the last word. It depends heavily
- . on alignment, at least on the 486. Maybe it would be
- . a good idea to check which is optimal? But that could take
- . almost as much time as is saved?
- */
-#ifdef USE_32_BIT
- if ( length & 0x2 ) {
- outsl(ioaddr + DATA_1, buf, length >> 2 );
- outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
- }
- else
- outsl(ioaddr + DATA_1, buf, length >> 2 );
-#else
- outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
-#endif
- /* Send the last byte, if there is one. */
-
- if ( (length & 1) == 0 ) {
- outw( 0, ioaddr + DATA_1 );
- } else {
- outb( buf[length -1 ], ioaddr + DATA_1 );
- outb( 0x20, ioaddr + DATA_1);
- }
-
- /* enable the interrupts */
- SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
-
- /* and let the chipset deal with it */
- outw( MC_ENQUEUE , ioaddr + MMU_CMD );
-
- PRINTK2((CARDNAME": Sent packet of length %d\n", length));
-
- lp->saved_skb = NULL;
- dev_kfree_skb_any (skb);
-
- netif_trans_update(dev);
-
- /* we can send another packet */
- netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------------------
- |
- | smc_init(int unit)
- | Input parameters:
- | dev->base_addr == 0, try to find all possible locations
- | dev->base_addr == 1, return failure code
- | dev->base_addr == 2, always allocate space, and return success
- | dev->base_addr == <anything else> this is the address to check
- |
- | Output:
- | pointer to net_device or ERR_PTR(error)
- |
- ---------------------------------------------------------------------------
-*/
-static int io;
-static int irq;
-static int ifport;
-
-struct net_device * __init smc_init(int unit)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct smc_local));
- struct devlist *smcdev = smc_devlist;
- 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 = smc_probe(dev, io);
- } else if (io != 0) { /* Don't probe at all. */
- err = -ENXIO;
- } else {
- for (;smcdev->port; smcdev++) {
- if (smc_probe(dev, smcdev->port) == 0)
- break;
- }
- if (!smcdev->port)
- err = -ENODEV;
- }
- if (err)
- goto out;
- err = register_netdev(dev);
- if (err)
- goto out1;
- return dev;
-out1:
- free_irq(dev->irq, dev);
- release_region(dev->base_addr, SMC_IO_EXTENT);
-out:
- free_netdev(dev);
- return ERR_PTR(err);
-}
-
-/*----------------------------------------------------------------------
- . smc_findirq
- .
- . This routine has a simple purpose -- make the SMC chip generate an
- . interrupt, so an auto-detect routine can detect it, and find the IRQ,
- ------------------------------------------------------------------------
-*/
-static int __init smc_findirq(int ioaddr)
-{
-#ifndef NO_AUTOPROBE
- int timeout = 20;
- unsigned long cookie;
-
-
- cookie = probe_irq_on();
-
- /*
- * What I try to do here is trigger an ALLOC_INT. This is done
- * by allocating a small chunk of memory, which will give an interrupt
- * when done.
- */
-
-
- SMC_SELECT_BANK(2);
- /* enable ALLOCation interrupts ONLY */
- outb( IM_ALLOC_INT, ioaddr + INT_MASK );
-
- /*
- . Allocate 512 bytes of memory. Note that the chip was just
- . reset so all the memory is available
- */
- outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
-
- /*
- . Wait until positive that the interrupt has been generated
- */
- while ( timeout ) {
- byte int_status;
-
- int_status = inb( ioaddr + INTERRUPT );
-
- if ( int_status & IM_ALLOC_INT )
- break; /* got the interrupt */
- timeout--;
- }
- /* there is really nothing that I can do here if timeout fails,
- as probe_irq_off will return a 0 anyway, which is what I
- want in this case. Plus, the clean up is needed in both
- cases. */
-
- /* DELAY HERE!
- On a fast machine, the status might change before the interrupt
- is given to the processor. This means that the interrupt was
- never detected, and probe_irq_off fails to report anything.
- This should fix probe_irq_* problems.
- */
- SMC_DELAY();
- SMC_DELAY();
-
- /* and disable all interrupts again */
- outb( 0, ioaddr + INT_MASK );
-
- /* and return what I found */
- return probe_irq_off(cookie);
-#else /* NO_AUTOPROBE */
- struct devlist *smcdev;
- for (smcdev = smc_devlist; smcdev->port; smcdev++) {
- if (smcdev->port == ioaddr)
- return smcdev->irq;
- }
- return 0;
-#endif
-}
-
-static const struct net_device_ops smc_netdev_ops = {
- .ndo_open = smc_open,
- .ndo_stop = smc_close,
- .ndo_start_xmit = smc_wait_to_send_packet,
- .ndo_tx_timeout = smc_timeout,
- .ndo_set_rx_mode = smc_set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-/*----------------------------------------------------------------------
- . Function: smc_probe( int ioaddr )
- .
- . Purpose:
- . Tests to see if a given ioaddr points to an SMC9xxx chip.
- . Returns a 0 on success
- .
- . Algorithm:
- . (1) see if the high byte of BANK_SELECT is 0x33
- . (2) compare the ioaddr with the base register's address
- . (3) see if I recognize the chip ID in the appropriate register
- .
- .---------------------------------------------------------------------
- */
-
-/*---------------------------------------------------------------
- . Here I do typical initialization tasks.
- .
- . o Initialize the structure if needed
- . o print out my vanity message if not done so already
- . o print out what type of hardware is detected
- . o print out the ethernet address
- . o find the IRQ
- . o set up my private data
- . o configure the dev structure with my subroutines
- . o actually GRAB the irq.
- . o GRAB the region
- .-----------------------------------------------------------------
-*/
-static int __init smc_probe(struct net_device *dev, int ioaddr)
-{
- int i, memory, retval;
- unsigned int bank;
-
- const char *version_string;
- const char *if_string;
-
- /* registers */
- word revision_register;
- word base_address_register;
- word configuration_register;
- word memory_info_register;
- word memory_cfg_register;
- u8 addr[ETH_ALEN];
-
- /* Grab the region so that no one else tries to probe our ioports. */
- if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
- return -EBUSY;
-
- dev->irq = irq;
- dev->if_port = ifport;
-
- /* First, see if the high byte is 0x33 */
- bank = inw( ioaddr + BANK_SELECT );
- if ( (bank & 0xFF00) != 0x3300 ) {
- retval = -ENODEV;
- goto err_out;
- }
- /* The above MIGHT indicate a device, but I need to write to further
- test this. */
- outw( 0x0, ioaddr + BANK_SELECT );
- bank = inw( ioaddr + BANK_SELECT );
- if ( (bank & 0xFF00 ) != 0x3300 ) {
- retval = -ENODEV;
- goto err_out;
- }
- /* well, we've already written once, so hopefully another time won't
- hurt. This time, I need to switch the bank register to bank 1,
- so I can access the base address register */
- SMC_SELECT_BANK(1);
- base_address_register = inw( ioaddr + BASE );
- if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) {
- printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). "
- "Probably not a SMC chip\n",
- ioaddr, base_address_register >> 3 & 0x3E0 );
- /* well, the base address register didn't match. Must not have
- been a SMC chip after all. */
- retval = -ENODEV;
- goto err_out;
- }
-
- /* check if the revision register is something that I recognize.
- These might need to be added to later, as future revisions
- could be added. */
- SMC_SELECT_BANK(3);
- revision_register = inw( ioaddr + REVISION );
- if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) {
- /* I don't recognize this chip, so... */
- printk(CARDNAME ": IO %x: Unrecognized revision register:"
- " %x, Contact author.\n", ioaddr, revision_register);
-
- retval = -ENODEV;
- goto err_out;
- }
-
- /* at this point I'll assume that the chip is an SMC9xxx.
- It might be prudent to check a listing of MAC addresses
- against the hardware address, or do some other tests. */
-
- pr_info_once("%s\n", version);
-
- /* fill in some of the fields */
- dev->base_addr = ioaddr;
-
- /*
- . Get the MAC address ( bank 1, regs 4 - 9 )
- */
- SMC_SELECT_BANK( 1 );
- for ( i = 0; i < 6; i += 2 ) {
- word address;
-
- address = inw( ioaddr + ADDR0 + i );
- addr[i + 1] = address >> 8;
- addr[i] = address & 0xFF;
- }
- eth_hw_addr_set(dev, addr);
-
- /* get the memory information */
-
- SMC_SELECT_BANK( 0 );
- memory_info_register = inw( ioaddr + MIR );
- memory_cfg_register = inw( ioaddr + MCR );
- memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */
- memory *= 256 * ( memory_info_register & 0xFF );
-
- /*
- Now, I want to find out more about the chip. This is sort of
- redundant, but it's cleaner to have it in both, rather than having
- one VERY long probe procedure.
- */
- SMC_SELECT_BANK(3);
- revision_register = inw( ioaddr + REVISION );
- version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ];
- if ( !version_string ) {
- /* I shouldn't get here because this call was done before.... */
- retval = -ENODEV;
- goto err_out;
- }
-
- /* is it using AUI or 10BaseT ? */
- if ( dev->if_port == 0 ) {
- SMC_SELECT_BANK(1);
- configuration_register = inw( ioaddr + CONFIG );
- if ( configuration_register & CFG_AUI_SELECT )
- dev->if_port = 2;
- else
- dev->if_port = 1;
- }
- if_string = interfaces[ dev->if_port - 1 ];
-
- /* now, reset the chip, and put it into a known state */
- smc_reset( ioaddr );
-
- /*
- . If dev->irq is 0, then the device has to be banged on to see
- . what the IRQ is.
- .
- . This banging doesn't always detect the IRQ, for unknown reasons.
- . a workaround is to reset the chip and try again.
- .
- . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
- . be what is requested on the command line. I don't do that, mostly
- . because the card that I have uses a non-standard method of accessing
- . the IRQs, and because this _should_ work in most configurations.
- .
- . Specifying an IRQ is done with the assumption that the user knows
- . what (s)he is doing. No checking is done!!!!
- .
- */
- if ( dev->irq < 2 ) {
- int trials;
-
- trials = 3;
- while ( trials-- ) {
- dev->irq = smc_findirq( ioaddr );
- if ( dev->irq )
- break;
- /* kick the card and try again */
- smc_reset( ioaddr );
- }
- }
- if (dev->irq == 0 ) {
- printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
- retval = -ENODEV;
- goto err_out;
- }
-
- /* now, print out the card info, in a short format.. */
-
- netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ",
- version_string, revision_register & 0xF, ioaddr, dev->irq,
- if_string, memory);
- /*
- . Print the Ethernet address
- */
- netdev_info(dev, "ADDR: %pM\n", dev->dev_addr);
-
- /* Grab the IRQ */
- retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
- if (retval) {
- netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n",
- DRV_NAME, dev->irq, retval);
- goto err_out;
- }
-
- dev->netdev_ops = &smc_netdev_ops;
- dev->watchdog_timeo = HZ/20;
-
- return 0;
-
-err_out:
- release_region(ioaddr, SMC_IO_EXTENT);
- return retval;
-}
-
-#if SMC_DEBUG > 2
-static void print_packet( byte * buf, int length )
-{
-#if 0
- print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1,
- buf, length, true);
-#endif
-}
-#endif
-
-
-/*
- * Open and Initialize the board
- *
- * Set up everything, reset the card, etc ..
- *
- */
-static int smc_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- int i; /* used to set hw ethernet address */
-
- /* clear out all the junk that was put here before... */
- memset(netdev_priv(dev), 0, sizeof(struct smc_local));
-
- /* reset the hardware */
-
- smc_reset( ioaddr );
- smc_enable( ioaddr );
-
- /* Select which interface to use */
-
- SMC_SELECT_BANK( 1 );
- if ( dev->if_port == 1 ) {
- outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
- ioaddr + CONFIG );
- }
- else if ( dev->if_port == 2 ) {
- outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
- ioaddr + CONFIG );
- }
-
- /*
- According to Becker, I have to set the hardware address
- at this point, because the (l)user can set it with an
- ioctl. Easily done...
- */
- SMC_SELECT_BANK( 1 );
- for ( i = 0; i < 6; i += 2 ) {
- word address;
-
- address = dev->dev_addr[ i + 1 ] << 8 ;
- address |= dev->dev_addr[ i ];
- outw( address, ioaddr + ADDR0 + i );
- }
-
- netif_start_queue(dev);
- return 0;
-}
-
-/*--------------------------------------------------------
- . Called by the kernel to send a packet out into the void
- . of the net. This routine is largely based on
- . skeleton.c, from Becker.
- .--------------------------------------------------------
-*/
-
-static void smc_timeout(struct net_device *dev, unsigned int txqueue)
-{
- /* If we get here, some higher level has decided we are broken.
- There should really be a "kick me" function call instead. */
- netdev_warn(dev, CARDNAME": transmit timed out, %s?\n",
- tx_done(dev) ? "IRQ conflict" : "network cable problem");
- /* "kick" the adaptor */
- smc_reset( dev->base_addr );
- smc_enable( dev->base_addr );
- netif_trans_update(dev); /* prevent tx timeout */
- /* clear anything saved */
- ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
- netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------
- .
- . smc_rcv - receive a packet from the card
- .
- . There is ( at least ) a packet waiting to be read from
- . chip-memory.
- .
- . o Read the status
- . o If an error, record it
- . o otherwise, read in the packet
- --------------------------------------------------------------
-*/
-static void smc_rcv(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int packet_number;
- word status;
- word packet_length;
-
- /* assume bank 2 */
-
- packet_number = inw( ioaddr + FIFO_PORTS );
-
- if ( packet_number & FP_RXEMPTY ) {
- /* we got called , but nothing was on the FIFO */
- PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n"));
- /* don't need to restore anything */
- return;
- }
-
- /* start reading from the start of the packet */
- outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
-
- /* First two words are status and packet_length */
- status = inw( ioaddr + DATA_1 );
- packet_length = inw( ioaddr + DATA_1 );
-
- packet_length &= 0x07ff; /* mask off top bits */
-
- PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
- /*
- . the packet length contains 3 extra words :
- . status, length, and an extra word with an odd byte .
- */
- packet_length -= 6;
-
- if ( !(status & RS_ERRORS ) ){
- /* do stuff to make a new packet */
- struct sk_buff * skb;
- byte * data;
-
- /* read one extra byte */
- if ( status & RS_ODDFRAME )
- packet_length++;
-
- /* set multicast stats */
- if ( status & RS_MULTICAST )
- dev->stats.multicast++;
-
- skb = netdev_alloc_skb(dev, packet_length + 5);
- if ( skb == NULL ) {
- dev->stats.rx_dropped++;
- goto done;
- }
-
- /*
- ! This should work without alignment, but it could be
- ! in the worse case
- */
-
- skb_reserve( skb, 2 ); /* 16 bit alignment */
-
- data = skb_put( skb, packet_length);
-
-#ifdef USE_32_BIT
- /* QUESTION: Like in the TX routine, do I want
- to send the DWORDs or the bytes first, or some
- mixture. A mixture might improve already slow PIO
- performance */
- PRINTK3((" Reading %d dwords (and %d bytes)\n",
- packet_length >> 2, packet_length & 3 ));
- insl(ioaddr + DATA_1 , data, packet_length >> 2 );
- /* read the left over bytes */
- insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
- packet_length & 0x3 );
-#else
- PRINTK3((" Reading %d words and %d byte(s)\n",
- (packet_length >> 1 ), packet_length & 1 ));
- insw(ioaddr + DATA_1 , data, packet_length >> 1);
- if ( packet_length & 1 ) {
- data += packet_length & ~1;
- *(data++) = inb( ioaddr + DATA_1 );
- }
-#endif
-#if SMC_DEBUG > 2
- print_packet( data, packet_length );
-#endif
-
- skb->protocol = eth_type_trans(skb, dev );
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += packet_length;
- } else {
- /* error ... */
- dev->stats.rx_errors++;
-
- if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++;
- if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
- dev->stats.rx_length_errors++;
- if ( status & RS_BADCRC) dev->stats.rx_crc_errors++;
- }
-
-done:
- /* error or good, tell the card to get rid of this packet */
- outw( MC_RELEASE, ioaddr + MMU_CMD );
-}
-
-
-/*************************************************************************
- . smc_tx
- .
- . Purpose: Handle a transmit error message. This will only be called
- . when an error, because of the AUTO_RELEASE mode.
- .
- . Algorithm:
- . Save pointer and packet no
- . Get the packet no from the top of the queue
- . check if it's valid ( if not, is this an error??? )
- . read the status word
- . record the error
- . ( resend? Not really, since we don't want old packets around )
- . Restore saved values
- ************************************************************************/
-static void smc_tx( struct net_device * dev )
-{
- int ioaddr = dev->base_addr;
- struct smc_local *lp = netdev_priv(dev);
- byte saved_packet;
- byte packet_no;
- word tx_status;
-
-
- /* assume bank 2 */
-
- saved_packet = inb( ioaddr + PNR_ARR );
- packet_no = inw( ioaddr + FIFO_PORTS );
- packet_no &= 0x7F;
-
- /* select this as the packet to read from */
- outb( packet_no, ioaddr + PNR_ARR );
-
- /* read the first word from this packet */
- outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
-
- tx_status = inw( ioaddr + DATA_1 );
- PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status));
-
- dev->stats.tx_errors++;
- if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
- if ( tx_status & TS_LATCOL ) {
- netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n");
- dev->stats.tx_window_errors++;
- }
-#if 0
- if ( tx_status & TS_16COL ) { ... }
-#endif
-
- if ( tx_status & TS_SUCCESS ) {
- netdev_info(dev, CARDNAME": Successful packet caused interrupt\n");
- }
- /* re-enable transmit */
- SMC_SELECT_BANK( 0 );
- outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
-
- /* kill the packet */
- SMC_SELECT_BANK( 2 );
- outw( MC_FREEPKT, ioaddr + MMU_CMD );
-
- /* one less packet waiting for me */
- lp->packets_waiting--;
-
- outb( saved_packet, ioaddr + PNR_ARR );
-}
-
-/*--------------------------------------------------------------------
- .
- . This is the main routine of the driver, to handle the device when
- . it needs some attention.
- .
- . So:
- . first, save state of the chipset
- . branch off into routines to handle each case, and acknowledge
- . each to the interrupt register
- . and finally restore state.
- .
- ---------------------------------------------------------------------*/
-
-static irqreturn_t smc_interrupt(int irq, void * dev_id)
-{
- struct net_device *dev = dev_id;
- int ioaddr = dev->base_addr;
- struct smc_local *lp = netdev_priv(dev);
-
- byte status;
- word card_stats;
- byte mask;
- int timeout;
- /* state registers */
- word saved_bank;
- word saved_pointer;
- int handled = 0;
-
-
- PRINTK3((CARDNAME": SMC interrupt started\n"));
-
- saved_bank = inw( ioaddr + BANK_SELECT );
-
- SMC_SELECT_BANK(2);
- saved_pointer = inw( ioaddr + POINTER );
-
- mask = inb( ioaddr + INT_MASK );
- /* clear all interrupts */
- outb( 0, ioaddr + INT_MASK );
-
-
- /* set a timeout value, so I don't stay here forever */
- timeout = 4;
-
- PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask));
- do {
- /* read the status flag, and mask it */
- status = inb( ioaddr + INTERRUPT ) & mask;
- if (!status )
- break;
-
- handled = 1;
-
- PRINTK3((KERN_WARNING CARDNAME
- ": Handling interrupt status %x\n", status));
-
- if (status & IM_RCV_INT) {
- /* Got a packet(s). */
- PRINTK2((KERN_WARNING CARDNAME
- ": Receive Interrupt\n"));
- smc_rcv(dev);
- } else if (status & IM_TX_INT ) {
- PRINTK2((KERN_WARNING CARDNAME
- ": TX ERROR handled\n"));
- smc_tx(dev);
- outb(IM_TX_INT, ioaddr + INTERRUPT );
- } else if (status & IM_TX_EMPTY_INT ) {
- /* update stats */
- SMC_SELECT_BANK( 0 );
- card_stats = inw( ioaddr + COUNTER );
- /* single collisions */
- dev->stats.collisions += card_stats & 0xF;
- card_stats >>= 4;
- /* multiple collisions */
- dev->stats.collisions += card_stats & 0xF;
-
- /* these are for when linux supports these statistics */
-
- SMC_SELECT_BANK( 2 );
- PRINTK2((KERN_WARNING CARDNAME
- ": TX_BUFFER_EMPTY handled\n"));
- outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
- mask &= ~IM_TX_EMPTY_INT;
- dev->stats.tx_packets += lp->packets_waiting;
- lp->packets_waiting = 0;
-
- } else if (status & IM_ALLOC_INT ) {
- PRINTK2((KERN_DEBUG CARDNAME
- ": Allocation interrupt\n"));
- /* clear this interrupt so it doesn't happen again */
- mask &= ~IM_ALLOC_INT;
-
- smc_hardware_send_packet( dev );
-
- /* enable xmit interrupts based on this */
- mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
-
- /* and let the card send more packets to me */
- netif_wake_queue(dev);
-
- PRINTK2((CARDNAME": Handoff done successfully.\n"));
- } else if (status & IM_RX_OVRN_INT ) {
- dev->stats.rx_errors++;
- dev->stats.rx_fifo_errors++;
- outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
- } else if (status & IM_EPH_INT ) {
- PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n"));
- } else if (status & IM_ERCV_INT ) {
- PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n"));
- outb( IM_ERCV_INT, ioaddr + INTERRUPT );
- }
- } while ( timeout -- );
-
-
- /* restore state register */
- SMC_SELECT_BANK( 2 );
- outb( mask, ioaddr + INT_MASK );
-
- PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask));
- outw( saved_pointer, ioaddr + POINTER );
-
- SMC_SELECT_BANK( saved_bank );
-
- PRINTK3((CARDNAME ": Interrupt done\n"));
- return IRQ_RETVAL(handled);
-}
-
-
-/*----------------------------------------------------
- . smc_close
- .
- . this makes the board clean up everything that it can
- . and not talk to the outside world. Caused by
- . an 'ifconfig ethX down'
- .
- -----------------------------------------------------*/
-static int smc_close(struct net_device *dev)
-{
- netif_stop_queue(dev);
- /* clear everything */
- smc_shutdown( dev->base_addr );
-
- /* Update the statistics here. */
- return 0;
-}
-
-/*-----------------------------------------------------------
- . smc_set_multicast_list
- .
- . This routine will, depending on the values passed to it,
- . either make it accept multicast packets, go into
- . promiscuous mode ( for TCPDUMP and cousins ) or accept
- . a select set of multicast packets
-*/
-static void smc_set_multicast_list(struct net_device *dev)
-{
- short ioaddr = dev->base_addr;
-
- SMC_SELECT_BANK(0);
- if ( dev->flags & IFF_PROMISC )
- outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
-
-/* BUG? I never disable promiscuous mode if multicasting was turned on.
- Now, I turn off promiscuous mode, but I don't do anything to multicasting
- when promiscuous mode is turned on.
-*/
-
- /* Here, I am setting this to accept all multicast packets.
- I don't need to zero the multicast table, because the flag is
- checked before the table is
- */
- else if (dev->flags & IFF_ALLMULTI)
- outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
-
- /* We just get all multicast packets even if we only want them
- . from one source. This will be changed at some future
- . point. */
- else if (!netdev_mc_empty(dev)) {
- /* support hardware multicasting */
-
- /* be sure I get rid of flags I might have set */
- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
- ioaddr + RCR );
- /* NOTE: this has to set the bank, so make sure it is the
- last thing called. The bank is set to zero at the top */
- smc_setmulticast(ioaddr, dev);
- }
- else {
- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
- ioaddr + RCR );
-
- /*
- since I'm disabling all multicast entirely, I need to
- clear the multicast list
- */
- SMC_SELECT_BANK( 3 );
- outw( 0, ioaddr + MULTICAST1 );
- outw( 0, ioaddr + MULTICAST2 );
- outw( 0, ioaddr + MULTICAST3 );
- outw( 0, ioaddr + MULTICAST4 );
- }
-}
-
-#ifdef MODULE
-
-static struct net_device *devSMC9194;
-MODULE_DESCRIPTION("SMC 9194 Ethernet driver");
-MODULE_LICENSE("GPL");
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(ifport, int, 0);
-MODULE_PARM_DESC(io, "SMC 99194 I/O base address");
-MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");
-MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");
-
-static int __init smc_init_module(void)
-{
- if (io == 0)
- printk(KERN_WARNING
- CARDNAME": You shouldn't use auto-probing with insmod!\n" );
-
- /* copy the parameters from insmod into the device structure */
- devSMC9194 = smc_init(-1);
- return PTR_ERR_OR_ZERO(devSMC9194);
-}
-module_init(smc_init_module);
-
-static void __exit smc_cleanup_module(void)
-{
- unregister_netdev(devSMC9194);
- free_irq(devSMC9194->irq, devSMC9194);
- release_region(devSMC9194->base_addr, SMC_IO_EXTENT);
- free_netdev(devSMC9194);
-}
-module_exit(smc_cleanup_module);
-
-#endif /* MODULE */
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 08/15] drivers: net: smsc: smc91c92: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The smc91c92 was written by David A Hinds in 1999. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Remove the Documentation as well, since it refers to kernel versions
1.2.13 until 1.3.71 and FTP sites which no longer exist.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Remove document as well
---
.../device_drivers/ethernet/smsc/smc9.rst | 48 -
drivers/net/ethernet/smsc/Kconfig | 12 -
drivers/net/ethernet/smsc/Makefile | 1 -
drivers/net/ethernet/smsc/smc91c92_cs.c | 2059 --------------------
4 files changed, 2120 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst b/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst
deleted file mode 100644
index e5eac896a631..000000000000
--- a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-================
-SMC 9xxxx Driver
-================
-
-Revision 0.12
-
-3/5/96
-
-Copyright 1996 Erik Stahlman
-
-Released under terms of the GNU General Public License.
-
-This file contains the instructions and caveats for my SMC9xxx driver. You
-should not be using the driver without reading this file.
-
-Things to note about installation:
-
- 1. The driver should work on all kernels from 1.2.13 until 1.3.71.
- (A kernel patch is supplied for 1.3.71 )
-
- 2. If you include this into the kernel, you might need to change some
- options, such as for forcing IRQ.
-
-
- 3. To compile as a module, run 'make'.
- Make will give you the appropriate options for various kernel support.
-
- 4. Loading the driver as a module::
-
- use: insmod smc9194.o
- optional parameters:
- io=xxxx : your base address
- irq=xx : your irq
- ifport=x : 0 for whatever is default
- 1 for twisted pair
- 2 for AUI ( or BNC on some cards )
-
-How to obtain the latest version?
-
-FTP:
- ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
- ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz
-
-
-Contacting me:
- erik@mail.vt.edu
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index d25bbcc98854..66bca803b19c 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -37,18 +37,6 @@ config SMC91X
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.rst>.
-config PCMCIA_SMC91C92
- tristate "SMC 91Cxx PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- select CRC32
- select MII
- help
- Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
- (PC-card) Ethernet or Fast Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called smc91c92_cs. If unsure, say N.
-
config EPIC100
tristate "SMC EtherPower II"
depends on PCI
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index afea0b94c2a4..ab6f03f7ba17 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -4,7 +4,6 @@
#
obj-$(CONFIG_SMC91X) += smc91x.o
-obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
obj-$(CONFIG_EPIC100) += epic100.o
obj-$(CONFIG_SMSC9420) += smsc9420.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
deleted file mode 100644
index cc0c75694351..000000000000
--- a/drivers/net/ethernet/smsc/smc91c92_cs.c
+++ /dev/null
@@ -1,2059 +0,0 @@
-/*======================================================================
-
- A PCMCIA ethernet driver for SMC91c92-based cards.
-
- This driver supports Megahertz PCMCIA ethernet cards; and
- Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
- multifunction cards.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- smc91c92_cs.c 1.122 2002/10/25 06:26:39
-
- This driver contains code written by Donald Becker
- (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
- David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
- (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of
- Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've
- incorporated some parts of his driver here. I (Dave) wrote most
- of the PCMCIA glue code, and the Ositech support code. Kelly
- Stephens (kstephen@holli.com) added support for the Motorola
- Mariner, with help from Allen Brost.
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/crc32.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/jiffies.h>
-#include <linux/firmware.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/ss.h>
-
-#include <asm/io.h>
-#include <linux/uaccess.h>
-
-/*====================================================================*/
-
-static const char *if_names[] = { "auto", "10baseT", "10base2"};
-
-/* Firmware name */
-#define FIRMWARE_NAME "ositech/Xilinx7OD.bin"
-
-/* Module parameters */
-
-MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FIRMWARE_NAME);
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/*
- Transceiver/media type.
- 0 = auto
- 1 = 10baseT (and autoselect if #define AUTOSELECT),
- 2 = AUI/10base2,
-*/
-INT_MODULE_PARM(if_port, 0);
-
-
-#define DRV_NAME "smc91c92_cs"
-#define DRV_VERSION "1.123"
-
-/*====================================================================*/
-
-/* Operational parameter that usually are not changed. */
-
-/* Time in jiffies before concluding Tx hung */
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-#define INTR_WORK 4
-
-/* Times to check the check the chip before concluding that it doesn't
- currently have room for another Tx packet. */
-#define MEMORY_WAIT_TIME 8
-
-struct smc_private {
- struct pcmcia_device *p_dev;
- spinlock_t lock;
- u_short manfid;
- u_short cardid;
-
- struct sk_buff *saved_skb;
- int packets_waiting;
- void __iomem *base;
- u_short cfg;
- struct timer_list media;
- int watchdog, tx_err;
- u_short media_status;
- u_short fast_poll;
- u_short link_status;
- struct mii_if_info mii_if;
- int duplex;
- int rx_ovrn;
- unsigned long last_rx;
-};
-
-/* Special definitions for Megahertz multifunction cards */
-#define MEGAHERTZ_ISR 0x0380
-
-/* Special function registers for Motorola Mariner */
-#define MOT_LAN 0x0000
-#define MOT_UART 0x0020
-#define MOT_EEPROM 0x20
-
-#define MOT_NORMAL \
-(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
-
-/* Special function registers for Ositech cards */
-#define OSITECH_AUI_CTL 0x0c
-#define OSITECH_PWRDOWN 0x0d
-#define OSITECH_RESET 0x0e
-#define OSITECH_ISR 0x0f
-#define OSITECH_AUI_PWR 0x0c
-#define OSITECH_RESET_ISR 0x0e
-
-#define OSI_AUI_PWR 0x40
-#define OSI_LAN_PWRDOWN 0x02
-#define OSI_MODEM_PWRDOWN 0x01
-#define OSI_LAN_RESET 0x02
-#define OSI_MODEM_RESET 0x01
-
-/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
-#define BANK_SELECT 14 /* Window select register. */
-#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); }
-
-/* Bank 0 registers. */
-#define TCR 0 /* transmit control register */
-#define TCR_CLEAR 0 /* do NOTHING */
-#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
-#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */
-#define TCR_MONCSN 0x0400 /* Monitor Carrier. */
-#define TCR_FDUPLX 0x0800 /* Full duplex mode. */
-#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
-
-#define EPH 2 /* Ethernet Protocol Handler report. */
-#define EPH_TX_SUC 0x0001
-#define EPH_SNGLCOL 0x0002
-#define EPH_MULCOL 0x0004
-#define EPH_LTX_MULT 0x0008
-#define EPH_16COL 0x0010
-#define EPH_SQET 0x0020
-#define EPH_LTX_BRD 0x0040
-#define EPH_TX_DEFR 0x0080
-#define EPH_LAT_COL 0x0200
-#define EPH_LOST_CAR 0x0400
-#define EPH_EXC_DEF 0x0800
-#define EPH_CTR_ROL 0x1000
-#define EPH_RX_OVRN 0x2000
-#define EPH_LINK_OK 0x4000
-#define EPH_TX_UNRN 0x8000
-#define MEMINFO 8 /* Memory Information Register */
-#define MEMCFG 10 /* Memory Configuration Register */
-
-/* Bank 1 registers. */
-#define CONFIG 0
-#define CFG_MII_SELECT 0x8000 /* 91C100 only */
-#define CFG_NO_WAIT 0x1000
-#define CFG_FULL_STEP 0x0400
-#define CFG_SET_SQLCH 0x0200
-#define CFG_AUI_SELECT 0x0100
-#define CFG_16BIT 0x0080
-#define CFG_DIS_LINK 0x0040
-#define CFG_STATIC 0x0030
-#define CFG_IRQ_SEL_1 0x0004
-#define CFG_IRQ_SEL_0 0x0002
-#define BASE_ADDR 2
-#define ADDR0 4
-#define GENERAL 10
-#define CONTROL 12
-#define CTL_STORE 0x0001
-#define CTL_RELOAD 0x0002
-#define CTL_EE_SELECT 0x0004
-#define CTL_TE_ENABLE 0x0020
-#define CTL_CR_ENABLE 0x0040
-#define CTL_LE_ENABLE 0x0080
-#define CTL_AUTO_RELEASE 0x0800
-#define CTL_POWERDOWN 0x2000
-
-/* Bank 2 registers. */
-#define MMU_CMD 0
-#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
-#define MC_RESET 0x40
-#define MC_RELEASE 0x80 /* remove and release the current rx packet */
-#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
-#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
-#define PNR_ARR 2
-#define FIFO_PORTS 4
-#define FP_RXEMPTY 0x8000
-#define POINTER 6
-#define PTR_AUTO_INC 0x0040
-#define PTR_READ 0x2000
-#define PTR_AUTOINC 0x4000
-#define PTR_RCV 0x8000
-#define DATA_1 8
-#define INTERRUPT 12
-#define IM_RCV_INT 0x1
-#define IM_TX_INT 0x2
-#define IM_TX_EMPTY_INT 0x4
-#define IM_ALLOC_INT 0x8
-#define IM_RX_OVRN_INT 0x10
-#define IM_EPH_INT 0x20
-
-#define RCR 4
-enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
- RxEnable = 0x0100, RxStripCRC = 0x0200};
-#define RCR_SOFTRESET 0x8000 /* resets the chip */
-#define RCR_STRIP_CRC 0x200 /* strips CRC */
-#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
-#define RCR_ALMUL 0x4 /* receive all multicast packets */
-#define RCR_PROMISC 0x2 /* enable promiscuous mode */
-
-/* the normal settings for the RCR register : */
-#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
-#define RCR_CLEAR 0x0 /* set it to a base state */
-#define COUNTER 6
-
-/* BANK 3 -- not the same values as in smc9194! */
-#define MULTICAST0 0
-#define MULTICAST2 2
-#define MULTICAST4 4
-#define MULTICAST6 6
-#define MGMT 8
-#define REVISION 0x0a
-
-/* Transmit status bits. */
-#define TS_SUCCESS 0x0001
-#define TS_16COL 0x0010
-#define TS_LATCOL 0x0200
-#define TS_LOSTCAR 0x0400
-
-/* Receive status bits. */
-#define RS_ALGNERR 0x8000
-#define RS_BADCRC 0x2000
-#define RS_ODDFRAME 0x1000
-#define RS_TOOLONG 0x0800
-#define RS_TOOSHORT 0x0400
-#define RS_MULTICAST 0x0001
-#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
-
-#define set_bits(v, p) outw(inw(p)|(v), (p))
-#define mask_bits(v, p) outw(inw(p)&(v), (p))
-
-/*====================================================================*/
-
-static void smc91c92_detach(struct pcmcia_device *p_dev);
-static int smc91c92_config(struct pcmcia_device *link);
-static void smc91c92_release(struct pcmcia_device *link);
-
-static int smc_open(struct net_device *dev);
-static int smc_close(struct net_device *dev);
-static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t smc_interrupt(int irq, void *dev_id);
-static void smc_rx(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static int s9k_config(struct net_device *dev, struct ifmap *map);
-static void smc_set_xcvr(struct net_device *dev, int if_port);
-static void smc_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static void mdio_sync(unsigned int addr);
-static int mdio_read(struct net_device *dev, int phy_id, int loc);
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
-static int smc_link_ok(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-
-static const struct net_device_ops smc_netdev_ops = {
- .ndo_open = smc_open,
- .ndo_stop = smc_close,
- .ndo_start_xmit = smc_start_xmit,
- .ndo_tx_timeout = smc_tx_timeout,
- .ndo_set_config = s9k_config,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_eth_ioctl = smc_ioctl,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int smc91c92_probe(struct pcmcia_device *link)
-{
- struct smc_private *smc;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "smc91c92_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(struct smc_private));
- if (!dev)
- return -ENOMEM;
- smc = netdev_priv(dev);
- smc->p_dev = link;
- link->priv = dev;
-
- spin_lock_init(&smc->lock);
-
- /* The SMC91c92-specific entries in the device structure. */
- dev->netdev_ops = &smc_netdev_ops;
- dev->ethtool_ops = ðtool_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- smc->mii_if.dev = dev;
- smc->mii_if.mdio_read = mdio_read;
- smc->mii_if.mdio_write = mdio_write;
- smc->mii_if.phy_id_mask = 0x1f;
- smc->mii_if.reg_num_mask = 0x1f;
-
- return smc91c92_config(link);
-} /* smc91c92_attach */
-
-static void smc91c92_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "smc91c92_detach\n");
-
- unregister_netdev(dev);
-
- smc91c92_release(link);
-
- free_netdev(dev);
-} /* smc91c92_detach */
-
-/*====================================================================*/
-
-static int cvt_ascii_address(struct net_device *dev, char *s)
-{
- u8 mac[ETH_ALEN];
- int i, j, da, c;
-
- if (strlen(s) != 12)
- return -1;
- for (i = 0; i < 6; i++) {
- da = 0;
- for (j = 0; j < 2; j++) {
- c = *s++;
- da <<= 4;
- da += ((c >= '0') && (c <= '9')) ?
- (c - '0') : ((c & 0x0f) + 9);
- }
- mac[i] = da;
- }
- eth_hw_addr_set(dev, mac);
- return 0;
-}
-
-/*====================================================================
-
- Configuration stuff for Megahertz cards
-
- mhz_3288_power() is used to power up a 3288's ethernet chip.
- mhz_mfc_config() handles socket setup for multifunction (1144
- and 3288) cards. mhz_setup() gets a card's hardware ethernet
- address.
-
-======================================================================*/
-
-static int mhz_3288_power(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- u_char tmp;
-
- /* Read the ISR twice... */
- readb(smc->base+MEGAHERTZ_ISR);
- udelay(5);
- readb(smc->base+MEGAHERTZ_ISR);
-
- /* Pause 200ms... */
- mdelay(200);
-
- /* Now read and write the COR... */
- tmp = readb(smc->base + link->config_base + CISREG_COR);
- udelay(5);
- writeb(tmp, smc->base + link->config_base + CISREG_COR);
-
- return 0;
-}
-
-static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- int k;
- p_dev->io_lines = 16;
- p_dev->resource[1]->start = p_dev->resource[0]->start;
- p_dev->resource[1]->end = 8;
- p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- for (k = 0; k < 0x400; k += 0x10) {
- if (k & 0x80)
- continue;
- p_dev->resource[0]->start = k ^ 0x300;
- if (!pcmcia_request_io(p_dev))
- return 0;
- }
- return -ENODEV;
-}
-
-static int mhz_mfc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int offset;
- int i;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
- CONF_AUTO_SET_IO;
-
- /* The Megahertz combo cards have modem-like CIS entries, so
- we have to explicitly try a bunch of port combinations. */
- if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
- return -ENODEV;
-
- dev->base_addr = link->resource[0]->start;
-
- /* Allocate a memory window, for accessing the ISR */
- link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return -ENODEV;
-
- smc->base = ioremap(link->resource[2]->start,
- resource_size(link->resource[2]));
- offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
- i = pcmcia_map_mem_page(link, link->resource[2], offset);
- if ((i == 0) &&
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
-
- return 0;
-}
-
-static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
- cisparse_t parse;
- u8 *buf;
-
- if (pcmcia_parse_tuple(tuple, &parse))
- return -EINVAL;
-
- buf = parse.version_1.str + parse.version_1.ofs[3];
-
- if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
- return 0;
-
- return -EINVAL;
-};
-
-static int mhz_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- size_t len;
- u8 *buf;
- int rc;
-
- /* Read the station address from the CIS. It is stored as the last
- (fourth) string in the Version 1 Version/ID tuple. */
- if ((link->prod_id[3]) &&
- (cvt_ascii_address(dev, link->prod_id[3]) == 0))
- return 0;
-
- /* Workarounds for broken cards start here. */
- /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
- if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
- return 0;
-
- /* Another possibility: for the EM3288, in a special tuple */
- rc = -1;
- len = pcmcia_get_tuple(link, 0x81, &buf);
- if (buf && len >= 13) {
- buf[12] = '\0';
- if (cvt_ascii_address(dev, buf) == 0)
- rc = 0;
- }
- kfree(buf);
-
- return rc;
-};
-
-/*======================================================================
-
- Configuration stuff for the Motorola Mariner
-
- mot_config() writes directly to the Mariner configuration
- registers because the CIS is just bogus.
-
-======================================================================*/
-
-static void mot_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- unsigned int iouart = link->resource[1]->start;
-
- /* Set UART base address and force map with COR bit 1 */
- writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0);
- writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR);
-
- /* Set SMC base address and force map with COR bit 1 */
- writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0);
- writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR);
-
- /* Wait for things to settle down */
- mdelay(100);
-}
-
-static int mot_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int i, wait, loop;
- u8 mac[ETH_ALEN];
- u_int addr;
-
- /* Read Ethernet address from Serial EEPROM */
-
- for (i = 0; i < 3; i++) {
- SMC_SELECT_BANK(2);
- outw(MOT_EEPROM + i, ioaddr + POINTER);
- SMC_SELECT_BANK(1);
- outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
-
- for (loop = wait = 0; loop < 200; loop++) {
- udelay(10);
- wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
- if (wait == 0) break;
- }
-
- if (wait)
- return -1;
-
- addr = inw(ioaddr + GENERAL);
- mac[2*i] = addr & 0xff;
- mac[2*i+1] = (addr >> 8) & 0xff;
- }
- eth_hw_addr_set(dev, mac);
-
- return 0;
-}
-
-/*====================================================================*/
-
-static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-
- return pcmcia_request_io(p_dev);
-}
-
-static int smc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- int i;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- i = pcmcia_loop_config(link, smc_configcheck, NULL);
- if (!i)
- dev->base_addr = link->resource[0]->start;
-
- return i;
-}
-
-
-static int smc_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- /* Check for a LAN function extension tuple */
- if (!pcmcia_get_mac_from_cis(link, dev))
- return 0;
-
- /* Try the third string in the Version 1 Version/ID tuple. */
- if (link->prod_id[2]) {
- if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
- return 0;
- }
- return -1;
-}
-
-/*====================================================================*/
-
-static int osi_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
- int i, j;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
- link->resource[0]->end = 64;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->end = 8;
-
- /* Enable Hard Decode, LAN, Modem */
- link->io_lines = 16;
- link->config_index = 0x23;
-
- for (i = j = 0; j < 4; j++) {
- link->resource[1]->start = com[j];
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0) {
- /* Fallback: turn off hard decode */
- link->config_index = 0x03;
- link->resource[1]->end = 0;
- i = pcmcia_request_io(link);
- }
- dev->base_addr = link->resource[0]->start + 0x10;
- return i;
-}
-
-static int osi_load_firmware(struct pcmcia_device *link)
-{
- const struct firmware *fw;
- int i, err;
-
- err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
- if (err) {
- pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
- return err;
- }
-
- /* Download the Seven of Diamonds firmware */
- for (i = 0; i < fw->size; i++) {
- outb(fw->data[i], link->resource[0]->start + 2);
- udelay(50);
- }
- release_firmware(fw);
- return err;
-}
-
-static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
-
- if (tuple->TupleDataLen < 8)
- return -EINVAL;
- if (tuple->TupleData[0] != 0x04)
- return -EINVAL;
-
- eth_hw_addr_set(dev, &tuple->TupleData[2]);
- return 0;
-};
-
-
-static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
-{
- struct net_device *dev = link->priv;
- int rc;
-
- /* Read the station address from tuple 0x90, subtuple 0x04 */
- if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
- return -1;
-
- if (((manfid == MANFID_OSITECH) &&
- (cardid == PRODID_OSITECH_SEVEN)) ||
- ((manfid == MANFID_PSION) &&
- (cardid == PRODID_PSION_NET100))) {
- rc = osi_load_firmware(link);
- if (rc)
- return rc;
- } else if (manfid == MANFID_OSITECH) {
- /* Make sure both functions are powered up */
- set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
- /* Now, turn on the interrupt for both card functions */
- set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
- dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
- inw(link->resource[0]->start + OSITECH_AUI_PWR),
- inw(link->resource[0]->start + OSITECH_RESET_ISR));
- }
- return 0;
-}
-
-static int smc91c92_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int smc91c92_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- if ((smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Power up the card and enable interrupts */
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
- }
- if (((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid == PRODID_OSITECH_SEVEN)) ||
- ((smc->manfid == MANFID_PSION) &&
- (smc->cardid == PRODID_PSION_NET100))) {
- i = osi_load_firmware(link);
- if (i) {
- netdev_err(dev, "Failed to load firmware\n");
- return i;
- }
- }
- if (link->open) {
- smc_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- This verifies that the chip is some SMC91cXX variant, and returns
- the revision code if successful. Otherwise, it returns -ENODEV.
-
-======================================================================*/
-
-static int check_sig(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int width;
- u_short s;
-
- SMC_SELECT_BANK(1);
- if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
- /* Try powering up the chip */
- outw(0, ioaddr + CONTROL);
- mdelay(55);
- }
-
- /* Try setting bus width */
- width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
- s = inb(ioaddr + CONFIG);
- if (width)
- s |= CFG_16BIT;
- else
- s &= ~CFG_16BIT;
- outb(s, ioaddr + CONFIG);
-
- /* Check Base Address Register to make sure bus width is OK */
- s = inw(ioaddr + BASE_ADDR);
- if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
- ((s >> 8) != (s & 0xff))) {
- SMC_SELECT_BANK(3);
- s = inw(ioaddr + REVISION);
- return s & 0xff;
- }
-
- if (width) {
- netdev_info(dev, "using 8-bit IO window\n");
-
- smc91c92_suspend(link);
- pcmcia_fixup_iowidth(link);
- smc91c92_resume(link);
- return check_sig(link);
- }
- return -ENODEV;
-}
-
-static int smc91c92_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- char *name;
- int i, rev, j = 0;
- unsigned int ioaddr;
- u_long mir;
-
- dev_dbg(&link->dev, "smc91c92_config\n");
-
- smc->manfid = link->manf_id;
- smc->cardid = link->card_id;
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- i = osi_config(link);
- } else if ((smc->manfid == MANFID_MOTOROLA) ||
- ((smc->manfid == MANFID_MEGAHERTZ) &&
- ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
- i = mhz_mfc_config(link);
- } else {
- i = smc_config(link);
- }
- if (i)
- goto config_failed;
-
- i = pcmcia_request_irq(link, smc_interrupt);
- if (i)
- goto config_failed;
- i = pcmcia_enable_device(link);
- if (i)
- goto config_failed;
-
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
-
- dev->irq = link->irq;
-
- if ((if_port >= 0) && (if_port <= 2))
- dev->if_port = if_port;
- else
- dev_notice(&link->dev, "invalid if_port requested\n");
-
- switch (smc->manfid) {
- case MANFID_OSITECH:
- case MANFID_PSION:
- i = osi_setup(link, smc->manfid, smc->cardid); break;
- case MANFID_SMC:
- case MANFID_NEW_MEDIA:
- i = smc_setup(link); break;
- case 0x128: /* For broken Megahertz cards */
- case MANFID_MEGAHERTZ:
- i = mhz_setup(link); break;
- case MANFID_MOTOROLA:
- default: /* get the hw address from EEPROM */
- i = mot_setup(link); break;
- }
-
- if (i != 0) {
- dev_notice(&link->dev, "Unable to find hardware address.\n");
- goto config_failed;
- }
-
- smc->duplex = 0;
- smc->rx_ovrn = 0;
-
- rev = check_sig(link);
- name = "???";
- if (rev > 0)
- switch (rev >> 4) {
- case 3: name = "92"; break;
- case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
- case 5: name = "95"; break;
- case 7: name = "100"; break;
- case 8: name = "100-FD"; break;
- case 9: name = "110"; break;
- }
-
- ioaddr = dev->base_addr;
- if (rev > 0) {
- u_long mcr;
- SMC_SELECT_BANK(0);
- mir = inw(ioaddr + MEMINFO) & 0xff;
- if (mir == 0xff) mir++;
- /* Get scale factor for memory size */
- mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
- mir *= 128 * (1<<((mcr >> 9) & 7));
- SMC_SELECT_BANK(1);
- smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
- smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
- if (smc->manfid == MANFID_OSITECH)
- smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
- if ((rev >> 4) >= 7)
- smc->cfg |= CFG_MII_SELECT;
- } else
- mir = 0;
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- for (i = 0; i < 32; i++) {
- j = mdio_read(dev, i, 1);
- if ((j != 0) && (j != 0xffff)) break;
- }
- smc->mii_if.phy_id = (i < 32) ? i : -1;
-
- SMC_SELECT_BANK(0);
- }
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- dev_err(&link->dev, "register_netdev() failed\n");
- goto config_undo;
- }
-
- netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
- name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
-
- if (rev > 0) {
- if (mir & 0x3ff)
- netdev_info(dev, " %lu byte", mir);
- else
- netdev_info(dev, " %lu kb", mir>>10);
- pr_cont(" buffer, %s xcvr\n",
- (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
- }
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id != -1) {
- netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
- smc->mii_if.phy_id, j);
- } else {
- netdev_notice(dev, " No MII transceivers found!\n");
- }
- }
- return 0;
-
-config_undo:
- unregister_netdev(dev);
-config_failed:
- smc91c92_release(link);
- free_netdev(dev);
- return -ENODEV;
-} /* smc91c92_config */
-
-static void smc91c92_release(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "smc91c92_release\n");
- if (link->resource[2]->end) {
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- iounmap(smc->base);
- }
- pcmcia_disable_device(link);
-}
-
-/*======================================================================
-
- MII interface support for SMC91cXX based cards
-======================================================================*/
-
-#define MDIO_SHIFT_CLK 0x04
-#define MDIO_DATA_OUT 0x01
-#define MDIO_DIR_WRITE 0x08
-#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
-#define MDIO_DATA_READ 0x02
-
-static void mdio_sync(unsigned int addr)
-{
- int bits;
- for (bits = 0; bits < 32; bits++) {
- outb(MDIO_DATA_WRITE1, addr);
- outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int loc)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
- int i, retval = 0;
-
- mdio_sync(addr);
- for (i = 13; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb(0, addr);
- retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
- outb(MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb(0, addr);
- outb(MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*======================================================================
-
- The driver core code, most of which should be common with a
- non-PCMCIA implementation.
-
-======================================================================*/
-
-#ifdef PCMCIA_DEBUG
-static void smc_dump(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u_short i, w, save;
- save = inw(ioaddr + BANK_SELECT);
- for (w = 0; w < 4; w++) {
- SMC_SELECT_BANK(w);
- netdev_dbg(dev, "bank %d: ", w);
- for (i = 0; i < 14; i += 2)
- pr_cont(" %04x", inw(ioaddr + i));
- pr_cont("\n");
- }
- outw(save, ioaddr + BANK_SELECT);
-}
-#endif
-
-static int smc_open(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
-
- dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
- dev->name, dev, inw(dev->base_addr + BANK_SELECT));
-#ifdef PCMCIA_DEBUG
- smc_dump(dev);
-#endif
-
- /* Check that the PCMCIA card is still here. */
- if (!pcmcia_dev_present(link))
- return -ENODEV;
- /* Physical device present signature. */
- if (check_sig(link) < 0) {
- netdev_info(dev, "Yikes! Bad chip signature!\n");
- return -ENODEV;
- }
- link->open++;
-
- netif_start_queue(dev);
- smc->saved_skb = NULL;
- smc->packets_waiting = 0;
-
- smc_reset(dev);
- timer_setup(&smc->media, media_check, 0);
- mod_timer(&smc->media, jiffies + HZ);
-
- return 0;
-} /* smc_open */
-
-/*====================================================================*/
-
-static int smc_close(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
- dev->name, inw(ioaddr + BANK_SELECT));
-
- netif_stop_queue(dev);
-
- /* Shut off all interrupts, and turn off the Tx and Rx sections.
- Don't bother to check for chip present. */
- SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */
- outw(0, ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- mask_bits(0xff00, ioaddr + RCR);
- mask_bits(0xff00, ioaddr + TCR);
-
- /* Put the chip into power-down mode. */
- SMC_SELECT_BANK(1);
- outw(CTL_POWERDOWN, ioaddr + CONTROL );
-
- link->open--;
- timer_delete_sync(&smc->media);
-
- return 0;
-} /* smc_close */
-
-/*======================================================================
-
- Transfer a packet to the hardware and trigger the packet send.
- This may be called at either from either the Tx queue code
- or the interrupt handler.
-
-======================================================================*/
-
-static void smc_hardware_send_packet(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct sk_buff *skb = smc->saved_skb;
- unsigned int ioaddr = dev->base_addr;
- u_char packet_no;
-
- if (!skb) {
- netdev_err(dev, "In XMIT with no packet to send\n");
- return;
- }
-
- /* There should be a packet slot waiting. */
- packet_no = inw(ioaddr + PNR_ARR) >> 8;
- if (packet_no & 0x80) {
- /* If not, there is a hardware problem! Likely an ejected card. */
- netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
- packet_no);
- dev_kfree_skb_irq(skb);
- smc->saved_skb = NULL;
- netif_start_queue(dev);
- return;
- }
-
- dev->stats.tx_bytes += skb->len;
- /* The card should use the just-allocated buffer. */
- outw(packet_no, ioaddr + PNR_ARR);
- /* point to the beginning of the packet */
- outw(PTR_AUTOINC , ioaddr + POINTER);
-
- /* Send the packet length (+6 for status, length and ctl byte)
- and the status word (set to zeros). */
- {
- u_char *buf = skb->data;
- u_int length = skb->len; /* The chip will pad to ethernet min. */
-
- netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
-
- /* send the packet length: +6 for status word, length, and ctl */
- outw(0, ioaddr + DATA_1);
- outw(length + 6, ioaddr + DATA_1);
- outsw(ioaddr + DATA_1, buf, length >> 1);
-
- /* The odd last byte, if there is one, goes in the control word. */
- outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
- }
-
- /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
- outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
- (inw(ioaddr + INTERRUPT) & 0xff00),
- ioaddr + INTERRUPT);
-
- /* The chip does the rest of the work. */
- outw(MC_ENQUEUE , ioaddr + MMU_CMD);
-
- smc->saved_skb = NULL;
- dev_kfree_skb_irq(skb);
- netif_trans_update(dev);
- netif_start_queue(dev);
-}
-
-/*====================================================================*/
-
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
- inw(ioaddr)&0xff, inw(ioaddr + 2));
- dev->stats.tx_errors++;
- smc_reset(dev);
- netif_trans_update(dev); /* prevent tx timeout */
- smc->saved_skb = NULL;
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short num_pages;
- short time_out, ir;
- unsigned long flags;
-
- netif_stop_queue(dev);
-
- netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
- skb->len, inw(ioaddr + 2));
-
- if (smc->saved_skb) {
- /* THIS SHOULD NEVER HAPPEN. */
- dev->stats.tx_aborted_errors++;
- netdev_dbg(dev, "Internal error -- sent packet while busy\n");
- return NETDEV_TX_BUSY;
- }
- smc->saved_skb = skb;
-
- num_pages = skb->len >> 8;
-
- if (num_pages > 7) {
- netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
- dev_kfree_skb (skb);
- smc->saved_skb = NULL;
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK; /* Do not re-queue this packet. */
- }
- /* A packet is now waiting. */
- smc->packets_waiting++;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
-
- /* Allocate the memory; send the packet now if we win. */
- outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
- for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
- ir = inw(ioaddr+INTERRUPT);
- if (ir & IM_ALLOC_INT) {
- /* Acknowledge the interrupt, send the packet. */
- outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
- smc_hardware_send_packet(dev); /* Send the packet now.. */
- spin_unlock_irqrestore(&smc->lock, flags);
- return NETDEV_TX_OK;
- }
- }
-
- /* Otherwise defer until the Tx-space-allocated interrupt. */
- netdev_dbg(dev, "memory allocation deferred.\n");
- outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/*======================================================================
-
- Handle a Tx anomalous event. Entered while in Window 2.
-
-======================================================================*/
-
-static void smc_tx_err(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
- int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
- int tx_status;
-
- /* select this as the packet to read from */
- outw(packet_no, ioaddr + PNR_ARR);
-
- /* read the first word from this packet */
- outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
-
- tx_status = inw(ioaddr + DATA_1);
-
- dev->stats.tx_errors++;
- if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
- if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++;
- if (tx_status & TS_16COL) {
- dev->stats.tx_aborted_errors++;
- smc->tx_err++;
- }
-
- if (tx_status & TS_SUCCESS) {
- netdev_notice(dev, "Successful packet caused error interrupt?\n");
- }
- /* re-enable transmit */
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
- SMC_SELECT_BANK(2);
-
- outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */
-
- /* one less packet waiting for me */
- smc->packets_waiting--;
-
- outw(saved_packet, ioaddr + PNR_ARR);
-}
-
-/*====================================================================*/
-
-static void smc_eph_irq(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short card_stats, ephs;
-
- SMC_SELECT_BANK(0);
- ephs = inw(ioaddr + EPH);
- netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n",
- ephs);
- /* Could be a counter roll-over warning: update stats. */
- card_stats = inw(ioaddr + COUNTER);
- /* single collisions */
- dev->stats.collisions += card_stats & 0xF;
- card_stats >>= 4;
- /* multiple collisions */
- dev->stats.collisions += card_stats & 0xF;
-#if 0 /* These are for when linux supports these statistics */
- card_stats >>= 4; /* deferred */
- card_stats >>= 4; /* excess deferred */
-#endif
- /* If we had a transmit error we must re-enable the transmitter. */
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
-
- /* Clear a link error interrupt. */
- SMC_SELECT_BANK(1);
- outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- SMC_SELECT_BANK(2);
-}
-
-/*====================================================================*/
-
-static irqreturn_t smc_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr;
- u_short saved_bank, saved_pointer, mask, status;
- unsigned int handled = 1;
- char bogus_cnt = INTR_WORK; /* Work we are willing to do. */
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
-
- ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n",
- irq, ioaddr);
-
- spin_lock(&smc->lock);
- smc->watchdog = 0;
- saved_bank = inw(ioaddr + BANK_SELECT);
- if ((saved_bank & 0xff00) != 0x3300) {
- /* The device does not exist -- the card could be off-line, or
- maybe it has been ejected. */
- netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n",
- irq);
- handled = 0;
- goto irq_done;
- }
-
- SMC_SELECT_BANK(2);
- saved_pointer = inw(ioaddr + POINTER);
- mask = inw(ioaddr + INTERRUPT) >> 8;
- /* clear all interrupts */
- outw(0, ioaddr + INTERRUPT);
-
- do { /* read the status flag, and mask it */
- status = inw(ioaddr + INTERRUPT) & 0xff;
- netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n",
- status, mask);
- if ((status & mask) == 0) {
- if (bogus_cnt == INTR_WORK)
- handled = 0;
- break;
- }
- if (status & IM_RCV_INT) {
- /* Got a packet(s). */
- smc_rx(dev);
- }
- if (status & IM_TX_INT) {
- smc_tx_err(dev);
- outw(IM_TX_INT, ioaddr + INTERRUPT);
- }
- status &= mask;
- if (status & IM_TX_EMPTY_INT) {
- outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
- mask &= ~IM_TX_EMPTY_INT;
- dev->stats.tx_packets += smc->packets_waiting;
- smc->packets_waiting = 0;
- }
- if (status & IM_ALLOC_INT) {
- /* Clear this interrupt so it doesn't happen again */
- mask &= ~IM_ALLOC_INT;
-
- smc_hardware_send_packet(dev);
-
- /* enable xmit interrupts based on this */
- mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
-
- /* and let the card send more packets to me */
- netif_wake_queue(dev);
- }
- if (status & IM_RX_OVRN_INT) {
- dev->stats.rx_errors++;
- dev->stats.rx_fifo_errors++;
- if (smc->duplex)
- smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
- outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
- }
- if (status & IM_EPH_INT)
- smc_eph_irq(dev);
- } while (--bogus_cnt);
-
- netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n",
- mask, saved_bank, saved_pointer);
-
- /* restore state register */
- outw((mask<<8), ioaddr + INTERRUPT);
- outw(saved_pointer, ioaddr + POINTER);
- SMC_SELECT_BANK(saved_bank);
-
- netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq);
-
-irq_done:
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Retrigger interrupt if needed */
- mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
- set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
- }
- if (smc->manfid == MANFID_MOTOROLA) {
- u_char cor;
- cor = readb(smc->base + MOT_UART + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
- writeb(cor, smc->base + MOT_UART + CISREG_COR);
- cor = readb(smc->base + MOT_LAN + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
- writeb(cor, smc->base + MOT_LAN + CISREG_COR);
- }
-
- if ((smc->base != NULL) && /* Megahertz MFC's */
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
-
- u_char tmp;
- tmp = readb(smc->base+MEGAHERTZ_ISR);
- tmp = readb(smc->base+MEGAHERTZ_ISR);
-
- /* Retrigger interrupt if needed */
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- }
-
- spin_unlock(&smc->lock);
- return IRQ_RETVAL(handled);
-}
-
-/*====================================================================*/
-
-static void smc_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int rx_status;
- int packet_length; /* Caution: not frame length, rather words
- to transfer from the chip. */
-
- /* Assertion: we are in Window 2. */
-
- if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
- netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
- return;
- }
-
- /* Reset the read pointer, and read the status and packet length. */
- outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
- rx_status = inw(ioaddr + DATA_1);
- packet_length = inw(ioaddr + DATA_1) & 0x07ff;
-
- netdev_dbg(dev, "Receive status %4.4x length %d.\n",
- rx_status, packet_length);
-
- if (!(rx_status & RS_ERRORS)) {
- /* do stuff to make a new packet */
- struct sk_buff *skb;
- struct smc_private *smc = netdev_priv(dev);
-
- /* Note: packet_length adds 5 or 6 extra bytes here! */
- skb = netdev_alloc_skb(dev, packet_length+2);
-
- if (skb == NULL) {
- netdev_dbg(dev, "Low memory, packet dropped.\n");
- dev->stats.rx_dropped++;
- outw(MC_RELEASE, ioaddr + MMU_CMD);
- return;
- }
-
- packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
- skb_reserve(skb, 2);
- insw(ioaddr+DATA_1, skb_put(skb, packet_length),
- (packet_length+1)>>1);
- skb->protocol = eth_type_trans(skb, dev);
-
- netif_rx(skb);
- smc->last_rx = jiffies;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += packet_length;
- if (rx_status & RS_MULTICAST)
- dev->stats.multicast++;
- } else {
- /* error ... */
- dev->stats.rx_errors++;
-
- if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++;
- if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
- dev->stats.rx_length_errors++;
- if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++;
- }
- /* Let the MMU free the memory of this packet. */
- outw(MC_RELEASE, ioaddr + MMU_CMD);
-}
-
-/*======================================================================
-
- Set the receive mode.
-
- This routine is used by both the protocol level to notify us of
- promiscuous/multicast mode changes, and by the open/reset code to
- initialize the Rx registers. We always set the multicast list and
- leave the receiver running.
-
-======================================================================*/
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- unsigned char multicast_table[8];
- unsigned long flags;
- u_short rx_cfg_setting;
- int i;
-
- memset(multicast_table, 0, sizeof(multicast_table));
-
- if (dev->flags & IFF_PROMISC) {
- rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
- } else if (dev->flags & IFF_ALLMULTI)
- rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
- else {
- if (!netdev_mc_empty(dev)) {
- struct netdev_hw_addr *ha;
-
- netdev_for_each_mc_addr(ha, dev) {
- u_int position = ether_crc(6, ha->addr);
- multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
- }
- }
- rx_cfg_setting = RxStripCRC | RxEnable;
- }
-
- /* Load MC table and Rx setting into the chip without interrupts. */
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- for (i = 0; i < 8; i++)
- outb(multicast_table[i], ioaddr + MULTICAST0 + i);
- SMC_SELECT_BANK(0);
- outw(rx_cfg_setting, ioaddr + RCR);
- SMC_SELECT_BANK(2);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-/*======================================================================
-
- Senses when a card's config changes. Here, it's coax or TP.
-
-======================================================================*/
-
-static int s9k_config(struct net_device *dev, struct ifmap *map)
-{
- struct smc_private *smc = netdev_priv(dev);
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (smc->cfg & CFG_MII_SELECT)
- return -EOPNOTSUPP;
- else if (map->port > 2)
- return -EINVAL;
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- smc_reset(dev);
- }
- return 0;
-}
-
-/*======================================================================
-
- Reset the chip, reloading every register that might be corrupted.
-
-======================================================================*/
-
-/*
- Set transceiver type, perhaps to something other than what the user
- specified in dev->if_port.
-*/
-static void smc_set_xcvr(struct net_device *dev, int if_port)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short saved_bank;
-
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(1);
- if (if_port == 2) {
- outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
- } else {
- outw(smc->cfg, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
- }
- SMC_SELECT_BANK(saved_bank);
-}
-
-static void smc_reset(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- netdev_dbg(dev, "smc91c92 reset called.\n");
-
- /* The first interaction must be a write to bring the chip out
- of sleep mode. */
- SMC_SELECT_BANK(0);
- /* Reset the chip. */
- outw(RCR_SOFTRESET, ioaddr + RCR);
- udelay(10);
-
- /* Clear the transmit and receive configuration registers. */
- outw(RCR_CLEAR, ioaddr + RCR);
- outw(TCR_CLEAR, ioaddr + TCR);
-
- /* Set the Window 1 control, configuration and station addr registers.
- No point in writing the I/O base register ;-> */
- SMC_SELECT_BANK(1);
- /* Automatically release successfully transmitted packets,
- Accept link errors, counter and Tx error interrupts. */
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- smc_set_xcvr(dev, dev->if_port);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
- (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
- ioaddr - 0x10 + OSITECH_AUI_PWR);
-
- /* Fill in the physical address. The databook is wrong about the order! */
- for (i = 0; i < 6; i += 2)
- outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
- ioaddr + ADDR0 + i);
-
- /* Reset the MMU */
- SMC_SELECT_BANK(2);
- outw(MC_RESET, ioaddr + MMU_CMD);
- outw(0, ioaddr + INTERRUPT);
-
- /* Re-enable the chip. */
- SMC_SELECT_BANK(0);
- outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
- TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
- set_rx_mode(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- /* Reset MII */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
-
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
-
- /* Restart MII autonegotiation */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
- }
-
- /* Enable interrupts. */
- SMC_SELECT_BANK(2);
- outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
- ioaddr + INTERRUPT);
-}
-
-/*======================================================================
-
- Media selection timer routine
-
-======================================================================*/
-
-static void media_check(struct timer_list *t)
-{
- struct smc_private *smc = timer_container_of(smc, t, media);
- struct net_device *dev = smc->mii_if.dev;
- unsigned int ioaddr = dev->base_addr;
- u_short i, media, saved_bank;
- u_short link;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (!netif_device_present(dev))
- goto reschedule;
-
- SMC_SELECT_BANK(2);
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
- i = inw(ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- media = inw(ioaddr + EPH) & EPH_LINK_OK;
- SMC_SELECT_BANK(1);
- media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
-
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- /* Check for pending interrupt with watchdog flag set: with
- this, we can limp along even if the interrupt is blocked */
- if (smc->watchdog++ && ((i>>8) & i)) {
- if (!smc->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- local_irq_save(flags);
- smc_interrupt(dev->irq, dev);
- local_irq_restore(flags);
- smc->fast_poll = HZ;
- }
- if (smc->fast_poll) {
- smc->fast_poll--;
- smc->media.expires = jiffies + HZ/100;
- add_timer(&smc->media);
- return;
- }
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id < 0)
- goto reschedule;
-
- SMC_SELECT_BANK(3);
- link = mdio_read(dev, smc->mii_if.phy_id, 1);
- if (!link || (link == 0xffff)) {
- netdev_info(dev, "MII is missing!\n");
- smc->mii_if.phy_id = -1;
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != smc->link_status) {
- u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
- ? TCR_FDUPLX : 0);
- if (link) {
- netdev_info(dev, "autonegotiation complete: "
- "%dbaseT-%cD selected\n",
- (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
- }
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
- smc->link_status = link;
- }
- goto reschedule;
- }
-
- /* Ignore collisions unless we've had no rx's recently */
- if (time_after(jiffies, smc->last_rx + HZ)) {
- if (smc->tx_err || (smc->media_status & EPH_16COL))
- media |= EPH_16COL;
- }
- smc->tx_err = 0;
-
- if (media != smc->media_status) {
- if ((media & smc->media_status & 1) &&
- ((smc->media_status ^ media) & EPH_LINK_OK))
- netdev_info(dev, "%s link beat\n",
- smc->media_status & EPH_LINK_OK ? "lost" : "found");
- else if ((media & smc->media_status & 2) &&
- ((smc->media_status ^ media) & EPH_16COL))
- netdev_info(dev, "coax cable %s\n",
- media & EPH_16COL ? "problem" : "ok");
- if (dev->if_port == 0) {
- if (media & 1) {
- if (media & EPH_LINK_OK)
- netdev_info(dev, "flipped to 10baseT\n");
- else
- smc_set_xcvr(dev, 2);
- } else {
- if (media & EPH_16COL)
- smc_set_xcvr(dev, 1);
- else
- netdev_info(dev, "flipped to 10base2\n");
- }
- }
- smc->media_status = media;
- }
-
-reschedule:
- smc->media.expires = jiffies + HZ;
- add_timer(&smc->media);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-static int smc_link_ok(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- return mii_link_ok(&smc->mii_if);
- } else {
- SMC_SELECT_BANK(0);
- return inw(ioaddr + EPH) & EPH_LINK_OK;
- }
-}
-
-static void smc_netdev_get_ecmd(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
- u32 supported;
-
- supported = (SUPPORTED_TP | SUPPORTED_AUI |
- SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
-
- SMC_SELECT_BANK(1);
- tmp = inw(ioaddr + CONFIG);
- ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
- ecmd->base.speed = SPEED_10;
- ecmd->base.phy_address = ioaddr + MGMT;
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
-
- ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
- supported);
-}
-
-static int smc_netdev_set_ecmd(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
-
- if (ecmd->base.speed != SPEED_10)
- return -EINVAL;
- if (ecmd->base.duplex != DUPLEX_HALF &&
- ecmd->base.duplex != DUPLEX_FULL)
- return -EINVAL;
- if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI)
- return -EINVAL;
-
- if (ecmd->base.port == PORT_AUI)
- smc_set_xcvr(dev, 1);
- else
- smc_set_xcvr(dev, 0);
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- if (ecmd->base.duplex == DUPLEX_FULL)
- tmp |= TCR_FDUPLX;
- else
- tmp &= ~TCR_FDUPLX;
- outw(tmp, ioaddr + TCR);
-
- return 0;
-}
-
-static int check_if_running(struct net_device *dev)
-{
- if (!netif_running(dev))
- return -EINVAL;
- return 0;
-}
-
-static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
-}
-
-static int smc_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd);
- else
- smc_netdev_get_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return 0;
-}
-
-static int smc_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd);
- else
- ret = smc_netdev_set_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static u32 smc_get_link(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- u32 ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- ret = smc_link_ok(dev);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static int smc_nway_reset(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- if (smc->cfg & CFG_MII_SELECT) {
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int res;
-
- SMC_SELECT_BANK(3);
- res = mii_nway_restart(&smc->mii_if);
- SMC_SELECT_BANK(saved_bank);
-
- return res;
- } else
- return -EOPNOTSUPP;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .begin = check_if_running,
- .get_drvinfo = smc_get_drvinfo,
- .get_link = smc_get_link,
- .nway_reset = smc_nway_reset,
- .get_link_ksettings = smc_get_link_ksettings,
- .set_link_ksettings = smc_set_link_ksettings,
-};
-
-static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct mii_ioctl_data *mii = if_mii(rq);
- int rc = 0;
- u16 saved_bank;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
-
- if (!netif_running(dev))
- return -EINVAL;
-
- spin_lock_irqsave(&smc->lock, flags);
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(3);
- rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return rc;
-}
-
-static const struct pcmcia_device_id smc91c92_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
- PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
- PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
- PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
- PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
- PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
- PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
- PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
- PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
- /* These conflict with other cards! */
- /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
- /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
-
-static struct pcmcia_driver smc91c92_cs_driver = {
- .owner = THIS_MODULE,
- .name = "smc91c92_cs",
- .probe = smc91c92_probe,
- .remove = smc91c92_detach,
- .id_table = smc91c92_ids,
- .suspend = smc91c92_suspend,
- .resume = smc91c92_resume,
-};
-module_pcmcia_driver(smc91c92_cs_driver);
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 09/15] drivers: net: cirrus: cs89x0: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The cs89x0 was written by Bonald Becker 1993 to 1994. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Remove documentation as well
---
.../device_drivers/ethernet/cirrus/cs89x0.rst | 647 -------
drivers/net/ethernet/cirrus/Kconfig | 30 -
drivers/net/ethernet/cirrus/Makefile | 1 -
drivers/net/ethernet/cirrus/cs89x0.c | 1915 --------------------
4 files changed, 2593 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst b/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst
deleted file mode 100644
index e5c283940ac5..000000000000
--- a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst
+++ /dev/null
@@ -1,647 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-================================================
-Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
-================================================
-
-.. note::
-
- This document was contributed by Cirrus Logic for kernel 2.2.5. This version
- has been updated for 2.3.48 by Andrew Morton.
-
- Still, this is too outdated! A major cleanup is needed here.
-
-Cirrus make a copy of this driver available at their website, as
-described below. In general, you should use the driver version which
-comes with your Linux distribution.
-
-
-Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
-
-
-.. TABLE OF CONTENTS
-
- 1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
- 1.1 Product Overview
- 1.2 Driver Description
- 1.2.1 Driver Name
- 1.2.2 File in the Driver Package
- 1.3 System Requirements
- 1.4 Licensing Information
-
- 2.0 ADAPTER INSTALLATION and CONFIGURATION
- 2.1 CS8900-based Adapter Configuration
- 2.2 CS8920-based Adapter Configuration
-
- 3.0 LOADING THE DRIVER AS A MODULE
-
- 4.0 COMPILING THE DRIVER
- 4.1 Compiling the Driver as a Loadable Module
- 4.2 Compiling the driver to support memory mode
- 4.3 Compiling the driver to support Rx DMA
-
- 5.0 TESTING AND TROUBLESHOOTING
- 5.1 Known Defects and Limitations
- 5.2 Testing the Adapter
- 5.2.1 Diagnostic Self-Test
- 5.2.2 Diagnostic Network Test
- 5.3 Using the Adapter's LEDs
- 5.4 Resolving I/O Conflicts
-
- 6.0 TECHNICAL SUPPORT
- 6.1 Contacting Cirrus Logic's Technical Support
- 6.2 Information Required Before Contacting Technical Support
- 6.3 Obtaining the Latest Driver Version
- 6.4 Current maintainer
- 6.5 Kernel boot parameters
-
-
-1. Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
-===================================================
-
-
-1.1. Product Overview
-=====================
-
-The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow
-IEEE 802.3 standards and support half or full-duplex operation in ISA bus
-computers on 10 Mbps Ethernet networks. The adapters are designed for operation
-in 16-bit ISA or EISA bus expansion slots and are available in
-10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5
-or fiber networks).
-
-CS8920-based adapters are similar to the CS8900-based adapter with additional
-features for Plug and Play (PnP) support and Wakeup Frame recognition. As
-such, the configuration procedures differ somewhat between the two types of
-adapters. Refer to the "Adapter Configuration" section for details on
-configuring both types of adapters.
-
-
-1.2. Driver Description
-=======================
-
-The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
-v2.3.48 or greater kernel. It can be compiled directly into the kernel
-or loaded at run-time as a device driver module.
-
-1.2.1 Driver Name: cs89x0
-
-1.2.2 Files in the Driver Archive:
-
-The files in the driver at Cirrus' website include:
-
- =================== ====================================================
- readme.txt this file
- build batch file to compile cs89x0.c.
- cs89x0.c driver C code
- cs89x0.h driver header file
- cs89x0.o pre-compiled module (for v2.2.5 kernel)
- config/Config.in sample file to include cs89x0 driver in the kernel.
- config/Makefile sample file to include cs89x0 driver in the kernel.
- config/Space.c sample file to include cs89x0 driver in the kernel.
- =================== ====================================================
-
-
-
-1.3. System Requirements
-------------------------
-
-The following hardware is required:
-
- * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter
-
- * IBM or IBM-compatible PC with:
- * An 80386 or higher processor
- * 16 bytes of contiguous IO space available between 210h - 370h
- * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
-
- * Appropriate cable (and connector for AUI, 10BASE-2) for your network
- topology.
-
-The following software is required:
-
-* LINUX kernel version 2.3.48 or higher
-
- * CS8900/20 Setup Utility (DOS-based)
-
- * LINUX kernel sources for your kernel (if compiling into kernel)
-
- * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel
- or a module)
-
-
-
-1.4. Licensing Information
---------------------------
-
-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, version 1.
-
-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.
-
-For a full copy of the GNU General Public License, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-
-2. Adapter Installation and Configuration
-=========================================
-
-Both the CS8900 and CS8920-based adapters can be configured using parameters
-stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup
-Utility if you want to change the adapter's configuration in EEPROM.
-
-When loading the driver as a module, you can specify many of the adapter's
-configuration parameters on the command-line to override the EEPROM's settings
-or for interface configuration when an EEPROM is not used. (CS8920-based
-adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
-
-Since the CS8900/20 Setup Utility is a DOS-based application, you must install
-and configure the adapter in a DOS-based system using the CS8900/20 Setup
-Utility before installation in the target LINUX system. (Not required if
-installing a CS8900-based adapter and the default configuration is acceptable.)
-
-
-2.1. CS8900-based Adapter Configuration
----------------------------------------
-
-CS8900-based adapters shipped from Cirrus Logic have been configured
-with the following "default" settings::
-
- Operation Mode: Memory Mode
- IRQ: 10
- Base I/O Address: 300
- Memory Base Address: D0000
- Optimization: DOS Client
- Transmission Mode: Half-duplex
- BootProm: None
- Media Type: Autodetect (3-media cards) or
- 10BASE-T (10BASE-T only adapter)
-
-You should only change the default configuration settings if conflicts with
-another adapter exists. To change the adapter's configuration, run the
-CS8900/20 Setup Utility.
-
-
-2.2. CS8920-based Adapter Configuration
----------------------------------------
-
-CS8920-based adapters are shipped from Cirrus Logic configured as Plug
-and Play (PnP) enabled. However, since the cs89x0 driver does NOT
-support PnP, you must install the CS8920 adapter in a DOS-based PC and
-run the CS8900/20 Setup Utility to disable PnP and configure the
-adapter before installation in the target Linux system. Failure to do
-this will leave the adapter inactive and the driver will be unable to
-communicate with the adapter.
-
-::
-
- ****************************************************************
- * CS8920-BASED ADAPTERS: *
- * *
- * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT. *
- * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST *
- * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND *
- * TO ACTIVATE THE ADAPTER. *
- ****************************************************************
-
-
-
-
-3. Loading the Driver as a Module
-=================================
-
-If the driver is compiled as a loadable module, you can load the driver module
-with the 'modprobe' command. Many of the adapter's configuration parameters can
-be specified as command-line arguments to the load command. This facility
-provides a means to override the EEPROM's settings or for interface
-configuration when an EEPROM is not used.
-
-Example::
-
- insmod cs89x0.o io=0x200 irq=0xA media=aui
-
-This example loads the module and configures the adapter to use an IO port base
-address of 200h, interrupt 10, and use the AUI media connection. The following
-configuration options are available on the command line::
-
- io=### - specify IO address (200h-360h)
- irq=## - specify interrupt level
- use_dma=1 - Enable DMA
- dma=# - specify dma channel (Driver is compiled to support
- Rx DMA only)
- dmasize=# (16 or 64) - DMA size 16K or 64K. Default value is set to 16.
- media=rj45 - specify media type
- or media=bnc
- or media=aui
- or media=auto
- duplex=full - specify forced half/full/autonegotiate duplex
- or duplex=half
- or duplex=auto
- debug=# - debug level (only available if the driver was compiled
- for debugging)
-
-**Notes:**
-
-a) If an EEPROM is present, any specified command-line parameter
- will override the corresponding configuration value stored in
- EEPROM.
-
-b) The "io" parameter must be specified on the command-line.
-
-c) The driver's hardware probe routine is designed to avoid
- writing to I/O space until it knows that there is a cs89x0
- card at the written addresses. This could cause problems
- with device probing. To avoid this behaviour, add one
- to the ``io=`` module parameter. This doesn't actually change
- the I/O address, but it is a flag to tell the driver
- to partially initialise the hardware before trying to
- identify the card. This could be dangerous if you are
- not sure that there is a cs89x0 card at the provided address.
-
- For example, to scan for an adapter located at IO base 0x300,
- specify an IO address of 0x301.
-
-d) The "duplex=auto" parameter is only supported for the CS8920.
-
-e) The minimum command-line configuration required if an EEPROM is
- not present is:
-
- io
- irq
- media type (no autodetect)
-
-f) The following additional parameters are CS89XX defaults (values
- used with no EEPROM or command-line argument).
-
- * DMA Burst = enabled
- * IOCHRDY Enabled = enabled
- * UseSA = enabled
- * CS8900 defaults to half-duplex if not specified on command-line
- * CS8920 defaults to autoneg if not specified on command-line
- * Use reset defaults for other config parameters
- * dma_mode = 0
-
-g) You can use ifconfig to set the adapter's Ethernet address.
-
-h) Many Linux distributions use the 'modprobe' command to load
- modules. This program uses the '/etc/conf.modules' file to
- determine configuration information which is passed to a driver
- module when it is loaded. All the configuration options which are
- described above may be placed within /etc/conf.modules.
-
- For example::
-
- > cat /etc/conf.modules
- ...
- alias eth0 cs89x0
- options cs89x0 io=0x0200 dma=5 use_dma=1
- ...
-
- In this example we are telling the module system that the
- ethernet driver for this machine should use the cs89x0 driver. We
- are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
- arguments to the driver when it is loaded.
-
-i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
- 7. You will probably find that other DMA channels will not work.
-
-j) The cs89x0 supports DMA for receiving only. DMA mode is
- significantly more efficient. Flooding a 400 MHz Celeron machine
- with large ping packets consumes 82% of its CPU capacity in non-DMA
- mode. With DMA this is reduced to 45%.
-
-k) If your Linux kernel was compiled with inbuilt plug-and-play
- support you will be able to find information about the cs89x0 card
- with the command::
-
- cat /proc/isapnp
-
-l) If during DMA operation you find erratic behavior or network data
- corruption you should use your PC's BIOS to slow the EISA bus clock.
-
-m) If the cs89x0 driver is compiled directly into the kernel
- (non-modular) then its I/O address is automatically determined by
- ISA bus probing. The IRQ number, media options, etc are determined
- from the card's EEPROM.
-
-n) If the cs89x0 driver is compiled directly into the kernel, DMA
- mode may be selected by providing the kernel with a boot option
- 'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
-
- Kernel boot options may be provided on the LILO command line::
-
- LILO boot: linux cs89x0_dma=5
-
- or they may be placed in /etc/lilo.conf::
-
- image=/boot/bzImage-2.3.48
- append="cs89x0_dma=5"
- label=linux
- root=/dev/hda5
- read-only
-
- The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
- (64k mode is not available).
-
-
-4. Compiling the Driver
-=======================
-
-The cs89x0 driver can be compiled directly into the kernel or compiled into
-a loadable device driver module.
-
-Just use the standard way to configure the driver and compile the Kernel.
-
-
-4.1. Compiling the Driver to Support Rx DMA
--------------------------------------------
-
-The compile-time optionality for DMA was removed in the 2.3 kernel
-series. DMA support is now unconditionally part of the driver. It is
-enabled by the 'use_dma=1' module option.
-
-
-5. Testing and Troubleshooting
-==============================
-
-5.1. Known Defects and Limitations
-----------------------------------
-
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of
-known defects, driver limitations, and work arounds.
-
-
-5.2. Testing the Adapter
-------------------------
-
-Once the adapter has been installed and configured, the diagnostic option of
-the CS8900/20 Setup Utility can be used to test the functionality of the
-adapter and its network connection. Use the diagnostics 'Self Test' option to
-test the functionality of the adapter with the hardware configuration you have
-assigned. You can use the diagnostics 'Network Test' to test the ability of the
-adapter to communicate across the Ethernet with another PC equipped with a
-CS8900/20-based adapter card (it must also be running the CS8900/20 Setup
-Utility).
-
-.. note::
-
- The Setup Utility's diagnostics are designed to run in a
- DOS-only operating system environment. DO NOT run the diagnostics
- from a DOS or command prompt session under Windows 95, Windows NT,
- OS/2, or other operating system.
-
-To run the diagnostics tests on the CS8900/20 adapter:
-
- 1. Boot DOS on the PC and start the CS8900/20 Setup Utility.
-
- 2. The adapter's current configuration is displayed. Hit the ENTER key to
- get to the main menu.
-
- 4. Select 'Diagnostics' (ALT-G) from the main menu.
- * Select 'Self-Test' to test the adapter's basic functionality.
- * Select 'Network Test' to test the network connection and cabling.
-
-
-5.2.1. Diagnostic Self-test
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The diagnostic self-test checks the adapter's basic functionality as well as
-its ability to communicate across the ISA bus based on the system resources
-assigned during hardware configuration. The following tests are performed:
-
- * IO Register Read/Write Test
-
- The IO Register Read/Write test insures that the CS8900/20 can be
- accessed in IO mode, and that the IO base address is correct.
-
- * Shared Memory Test
-
- The Shared Memory test insures the CS8900/20 can be accessed in memory
- mode and that the range of memory addresses assigned does not conflict
- with other devices in the system.
-
- * Interrupt Test
-
- The Interrupt test insures there are no conflicts with the assigned IRQ
- signal.
-
- * EEPROM Test
-
- The EEPROM test insures the EEPROM can be read.
-
- * Chip RAM Test
-
- The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
- working properly.
-
- * Internal Loop-back Test
-
- The Internal Loop Back test insures the adapter's transmitter and
- receiver are operating properly. If this test fails, make sure the
- adapter's cable is connected to the network (check for LED activity for
- example).
-
- * Boot PROM Test
-
- The Boot PROM test insures the Boot PROM is present, and can be read.
- Failure indicates the Boot PROM was not successfully read due to a
- hardware problem or due to a conflicts on the Boot PROM address
- assignment. (Test only applies if the adapter is configured to use the
- Boot PROM option.)
-
-Failure of a test item indicates a possible system resource conflict with
-another device on the ISA bus. In this case, you should use the Manual Setup
-option to reconfigure the adapter by selecting a different value for the system
-resource that failed.
-
-
-5.2.2. Diagnostic Network Test
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The Diagnostic Network Test verifies a working network connection by
-transferring data between two CS8900/20 adapters installed in different PCs
-on the same network. (Note: the diagnostic network test should not be run
-between two nodes across a router.)
-
-This test requires that each of the two PCs have a CS8900/20-based adapter
-installed and have the CS8900/20 Setup Utility running. The first PC is
-configured as a Responder and the other PC is configured as an Initiator.
-Once the Initiator is started, it sends data frames to the Responder which
-returns the frames to the Initiator.
-
-The total number of frames received and transmitted are displayed on the
-Initiator's display, along with a count of the number of frames received and
-transmitted OK or in error. The test can be terminated anytime by the user at
-either PC.
-
-To setup the Diagnostic Network Test:
-
- 1. Select a PC with a CS8900/20-based adapter and a known working network
- connection to act as the Responder. Run the CS8900/20 Setup Utility
- and select 'Diagnostics -> Network Test -> Responder' from the main
- menu. Hit ENTER to start the Responder.
-
- 2. Return to the PC with the CS8900/20-based adapter you want to test and
- start the CS8900/20 Setup Utility.
-
- 3. From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
- Hit ENTER to start the test.
-
-You may stop the test on the Initiator at any time while allowing the Responder
-to continue running. In this manner, you can move to additional PCs and test
-them by starting the Initiator on another PC without having to stop/start the
-Responder.
-
-
-
-5.3. Using the Adapter's LEDs
------------------------------
-
-The 2 and 3-media adapters have two LEDs visible on the back end of the board
-located near the 10Base-T connector.
-
-Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T
-connection. (Only applies to 10Base-T. The green LED has no significance for
-a 10Base-2 or AUI connection.)
-
-TX/RX LED: The yellow LED lights briefly each time the adapter transmits or
-receives data. (The yellow LED will appear to "flicker" on a typical network.)
-
-
-5.4. Resolving I/O Conflicts
-----------------------------
-
-An IO conflict occurs when two or more adapter use the same ISA resource (IO
-address, memory address or IRQ). You can usually detect an IO conflict in one
-of four ways after installing and or configuring the CS8900/20-based adapter:
-
- 1. The system does not boot properly (or at all).
-
- 2. The driver cannot communicate with the adapter, reporting an "Adapter
- not found" error message.
-
- 3. You cannot connect to the network or the driver will not load.
-
- 4. If you have configured the adapter to run in memory mode but the driver
- reports it is using IO mode when loading, this is an indication of a
- memory address conflict.
-
-If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a
-diagnostic self-test. Normally, the ISA resource in conflict will fail the
-self-test. If so, reconfigure the adapter selecting another choice for the
-resource in conflict. Run the diagnostics again to check for further IO
-conflicts.
-
-In some cases, such as when the PC will not boot, it may be necessary to remove
-the adapter and reconfigure it by installing it in another PC to run the
-CS8900/20 Setup Utility. Once reinstalled in the target system, run the
-diagnostics self-test to ensure the new configuration is free of conflicts
-before loading the driver again.
-
-When manually configuring the adapter, keep in mind the typical ISA system
-resource usage as indicated in the tables below.
-
-::
-
- I/O Address Device IRQ Device
- ----------- -------- --- --------
- 200-20F Game I/O adapter 3 COM2, Bus Mouse
- 230-23F Bus Mouse 4 COM1
- 270-27F LPT3: third parallel port 5 LPT2
- 2F0-2FF COM2: second serial port 6 Floppy Disk controller
- 320-32F Fixed disk controller 7 LPT1
- 8 Real-time Clock
- 9 EGA/VGA display adapter
- 12 Mouse (PS/2)
- Memory Address Device 13 Math Coprocessor
- -------------- --------------------- 14 Hard Disk controller
- A000-BFFF EGA Graphics Adapter
- A000-C7FF VGA Graphics Adapter
- B000-BFFF Mono Graphics Adapter
- B800-BFFF Color Graphics Adapter
- E000-FFFF AT BIOS
-
-
-
-
-6. Technical Support
-====================
-
-6.1. Contacting Cirrus Logic's Technical Support
-------------------------------------------------
-
-Cirrus Logic's CS89XX Technical Application Support can be reached at::
-
- Telephone :(800) 888-5016 (from inside U.S. and Canada)
- :(512) 442-7555 (from outside the U.S. and Canada)
- Fax :(512) 912-3871
- Email :ethernet@crystal.cirrus.com
- WWW :http://www.cirrus.com
-
-
-6.2. Information Required before Contacting Technical Support
--------------------------------------------------------------
-
-Before contacting Cirrus Logic for technical support, be prepared to provide as
-Much of the following information as possible.
-
-1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
-
-2.) Adapter configuration
-
- * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
- * Plug and Play enabled/disabled (CS8920-based adapters only)
- * Configured for media auto-detect or specific media type (which type).
-
-3.) PC System's Configuration
-
- * Plug and Play system (yes/no)
- * BIOS (make and version)
- * System make and model
- * CPU (type and speed)
- * System RAM
- * SCSI Adapter
-
-4.) Software
-
- * CS89XX driver and version
- * Your network operating system and version
- * Your system's OS version
- * Version of all protocol support files
-
-5.) Any Error Message displayed.
-
-
-
-6.3 Obtaining the Latest Driver Version
----------------------------------------
-
-You can obtain the latest CS89XX drivers and support software from Cirrus Logic's
-Web site. You can also contact Cirrus Logic's Technical Support (email:
-ethernet@crystal.cirrus.com) and request that you be registered for automatic
-software-update notification.
-
-Cirrus Logic maintains a web page at http://www.cirrus.com with the
-latest drivers and technical publications.
-
-
-6.4. Current maintainer
------------------------
-
-In February 2000 the maintenance of this driver was assumed by Andrew
-Morton.
-
-6.5 Kernel module parameters
-----------------------------
-
-For use in embedded environments with no cs89x0 EEPROM, the kernel boot
-parameter ``cs89x0_media=`` has been implemented. Usage is::
-
- cs89x0_media=rj45 or
- cs89x0_media=aui or
- cs89x0_media=bnc
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
index 5bdf731d9503..1a0c7b3bfcd6 100644
--- a/drivers/net/ethernet/cirrus/Kconfig
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -17,36 +17,6 @@ config NET_VENDOR_CIRRUS
if NET_VENDOR_CIRRUS
-config CS89x0
- tristate
-
-config CS89x0_ISA
- tristate "CS89x0 ISA driver support"
- depends on HAS_IOPORT_MAP
- depends on ISA
- depends on !PPC32
- depends on CS89x0_PLATFORM=n
- select NETDEV_LEGACY_INIT
- select CS89x0
- help
- Support for CS89x0 chipset based Ethernet cards. If you have a
- network (Ethernet) card of this type, say Y and read the file
- <file:Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst>.
-
- To compile this driver as a module, choose M here. The module
- will be called cs89x0.
-
-config CS89x0_PLATFORM
- tristate "CS89x0 platform driver support"
- depends on ARM || (COMPILE_TEST && !PPC)
- select CS89x0
- help
- Say Y to compile the cs89x0 platform driver. This makes this driver
- suitable for use on certain evaluation boards such as the iMX21ADS.
-
- To compile this driver as a module, choose M here. The module
- will be called cs89x0.
-
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on (ARM && ARCH_EP93XX) || COMPILE_TEST
diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
index 84865e593788..cb740939d976 100644
--- a/drivers/net/ethernet/cirrus/Makefile
+++ b/drivers/net/ethernet/cirrus/Makefile
@@ -3,6 +3,5 @@
# Makefile for the Cirrus network device drivers.
#
-obj-$(CONFIG_CS89x0) += cs89x0.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_MAC89x0) += mac89x0.o
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
deleted file mode 100644
index fa5857923db4..000000000000
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ /dev/null
@@ -1,1915 +0,0 @@
-/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
- * driver for linux.
- * Written 1996 by Russell Nelson, with reference to skeleton.c
- * written 1993-1994 by Donald Becker.
- *
- * 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 at nelson@crynwr.com, Crynwr
- * Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
- *
- * Other contributors:
- * Mike Cruse : mcruse@cti-ltd.com
- * Russ Nelson
- * Melody Lee : ethernet@crystal.cirrus.com
- * Alan Cox
- * Andrew Morton
- * Oskar Schirmer : oskar@scara.com
- * Deepak Saxena : dsaxena@plexity.net
- * Dmitry Pervushin : dpervushin@ru.mvista.com
- * Deepak Saxena : dsaxena@plexity.net
- * Domenico Andreoli : cavokz@gmail.com
- */
-
-
-/*
- * Set this to zero to disable DMA code
- *
- * Note that even if DMA is turned off we still support the 'dma' and 'use_dma'
- * module options so we don't break any startup scripts.
- */
-#ifndef CONFIG_ISA_DMA_API
-#define ALLOW_DMA 0
-#else
-#define ALLOW_DMA 1
-#endif
-
-/*
- * Set this to zero to remove all the debug statements via
- * dead code elimination
- */
-#define DEBUGGING 1
-
-/* Sources:
- * Crynwr packet driver epktisa.
- * Crystal Semiconductor data sheets.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/printk.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/of.h>
-#include <linux/platform_device.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/jiffies.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/io.h>
-
-#include <net/Space.h>
-
-#include <asm/irq.h>
-#include <linux/atomic.h>
-#if ALLOW_DMA
-#include <asm/dma.h>
-#endif
-
-#include "cs89x0.h"
-
-#define cs89_dbg(val, level, fmt, ...) \
-do { \
- if (val <= net_debug) \
- pr_##level(fmt, ##__VA_ARGS__); \
-} while (0)
-
-static char version[] __initdata =
- "v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton";
-
-#define DRV_NAME "cs89x0"
-
-/* First, a few definitions that the brave might change.
- * A zero-terminated list of I/O addresses to be probed. Some special flags..
- * Addr & 1 = Read back the address port, look for signature and reset
- * the page window before probing
- * Addr & 3 = Reset the page window and probe
- * The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space,
- * but it is possible that a Cirrus board could be plugged into the ISA
- * slots.
- */
-/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps
- * them to system IRQ numbers. This mapping is card specific and is set to
- * the configuration of the Cirrus Eval board for this chip.
- */
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
-static unsigned int netcard_portlist[] __used __initdata = {
- 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240,
- 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0
-};
-static unsigned int cs8900_irq_map[] = {
- 10, 11, 12, 5
-};
-#endif
-
-#if DEBUGGING
-static unsigned int net_debug = DEBUGGING;
-#else
-#define net_debug 0 /* gcc will remove all the debug code for us */
-#endif
-
-/* The number of low I/O ports used by the ethercard. */
-#define NETCARD_IO_EXTENT 16
-
-/* we allow the user to override various values normally set in the EEPROM */
-#define FORCE_RJ45 0x0001 /* pick one of these three */
-#define FORCE_AUI 0x0002
-#define FORCE_BNC 0x0004
-
-#define FORCE_AUTO 0x0010 /* pick one of these three */
-#define FORCE_HALF 0x0020
-#define FORCE_FULL 0x0030
-
-/* Information that need to be kept for each board. */
-struct net_local {
- int chip_type; /* one of: CS8900, CS8920, CS8920M */
- char chip_revision; /* revision letter of the chip ('A'...) */
- int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
- int auto_neg_cnf; /* auto-negotiation word from EEPROM */
- int adapter_cnf; /* adapter configuration from EEPROM */
- int isa_config; /* ISA configuration from EEPROM */
- int irq_map; /* IRQ map from EEPROM */
- int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
- int curr_rx_cfg; /* a copy of PP_RxCFG */
- int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */
- int send_underrun; /* keep track of how many underruns in a row we get */
- int force; /* force various values; see FORCE* above. */
- spinlock_t lock;
- void __iomem *virt_addr;/* CS89x0 virtual address. */
-#if ALLOW_DMA
- int use_dma; /* Flag: we're using dma */
- int dma; /* DMA channel */
- int dmasize; /* 16 or 64 */
- unsigned char *dma_buff; /* points to the beginning of the buffer */
- unsigned char *end_dma_buff; /* points to the end of the buffer */
- unsigned char *rx_dma_ptr; /* points to the next packet */
-#endif
-};
-
-/* Example routines you must write ;->. */
-#define tx_done(dev) 1
-
-/*
- * Permit 'cs89x0_dma=N' in the kernel boot environment
- */
-#if !defined(MODULE)
-#if ALLOW_DMA
-static int g_cs89x0_dma;
-
-static int __init dma_fn(char *str)
-{
- g_cs89x0_dma = simple_strtol(str, NULL, 0);
- return 1;
-}
-
-__setup("cs89x0_dma=", dma_fn);
-#endif /* ALLOW_DMA */
-
-static int g_cs89x0_media__force;
-
-static int __init media_fn(char *str)
-{
- if (!strcmp(str, "rj45"))
- g_cs89x0_media__force = FORCE_RJ45;
- else if (!strcmp(str, "aui"))
- g_cs89x0_media__force = FORCE_AUI;
- else if (!strcmp(str, "bnc"))
- g_cs89x0_media__force = FORCE_BNC;
-
- return 1;
-}
-
-__setup("cs89x0_media=", media_fn);
-#endif
-
-static void readwords(struct net_local *lp, int portno, void *buf, int length)
-{
- u8 *buf8 = (u8 *)buf;
-
- do {
- u16 tmp16;
-
- tmp16 = ioread16(lp->virt_addr + portno);
- *buf8++ = (u8)tmp16;
- *buf8++ = (u8)(tmp16 >> 8);
- } while (--length);
-}
-
-static void writewords(struct net_local *lp, int portno, void *buf, int length)
-{
- u8 *buf8 = (u8 *)buf;
-
- do {
- u16 tmp16;
-
- tmp16 = *buf8++;
- tmp16 |= (*buf8++) << 8;
- iowrite16(tmp16, lp->virt_addr + portno);
- } while (--length);
-}
-
-static u16
-readreg(struct net_device *dev, u16 regno)
-{
- struct net_local *lp = netdev_priv(dev);
-
- iowrite16(regno, lp->virt_addr + ADD_PORT);
- return ioread16(lp->virt_addr + DATA_PORT);
-}
-
-static void
-writereg(struct net_device *dev, u16 regno, u16 value)
-{
- struct net_local *lp = netdev_priv(dev);
-
- iowrite16(regno, lp->virt_addr + ADD_PORT);
- iowrite16(value, lp->virt_addr + DATA_PORT);
-}
-
-static int __init
-wait_eeprom_ready(struct net_device *dev)
-{
- unsigned long timeout = jiffies;
- /* check to see if the EEPROM is ready,
- * a timeout is used just in case EEPROM is ready when
- * SI_BUSY in the PP_SelfST is clear
- */
- while (readreg(dev, PP_SelfST) & SI_BUSY)
- if (time_after_eq(jiffies, timeout + 40))
- return -1;
- return 0;
-}
-
-static int __init
-get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
-{
- int i;
-
- cs89_dbg(3, info, "EEPROM data from %x for %x:", off, len);
- for (i = 0; i < len; i++) {
- if (wait_eeprom_ready(dev) < 0)
- return -1;
- /* Now send the EEPROM read command and EEPROM location to read */
- writereg(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD);
- if (wait_eeprom_ready(dev) < 0)
- return -1;
- buffer[i] = readreg(dev, PP_EEData);
- cs89_dbg(3, cont, " %04x", buffer[i]);
- }
- cs89_dbg(3, cont, "\n");
- return 0;
-}
-
-static int __init
-get_eeprom_cksum(int off, int len, int *buffer)
-{
- int i, cksum;
-
- cksum = 0;
- for (i = 0; i < len; i++)
- cksum += buffer[i];
- cksum &= 0xffff;
- if (cksum == 0)
- return 0;
- return -1;
-}
-
-static void
-write_irq(struct net_device *dev, int chip_type, int irq)
-{
- int i;
-
- if (chip_type == CS8900) {
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- /* Search the mapping table for the corresponding IRQ pin. */
- for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++)
- if (cs8900_irq_map[i] == irq)
- break;
- /* Not found */
- if (i == ARRAY_SIZE(cs8900_irq_map))
- i = 3;
-#else
- /* INTRQ0 pin is used for interrupt generation. */
- i = 0;
-#endif
- writereg(dev, PP_CS8900_ISAINT, i);
- } else {
- writereg(dev, PP_CS8920_ISAINT, irq);
- }
-}
-
-static void
-count_rx_errors(int status, struct net_device *dev)
-{
- dev->stats.rx_errors++;
- if (status & RX_RUNT)
- dev->stats.rx_length_errors++;
- if (status & RX_EXTRA_DATA)
- dev->stats.rx_length_errors++;
- if ((status & RX_CRC_ERROR) && !(status & (RX_EXTRA_DATA | RX_RUNT)))
- /* per str 172 */
- dev->stats.rx_crc_errors++;
- if (status & RX_DRIBBLE)
- dev->stats.rx_frame_errors++;
-}
-
-/*********************************
- * This page contains DMA routines
- *********************************/
-
-#if ALLOW_DMA
-
-#define dma_page_eq(ptr1, ptr2) ((long)(ptr1) >> 17 == (long)(ptr2) >> 17)
-
-static void
-get_dma_channel(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- if (lp->dma) {
- dev->dma = lp->dma;
- lp->isa_config |= ISA_RxDMA;
- } else {
- if ((lp->isa_config & ANY_ISA_DMA) == 0)
- return;
- dev->dma = lp->isa_config & DMA_NO_MASK;
- if (lp->chip_type == CS8900)
- dev->dma += 5;
- if (dev->dma < 5 || dev->dma > 7) {
- lp->isa_config &= ~ANY_ISA_DMA;
- return;
- }
- }
-}
-
-static void
-write_dma(struct net_device *dev, int chip_type, int dma)
-{
- struct net_local *lp = netdev_priv(dev);
- if ((lp->isa_config & ANY_ISA_DMA) == 0)
- return;
- if (chip_type == CS8900)
- writereg(dev, PP_CS8900_ISADMA, dma - 5);
- else
- writereg(dev, PP_CS8920_ISADMA, dma);
-}
-
-static void
-set_dma_cfg(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- if (lp->use_dma) {
- if ((lp->isa_config & ANY_ISA_DMA) == 0) {
- cs89_dbg(3, err, "set_dma_cfg(): no DMA\n");
- return;
- }
- if (lp->isa_config & ISA_RxDMA) {
- lp->curr_rx_cfg |= RX_DMA_ONLY;
- cs89_dbg(3, info, "set_dma_cfg(): RX_DMA_ONLY\n");
- } else {
- lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */
- cs89_dbg(3, info, "set_dma_cfg(): AUTO_RX_DMA\n");
- }
- }
-}
-
-static int
-dma_bufcfg(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- if (lp->use_dma)
- return (lp->isa_config & ANY_ISA_DMA) ? RX_DMA_ENBL : 0;
- else
- return 0;
-}
-
-static int
-dma_busctl(struct net_device *dev)
-{
- int retval = 0;
- struct net_local *lp = netdev_priv(dev);
- if (lp->use_dma) {
- if (lp->isa_config & ANY_ISA_DMA)
- retval |= RESET_RX_DMA; /* Reset the DMA pointer */
- if (lp->isa_config & DMA_BURST)
- retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
- if (lp->dmasize == 64)
- retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
- retval |= MEMORY_ON; /* we need memory enabled to use DMA. */
- }
- return retval;
-}
-
-static void
-dma_rx(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- struct sk_buff *skb;
- int status, length;
- unsigned char *bp = lp->rx_dma_ptr;
-
- status = bp[0] + (bp[1] << 8);
- length = bp[2] + (bp[3] << 8);
- bp += 4;
-
- cs89_dbg(5, debug, "%s: receiving DMA packet at %lx, status %x, length %x\n",
- dev->name, (unsigned long)bp, status, length);
-
- if ((status & RX_OK) == 0) {
- count_rx_errors(status, dev);
- goto skip_this_frame;
- }
-
- /* Malloc up new buffer. */
- skb = netdev_alloc_skb(dev, length + 2);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
-
- /* AKPM: advance bp to the next frame */
-skip_this_frame:
- bp += (length + 3) & ~3;
- if (bp >= lp->end_dma_buff)
- bp -= lp->dmasize * 1024;
- lp->rx_dma_ptr = bp;
- return;
- }
- skb_reserve(skb, 2); /* longword align L3 header */
-
- if (bp + length > lp->end_dma_buff) {
- int semi_cnt = lp->end_dma_buff - bp;
- skb_put_data(skb, bp, semi_cnt);
- skb_put_data(skb, lp->dma_buff, length - semi_cnt);
- } else {
- skb_put_data(skb, bp, length);
- }
- bp += (length + 3) & ~3;
- if (bp >= lp->end_dma_buff)
- bp -= lp->dmasize*1024;
- lp->rx_dma_ptr = bp;
-
- cs89_dbg(3, info, "%s: received %d byte DMA packet of type %x\n",
- dev->name, length,
- ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]));
-
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += length;
-}
-
-static void release_dma_buff(struct net_local *lp)
-{
- if (lp->dma_buff) {
- free_pages((unsigned long)(lp->dma_buff),
- get_order(lp->dmasize * 1024));
- lp->dma_buff = NULL;
- }
-}
-
-#endif /* ALLOW_DMA */
-
-static void
-control_dc_dc(struct net_device *dev, int on_not_off)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned int selfcontrol;
- unsigned long timenow = jiffies;
- /* control the DC to DC convertor in the SelfControl register.
- * Note: This is hooked up to a general purpose pin, might not
- * always be a DC to DC convertor.
- */
-
- selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
- if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
- selfcontrol |= HCB1;
- else
- selfcontrol &= ~HCB1;
- writereg(dev, PP_SelfCTL, selfcontrol);
-
- /* Wait for the DC/DC converter to power up - 500ms */
- while (time_before(jiffies, timenow + HZ))
- ;
-}
-
-/* send a test packet - return true if carrier bits are ok */
-static int
-send_test_pkt(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- char test_packet[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 46, /* A 46 in network order */
- 0, 0, /* DSAP=0 & SSAP=0 fields */
- 0xf3, 0 /* Control (Test Req + P bit set) */
- };
- unsigned long timenow = jiffies;
-
- writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_TX_ON);
-
- memcpy(test_packet, dev->dev_addr, ETH_ALEN);
- memcpy(test_packet + ETH_ALEN, dev->dev_addr, ETH_ALEN);
-
- iowrite16(TX_AFTER_ALL, lp->virt_addr + TX_CMD_PORT);
- iowrite16(ETH_ZLEN, lp->virt_addr + TX_LEN_PORT);
-
- /* Test to see if the chip has allocated memory for the packet */
- while (time_before(jiffies, timenow + 5))
- if (readreg(dev, PP_BusST) & READY_FOR_TX_NOW)
- break;
- if (time_after_eq(jiffies, timenow + 5))
- return 0; /* this shouldn't happen */
-
- /* Write the contents of the packet */
- writewords(lp, TX_FRAME_PORT, test_packet, (ETH_ZLEN + 1) >> 1);
-
- cs89_dbg(1, debug, "Sending test packet ");
- /* wait a couple of jiffies for packet to be received */
- for (timenow = jiffies; time_before(jiffies, timenow + 3);)
- ;
- if ((readreg(dev, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
- cs89_dbg(1, cont, "succeeded\n");
- return 1;
- }
- cs89_dbg(1, cont, "failed\n");
- return 0;
-}
-
-#define DETECTED_NONE 0
-#define DETECTED_RJ45H 1
-#define DETECTED_RJ45F 2
-#define DETECTED_AUI 3
-#define DETECTED_BNC 4
-
-static int
-detect_tp(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long timenow = jiffies;
- int fdx;
-
- cs89_dbg(1, debug, "%s: Attempting TP\n", dev->name);
-
- /* If connected to another full duplex capable 10-Base-T card
- * the link pulses seem to be lost when the auto detect bit in
- * the LineCTL is set. To overcome this the auto detect bit will
- * be cleared whilst testing the 10-Base-T interface. This would
- * not be necessary for the sparrow chip but is simpler to do it
- * anyway.
- */
- writereg(dev, PP_LineCTL, lp->linectl & ~AUI_ONLY);
- control_dc_dc(dev, 0);
-
- /* Delay for the hardware to work out if the TP cable is present
- * - 150ms
- */
- for (timenow = jiffies; time_before(jiffies, timenow + 15);)
- ;
- if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
- return DETECTED_NONE;
-
- if (lp->chip_type == CS8900) {
- switch (lp->force & 0xf0) {
-#if 0
- case FORCE_AUTO:
- pr_info("%s: cs8900 doesn't autonegotiate\n",
- dev->name);
- return DETECTED_NONE;
-#endif
- /* CS8900 doesn't support AUTO, change to HALF*/
- case FORCE_AUTO:
- lp->force &= ~FORCE_AUTO;
- lp->force |= FORCE_HALF;
- break;
- case FORCE_HALF:
- break;
- case FORCE_FULL:
- writereg(dev, PP_TestCTL,
- readreg(dev, PP_TestCTL) | FDX_8900);
- break;
- }
- fdx = readreg(dev, PP_TestCTL) & FDX_8900;
- } else {
- switch (lp->force & 0xf0) {
- case FORCE_AUTO:
- lp->auto_neg_cnf = AUTO_NEG_ENABLE;
- break;
- case FORCE_HALF:
- lp->auto_neg_cnf = 0;
- break;
- case FORCE_FULL:
- lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
- break;
- }
-
- writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
-
- if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
- pr_info("%s: negotiating duplex...\n", dev->name);
- while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
- if (time_after(jiffies, timenow + 4000)) {
- pr_err("**** Full / half duplex auto-negotiation timed out ****\n");
- break;
- }
- }
- }
- fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
- }
- if (fdx)
- return DETECTED_RJ45F;
- else
- return DETECTED_RJ45H;
-}
-
-static int
-detect_bnc(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- cs89_dbg(1, debug, "%s: Attempting BNC\n", dev->name);
- control_dc_dc(dev, 1);
-
- writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
-
- if (send_test_pkt(dev))
- return DETECTED_BNC;
- else
- return DETECTED_NONE;
-}
-
-static int
-detect_aui(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- cs89_dbg(1, debug, "%s: Attempting AUI\n", dev->name);
- control_dc_dc(dev, 0);
-
- writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
-
- if (send_test_pkt(dev))
- return DETECTED_AUI;
- else
- return DETECTED_NONE;
-}
-
-/* We have a good packet(s), get it/them out of the buffers. */
-static void
-net_rx(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- struct sk_buff *skb;
- int status, length;
-
- status = ioread16(lp->virt_addr + RX_FRAME_PORT);
- length = ioread16(lp->virt_addr + RX_FRAME_PORT);
-
- if ((status & RX_OK) == 0) {
- count_rx_errors(status, dev);
- return;
- }
-
- /* Malloc up new buffer. */
- skb = netdev_alloc_skb(dev, length + 2);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
- return;
- }
- skb_reserve(skb, 2); /* longword align L3 header */
-
- readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
- if (length & 1)
- skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
-
- cs89_dbg(3, debug, "%s: received %d byte packet of type %x\n",
- dev->name, length,
- (skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]);
-
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += length;
-}
-
-/* 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 status;
- int handled = 0;
-
- lp = netdev_priv(dev);
-
- /* we MUST read all the events out of the ISQ, otherwise we'll never
- * get interrupted again. As a consequence, we can't have any limit
- * on the number of times we loop in the interrupt handler. The
- * hardware guarantees that eventually we'll run out of events. Of
- * course, if you're on a slow machine, and packets are arriving
- * faster than you can read them off, you're screwed. Hasta la
- * vista, baby!
- */
- while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
- cs89_dbg(4, debug, "%s: event=%04x\n", dev->name, status);
- handled = 1;
- switch (status & ISQ_EVENT_MASK) {
- case ISQ_RECEIVER_EVENT:
- /* Got a packet(s). */
- net_rx(dev);
- break;
- case ISQ_TRANSMITTER_EVENT:
- dev->stats.tx_packets++;
- netif_wake_queue(dev); /* Inform upper layers. */
- if ((status & (TX_OK |
- TX_LOST_CRS |
- TX_SQE_ERROR |
- TX_LATE_COL |
- TX_16_COL)) != TX_OK) {
- if ((status & TX_OK) == 0)
- dev->stats.tx_errors++;
- if (status & TX_LOST_CRS)
- dev->stats.tx_carrier_errors++;
- if (status & TX_SQE_ERROR)
- dev->stats.tx_heartbeat_errors++;
- if (status & TX_LATE_COL)
- dev->stats.tx_window_errors++;
- if (status & TX_16_COL)
- dev->stats.tx_aborted_errors++;
- }
- break;
- case ISQ_BUFFER_EVENT:
- if (status & READY_FOR_TX) {
- /* we tried to transmit a packet earlier,
- * but inexplicably ran out of buffers.
- * That shouldn't happen since we only ever
- * load one packet. Shrug. Do the right
- * thing anyway.
- */
- netif_wake_queue(dev); /* Inform upper layers. */
- }
- if (status & TX_UNDERRUN) {
- cs89_dbg(0, err, "%s: transmit underrun\n",
- dev->name);
- lp->send_underrun++;
- if (lp->send_underrun == 3)
- lp->send_cmd = TX_AFTER_381;
- else if (lp->send_underrun == 6)
- lp->send_cmd = TX_AFTER_ALL;
- /* transmit cycle is done, although
- * frame wasn't transmitted - this
- * avoids having to wait for the upper
- * layers to timeout on us, in the
- * event of a tx underrun
- */
- netif_wake_queue(dev); /* Inform upper layers. */
- }
-#if ALLOW_DMA
- if (lp->use_dma && (status & RX_DMA)) {
- int count = readreg(dev, PP_DmaFrameCnt);
- while (count) {
- cs89_dbg(5, debug,
- "%s: receiving %d DMA frames\n",
- dev->name, count);
- if (count > 1)
- cs89_dbg(2, debug,
- "%s: receiving %d DMA frames\n",
- dev->name, count);
- dma_rx(dev);
- if (--count == 0)
- count = readreg(dev, PP_DmaFrameCnt);
- if (count > 0)
- cs89_dbg(2, debug,
- "%s: continuing with %d DMA frames\n",
- dev->name, count);
- }
- }
-#endif
- break;
- case ISQ_RX_MISS_EVENT:
- dev->stats.rx_missed_errors += (status >> 6);
- break;
- case ISQ_TX_COL_EVENT:
- dev->stats.collisions += (status >> 6);
- break;
- }
- }
- return IRQ_RETVAL(handled);
-}
-
-/* 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.
-*/
-
-/* AKPM: do we need to do any locking here? */
-
-static int
-net_open(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- int result = 0;
- int i;
- int ret;
-
- if (dev->irq < 2) {
- /* Allow interrupts to be generated by the chip */
-/* Cirrus' release had this: */
-#if 0
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
-#endif
-/* And 2.3.47 had this: */
- writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
-
- for (i = 2; i < CS8920_NO_INTS; i++) {
- if ((1 << i) & lp->irq_map) {
- if (request_irq(i, net_interrupt, 0, dev->name,
- dev) == 0) {
- dev->irq = i;
- write_irq(dev, lp->chip_type, i);
- /* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */
- break;
- }
- }
- }
-
- if (i >= CS8920_NO_INTS) {
- writereg(dev, PP_BusCTL, 0); /* disable interrupts. */
- pr_err("can't get an interrupt\n");
- ret = -EAGAIN;
- goto bad_out;
- }
- } else {
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- if (((1 << dev->irq) & lp->irq_map) == 0) {
- pr_err("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
- dev->name, dev->irq, lp->irq_map);
- ret = -EAGAIN;
- goto bad_out;
- }
-#endif
-/* FIXME: Cirrus' release had this: */
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ);
-/* And 2.3.47 had this: */
-#if 0
- writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
-#endif
- write_irq(dev, lp->chip_type, dev->irq);
- ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
- if (ret) {
- pr_err("request_irq(%d) failed\n", dev->irq);
- goto bad_out;
- }
- }
-
-#if ALLOW_DMA
- if (lp->use_dma && (lp->isa_config & ANY_ISA_DMA)) {
- unsigned long flags;
- lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
- get_order(lp->dmasize * 1024));
- if (!lp->dma_buff) {
- pr_err("%s: cannot get %dK memory for DMA\n",
- dev->name, lp->dmasize);
- goto release_irq;
- }
- cs89_dbg(1, debug, "%s: dma %lx %lx\n",
- dev->name,
- (unsigned long)lp->dma_buff,
- (unsigned long)isa_virt_to_bus(lp->dma_buff));
- if ((unsigned long)lp->dma_buff >= MAX_DMA_ADDRESS ||
- !dma_page_eq(lp->dma_buff,
- lp->dma_buff + lp->dmasize * 1024 - 1)) {
- pr_err("%s: not usable as DMA buffer\n", dev->name);
- goto release_irq;
- }
- memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */
- if (request_dma(dev->dma, dev->name)) {
- pr_err("%s: cannot get dma channel %d\n",
- dev->name, dev->dma);
- goto release_irq;
- }
- write_dma(dev, lp->chip_type, dev->dma);
- lp->rx_dma_ptr = lp->dma_buff;
- lp->end_dma_buff = lp->dma_buff + lp->dmasize * 1024;
- spin_lock_irqsave(&lp->lock, flags);
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- set_dma_mode(dev->dma, DMA_RX_MODE); /* auto_init as well */
- set_dma_addr(dev->dma, isa_virt_to_bus(lp->dma_buff));
- set_dma_count(dev->dma, lp->dmasize * 1024);
- enable_dma(dev->dma);
- spin_unlock_irqrestore(&lp->lock, flags);
- }
-#endif /* ALLOW_DMA */
-
- /* set the Ethernet address */
- for (i = 0; i < ETH_ALEN / 2; i++)
- writereg(dev, PP_IA + i * 2,
- (dev->dev_addr[i * 2] |
- (dev->dev_addr[i * 2 + 1] << 8)));
-
- /* while we're testing the interface, leave interrupts disabled */
- writereg(dev, PP_BusCTL, MEMORY_ON);
-
- /* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
- if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
- (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
- lp->linectl = LOW_RX_SQUELCH;
- else
- lp->linectl = 0;
-
- /* check to make sure that they have the "right" hardware available */
- switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
- case A_CNF_MEDIA_10B_T:
- result = lp->adapter_cnf & A_CNF_10B_T;
- break;
- case A_CNF_MEDIA_AUI:
- result = lp->adapter_cnf & A_CNF_AUI;
- break;
- case A_CNF_MEDIA_10B_2:
- result = lp->adapter_cnf & A_CNF_10B_2;
- break;
- default:
- result = lp->adapter_cnf & (A_CNF_10B_T |
- A_CNF_AUI |
- A_CNF_10B_2);
- }
- if (!result) {
- pr_err("%s: EEPROM is configured for unavailable media\n",
- dev->name);
-release_dma:
-#if ALLOW_DMA
- free_dma(dev->dma);
-release_irq:
- release_dma_buff(lp);
-#endif
- writereg(dev, PP_LineCTL,
- readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
- free_irq(dev->irq, dev);
- ret = -EAGAIN;
- goto bad_out;
- }
-
- /* set the hardware to the configured choice */
- switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
- case A_CNF_MEDIA_10B_T:
- result = detect_tp(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-T (RJ-45) has no cable\n",
- dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
- }
- break;
- case A_CNF_MEDIA_AUI:
- result = detect_aui(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-5 (AUI) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_AUI; /* Yes! I don't care if I see a carrier */
- }
- break;
- case A_CNF_MEDIA_10B_2:
- result = detect_bnc(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-2 (BNC) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
- }
- break;
- case A_CNF_MEDIA_AUTO:
- writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
- if (lp->adapter_cnf & A_CNF_10B_T) {
- result = detect_tp(dev);
- if (result != DETECTED_NONE)
- break;
- }
- if (lp->adapter_cnf & A_CNF_AUI) {
- result = detect_aui(dev);
- if (result != DETECTED_NONE)
- break;
- }
- if (lp->adapter_cnf & A_CNF_10B_2) {
- result = detect_bnc(dev);
- if (result != DETECTED_NONE)
- break;
- }
- pr_err("%s: no media detected\n", dev->name);
- goto release_dma;
- }
- switch (result) {
- case DETECTED_NONE:
- pr_err("%s: no network cable attached to configured media\n",
- dev->name);
- goto release_dma;
- case DETECTED_RJ45H:
- pr_info("%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
- break;
- case DETECTED_RJ45F:
- pr_info("%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
- break;
- case DETECTED_AUI:
- pr_info("%s: using 10Base-5 (AUI)\n", dev->name);
- break;
- case DETECTED_BNC:
- pr_info("%s: using 10Base-2 (BNC)\n", dev->name);
- break;
- }
-
- /* Turn on both receive and transmit operations */
- writereg(dev, PP_LineCTL,
- readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
-
- /* Receive only error free packets addressed to this card */
- lp->rx_mode = 0;
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
-
- lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
-
- if (lp->isa_config & STREAM_TRANSFER)
- lp->curr_rx_cfg |= RX_STREAM_ENBL;
-#if ALLOW_DMA
- set_dma_cfg(dev);
-#endif
- writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
-
- writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL |
- TX_SQE_ERROR_ENBL |
- TX_OK_ENBL |
- TX_LATE_COL_ENBL |
- TX_JBR_ENBL |
- TX_ANY_COL_ENBL |
- TX_16_COL_ENBL));
-
- writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL |
- RX_MISS_COUNT_OVRFLOW_ENBL |
-#if ALLOW_DMA
- dma_bufcfg(dev) |
-#endif
- TX_COL_COUNT_OVRFLOW_ENBL |
- TX_UNDERRUN_ENBL));
-
- /* now that we've got our act together, enable everything */
- writereg(dev, PP_BusCTL, (ENABLE_IRQ
- | (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */
-#if ALLOW_DMA
- | dma_busctl(dev)
-#endif
- ));
- netif_start_queue(dev);
- cs89_dbg(1, debug, "net_open() succeeded\n");
- return 0;
-bad_out:
- return ret;
-}
-
-/* The inverse routine to net_open(). */
-static int
-net_close(struct net_device *dev)
-{
-#if ALLOW_DMA
- struct net_local *lp = netdev_priv(dev);
-#endif
-
- netif_stop_queue(dev);
-
- writereg(dev, PP_RxCFG, 0);
- writereg(dev, PP_TxCFG, 0);
- writereg(dev, PP_BufCFG, 0);
- writereg(dev, PP_BusCTL, 0);
-
- free_irq(dev->irq, dev);
-
-#if ALLOW_DMA
- if (lp->use_dma && lp->dma) {
- free_dma(dev->dma);
- release_dma_buff(lp);
- }
-#endif
-
- /* Update the statistics here. */
- return 0;
-}
-
-/* Get the current statistics.
- * This may be called with the card open or closed.
- */
-static struct net_device_stats *
-net_get_stats(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&lp->lock, flags);
- /* Update the statistics from the device registers. */
- dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
- dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
- spin_unlock_irqrestore(&lp->lock, flags);
-
- return &dev->stats;
-}
-
-static void net_timeout(struct net_device *dev, unsigned int txqueue)
-{
- /* If we get here, some higher level has decided we are broken.
- There should really be a "kick me" function call instead. */
- cs89_dbg(0, err, "%s: transmit timed out, %s?\n",
- dev->name,
- tx_done(dev) ? "IRQ conflict" : "network cable problem");
- /* Try to restart the adaptor. */
- 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);
- unsigned long flags;
-
- cs89_dbg(3, debug, "%s: sent %d byte packet of type %x\n",
- dev->name, skb->len,
- ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]));
-
- /* keep the upload from being interrupted, since we
- * ask the chip to start transmitting before the
- * whole packet has been completely uploaded.
- */
-
- spin_lock_irqsave(&lp->lock, flags);
- netif_stop_queue(dev);
-
- /* initiate a transmit sequence */
- iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
- iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
-
- /* Test to see if the chip has allocated memory for the packet */
- if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
- /* Gasp! It hasn't. But that shouldn't happen since
- * we're waiting for TxOk, so return 1 and requeue this packet.
- */
-
- spin_unlock_irqrestore(&lp->lock, flags);
- cs89_dbg(0, err, "Tx buffer not free!\n");
- return NETDEV_TX_BUSY;
- }
- /* Write the contents of the packet */
- writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
- spin_unlock_irqrestore(&lp->lock, flags);
- dev->stats.tx_bytes += skb->len;
- dev_consume_skb_any(skb);
-
- /* We DO NOT call netif_wake_queue() here.
- * We also DO NOT call netif_start_queue().
- *
- * Either of these would cause another bottom half run through
- * net_send_packet() before this packet has fully gone out.
- * That causes us to hit the "Gasp!" above and the send is rescheduled.
- * it runs like a dog. We just return and wait for the Tx completion
- * interrupt handler to restart the netdevice layer
- */
-
- return NETDEV_TX_OK;
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
- u16 cfg;
-
- spin_lock_irqsave(&lp->lock, flags);
- if (dev->flags & IFF_PROMISC)
- lp->rx_mode = RX_ALL_ACCEPT;
- else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
- /* The multicast-accept list is initialized to accept-all,
- * and we rely on higher-level filtering for now.
- */
- lp->rx_mode = RX_MULTCAST_ACCEPT;
- else
- lp->rx_mode = 0;
-
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
-
- /* in promiscuous mode, we accept errored packets,
- * so we have to enable interrupts on them also
- */
- cfg = lp->curr_rx_cfg;
- if (lp->rx_mode == RX_ALL_ACCEPT)
- cfg |= RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL;
- writereg(dev, PP_RxCFG, cfg);
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int set_mac_address(struct net_device *dev, void *p)
-{
- int i;
- struct sockaddr *addr = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- eth_hw_addr_set(dev, addr->sa_data);
-
- cs89_dbg(0, debug, "%s: Setting MAC address to %pM\n",
- dev->name, dev->dev_addr);
-
- /* set the Ethernet address */
- for (i = 0; i < ETH_ALEN / 2; i++)
- writereg(dev, PP_IA + i * 2,
- (dev->dev_addr[i * 2] |
- (dev->dev_addr[i * 2 + 1] << 8)));
-
- return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void net_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- net_interrupt(dev->irq, dev);
- enable_irq(dev->irq);
-}
-#endif
-
-static const struct net_device_ops net_ops = {
- .ndo_open = net_open,
- .ndo_stop = net_close,
- .ndo_tx_timeout = net_timeout,
- .ndo_start_xmit = net_send_packet,
- .ndo_get_stats = net_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = set_mac_address,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = net_poll_controller,
-#endif
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static void __init reset_chip(struct net_device *dev)
-{
-#if !defined(CONFIG_MACH_MX31ADS)
- struct net_local *lp = netdev_priv(dev);
- unsigned long reset_start_time;
-
- writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
-
- /* wait 30 ms */
- msleep(30);
-
- if (lp->chip_type != CS8900) {
- /* Hardware problem requires PNP registers to be reconfigured after a reset */
- iowrite16(PP_CS8920_ISAINT, lp->virt_addr + ADD_PORT);
- iowrite8(dev->irq, lp->virt_addr + DATA_PORT);
- iowrite8(0, lp->virt_addr + DATA_PORT + 1);
-
- iowrite16(PP_CS8920_ISAMemB, lp->virt_addr + ADD_PORT);
- iowrite8((dev->mem_start >> 16) & 0xff,
- lp->virt_addr + DATA_PORT);
- iowrite8((dev->mem_start >> 8) & 0xff,
- lp->virt_addr + DATA_PORT + 1);
- }
-
- /* Wait until the chip is reset */
- reset_start_time = jiffies;
- while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
- time_before(jiffies, reset_start_time + 2))
- ;
-#endif /* !CONFIG_MACH_MX31ADS */
-}
-
-/* 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.
- * Return 0 on success.
- */
-static int __init
-cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
-{
- struct net_local *lp = netdev_priv(dev);
- int i;
- int tmp;
- unsigned rev_type = 0;
- int eeprom_buff[CHKSUM_LEN];
- u8 addr[ETH_ALEN];
- int retval;
-
- /* Initialize the device structure. */
- if (!modular) {
- memset(lp, 0, sizeof(*lp));
- spin_lock_init(&lp->lock);
-#ifndef MODULE
-#if ALLOW_DMA
- if (g_cs89x0_dma) {
- lp->use_dma = 1;
- lp->dma = g_cs89x0_dma;
- lp->dmasize = 16; /* Could make this an option... */
- }
-#endif
- lp->force = g_cs89x0_media__force;
-#endif
- }
-
- pr_debug("PP_addr at %p[%x]: 0x%x\n",
- ioaddr, ADD_PORT, ioread16(ioaddr + ADD_PORT));
- iowrite16(PP_ChipID, ioaddr + ADD_PORT);
-
- tmp = ioread16(ioaddr + DATA_PORT);
- if (tmp != CHIP_EISA_ID_SIG) {
- pr_debug("%s: incorrect signature at %p[%x]: 0x%x!="
- CHIP_EISA_ID_SIG_STR "\n",
- dev->name, ioaddr, DATA_PORT, tmp);
- retval = -ENODEV;
- goto out1;
- }
-
- lp->virt_addr = ioaddr;
-
- /* get the chip type */
- rev_type = readreg(dev, PRODUCT_ID_ADD);
- lp->chip_type = rev_type & ~REVISON_BITS;
- lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
-
- /* Check the chip type and revision in order to set the correct
- * send command. CS8920 revision C and CS8900 revision F can use
- * the faster send.
- */
- lp->send_cmd = TX_AFTER_381;
- if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
- lp->send_cmd = TX_NOW;
- if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
- lp->send_cmd = TX_NOW;
-
- pr_info_once("%s\n", version);
-
- pr_info("%s: cs89%c0%s rev %c found at %p ",
- dev->name,
- lp->chip_type == CS8900 ? '0' : '2',
- lp->chip_type == CS8920M ? "M" : "",
- lp->chip_revision,
- lp->virt_addr);
-
- reset_chip(dev);
-
- /* Here we read the current configuration of the chip.
- * If there is no Extended EEPROM then the idea is to not disturb
- * the chip configuration, it should have been correctly setup by
- * automatic EEPROM read on reset. So, if the chip says it read
- * the EEPROM the driver will always do *something* instead of
- * complain that adapter_cnf is 0.
- */
-
- if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) ==
- (EEPROM_OK | EEPROM_PRESENT)) {
- /* Load the MAC. */
- for (i = 0; i < ETH_ALEN / 2; i++) {
- unsigned int Addr;
- Addr = readreg(dev, PP_IA + i * 2);
- addr[i * 2] = Addr & 0xFF;
- addr[i * 2 + 1] = Addr >> 8;
- }
- eth_hw_addr_set(dev, addr);
-
- /* Load the Adapter Configuration.
- * Note: Barring any more specific information from some
- * other source (ie EEPROM+Schematics), we would not know
- * how to operate a 10Base2 interface on the AUI port.
- * However, since we do read the status of HCB1 and use
- * settings that always result in calls to control_dc_dc(dev,0)
- * a BNC interface should work if the enable pin
- * (dc/dc converter) is on HCB1.
- * It will be called AUI however.
- */
-
- lp->adapter_cnf = 0;
- i = readreg(dev, PP_LineCTL);
- /* Preserve the setting of the HCB1 pin. */
- if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL))
- lp->adapter_cnf |= A_CNF_DC_DC_POLARITY;
- /* Save the sqelch bit */
- if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH)
- lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH;
- /* Check if the card is in 10Base-t only mode */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0)
- lp->adapter_cnf |= A_CNF_10B_T | A_CNF_MEDIA_10B_T;
- /* Check if the card is in AUI only mode */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY)
- lp->adapter_cnf |= A_CNF_AUI | A_CNF_MEDIA_AUI;
- /* Check if the card is in Auto mode. */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET)
- lp->adapter_cnf |= A_CNF_AUI | A_CNF_10B_T |
- A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO;
-
- cs89_dbg(1, info, "%s: PP_LineCTL=0x%x, adapter_cnf=0x%x\n",
- dev->name, i, lp->adapter_cnf);
-
- /* IRQ. Other chips already probe, see below. */
- if (lp->chip_type == CS8900)
- lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK;
-
- pr_cont("[Cirrus EEPROM] ");
- }
-
- pr_cont("\n");
-
- /* First check to see if an EEPROM is attached. */
-
- if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
- pr_warn("No EEPROM, relying on command line....\n");
- else if (get_eeprom_data(dev, START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
- pr_warn("EEPROM read failed, relying on command line\n");
- } else if (get_eeprom_cksum(START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
- /* Check if the chip was able to read its own configuration starting
- at 0 in the EEPROM*/
- if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) !=
- (EEPROM_OK | EEPROM_PRESENT))
- pr_warn("Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n");
-
- } else {
- /* This reads an extended EEPROM that is not documented
- * in the CS8900 datasheet.
- */
-
- /* get transmission control word but keep the autonegotiation bits */
- if (!lp->auto_neg_cnf)
- lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET / 2];
- /* Store adapter configuration */
- if (!lp->adapter_cnf)
- lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET / 2];
- /* Store ISA configuration */
- lp->isa_config = eeprom_buff[ISA_CNF_OFFSET / 2];
- dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET / 2] << 8;
-
- /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
- /* store the initial memory base address */
- for (i = 0; i < ETH_ALEN / 2; i++) {
- addr[i * 2] = eeprom_buff[i];
- addr[i * 2 + 1] = eeprom_buff[i] >> 8;
- }
- eth_hw_addr_set(dev, addr);
- cs89_dbg(1, debug, "%s: new adapter_cnf: 0x%x\n",
- dev->name, lp->adapter_cnf);
- }
-
- /* allow them to force multiple transceivers. If they force multiple, autosense */
- {
- int count = 0;
- if (lp->force & FORCE_RJ45) {
- lp->adapter_cnf |= A_CNF_10B_T;
- count++;
- }
- if (lp->force & FORCE_AUI) {
- lp->adapter_cnf |= A_CNF_AUI;
- count++;
- }
- if (lp->force & FORCE_BNC) {
- lp->adapter_cnf |= A_CNF_10B_2;
- count++;
- }
- if (count > 1)
- lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
- else if (lp->force & FORCE_RJ45)
- lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
- else if (lp->force & FORCE_AUI)
- lp->adapter_cnf |= A_CNF_MEDIA_AUI;
- else if (lp->force & FORCE_BNC)
- lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
- }
-
- cs89_dbg(1, debug, "%s: after force 0x%x, adapter_cnf=0x%x\n",
- dev->name, lp->force, lp->adapter_cnf);
-
- /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
-
- /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
-
- /* FIXME: we don't set the Ethernet address on the command line. Use
- * ifconfig IFACE hw ether AABBCCDDEEFF
- */
-
- pr_info("media %s%s%s",
- (lp->adapter_cnf & A_CNF_10B_T) ? "RJ-45," : "",
- (lp->adapter_cnf & A_CNF_AUI) ? "AUI," : "",
- (lp->adapter_cnf & A_CNF_10B_2) ? "BNC," : "");
-
- lp->irq_map = 0xffff;
-
- /* If this is a CS8900 then no pnp soft */
- if (lp->chip_type != CS8900 &&
- /* Check if the ISA IRQ has been set */
- (i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
- (i != 0 && i < CS8920_NO_INTS))) {
- if (!dev->irq)
- dev->irq = i;
- } else {
- i = lp->isa_config & INT_NO_MASK;
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- if (lp->chip_type == CS8900) {
- /* Translate the IRQ using the IRQ mapping table. */
- if (i >= ARRAY_SIZE(cs8900_irq_map))
- pr_err("invalid ISA interrupt number %d\n", i);
- else
- i = cs8900_irq_map[i];
-
- lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
- } else {
- int irq_map_buff[IRQ_MAP_LEN/2];
-
- if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
- IRQ_MAP_LEN / 2,
- irq_map_buff) >= 0) {
- if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
- lp->irq_map = ((irq_map_buff[0] >> 8) |
- (irq_map_buff[1] << 8));
- }
- }
-#endif
- if (!dev->irq)
- dev->irq = i;
- }
-
- pr_cont(" IRQ %d", dev->irq);
-
-#if ALLOW_DMA
- if (lp->use_dma) {
- get_dma_channel(dev);
- pr_cont(", DMA %d", dev->dma);
- } else
-#endif
- pr_cont(", programmed I/O");
-
- /* print the ethernet address. */
- pr_cont(", MAC %pM\n", dev->dev_addr);
-
- dev->netdev_ops = &net_ops;
- dev->watchdog_timeo = HZ;
-
- cs89_dbg(0, info, "cs89x0_probe1() successful\n");
-
- retval = register_netdev(dev);
- if (retval)
- goto out2;
- return 0;
-out2:
- iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
-out1:
- return retval;
-}
-
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
-/*
- * This function converts the I/O port address used by the cs89x0_probe() and
- * init_module() functions to the I/O memory address used by the
- * cs89x0_probe1() function.
- */
-static int __init
-cs89x0_ioport_probe(struct net_device *dev, unsigned long ioport, int modular)
-{
- struct net_local *lp = netdev_priv(dev);
- int ret;
- void __iomem *io_mem;
-
- if (!lp)
- return -ENOMEM;
-
- dev->base_addr = ioport;
-
- if (!request_region(ioport, NETCARD_IO_EXTENT, DRV_NAME)) {
- ret = -EBUSY;
- goto out;
- }
-
- io_mem = ioport_map(ioport & ~3, NETCARD_IO_EXTENT);
- if (!io_mem) {
- ret = -ENOMEM;
- goto release;
- }
-
- /* if they give us an odd I/O address, then do ONE write to
- * the address port, to get it back to address zero, where we
- * expect to find the EISA signature word. An IO with a base of 0x3
- * will skip the test for the ADD_PORT.
- */
- if (ioport & 1) {
- cs89_dbg(1, info, "%s: odd ioaddr 0x%lx\n", dev->name, ioport);
- if ((ioport & 2) != 2) {
- if ((ioread16(io_mem + ADD_PORT) & ADD_MASK) !=
- ADD_SIG) {
- pr_err("%s: bad signature 0x%x\n",
- dev->name, ioread16(io_mem + ADD_PORT));
- ret = -ENODEV;
- goto unmap;
- }
- }
- }
-
- ret = cs89x0_probe1(dev, io_mem, modular);
- if (!ret)
- goto out;
-unmap:
- ioport_unmap(io_mem);
-release:
- release_region(ioport, NETCARD_IO_EXTENT);
-out:
- return ret;
-}
-
-#ifndef MODULE
-/* 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).
- * Return 0 on success.
- */
-
-struct net_device * __init cs89x0_probe(int unit)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
- unsigned *port;
- int err = 0;
- int irq;
- int io;
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- io = dev->base_addr;
- irq = dev->irq;
-
- cs89_dbg(0, info, "cs89x0_probe(0x%x)\n", io);
-
- if (io > 0x1ff) { /* Check a single specified location. */
- err = cs89x0_ioport_probe(dev, io, 0);
- } else if (io != 0) { /* Don't probe at all. */
- err = -ENXIO;
- } else {
- for (port = netcard_portlist; *port; port++) {
- if (cs89x0_ioport_probe(dev, *port, 0) == 0)
- break;
- dev->irq = irq;
- }
- if (!*port)
- err = -ENODEV;
- }
- if (err)
- goto out;
- return dev;
-out:
- free_netdev(dev);
- pr_warn("no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
- return ERR_PTR(err);
-}
-#else
-static struct net_device *dev_cs89x0;
-
-/* Support the 'debug' module parm even if we're compiled for non-debug to
- * avoid breaking someone's startup scripts
- */
-
-static int io;
-static int irq;
-static int debug;
-static char media[8];
-static int duplex = -1;
-
-static int use_dma; /* These generate unused var warnings if ALLOW_DMA = 0 */
-static int dma;
-static int dmasize = 16; /* or 64 */
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(debug, int, 0);
-module_param_string(media, media, sizeof(media), 0);
-module_param(duplex, int, 0);
-module_param_hw(dma , int, dma, 0);
-module_param(dmasize , int, 0);
-module_param(use_dma , int, 0);
-MODULE_PARM_DESC(io, "cs89x0 I/O base address");
-MODULE_PARM_DESC(irq, "cs89x0 IRQ number");
-#if DEBUGGING
-MODULE_PARM_DESC(debug, "cs89x0 debug level (0-6)");
-#else
-MODULE_PARM_DESC(debug, "(ignored)");
-#endif
-MODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)");
-/* No other value than -1 for duplex seems to be currently interpreted */
-MODULE_PARM_DESC(duplex, "(ignored)");
-#if ALLOW_DMA
-MODULE_PARM_DESC(dma , "cs89x0 ISA DMA channel; ignored if use_dma=0");
-MODULE_PARM_DESC(dmasize , "cs89x0 DMA size in kB (16,64); ignored if use_dma=0");
-MODULE_PARM_DESC(use_dma , "cs89x0 using DMA (0-1)");
-#else
-MODULE_PARM_DESC(dma , "(ignored)");
-MODULE_PARM_DESC(dmasize , "(ignored)");
-MODULE_PARM_DESC(use_dma , "(ignored)");
-#endif
-
-MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton");
-MODULE_LICENSE("GPL");
-
-/*
- * media=t - specify media type
- * or media=2
- * or media=aui
- * or medai=auto
- * duplex=0 - specify forced half/full/autonegotiate duplex
- * debug=# - debug level
- *
- * Default Chip Configuration:
- * DMA Burst = enabled
- * IOCHRDY Enabled = enabled
- * UseSA = enabled
- * CS8900 defaults to half-duplex if not specified on command-line
- * CS8920 defaults to autoneg if not specified on command-line
- * Use reset defaults for other config parameters
- *
- * Assumptions:
- * media type specified is supported (circuitry is present)
- * if memory address is > 1MB, then required mem decode hw is present
- * if 10B-2, then agent other than driver will enable DC/DC converter
- * (hw or software util)
- */
-
-static int __init cs89x0_isa_init_module(void)
-{
- struct net_device *dev;
- struct net_local *lp;
- int ret = 0;
-
-#if DEBUGGING
- net_debug = debug;
-#else
- debug = 0;
-#endif
- dev = alloc_etherdev(sizeof(struct net_local));
- if (!dev)
- return -ENOMEM;
-
- dev->irq = irq;
- dev->base_addr = io;
- lp = netdev_priv(dev);
-
-#if ALLOW_DMA
- if (use_dma) {
- lp->use_dma = use_dma;
- lp->dma = dma;
- lp->dmasize = dmasize;
- }
-#endif
-
- spin_lock_init(&lp->lock);
-
- /* boy, they'd better get these right */
- if (!strcmp(media, "rj45"))
- lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
- else if (!strcmp(media, "aui"))
- lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI;
- else if (!strcmp(media, "bnc"))
- lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
- else
- lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
-
- if (duplex == -1)
- lp->auto_neg_cnf = AUTO_NEG_ENABLE;
-
- if (io == 0) {
- pr_err("Module autoprobing not allowed\n");
- pr_err("Append io=0xNNN\n");
- ret = -EPERM;
- goto out;
- } else if (io <= 0x1ff) {
- ret = -ENXIO;
- goto out;
- }
-
-#if ALLOW_DMA
- if (use_dma && dmasize != 16 && dmasize != 64) {
- pr_err("dma size must be either 16K or 64K, not %dK\n",
- dmasize);
- ret = -EPERM;
- goto out;
- }
-#endif
- ret = cs89x0_ioport_probe(dev, io, 1);
- if (ret)
- goto out;
-
- dev_cs89x0 = dev;
- return 0;
-out:
- free_netdev(dev);
- return ret;
-}
-module_init(cs89x0_isa_init_module);
-
-static void __exit cs89x0_isa_cleanup_module(void)
-{
- struct net_local *lp = netdev_priv(dev_cs89x0);
-
- unregister_netdev(dev_cs89x0);
- iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
- ioport_unmap(lp->virt_addr);
- release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT);
- free_netdev(dev_cs89x0);
-}
-module_exit(cs89x0_isa_cleanup_module);
-#endif /* MODULE */
-#endif /* CONFIG_CS89x0_ISA */
-
-#if IS_ENABLED(CONFIG_CS89x0_PLATFORM)
-static int __init cs89x0_platform_probe(struct platform_device *pdev)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
- void __iomem *virt_addr;
- int err;
-
- if (!dev)
- return -ENOMEM;
-
- dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq < 0) {
- err = dev->irq;
- goto free;
- }
-
- virt_addr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(virt_addr)) {
- err = PTR_ERR(virt_addr);
- goto free;
- }
-
- err = cs89x0_probe1(dev, virt_addr, 0);
- if (err) {
- dev_warn(&dev->dev, "no cs8900 or cs8920 detected\n");
- goto free;
- }
-
- platform_set_drvdata(pdev, dev);
- return 0;
-
-free:
- free_netdev(dev);
- return err;
-}
-
-static void cs89x0_platform_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
-
- /* This platform_get_resource() call will not return NULL, because
- * the same call in cs89x0_platform_probe() has returned a non NULL
- * value.
- */
- unregister_netdev(dev);
- free_netdev(dev);
-}
-
-static const struct of_device_id __maybe_unused cs89x0_match[] = {
- { .compatible = "cirrus,cs8900", },
- { .compatible = "cirrus,cs8920", },
- { },
-};
-MODULE_DEVICE_TABLE(of, cs89x0_match);
-
-static struct platform_driver cs89x0_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(cs89x0_match),
- },
- .remove = cs89x0_platform_remove,
-};
-
-module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
-
-#endif /* CONFIG_CS89x0_PLATFORM */
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 network driver");
-MODULE_AUTHOR("Russell Nelson <nelson@crynwr.com>");
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net v3 1/2] bnge: fix initial HWRM sequence
From: Vikas Gupta @ 2026-04-22 18:11 UTC (permalink / raw)
To: Simon Horman
Cc: davem, edumazet, kuba, pabeni, andrew+netdev, netdev,
linux-kernel, vsrama-krishna.nemani, bhargava.marreddy,
rajashekar.hudumula, ajit.khaparde, dharmender.garg,
rahul-rg.gupta
In-Reply-To: <20260422170641.846322-1-horms@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 3764 bytes --]
Hi Simon,
We have discussed the AI review comments on the behaviour of
bnge_alloc_ctx_mem().
Please review the explanation and let me know if it does not provide
sufficient clarity.
The discussion on v2 is available at:
https://lore.kernel.org/netdev/CAHLZf_uARgZzoTPnnPjxRu5AGeHEOw3yyTEbNHYP3brfwuW0Sw@mail.gmail.com/
https://lore.kernel.org/netdev/CAHLZf_voEPTrfkuO6pJcchaQaOqJin8m7j-+hwMrjJcGFmJv0A@mail.gmail.com/
Thanks,
Vikas
On Wed, Apr 22, 2026 at 10:36 PM Simon Horman <horms@kernel.org> wrote:
>
> From: 'Simon Horman' <horms@kernel.org>
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> bnge: fix initial HWRM sequence
>
> Firmware may not advertize correct resources if backing store is not
> enabled before resource information is queried.
> Fix the initial sequence of HWRMs so that driver gets capabilities
> and resource information correctly.
>
> > diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> > index 1c14c5fe8d613..68b74eb2c3a29 100644
> > --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> > +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> [ ... ]
> > @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
> >
> > bnge_hwrm_fw_set_time(bd);
> >
> > - rc = bnge_hwrm_func_drv_rgtr(bd);
> > + /* Get the resources and configuration from firmware */
> > + rc = bnge_func_qcaps(bd);
> > if (rc) {
> > - dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> > + dev_err(bd->dev, "Failed querying caps rc: %d\n", rc);
> > return rc;
> > }
> >
> > rc = bnge_alloc_ctx_mem(bd);
> > if (rc) {
> > dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc);
> > - goto err_func_unrgtr;
> > + goto err_free_ctx_mem;
> > }
> >
> > - /* Get the resources and configuration from firmware */
> > - rc = bnge_func_qcaps(bd);
> > + rc = bnge_hwrm_func_drv_rgtr(bd);
> > if (rc) {
> > - dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc);
> > - rc = -ENODEV;
> > + dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> > + goto err_free_ctx_mem;
>
> Does this regression leave the hardware with active DMA pointers to freed
> memory?
>
> If bnge_alloc_ctx_mem() allocates host DMA memory and passes the addresses
> to the firmware via bnge_hwrm_func_backing_store(), and then
> bnge_hwrm_func_drv_rgtr() fails, the code jumps directly to err_free_ctx_mem.
>
> > + }
> > +
> > + rc = bnge_func_qrcaps_qcfg(bd);
> > + if (rc) {
> > + dev_err(bd->dev, "Failed querying resources rc: %d\n", rc);
> > goto err_func_unrgtr;
> > }
> >
> > @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
> > return 0;
> >
> > err_func_unrgtr:
> > - bnge_fw_unregister_dev(bd);
> > + bnge_hwrm_func_drv_unrgtr(bd);
> > +err_free_ctx_mem:
> > + bnge_free_ctx_mem(bd);
>
> Since the err_func_unrgtr label is bypassed, is the firmware ever told to
> drop the backing store configuration before the host frees the memory in
> bnge_free_ctx_mem()?
>
> In the pre-patch sequence, the driver was registered first, so any failure
> subsequently triggered a driver unregister, which implicitly told the
> firmware to drop the backing store configuration. With the new order, does
> the firmware maintain active DMA pointers to the freed host memory?
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5465 bytes --]
^ permalink raw reply
* RE: [EXTERNAL] Re: [PATCH net] hv_sock: Return -EIO for malformed/short packets
From: Dexuan Cui @ 2026-04-22 18:14 UTC (permalink / raw)
To: Stefano Garzarella
Cc: KY Srinivasan, Haiyang Zhang, wei.liu@kernel.org, Long Li,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, niuxuewei.nxw@antgroup.com,
linux-hyperv@vger.kernel.org, virtualization@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
In-Reply-To: <aeiEsYqcKumplu5P@sgarzare-redhat>
> From: Stefano Garzarella <sgarzare@redhat.com>
> Sent: Wednesday, April 22, 2026 2:40 AM
> ...
> >+ if (hvs->vsk->peer_shutdown & SEND_SHUTDOWN)
>
> We can access `vsk` directly, I mean `vsk->peer_shutdown`.
>
> >+ return 0;
> >+ else
>
> nit: we usually avoid the `else` if the other branch returns early, and
> maybe have the error returned first, so it's more clear when reading the
> comment on top. I mean something like this:
>
> if (!(vsk->peer_shutdown & SEND_SHUTDOWN))
> return -EIO;
>
> return 0;
>
> BTW, not a strong opinion on that.
>
> The rest, LGTM!
>
> Thanks,
> Stefano
Thank you Stefano! I'll post v2 later today.
^ permalink raw reply
* Re: [PATCH net v2] net: dsa: mt7530: fix .get_stats64 sleeping in atomic context
From: Simon Horman @ 2026-04-22 18:23 UTC (permalink / raw)
To: Daniel Golle
Cc: Chester A. Unal, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King, Christian Marangi,
netdev, linux-kernel, linux-arm-kernel, linux-mediatek,
Frank Wunderlich, John Crispin
In-Reply-To: <58aff8b5b1d691872342a6ffd3315f27854788a6.1776595131.git.daniel@makrotopia.org>
On Sun, Apr 19, 2026 at 11:43:05AM +0100, Daniel Golle wrote:
> The .get_stats64 callback runs in atomic context, but on
> MDIO-connected switches every register read acquires the MDIO bus
> mutex, which can sleep:
> [ 12.645973] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:609
> [ 12.654442] in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 759, name: grep
> [ 12.663377] preempt_count: 0, expected: 0
> [ 12.667410] RCU nest depth: 1, expected: 0
> [ 12.671511] INFO: lockdep is turned off.
> [ 12.675441] CPU: 0 UID: 0 PID: 759 Comm: grep Tainted: G S W 7.0.0+ #0 PREEMPT
> [ 12.675453] Tainted: [S]=CPU_OUT_OF_SPEC, [W]=WARN
> [ 12.675456] Hardware name: Bananapi BPI-R64 (DT)
> [ 12.675459] Call trace:
> [ 12.675462] show_stack+0x14/0x1c (C)
> [ 12.675477] dump_stack_lvl+0x68/0x8c
> [ 12.675487] dump_stack+0x14/0x1c
> [ 12.675495] __might_resched+0x14c/0x220
> [ 12.675504] __might_sleep+0x44/0x80
> [ 12.675511] __mutex_lock+0x50/0xb10
> [ 12.675523] mutex_lock_nested+0x20/0x30
> [ 12.675532] mt7530_get_stats64+0x40/0x2ac
> [ 12.675542] dsa_user_get_stats64+0x2c/0x40
> [ 12.675553] dev_get_stats+0x44/0x1e0
> [ 12.675564] dev_seq_printf_stats+0x24/0xe0
> [ 12.675575] dev_seq_show+0x14/0x3c
> [ 12.675583] seq_read_iter+0x37c/0x480
> [ 12.675595] seq_read+0xd0/0xec
> [ 12.675605] proc_reg_read+0x94/0xe4
> [ 12.675615] vfs_read+0x98/0x29c
> [ 12.675625] ksys_read+0x54/0xdc
> [ 12.675633] __arm64_sys_read+0x18/0x20
> [ 12.675642] invoke_syscall.constprop.0+0x54/0xec
> [ 12.675653] do_el0_svc+0x3c/0xb4
> [ 12.675662] el0_svc+0x38/0x200
> [ 12.675670] el0t_64_sync_handler+0x98/0xdc
> [ 12.675679] el0t_64_sync+0x158/0x15c
>
> For MDIO-connected switches, poll MIB counters asynchronously using a
> delayed workqueue every second and let .get_stats64 return the cached
> values under a spinlock. A mod_delayed_work() call on each read
> triggers an immediate refresh so counters stay responsive when queried
> more frequently.
>
> MMIO-connected switches (MT7988, EN7581, AN7583) are not affected
> because their regmap does not sleep, so they continue to read MIB
> counters directly in .get_stats64.
>
> Fixes: 88c810f35ed5 ("net: dsa: mt7530: implement .get_stats64")
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> ---
> v2:
> * use spin_lock_bh()/spin_unlock_bh() to prevent potential deadlock
> * rate-limit mod_delayed_work() refresh to at most once per 100ms
> * move cancel_delayed_work_sync() after dsa_unregister_switch()
> * add mt753x_teardown() callback to cancel the stats work
> * fix commit message
FWIIW, I looked over the feedback generated by Sashiko for this patch,
and I believe the issues it flags are false positives.
^ permalink raw reply
* [PATCH net v2 11/15] drivers: net: fujitsu: fmvj18x: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>
The fmvj18x was written by Shingo Fujimoto in 2002. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/Kconfig | 1 -
drivers/net/ethernet/fujitsu/Kconfig | 30 -
drivers/net/ethernet/fujitsu/Makefile | 6 -
drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 1176 -----------------------------
4 files changed, 1213 deletions(-)
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index bdc29d143160..c94e8f27af94 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -61,7 +61,6 @@ source "drivers/net/ethernet/engleder/Kconfig"
source "drivers/net/ethernet/ezchip/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
-source "drivers/net/ethernet/fujitsu/Kconfig"
source "drivers/net/ethernet/fungible/Kconfig"
source "drivers/net/ethernet/google/Kconfig"
source "drivers/net/ethernet/hisilicon/Kconfig"
diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig
deleted file mode 100644
index 06a28bce5d27..000000000000
--- a/drivers/net/ethernet/fujitsu/Kconfig
+++ /dev/null
@@ -1,30 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Fujitsu Network device configuration
-#
-
-config NET_VENDOR_FUJITSU
- bool "Fujitsu devices"
- default y
- depends on PCMCIA
- help
- If you have a network (Ethernet) card belonging to this class, say Y.
-
- Note that the answer to this question doesn't directly affect the
- the questions about Fujitsu cards. If you say Y, you will be asked for
- your specific card in the following questions.
-
-if NET_VENDOR_FUJITSU
-
-config PCMCIA_FMVJ18X
- tristate "Fujitsu FMV-J18x PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- select CRC32
- help
- Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
- PCMCIA (PC-card) Ethernet card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called fmvj18x_cs. If unsure, say N.
-
-endif # NET_VENDOR_FUJITSU
diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile
deleted file mode 100644
index 74feebbf4572..000000000000
--- a/drivers/net/ethernet/fujitsu/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Makefile for the Fujitsu network device drivers.
-#
-
-obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
deleted file mode 100644
index 4859493471db..000000000000
--- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
+++ /dev/null
@@ -1,1176 +0,0 @@
-/*======================================================================
- fmvj18x_cs.c 2.8 2002/03/23
-
- A fmvj18x (and its compatibles) PCMCIA client driver
-
- Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp
-
- TDK LAK-CD021 and CONTEC C-NET(PC)C support added by
- Nobuhiro Katayama, kata-n@po.iijnet.or.jp
-
- The PCMCIA client code is based on code written by David Hinds.
- Network code is based on the "FMV-18x driver" by Yutaka TAMIYA
- but is actually largely Donald Becker's AT1700 driver, which
- carries the following attribution:
-
- 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
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME "fmvj18x_cs"
-#define DRV_VERSION "2.9"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/crc32.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* SRAM configuration */
-/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */
-INT_MODULE_PARM(sram_config, 0);
-
-
-/*====================================================================*/
-/*
- PCMCIA event handlers
- */
-static int fmvj18x_config(struct pcmcia_device *link);
-static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id);
-static int fmvj18x_setup_mfc(struct pcmcia_device *link);
-static void fmvj18x_release(struct pcmcia_device *link);
-static void fmvj18x_detach(struct pcmcia_device *p_dev);
-
-/*
- LAN controller(MBH86960A) specific routines
- */
-static int fjn_config(struct net_device *dev, struct ifmap *map);
-static int fjn_open(struct net_device *dev);
-static int fjn_close(struct net_device *dev);
-static netdev_tx_t fjn_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t fjn_interrupt(int irq, void *dev_id);
-static void fjn_rx(struct net_device *dev);
-static void fjn_reset(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-/*
- card type
- */
-enum cardtype { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN,
- XXX10304, NEC, KME
-};
-
-/*
- driver specific data structure
-*/
-struct local_info {
- struct pcmcia_device *p_dev;
- long open_time;
- uint tx_started:1;
- uint tx_queue;
- u_short tx_queue_len;
- enum cardtype cardtype;
- u_short sent;
- u_char __iomem *base;
-};
-
-#define MC_FILTERBREAK 64
-
-/*====================================================================*/
-/*
- ioport offset from the base address
- */
-#define TX_STATUS 0 /* transmit status register */
-#define RX_STATUS 1 /* receive status register */
-#define TX_INTR 2 /* transmit interrupt mask register */
-#define RX_INTR 3 /* receive interrupt mask register */
-#define TX_MODE 4 /* transmit mode register */
-#define RX_MODE 5 /* receive mode register */
-#define CONFIG_0 6 /* configuration register 0 */
-#define CONFIG_1 7 /* configuration register 1 */
-
-#define NODE_ID 8 /* node ID register (bank 0) */
-#define MAR_ADR 8 /* multicast address registers (bank 1) */
-
-#define DATAPORT 8 /* buffer mem port registers (bank 2) */
-#define TX_START 10 /* transmit start register */
-#define COL_CTRL 11 /* 16 collision control register */
-#define BMPR12 12 /* reserved */
-#define BMPR13 13 /* reserved */
-#define RX_SKIP 14 /* skip received packet register */
-
-#define LAN_CTRL 16 /* LAN card control register */
-
-#define MAC_ID 0x1a /* hardware address */
-#define UNGERMANN_MAC_ID 0x18 /* UNGERMANN-BASS hardware address */
-
-/*
- control bits
- */
-#define ENA_TMT_OK 0x80
-#define ENA_TMT_REC 0x20
-#define ENA_COL 0x04
-#define ENA_16_COL 0x02
-#define ENA_TBUS_ERR 0x01
-
-#define ENA_PKT_RDY 0x80
-#define ENA_BUS_ERR 0x40
-#define ENA_LEN_ERR 0x08
-#define ENA_ALG_ERR 0x04
-#define ENA_CRC_ERR 0x02
-#define ENA_OVR_FLO 0x01
-
-/* flags */
-#define F_TMT_RDY 0x80 /* can accept new packet */
-#define F_NET_BSY 0x40 /* carrier is detected */
-#define F_TMT_OK 0x20 /* send packet successfully */
-#define F_SRT_PKT 0x10 /* short packet error */
-#define F_COL_ERR 0x04 /* collision error */
-#define F_16_COL 0x02 /* 16 collision error */
-#define F_TBUS_ERR 0x01 /* bus read error */
-
-#define F_PKT_RDY 0x80 /* packet(s) in buffer */
-#define F_BUS_ERR 0x40 /* bus read error */
-#define F_LEN_ERR 0x08 /* short packet */
-#define F_ALG_ERR 0x04 /* frame error */
-#define F_CRC_ERR 0x02 /* CRC error */
-#define F_OVR_FLO 0x01 /* overflow error */
-
-#define F_BUF_EMP 0x40 /* receive buffer is empty */
-
-#define F_SKP_PKT 0x05 /* drop packet in buffer */
-
-/* default bitmaps */
-#define D_TX_INTR ( ENA_TMT_OK )
-#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \
- | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO )
-#define TX_STAT_M ( F_TMT_RDY )
-#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \
- | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO )
-
-/* commands */
-#define D_TX_MODE 0x06 /* no tests, detect carrier */
-#define ID_MATCHED 0x02 /* (RX_MODE) */
-#define RECV_ALL 0x03 /* (RX_MODE) */
-#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */
-#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */
-#define CONFIG0_RST 0xda /* Data Link Controller off (CONFIG_0) */
-#define CONFIG0_RST_1 0xde /* Data Link Controller off (CONFIG_0) */
-#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */
-#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */
-#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */
-#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */
-#define DO_TX 0x80 /* do transmit packet */
-#define SEND_PKT 0x81 /* send a packet */
-#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */
-#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */
-#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */
-#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */
-#define INTR_OFF 0x0d /* LAN controller ignores interrupts */
-#define INTR_ON 0x1d /* LAN controller will catch interrupts */
-
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-#define BANK_0U 0x20 /* bank 0 (CONFIG_1) */
-#define BANK_1U 0x24 /* bank 1 (CONFIG_1) */
-#define BANK_2U 0x28 /* bank 2 (CONFIG_1) */
-
-static const struct net_device_ops fjn_netdev_ops = {
- .ndo_open = fjn_open,
- .ndo_stop = fjn_close,
- .ndo_start_xmit = fjn_start_xmit,
- .ndo_tx_timeout = fjn_tx_timeout,
- .ndo_set_config = fjn_config,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int fmvj18x_probe(struct pcmcia_device *link)
-{
- struct local_info *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "fmvj18x_attach()\n");
-
- /* Make up a FMVJ18x specific data structure */
- dev = alloc_etherdev(sizeof(struct local_info));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- link->priv = dev;
- lp->p_dev = link;
- lp->base = NULL;
-
- /* The io structure describes IO port mapping */
- link->resource[0]->end = 32;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-
- /* General socket configuration */
- link->config_flags |= CONF_ENABLE_IRQ;
-
- dev->netdev_ops = &fjn_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- dev->ethtool_ops = &netdev_ethtool_ops;
-
- return fmvj18x_config(link);
-} /* fmvj18x_attach */
-
-/*====================================================================*/
-
-static void fmvj18x_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "fmvj18x_detach\n");
-
- unregister_netdev(dev);
-
- fmvj18x_release(link);
-
- free_netdev(dev);
-} /* fmvj18x_detach */
-
-/*====================================================================*/
-
-static int mfc_try_io_port(struct pcmcia_device *link)
-{
- int i, ret;
- static const unsigned int serial_base[5] =
- { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
-
- for (i = 0; i < 5; i++) {
- link->resource[1]->start = serial_base[i];
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- if (link->resource[1]->start == 0) {
- link->resource[1]->end = 0;
- pr_notice("out of resource for serial\n");
- }
- ret = pcmcia_request_io(link);
- if (ret == 0)
- return ret;
- }
- return ret;
-}
-
-static int ungermann_try_io_port(struct pcmcia_device *link)
-{
- int ret;
- unsigned int ioaddr;
- /*
- Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360
- 0x380,0x3c0 only for ioport.
- */
- for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) {
- link->resource[0]->start = ioaddr;
- ret = pcmcia_request_io(link);
- if (ret == 0) {
- /* calculate ConfigIndex value */
- link->config_index =
- ((link->resource[0]->start & 0x0f0) >> 3) | 0x22;
- return ret;
- }
- }
- return ret; /* RequestIO failed */
-}
-
-static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
-{
- return 0; /* strange, but that's what the code did already before... */
-}
-
-static int fmvj18x_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct local_info *lp = netdev_priv(dev);
- int i, ret;
- unsigned int ioaddr;
- enum cardtype cardtype;
- char *card_name = "unknown";
- u8 *buf;
- size_t len;
- u_char buggybuf[32];
- u8 addr[ETH_ALEN];
-
- dev_dbg(&link->dev, "fmvj18x_config\n");
-
- link->io_lines = 5;
-
- len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
- kfree(buf);
-
- if (len) {
- /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
- ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL);
- if (ret != 0)
- goto failed;
-
- switch (link->manf_id) {
- case MANFID_TDK:
- cardtype = TDK;
- if (link->card_id == PRODID_TDK_GN3410 ||
- link->card_id == PRODID_TDK_NP9610 ||
- link->card_id == PRODID_TDK_MN3200) {
- /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- }
- break;
- case MANFID_NEC:
- cardtype = NEC; /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- break;
- case MANFID_KME:
- cardtype = KME; /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- break;
- case MANFID_CONTEC:
- cardtype = CONTEC;
- break;
- case MANFID_FUJITSU:
- if (link->config_base == 0x0fe0)
- cardtype = MBH10302;
- else if (link->card_id == PRODID_FUJITSU_MBH10302)
- /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302),
- but these are MBH10304 based card. */
- cardtype = MBH10304;
- else if (link->card_id == PRODID_FUJITSU_MBH10304)
- cardtype = MBH10304;
- else
- cardtype = LA501;
- break;
- default:
- cardtype = MBH10304;
- }
- } else {
- /* old type card */
- switch (link->manf_id) {
- case MANFID_FUJITSU:
- if (link->card_id == PRODID_FUJITSU_MBH10304) {
- cardtype = XXX10304; /* MBH10304 with buggy CIS */
- link->config_index = 0x20;
- } else {
- cardtype = MBH10302; /* NextCom NC5310, etc. */
- link->config_index = 1;
- }
- break;
- case MANFID_UNGERMANN:
- cardtype = UNGERMANN;
- break;
- default:
- cardtype = MBH10302;
- link->config_index = 1;
- }
- }
-
- if (link->resource[1]->end != 0) {
- ret = mfc_try_io_port(link);
- if (ret != 0) goto failed;
- } else if (cardtype == UNGERMANN) {
- ret = ungermann_try_io_port(link);
- if (ret != 0) goto failed;
- } else {
- ret = pcmcia_request_io(link);
- if (ret)
- goto failed;
- }
- ret = pcmcia_request_irq(link, fjn_interrupt);
- if (ret)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- if (resource_size(link->resource[1]) != 0) {
- ret = fmvj18x_setup_mfc(link);
- if (ret != 0) goto failed;
- }
-
- ioaddr = dev->base_addr;
-
- /* Reset controller */
- if (sram_config == 0)
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- /* Power On chip and select bank 0 */
- if (cardtype == MBH10302)
- outb(BANK_0, ioaddr + CONFIG_1);
- else
- outb(BANK_0U, ioaddr + CONFIG_1);
-
- /* Set hardware address */
- switch (cardtype) {
- case MBH10304:
- case TDK:
- case LA501:
- case CONTEC:
- case NEC:
- case KME:
- if (cardtype == MBH10304) {
- card_name = "FMV-J182";
-
- len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
- if (len < 11) {
- kfree(buf);
- goto failed;
- }
- /* Read MACID from CIS */
- eth_hw_addr_set(dev, &buf[5]);
- kfree(buf);
- } else {
- if (pcmcia_get_mac_from_cis(link, dev))
- goto failed;
- if( cardtype == TDK ) {
- card_name = "TDK LAK-CD021";
- } else if( cardtype == LA501 ) {
- card_name = "LA501";
- } else if( cardtype == NEC ) {
- card_name = "PK-UG-J001";
- } else if( cardtype == KME ) {
- card_name = "Panasonic";
- } else {
- card_name = "C-NET(PC)C";
- }
- }
- break;
- case UNGERMANN:
- /* Read MACID from register */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i);
- eth_hw_addr_set(dev, addr);
- card_name = "Access/CARD";
- break;
- case XXX10304:
- /* Read MACID from Buggy CIS */
- if (fmvj18x_get_hwinfo(link, buggybuf) == -1) {
- pr_notice("unable to read hardware net address\n");
- goto failed;
- }
- eth_hw_addr_set(dev, buggybuf);
- card_name = "FMV-J182";
- break;
- case MBH10302:
- default:
- /* Read MACID from register */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + MAC_ID + i);
- eth_hw_addr_set(dev, addr);
- card_name = "FMV-J181";
- break;
- }
-
- lp->cardtype = cardtype;
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- /* print current configuration */
- netdev_info(dev, "%s, sram %s, port %#3lx, irq %d, hw_addr %pM\n",
- card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2",
- dev->base_addr, dev->irq, dev->dev_addr);
-
- return 0;
-
-failed:
- fmvj18x_release(link);
- return -ENODEV;
-} /* fmvj18x_config */
-/*====================================================================*/
-
-static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
-{
- u_char __iomem *base;
- int i, j;
-
- /* Allocate a small memory window */
- link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = 0; link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return -1;
-
- base = ioremap(link->resource[2]->start, resource_size(link->resource[2]));
- if (!base) {
- pcmcia_release_window(link, link->resource[2]);
- return -1;
- }
-
- pcmcia_map_mem_page(link, link->resource[2], 0);
-
- /*
- * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format
- * 22 0d xx xx xx 04 06 yy yy yy yy yy yy ff
- * 'xx' is garbage.
- * 'yy' is MAC address.
- */
- for (i = 0; i < 0x200; i++) {
- if (readb(base+i*2) == 0x22) {
- if (readb(base+(i-1)*2) == 0xff &&
- readb(base+(i+5)*2) == 0x04 &&
- readb(base+(i+6)*2) == 0x06 &&
- readb(base+(i+13)*2) == 0xff)
- break;
- }
- }
-
- if (i != 0x200) {
- for (j = 0 ; j < 6; j++,i++) {
- node_id[j] = readb(base+(i+7)*2);
- }
- }
-
- iounmap(base);
- j = pcmcia_release_window(link, link->resource[2]);
- return (i != 0x200) ? 0 : -1;
-
-} /* fmvj18x_get_hwinfo */
-/*====================================================================*/
-
-static int fmvj18x_setup_mfc(struct pcmcia_device *link)
-{
- int i;
- struct net_device *dev = link->priv;
- unsigned int ioaddr;
- struct local_info *lp = netdev_priv(dev);
-
- /* Allocate a small memory window */
- link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[3]->start = link->resource[3]->end = 0;
- i = pcmcia_request_window(link, link->resource[3], 0);
- if (i != 0)
- return -1;
-
- lp->base = ioremap(link->resource[3]->start,
- resource_size(link->resource[3]));
- if (lp->base == NULL) {
- netdev_notice(dev, "ioremap failed\n");
- return -1;
- }
-
- i = pcmcia_map_mem_page(link, link->resource[3], 0);
- if (i != 0) {
- iounmap(lp->base);
- lp->base = NULL;
- return -1;
- }
-
- ioaddr = dev->base_addr;
- writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */
- writeb(0x0, lp->base+0x802); /* Config and Status Register */
-
- writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */
- writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */
-
- writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */
- writeb(0x8, lp->base+0x822); /* Config and Status Register */
-
- return 0;
-
-}
-/*====================================================================*/
-
-static void fmvj18x_release(struct pcmcia_device *link)
-{
-
- struct net_device *dev = link->priv;
- struct local_info *lp = netdev_priv(dev);
- u_char __iomem *tmp;
-
- dev_dbg(&link->dev, "fmvj18x_release\n");
-
- if (lp->base != NULL) {
- tmp = lp->base;
- lp->base = NULL; /* set NULL before iounmap */
- iounmap(tmp);
- }
-
- pcmcia_disable_device(link);
-
-}
-
-static int fmvj18x_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int fmvj18x_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- fjn_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-/*====================================================================*/
-
-static const struct pcmcia_device_id fmvj18x_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004),
- PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59),
- PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922),
- PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2),
- PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4),
- PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6),
- PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6),
- PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a),
- PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2),
- PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454),
- PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da),
- PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids);
-
-static struct pcmcia_driver fmvj18x_cs_driver = {
- .owner = THIS_MODULE,
- .name = "fmvj18x_cs",
- .probe = fmvj18x_probe,
- .remove = fmvj18x_detach,
- .id_table = fmvj18x_ids,
- .suspend = fmvj18x_suspend,
- .resume = fmvj18x_resume,
-};
-module_pcmcia_driver(fmvj18x_cs_driver);
-
-/*====================================================================*/
-
-static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr;
- unsigned short tx_stat, rx_stat;
-
- ioaddr = dev->base_addr;
-
- /* avoid multiple interrupts */
- outw(0x0000, ioaddr + TX_INTR);
-
- /* wait for a while */
- udelay(1);
-
- /* get status */
- tx_stat = inb(ioaddr + TX_STATUS);
- rx_stat = inb(ioaddr + RX_STATUS);
-
- /* clear status */
- outb(tx_stat, ioaddr + TX_STATUS);
- outb(rx_stat, ioaddr + RX_STATUS);
-
- pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat);
- pr_debug(" tx_status %02x.\n", tx_stat);
-
- if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
- /* there is packet(s) in rx buffer */
- fjn_rx(dev);
- }
- if (tx_stat & F_TMT_RDY) {
- dev->stats.tx_packets += lp->sent ;
- lp->sent = 0 ;
- if (lp->tx_queue) {
- outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
- lp->sent = lp->tx_queue ;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- netif_trans_update(dev);
- } else {
- lp->tx_started = 0;
- }
- netif_wake_queue(dev);
- }
- pr_debug("%s: exiting interrupt,\n", dev->name);
- pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat);
-
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
-
- if (lp->base != NULL) {
- /* Ack interrupt for multifunction card */
- writeb(0x01, lp->base+0x802);
- writeb(0x09, lp->base+0x822);
- }
-
- return IRQ_HANDLED;
-
-} /* fjn_interrupt */
-
-/*====================================================================*/
-
-static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "transmit timed out with status %04x, %s?\n",
- htons(inw(ioaddr + TX_STATUS)),
- inb(ioaddr + TX_STATUS) & F_TMT_RDY
- ? "IRQ conflict" : "network cable problem");
- netdev_notice(dev, "timeout registers: %04x %04x %04x "
- "%04x %04x %04x %04x %04x.\n",
- htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)),
- htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)),
- htons(inw(ioaddr + 8)), htons(inw(ioaddr + 10)),
- htons(inw(ioaddr + 12)), htons(inw(ioaddr + 14)));
- dev->stats.tx_errors++;
- /* ToDo: We should try to restart the adaptor... */
- local_irq_disable();
- fjn_reset(dev);
-
- lp->tx_started = 0;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->sent = 0;
- lp->open_time = jiffies;
- local_irq_enable();
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t fjn_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- short length = skb->len;
-
- if (length < ETH_ZLEN)
- {
- if (skb_padto(skb, ETH_ZLEN))
- return NETDEV_TX_OK;
- length = ETH_ZLEN;
- }
-
- netif_stop_queue(dev);
-
- {
- unsigned char *buf = skb->data;
-
- if (length > ETH_FRAME_LEN) {
- netdev_notice(dev, "Attempting to send a large packet (%d bytes)\n",
- length);
- return NETDEV_TX_BUSY;
- }
-
- netdev_dbg(dev, "Transmitting a packet of length %lu\n",
- (unsigned long)skb->len);
- dev->stats.tx_bytes += skb->len;
-
- /* Disable both interrupts. */
- outw(0x0000, ioaddr + TX_INTR);
-
- /* wait for a while */
- udelay(1);
-
- outw(length, ioaddr + DATAPORT);
- outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
-
- lp->tx_queue++;
- lp->tx_queue_len += ((length+3) & ~1);
-
- if (lp->tx_started == 0) {
- /* If the Tx is idle, always trigger a transmit. */
- outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
- lp->sent = lp->tx_queue ;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->tx_started = 1;
- netif_start_queue(dev);
- } else {
- if( sram_config == 0 ) {
- if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
- /* Yes, there is room for one more packet. */
- netif_start_queue(dev);
- } else {
- if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) &&
- lp->tx_queue < 127 )
- /* Yes, there is room for one more packet. */
- netif_start_queue(dev);
- }
- }
-
- /* Re-enable interrupts */
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
- }
- dev_kfree_skb (skb);
-
- return NETDEV_TX_OK;
-} /* fjn_start_xmit */
-
-/*====================================================================*/
-
-static void fjn_reset(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- netdev_dbg(dev, "fjn_reset() called\n");
-
- /* Reset controller */
- if( sram_config == 0 )
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- /* Power On chip and select bank 0 */
- if (lp->cardtype == MBH10302)
- outb(BANK_0, ioaddr + CONFIG_1);
- else
- outb(BANK_0U, ioaddr + CONFIG_1);
-
- /* Set Tx modes */
- outb(D_TX_MODE, ioaddr + TX_MODE);
- /* set Rx modes */
- outb(ID_MATCHED, ioaddr + RX_MODE);
-
- /* Set hardware address */
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + NODE_ID + i);
-
- /* (re)initialize the multicast table */
- set_rx_mode(dev);
-
- /* Switch to bank 2 (runtime mode) */
- if (lp->cardtype == MBH10302)
- outb(BANK_2, ioaddr + CONFIG_1);
- else
- outb(BANK_2U, ioaddr + CONFIG_1);
-
- /* set 16col ctrl bits */
- if( lp->cardtype == TDK || lp->cardtype == CONTEC)
- outb(TDK_AUTO_MODE, ioaddr + COL_CTRL);
- else
- outb(AUTO_MODE, ioaddr + COL_CTRL);
-
- /* clear Reserved Regs */
- outb(0x00, ioaddr + BMPR12);
- outb(0x00, ioaddr + BMPR13);
-
- /* reset Skip packet reg. */
- outb(0x01, ioaddr + RX_SKIP);
-
- /* Enable Tx and Rx */
- if( sram_config == 0 )
- outb(CONFIG0_DFL, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_DFL_1, ioaddr + CONFIG_0);
-
- /* Init receive pointer ? */
- inw(ioaddr + DATAPORT);
- inw(ioaddr + DATAPORT);
-
- /* Clear all status */
- outb(0xff, ioaddr + TX_STATUS);
- outb(0xff, ioaddr + RX_STATUS);
-
- if (lp->cardtype == MBH10302)
- outb(INTR_OFF, ioaddr + LAN_CTRL);
-
- /* Turn on Rx interrupts */
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
-
- /* Turn on interrupts from LAN card controller */
- if (lp->cardtype == MBH10302)
- outb(INTR_ON, ioaddr + LAN_CTRL);
-} /* fjn_reset */
-
-/*====================================================================*/
-
-static void fjn_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int boguscount = 10; /* 5 -> 10: by agy 19940922 */
-
- pr_debug("%s: in rx_packet(), rx_status %02x.\n",
- dev->name, inb(ioaddr + RX_STATUS));
-
- while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
- u_short status = inw(ioaddr + DATAPORT);
-
- netdev_dbg(dev, "Rxing packet mode %02x status %04x.\n",
- inb(ioaddr + RX_MODE), status);
-#ifndef final_version
- if (status == 0) {
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- break;
- }
-#endif
- if ((status & 0xF0) != 0x20) { /* There was an error. */
- dev->stats.rx_errors++;
- if (status & F_LEN_ERR) dev->stats.rx_length_errors++;
- if (status & F_ALG_ERR) dev->stats.rx_frame_errors++;
- if (status & F_CRC_ERR) dev->stats.rx_crc_errors++;
- if (status & F_OVR_FLO) dev->stats.rx_over_errors++;
- } else {
- u_short pkt_len = inw(ioaddr + DATAPORT);
- /* Malloc up new buffer. */
- struct sk_buff *skb;
-
- if (pkt_len > 1550) {
- netdev_notice(dev, "The FMV-18x claimed a very large packet, size %d\n",
- pkt_len);
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- dev->stats.rx_errors++;
- break;
- }
- skb = netdev_alloc_skb(dev, pkt_len + 2);
- if (skb == NULL) {
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- 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);
-
- {
- int i;
- pr_debug("%s: Rxed packet of length %d: ",
- dev->name, pkt_len);
- for (i = 0; i < 14; i++)
- pr_debug(" %02x", skb->data[i]);
- pr_debug(".\n");
- }
-
- 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 netif_wake_queue() for us and will work on them
- when we get to the bottom-half routine. */
-/*
- if (lp->cardtype != TDK) {
- int i;
- for (i = 0; i < 20; i++) {
- if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP)
- break;
- (void)inw(ioaddr + DATAPORT); /+ dummy status read +/
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- }
-
- if (i > 0)
- pr_debug("%s: Exint Rx packet with mode %02x after "
- "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
- }
-*/
-} /* fjn_rx */
-
-/*====================================================================*/
-
-static void netdev_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
- snprintf(info->bus_info, sizeof(info->bus_info),
- "PCMCIA 0x%lx", dev->base_addr);
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
- .get_drvinfo = netdev_get_drvinfo,
-};
-
-static int fjn_config(struct net_device *dev, struct ifmap *map){
- return 0;
-}
-
-static int fjn_open(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- pr_debug("fjn_open('%s').\n", dev->name);
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
-
- fjn_reset(dev);
-
- lp->tx_started = 0;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->open_time = jiffies;
- netif_start_queue(dev);
-
- return 0;
-} /* fjn_open */
-
-/*====================================================================*/
-
-static int fjn_close(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- pr_debug("fjn_close('%s').\n", dev->name);
-
- lp->open_time = 0;
- netif_stop_queue(dev);
-
- /* Set configuration register 0 to disable Tx and Rx. */
- if( sram_config == 0 )
- outb(CONFIG0_RST ,ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0);
-
- /* Update the statistics -- ToDo. */
-
- /* Power-down the chip. Green, green, green! */
- outb(CHIP_OFF ,ioaddr + CONFIG_1);
-
- /* Set the ethernet adaptor disable IRQ */
- if (lp->cardtype == MBH10302)
- outb(INTR_OFF, ioaddr + LAN_CTRL);
-
- link->open--;
-
- return 0;
-} /* fjn_close */
-
-/*====================================================================*/
-
-/*
- Set the multicast/promiscuous mode for this adaptor.
-*/
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u_char mc_filter[8]; /* Multicast hash filter */
- u_long flags;
- int i;
-
- int saved_bank;
- int saved_config_0 = inb(ioaddr + CONFIG_0);
-
- local_irq_save(flags);
-
- /* Disable Tx and Rx */
- if (sram_config == 0)
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- 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 & 7));
- }
- outb(2, ioaddr + RX_MODE); /* Use normal mode. */
- }
-
- /* Switch to bank 1 and set the multicast table. */
- saved_bank = inb(ioaddr + CONFIG_1);
- outb(0xe4, ioaddr + CONFIG_1);
-
- for (i = 0; i < 8; i++)
- outb(mc_filter[i], ioaddr + MAR_ADR + i);
- outb(saved_bank, ioaddr + CONFIG_1);
-
- outb(saved_config_0, ioaddr + CONFIG_0);
-
- local_irq_restore(flags);
-}
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox