* RE: [net-2.6 v2 5/7] ixgbe: DDP last buffer size work around
From: Hanania, Amir @ 2011-01-28 21:36 UTC (permalink / raw)
To: David Miller, Kirsher, Jeffrey T
Cc: netdev@vger.kernel.org, gospo@redhat.com, bphilips@novell.com
In-Reply-To: <20110128.115100.226772144.davem@davemloft.net>
David,
Sorry I missed your mail from last night.
You are right, it should be bufflen, which is 4096 and not PAGE_SIZE.
I will fix that and resubmit.
-Amir
-----Original Message-----
From: David Miller [mailto:davem@davemloft.net]
Sent: Friday, January 28, 2011 11:51 AM
To: Kirsher, Jeffrey T
Cc: Hanania, Amir; netdev@vger.kernel.org; gospo@redhat.com; bphilips@novell.com
Subject: Re: [net-2.6 v2 5/7] ixgbe: DDP last buffer size work around
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Fri, 28 Jan 2011 04:29:01 -0800
> From: Amir Hanania <amir.hanania@intel.com>
>
> We found a hardware erratum on 82599 hardware that can lead to buffer
> overwriting if the last buffer in FCoE DDP is exactly PAGE_SIZE.
> If this is the case, we will make sure that there is no HW access to
> this buffer.
>
> Please see the 82599 Specification Update for more information.
>
> Signed-off-by: Amir Hanania <amir.hanania@intel.com>
> Tested-by: Ross Brattain <ross.b.brattain@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Jeff, I still think this change is bogus.
PAGE_SIZE is variable, so the chip can't possibly only BUG on that
specific value.
Maybe the condition is "any power-of-2 larger than or equal to 4096"?
Or something like that?
I pointed this out last night, and I was really hoping I'd get actual
feedback on this issue before you're respin your tree.
I'm not pulling in these changes until I get a human being telling me
what the situation is here with this bug.
^ permalink raw reply
* Re: [net-2.6 v2 5/7] ixgbe: DDP last buffer size work around
From: David Miller @ 2011-01-28 21:45 UTC (permalink / raw)
To: amir.hanania; +Cc: jeffrey.t.kirsher, netdev, gospo, bphilips
In-Reply-To: <4C5E6457CD7911469A07260381288C28CBE83195@orsmsx502.amr.corp.intel.com>
From: "Hanania, Amir" <amir.hanania@intel.com>
Date: Fri, 28 Jan 2011 13:36:15 -0800
> Sorry I missed your mail from last night.
>
> You are right, it should be bufflen, which is 4096 and not PAGE_SIZE.
>
> I will fix that and resubmit.
Thanks for looking into this.
^ permalink raw reply
* [PATCH] enc28j60: Fix reading of transmit status vector
From: Stefan Weil @ 2011-01-28 22:25 UTC (permalink / raw)
To: davem
Cc: Stefan Weil, Eric Dumazet, Tejun Heo, Jiri Pirko, netdev,
linux-kernel
This error was reported by cppcheck:
drivers/net/enc28j60.c:815: error: Using sizeof for array given as function argument returns the size of pointer.
The original code reads 4 or 8 bytes instead of TSV_SIZE (= 100) bytes.
I just fixed the code, but did not run any tests.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Jiri Pirko <jpirko@redhat.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Weil <weil@mail.berlios.de>
---
drivers/net/enc28j60.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index 112c5aa..907b05a 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -812,7 +812,7 @@ static void enc28j60_read_tsv(struct enc28j60_net *priv, u8 tsv[TSV_SIZE])
if (netif_msg_hw(priv))
printk(KERN_DEBUG DRV_NAME ": reading TSV at addr:0x%04x\n",
endptr + 1);
- enc28j60_mem_read(priv, endptr + 1, sizeof(tsv), tsv);
+ enc28j60_mem_read(priv, endptr + 1, TSV_SIZE, tsv);
}
static void enc28j60_dump_tsv(struct enc28j60_net *priv, const char *msg,
--
1.7.2.3
^ permalink raw reply related
* [PATCH] vxge: Fix wrong boolean operator
From: Stefan Weil @ 2011-01-28 22:30 UTC (permalink / raw)
To: jon.mason
Cc: Stefan Weil, Ramkrishna Vepa, Sivakumar Subramani,
Sreenivasa Honnur, netdev, linux-kernel
This error is reported by cppcheck:
drivers/net/vxge/vxge-config.c:3693: warning: Mutual exclusion over || always evaluates to true. Did you intend to use && instead?
It looks like cppcheck is correct, so fix this. No test was run.
Cc: Ramkrishna Vepa <ramkrishna.vepa@exar.com>
Cc: Sivakumar Subramani <sivakumar.subramani@exar.com>
Cc: Sreenivasa Honnur <sreenivasa.honnur@exar.com>
Cc: Jon Mason <jon.mason@exar.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Stefan Weil <weil@mail.berlios.de>
---
drivers/net/vxge/vxge-config.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c
index 01c05f5..228d4f7 100644
--- a/drivers/net/vxge/vxge-config.c
+++ b/drivers/net/vxge/vxge-config.c
@@ -3690,7 +3690,7 @@ __vxge_hw_vpath_rts_table_get(struct __vxge_hw_vpath_handle *vp,
if (status != VXGE_HW_OK)
goto exit;
- if ((rts_table != VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA) ||
+ if ((rts_table != VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA) &&
(rts_table !=
VXGE_HW_RTS_ACS_STEER_CTRL_DATA_STRUCT_SEL_RTH_MULTI_IT))
*data1 = 0;
--
1.7.2.3
^ permalink raw reply related
* Re: [PATCH v5] Gemini: Gigabit ethernet driver
From: David Miller @ 2011-01-28 22:31 UTC (permalink / raw)
To: mirq-linux; +Cc: ulli.kroll, gemini-board-dev, netdev, linux-kernel.bfrz
In-Reply-To: <20110126232419.4794513909@rere.qmqm.pl>
From: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Date: Thu, 27 Jan 2011 00:24:19 +0100 (CET)
> +#define NETIF_TSO_FEATURES \
> + (NETIF_F_TSO|NETIF_F_TSO_ECN|NETIF_F_TSO6)
> +#define GMAC_TX_OFFLOAD_FEATURES \
> + (NETIF_TSO_FEATURES|NETIF_F_ALL_CSUM)
Please, when definiting macros locally for your driver, do not name
them with prefixes that match those defined generically by the
network stack. Otherwise it is confusing for people reading the
driver.
One should be able to see "NETIF_XXX" somewhere and expect to find
it's definition somewhere in the generic networking driver interfaces,
not in the driver itself.
> +static struct toe_private *netdev_to_toe(struct net_device *dev)
> +{
> + return dev->ml_priv;
> +}
There is no reason to use ->ml_priv just to have a common backpointer
to a structure shared between multiple interfaces.
Simply add a "struct toe_private *" to your "struct gmac_private" and
stick it there.
The cost of the dereference is identical in both cases, so there is not
even a performance incentive to use ->ml_priv.
> +static void __iomem *gmac_ctl_reg(struct net_device *dev, unsigned int reg)
> +{
> + return (void __iomem *)dev->base_addr + reg;
> +}
Please do not abuse dev->base_addr in this way, simply define another
"void __iomem *" pointer in your gmac_private and use that.
> + page = pfn_to_page(dma_to_pfn(toe->dev, rx->word2.buf_adr));
Please do not use non-portable routines such as dma_to_pfn() unless it
is absolutely unavoidable. Instead, use schemes for page struct
lookup like those used by drivers such as drivers/net/niu.c, which uses
a hash table to find pages based upon DMA address.
I'd like you to be able to enable this driver on as many platforms as
possible, not just ARM, so we can be build testing your driver as we
make changes to various network driver APIs, and we can't do that if
you put ARM specific stuff in here.
> + dev_err(&dev->dev, "Unsupported MII interface\n");
Please use "netdev_err(dev, ..."
Please use netdev_*() when possible elsewhere in this driver too.
> + writel(
> + (GMAC0_SWTQ00_EOF_INT_BIT|GMAC0_SWTQ00_FIN_INT_BIT)
> + << (6 * dev->dev_id + txq_num),
> + toe_reg(toe, GLOBAL_INTERRUPT_STATUS_0_REG));
Please format this more reasonably, this looks awful.
> + txq->ring[w].word0.bits32 = skb_headlen(skb);
> + txq->ring[w].word1.bits32 = skb->len | tss_flags;
> + txq->ring[w].word2.bits32 = mapping;
> + txq->ring[w].word3.bits32 = tss_pkt_len(skb) | SOF_BIT;
What is the endinness of the RX and TX descriptors of this chipset?
Please use "__be32", "__le32", and the endianness conversion interfaces
as needed.
^ permalink raw reply
* Re: [Bugme-new] [Bug 27742] New: PPP over SSH tunnel triggers OOPS
From: Andrew Morton @ 2011-01-28 22:32 UTC (permalink / raw)
To: netdev; +Cc: bugzilla-daemon, bugme-daemon, ktk
In-Reply-To: <bug-27742-10286@https.bugzilla.kernel.org/>
(switched to email. Please respond via emailed reply-to-all, not via the
bugzilla web interface).
On Fri, 28 Jan 2011 21:58:49 GMT
bugzilla-daemon@bugzilla.kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=27742
>
> Summary: PPP over SSH tunnel triggers OOPS
> Product: Networking
> Version: 2.5
> Kernel Version: 2.6.30 ?
> Platform: All
> OS/Version: Linux
> Tree: Mainline
> Status: NEW
> Severity: normal
> Priority: P1
> Component: Other
> AssignedTo: acme@ghostprotocols.net
> ReportedBy: ktk@bigfoot.com
> Regression: Yes
>
>
> Created an attachment (id=45412)
> --> (https://bugzilla.kernel.org/attachment.cgi?id=45412)
> Hand-copied OOPS from 2.6.37 kernel
>
> When creating a VPN connection by using PPP tunneled over SSH, the kernel will
> OOPS when certain traffic patterns are encountered. (See attached OOPS)
>
> I first created such a VPN connection using kernel 2.6.33, which is affected.
> Kernel 2.6.27 is not affected. I have not attempted to binary-search for the
> exact commit, but am merely guessing it is in kernel 2.6.30 (as a variety of
> ppp-related commits appear in the changelog there).
>
> The VPN tunnel is established by invoking 'pppd' with the 'pty' parameter set
> to invoke "ssh remotehost.com pppd" which establishes a SSH tunnel over IPv4 to
> the remote host and then invokes the remote pppd to handle the other end of the
> point-to-point VPN.
>
> Reproducing this bug is not easy. With the ppp-ssh-ppp tunnel open, I have
> tried triggering the OOPs by sending PINGs, rsync-ing files in both directions,
> opening interactive SSH connections. Nothing seems to trigger the OOPS except
> one: running Mozilla Thunderbird on the remote end; it opens several IMAP
> connections over the tunnel simultaneously. Typically, the OOPS will occur
> within 1 or 2 seconds of invoking Thunderbird.
>
> When the OOPS occurs, usually the console will be scrolling wildly with OOPS
> after OOPS, making copying impossible. It has taken me two months of repeated
> tries to get one OOPS that remained on-screen and could be copied. The kernel
> is in a hard-run state when the OOPS occurs; nothing gets logged to syslog, the
> keyboard is unresponsive (magic sysrq key does nothing).
>
> skb_over_panic: text:c12a354f len:847 put:847 head:f57e8c00 data:f57e8c00 tail:0xf57e8f4f end:0xf57e8e80 dev:<NULL>
> kernel BUG at net/core/skbuff.c:127!
> invalid opcode: 0000 [#1] SMP
> last sysfs file: /sys/devices/virtual/net/ppp0/flags
> Modules linked in:
>
> Pid: 0, comm: swapper Not tainted 2.6.37 #1 0KH290/OptiPlex GX620
> EIP: 0060:[<c1330110>] EFLAGS: 00010282 CPU: 0
> EIP is at skb_put+0x82/0x84
> EAX: 00000089 EBX: f57e8f4f ECX: c151579c EDX: 00000046
> ESI: 00000000 EDI: c1530760 EBP: f67bb384 ESP: f6409d50
> DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
> Process swapper (pid: 0, ti=f6408000 task=c15114a0 task.ti=c1502000)
> Stack:
> c14d4590 c12a354f 0000034f 0000034f f57e8c00 f57e8c00 f57e8f4f f57e8e80
> c14d2509 f67bb380 f454db80 c12a354f 000005e0 00000244 f5f944c2 0000034f
> c1390a0a f67bb3d4 f4648380 f67bb394 f67bb3a4 00000202 f454db80 f67bb000
> Call Trace:
> [<c12a354f>] ? ppp_xmit_process+0x45a/0x4e6
> [<c12a354f>] ? ppp_xmit_process+0x45a/0x4e6
> [<c1390a0a>] ? tcp_manip_pkt+0xad/0xcb
> [<c12a36d4>] ? ppp_start_xmit+0xf9/0x175
> [<c133a496>] ? dev_hard_start_xmit+0x2a4/0x5c3
> [<c1347cad>] ? sch_direct_xmit+0xb9/0x184
> [<c134c663>] ? nf_iterate+0x52/0x76
> [<c1362d56>] ? ip_finish_output+0x0/0x294
> [<c133a88e>] ? dev_queue_xmit+0xd9/0x3b0
> [<c1362d56>] ? ip_finish_output+0x0/0x294
> [<c1362f32>] ? ip_finish_output+0x1dc/0x294
> [<c1362d56>] ? ip_finish_output+0x0/0x294
> [<c1360d66>] ? ip_forward_finish+0x36/0x42
> [<c135f8a4>] ? ip_rcv_finish+0x42/0x323
> [<c13384ac>] ? __netif_receive_skb+0x225/0x299
> [<c1048b56>] ? getnstimeofday+0x42/0xe8
> [<c13386ab>] ? netif_receive_skb+0x41/0x64
> [<c1339356>] ? dev_gro_receive+0x146/0x1dd
> [<c133955e>] ? napi_gro_receive+0xa5/0xb3
> [<c129ae2f>] ? tg3_poll_wor+0x5df/0xaca
> [<c1007340>] ? nommu_sync_single_for_device+0x0/0x1
> [<c129b3e2>] ? tg3_poll+0x43/0x19a
> [<c133893b>] ? net_rx_action+0x6c/0xf4
> [<c1031eb5>] ? __do_softirq+0x77/0xf0
> [<c1031e3e>] ? __do_softirq+0x0/0xf0
> <IRQ>
> [<c1031fe6>] ? irq_exit+0x5d/0x5f
>
^ permalink raw reply
* [PATCH 0/3] ipv4 metrics super-sharing
From: David Miller @ 2011-01-28 22:35 UTC (permalink / raw)
To: netdev
As I mentioned a few days ago, we can "super-share" the metrics
in the most common case, which is that there are not explicit
metrics set for a route.
This makes the metrics all zero, which means we can just alias
the pointer to dst_default_metrics.
Most importantly, this means we avoid atomics on the fib_info ref
count at dst destroy time.
Depending upon the configuration, this can also save a non-trivial
amount of memory in the routing tables. Honestly, I doubt that
sites entertaining routing tables with hundreds of thousands of
routes have any non-zero metrics specified. :-)
^ permalink raw reply
* [PATCH 1/3] ipv4: Allocate fib metrics dynamically.
From: David Miller @ 2011-01-28 22:35 UTC (permalink / raw)
To: netdev
This is the initial gateway towards super-sharing metrics
if they are all set to zero for a route.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 2 +-
net/ipv4/fib_semantics.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 65d1fcd..2c0508a 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -77,7 +77,7 @@ struct fib_info {
int fib_protocol;
__be32 fib_prefsrc;
u32 fib_priority;
- u32 fib_metrics[RTAX_MAX];
+ u32 *fib_metrics;
#define fib_mtu fib_metrics[RTAX_MTU-1]
#define fib_window fib_metrics[RTAX_WINDOW-1]
#define fib_rtt fib_metrics[RTAX_RTT-1]
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 9aff11d7..363ec39 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -152,6 +152,7 @@ static void free_fib_info_rcu(struct rcu_head *head)
{
struct fib_info *fi = container_of(head, struct fib_info, rcu);
+ kfree(fi->fib_metrics);
kfree(fi);
}
@@ -742,6 +743,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
if (fi == NULL)
goto failure;
+ fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+ if (!fi->fib_metrics)
+ goto failure;
fib_info_cnt++;
fi->fib_net = hold_net(net);
--
1.7.3.4
^ permalink raw reply related
* [PATCH 2/3] ipv4: Attach FIB info to dst_default_metrics when possible
From: David Miller @ 2011-01-28 22:35 UTC (permalink / raw)
To: netdev
If there are no explicit metrics attached to a route, hook
fi->fib_info up to dst_default_metrics.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/dst.h | 1 +
net/core/dst.c | 2 +-
net/ipv4/fib_semantics.c | 12 ++++++++----
3 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/include/net/dst.h b/include/net/dst.h
index 94a8c23..484f80b 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -97,6 +97,7 @@ struct dst_entry {
#ifdef __KERNEL__
extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
+extern const u32 dst_default_metrics[RTAX_MAX];
#define DST_METRICS_READ_ONLY 0x1UL
#define __DST_METRICS_PTR(Y) \
diff --git a/net/core/dst.c b/net/core/dst.c
index 5788935..c1674fd 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -164,7 +164,7 @@ int dst_discard(struct sk_buff *skb)
}
EXPORT_SYMBOL(dst_discard);
-static const u32 dst_default_metrics[RTAX_MAX];
+const u32 dst_default_metrics[RTAX_MAX];
void *dst_alloc(struct dst_ops *ops)
{
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 363ec39..48e93a5 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -152,7 +152,8 @@ static void free_fib_info_rcu(struct rcu_head *head)
{
struct fib_info *fi = container_of(head, struct fib_info, rcu);
- kfree(fi->fib_metrics);
+ if (fi->fib_metrics != (u32 *) dst_default_metrics)
+ kfree(fi->fib_metrics);
kfree(fi);
}
@@ -743,9 +744,12 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
if (fi == NULL)
goto failure;
- fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
- if (!fi->fib_metrics)
- goto failure;
+ if (cfg->fc_mx) {
+ fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+ if (!fi->fib_metrics)
+ goto failure;
+ } else
+ fi->fib_metrics = (u32 *) dst_default_metrics;
fib_info_cnt++;
fi->fib_net = hold_net(net);
--
1.7.3.4
^ permalink raw reply related
* [PATCH 3/3] ipv4: If fib metrics are default, no need to grab ref to FIB info.
From: David Miller @ 2011-01-28 22:35 UTC (permalink / raw)
To: netdev
The fib metric memory in this case is static in the kernel image,
so we don't need to reference count it since it's never going
to go away on us.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
net/ipv4/route.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index dd57f48..b1e5d3a 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1861,8 +1861,10 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi)
{
if (!(rt->fl.flags & FLOWI_FLAG_PRECOW_METRICS)) {
no_cow:
- rt->fi = fi;
- atomic_inc(&fi->fib_clntref);
+ if (fi->fib_metrics != (u32 *) dst_default_metrics) {
+ rt->fi = fi;
+ atomic_inc(&fi->fib_clntref);
+ }
dst_init_metrics(&rt->dst, fi->fib_metrics, true);
} else {
struct inet_peer *peer;
--
1.7.3.4
^ permalink raw reply related
* Re: [Bugme-new] [Bug 27712] New: atl1e data corruption (via NFS over TCP)
From: Andrew Morton @ 2011-01-28 22:36 UTC (permalink / raw)
To: netdev
Cc: bugzilla-daemon, bugme-new, bugme-daemon, pmjdebruijn,
Jay Cliburn, Chris Snook, Jie Yang
In-Reply-To: <bug-27712-10286@https.bugzilla.kernel.org/>
(switched to email. Please respond via emailed reply-to-all, not via the
bugzilla web interface).
On Fri, 28 Jan 2011 09:48:13 GMT
bugzilla-daemon@bugzilla.kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=27712
>
> Summary: atl1e data corruption (via NFS over TCP)
> Product: Drivers
> Version: 2.5
> Kernel Version: 2.6.38-rc2
> Platform: All
> OS/Version: Linux
> Tree: Mainline
> Status: NEW
> Severity: high
> Priority: P1
> Component: Network
> AssignedTo: drivers_network@kernel-bugs.osdl.org
> ReportedBy: pmjdebruijn@pcode.nl
> Regression: No
>
>
> With the following device (very common on ASUS hardware):
>
> Atheros AR8121/AR8113/AR8114 PCI-E Ethernet Controller
>
> With PCI ID:
>
> 1969-1026-b0-00-1043-14f5
> http://pci-ids.ucw.cz/read/PC/1969/1026
>
> We get data corruption when transferring data via NFS over TCP, while data
> transfered via NFS over UDP seems to be fine.
>
> We tested this by md5summing large files (100MB+) over NFS.
>
> Turning off all offload features via ethool does not help either. However the
> RX checksum offload can't be turned off individually, but _seems_ to be turned
> off when the TX checksum offload is turned off.
>
> As indicated when transferring data via UDP the problem disappears, and the
> md5sums are correct again.
>
> The above would suggestion something is possibly wrong the RX checksum offload?
>
> Is there any way to force the RX checksum offload off?
>
^ permalink raw reply
* Re: Network performance with small packets
From: Steve Dobbelstein @ 2011-01-28 22:51 UTC (permalink / raw)
To: Steve Dobbelstein; +Cc: kvm, kvm-owner, mashirle, Michael S. Tsirkin, netdev
In-Reply-To: <OF381CCA86.B82857CC-ON86257826.00629F88-86257826.006596F5@us.ibm.com>
steved@us.ibm.com wrote on 01/28/2011 12:29:37 PM:
> > On Thu, 2011-01-27 at 22:05 +0200, Michael S. Tsirkin wrote:
> > > One simple theory is that guest net stack became faster
> > > and so the host can't keep up.
> >
> > Yes, that's what I think here. Some qdisc code has been changed
> > recently.
>
> I ran a test with txqueuelen set to 128, instead of the default of 1000,
in
> the guest in an attempt to slow down the guest transmits. The change had
> no effect on the throughput nor on the CPU usage.
>
> On the other hand, I ran some tests with different CPU pinnings and
> with/without hyperthreading enabled. Here is a summary of the results.
>
> Pinning configuration 1: pin the VCPUs and pin the vhost thread to one
of
> the VCPU CPUs
> Pinning configuration 2: pin the VCPUs and pin the vhost thread to a
> separate CPU on the same socket
> Pinning configuration 3: pin the VCPUs and pin the vhost thread to a
> separate CPU a different socket
>
> HT Pinning Throughput CPU
> Yes config 1 - 40% - 40%
> Yes config 2 - 37% - 35%
> Yes config 3 - 37% - 36%
> No none 0% - 5%
> No config 1 - 41% - 43%
> No config 2 + 32% - 4%
> No config 3 + 34% + 9%
>
> Pinning the vhost thread to the same CPU as a guest VCPU hurts
performance.
> Turning off hyperthreading and pinning the VPUS and vhost thread to
> separate CPUs significantly improves performance, getting it into the
> competitive range with other hypervisors.
>
> Steve D.
Those results for configs 2 and 3 with hyperthreading off are a little
strange. Digging into the cause I found that my automation script for
pinning the vhost thread failed and pinned it to CPU 1, the same as config
1, giving results similar to config 1. I reran the tests making sure the
pinning script did the right thing. The results are more consistent.
HT Pinning Throughput CPU
Yes config 1 - 40% - 40%
Yes config 2 + 33% - 8%
Yes config 3 + 34% + 9%
No none 0% - 5%
No config 1 - 41% - 43%
No config 2 + 32% - 4%
No config 3 + 34% + 9%
It appears that we have a scheduling problem. If the processes are pinned
we can get good performance.
We also se that hyperthreading makes little difference.
Sorry for the initial misleading data.
Steve D.
^ permalink raw reply
* Re: [Bugme-new] [Bug 27742] New: PPP over SSH tunnel triggers OOPS
From: David Miller @ 2011-01-28 22:55 UTC (permalink / raw)
To: akpm; +Cc: netdev, bugzilla-daemon, bugme-daemon, ktk, paulus
In-Reply-To: <20110128143238.446e1821.akpm@linux-foundation.org>
From: Andrew Morton <akpm@linux-foundation.org>
Date: Fri, 28 Jan 2011 14:32:38 -0800
>> skb_over_panic: text:c12a354f len:847 put:847 head:f57e8c00 data:f57e8c00 tail:0xf57e8f4f end:0xf57e8e80 dev:<NULL>
>> kernel BUG at net/core/skbuff.c:127!
...
>> Pid: 0, comm: swapper Not tainted 2.6.37 #1 0KH290/OptiPlex GX620
>> EIP: 0060:[<c1330110>] EFLAGS: 00010282 CPU: 0
>> EIP is at skb_put+0x82/0x84
...
>> Call Trace:
>> [<c12a354f>] ? ppp_xmit_process+0x45a/0x4e6
>> [<c12a354f>] ? ppp_xmit_process+0x45a/0x4e6
>> [<c1390a0a>] ? tcp_manip_pkt+0xad/0xcb
>> [<c12a36d4>] ? ppp_start_xmit+0xf9/0x175
I took a quick look at this, I can surmise that we have a packet we
are trying to compress (that's the only way I see in the
ppp_xmit_process() code paths that we can get an skb_put() call so
large).
And we can see from the skb_over_panic message that we have an SKB
which was allocated with 640 bytes of space, but we are trying to
"put" 847 bytes into it which is too large and overflows.
Can you run with the following debugging patch and see what it prints
out when this happens?
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 9f6d670..06c6ea7 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1093,6 +1093,15 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
if (len > 0 && (ppp->flags & SC_CCP_UP)) {
kfree_skb(skb);
skb = new_skb;
+#if 1
+ if (len > (skb->end - skb->tail)) {
+ printk(KERN_ERR "pad_compress_skb: Compression overflow ["
+ "new_skb_size(%d) compressor_skb_size(%d) "
+ "hard_header_len(%d) len(%d)]\n",
+ new_skb_size, compressor_skb_size,
+ ppp->dev->hard_header_len, len);
+ }
+#endif
skb_put(skb, len);
skb_pull(skb, 2); /* pull off A/C bytes */
} else if (len == 0) {
@@ -1179,6 +1188,9 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
/* didn't compress */
kfree_skb(new_skb);
} else {
+#if 1
+ unsigned int orig_skb_len = skb->len;
+#endif
if (cp[0] & SL_TYPE_COMPRESSED_TCP) {
proto = PPP_VJC_COMP;
cp[0] &= ~SL_TYPE_COMPRESSED_TCP;
@@ -1188,6 +1200,13 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
}
kfree_skb(skb);
skb = new_skb;
+#if 1
+ if (len > (skb->end - skb->tail)) {
+ printk(KERN_ERR "slhc_compress_skb: Compression overflow ["
+ "skb->len(%u) hard_header_len(%d) len(%d)]\n",
+ orig_skb_len, ppp->dev->hard_header_len, len);
+ }
+#endif
cp = skb_put(skb, len + 2);
cp[0] = 0;
cp[1] = proto;
^ permalink raw reply related
* Re: jme driver loses connection after resuming from suspend
From: Andrew Morton @ 2011-01-29 0:33 UTC (permalink / raw)
To: Leonardo L. P. da Mata; +Cc: linux-kernel, Guo-Fu Tseng, netdev
In-Reply-To: <AANLkTikrKEY7Zt5HvxQcJLk9eGne0BGD82NFEiWO4smt@mail.gmail.com>
(cc's added)
On Fri, 28 Jan 2011 16:03:11 -0200
"Leonardo L. P. da Mata" <barroca@gmail.com> wrote:
> Hello, i'm testing the kernel 2.6.37 on my hardware, Once connect on
> wired network, i call the suspend with:
> echo "mem" >/sys/power/state
>
> The system goes on suspend. After resuming from suspend, the network card
> cannot be used anymore.
>
>
> The bug is reported here:
> https://bugzilla.kernel.org/show_bug.cgi?id=27692
>
> Can you please point me to similar problems on other network cards so
> i can get possible solutions on this.
^ permalink raw reply
* Re: [net-next-2.6 2/3] igb: add support for VF Transmit rate limit using iproute2
From: David Miller @ 2011-01-29 0:38 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: lior.levy, netdev, gospo, bphilips
In-Reply-To: <1296217779-30133-3-git-send-email-jeffrey.t.kirsher@intel.com>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Fri, 28 Jan 2011 04:29:38 -0800
> + if (tx_rate != 0)
> + dev_info(&adapter->pdev->dev,
> + "Setting Transmit rate of %d Mbps for VF %d\n",
> + tx_rate, vf);
> + else
> + dev_info(&adapter->pdev->dev,
> + "Transmit rate limit for VF %d is disabled\n", vf);
If you're going to print this, use netdev_info(netdev, ...).
But I think you shouldn't be logging anything at all.
No other ethtool operation logs what it did except in extremely
exceptional error conditions. And there is nothing special
about this VF rate limiting ethtool operation to justify these
extraneous logging messages.
If people want to know if the VF is rate limited, and by how much,
then can query the configuration using ethtool.
^ permalink raw reply
* Re: Bonding on bond
From: Jay Vosburgh @ 2011-01-29 0:38 UTC (permalink / raw)
To: =?UTF-8?B?Tmljb2xhcyBkZSBQZXNsb8O8YW4=?=
Cc: Jiri Bohac, bonding-devel@lists.sourceforge.net,
netdev@vger.kernel.org
In-Reply-To: <4D3B60D2.30309@gmail.com>
Nicolas de Pesloüan <nicolas.2p.debian@gmail.com> wrote:
>Le 20/01/2011 20:53, Jay Vosburgh a écrit :
>> I'm in agreement that, by and large, nesting of bonds is
>> pointless. However, I suspect that there are users out in the world who
>> are happily doing so, and this patch may shut them down.
>
>Hi Jay,
>
>I tested the following nested bonding configuration:
>
>bond1 : eth1 + eth3, in balance-rr mode.
>bond2 : eth0 + eth2, in balance-rr mode.
>bond0 : bond1 + bond2, in active-backup mode.
>
>The egress path apparently works not so bad, even if I didn't take time
>yet to check proper load balancing nor fail over.
>
>However, the ingress path doesn't work at all. bond0 is unable to receive any packets (ARP or IP).
In light of this, I don't see a problem with disallowing nesting
of bonds. It should be documented in bonding.txt.
>It doesn't sound surprising to me, having a look at the current code in __netif_receive_skb() :
>
>> /*
>> * bonding note: skbs received on inactive slaves should only
>> * be delivered to pkt handlers that are exact matches. Also
>> * the deliver_no_wcard flag will be set. If packet handlers
>> * are sensitive to duplicate packets these skbs will need to
>> * be dropped at the handler.
>> */
>> null_or_orig = NULL;
>> orig_dev = skb->dev;
>> master = ACCESS_ONCE(orig_dev->master);
>> if (skb->deliver_no_wcard)
>> null_or_orig = orig_dev;
>> else if (master) {
>> if (skb_bond_should_drop(skb, master)) {
>> skb->deliver_no_wcard = 1;
>> null_or_orig = orig_dev; /* deliver only exact match */
>> } else
>> skb->dev = master;
>> }
>
>The skb_bond_should_drop() and skb->dev = master logic is only applied at a single level.
>
>After this code, skb->dev is the master dev of the receiving dev, but
>skb->dev->master can be != NULL, if another level of bonding
>exists. Nothing obvious would cause the packet to be delivered to this
>possible higher level bonding interface (skb->dev->master).
>
>Is something else expected to call __netif_receive_skb() again, with the
>current skb, to cause another level of bonding to be reachable? For as far
>as I understand, nothing will, but I might have missed something.
>
>> I've not tested with nesting in a while; I know it used to work
>> (at least for limited cases, typically an active-backup bond with a pair
>> of balance-xor or balance-rr or sometimes 802.3ad enslaved to it), but
>> has never really been a deliberate feature. Is nesting now utterly
>> broken, as suggested by the list of problems above?
>
>I don't know whether someone really use nested bonding, but I can hardly
>imagine how one can have it works with current kernel, except for a pure
>egress application, without any feedback from the network. And such very
>specific application wouldn't even be able to receive an ARP reply...
>
>> If nesting really doesn't work and is going to be disabled, then
>> at a minimum it should also have an update to the documentation
>> explaining this.
>
>At least, we should explain that nesting bonding interfaces is known to be
>mostly broken and unsupported.
>
>That being said, we still miss a way to achieve a simple configuration
>with several links doing load balancing to a switch and one or several
>links doing fail over to another switch, both switches *not* being 802.3ad
>capable.
This is a harder problem, but it's something that doesn't work
today (and I suspect hasn't for a long time, so if somebody was using
this, I think there would have been some discussion).
>Should we arrange for bonding to be allowed to nest, for this purpose, or
>should we find a way to setup this configuration with a single level of
>bonding ? I would prefer the second, but...
I'm not sure that either is necessary; 802.3ad will do this
today, and few current production switches lack 802.3ad support.
Adding support for etherchannel (i.e., not 802.3ad) gang
failover is nontrivial, because the multiple etherchannel port groups
will have to be managed separately, and most likely assigned manually.
Sure, it'd be nice to have, but I'm not sure if it's a benefit worth the
effort.
Either way, for now, since I recall you mentioned in another
email that you'd crashed the system from nesting bonds, I don't see a
problem with disallowing nesting and updating the documentation with a
bit of this discussion (e.g., "nesting doesn't work, you're probably
trying to do gang failover, which 802.3ad already does for you").
-J
---
-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
^ permalink raw reply
* Re: [PATCH] bonding: added 802.3ad round-robin hashing policy for single TCP session balancing
From: Jay Vosburgh @ 2011-01-29 2:28 UTC (permalink / raw)
To: Oleg V. Ukhno
Cc: =?UTF-8?B?Tmljb2xhcyBkZSBQZXNsb8O8YW4=?=, John Fastabend,
netdev@vger.kernel.org
In-Reply-To: <4D399062.3060004@yandex-team.ru>
Oleg V. Ukhno <olegu@yandex-team.ru> wrote:
>On 01/19/2011 11:12 PM, Nicolas de Pesloüan wrote:
>
>> If you have time for that, then yes, please, do the same test using
>> balance-rr+vlan to segregate path. With those results, we whould have
>> the opportunity to enhance the documentation with some well tested cases
>> of TCP load balancing on a LAN, not limited to 802.3ad automatic setup.
>> Both setups make sense, and assuming the results would be similar is
>> probably true, but not reliable enough to assert it into the documentation.
>>
>> Thanks,
>>
>> Nicolas.
>>
>Nicolas,
>I've ran similar tests for VLAN tunneling scenario. Results are identical,
>as I expected. The only significat difference is link failure
>handling. 802.3ad mode allows almost painless load reditribution,
>balance-rr causes packet loss.
>The only question for me now is if my patch could be applied to upstream
>version - fixing issues with adaptftion to net-next code aren't the
>problem, if nobody objects
I've thought about this whole thing, and here's what I view as
the proper way to do this.
In my mind, this proposal is two separate pieces:
First, a piece to make round-robin a selectable hash for
xmit_hash_policy. The documentation for this should follow the pattern
of the "layer3+4" hash policy, in particular noting that the new
algorithm violates the 802.3ad standard in exciting ways, will result in
out of order delivery, and that other 802.3ad implementations may or may
not tolerate this.
Second, a piece to make certain transmitted packets use the
source MAC of the sending slave instead of the bond's MAC. This should
be a separate option from the round-robin hash policy. I'd call it
something like "mac_select" with two values: "default" (what we do now)
and "slave_src_mac" to use the slave's real MAC for certain types of
traffic (I'm open to better names; that's just what I came up with while
writing this). I believe that "certain types" means "everything but
ARP," but might be "only IP and IPv6." Structuring the option in this
manner leaves the option open for additional selections in the future,
which a simple "on/off" option wouldn't. This option should probably
only affect a subset of modes; I'm thinking anything except balance-tlb
or -alb (because they do funky MAC things already) and active-backup (it
doesn't balance traffic, and already uses fail_over_mac to control
this). I think this option also needs a whole new section down in the
bottom explaining how to exploit it (the "pick special MACs on slaves to
trick switch hash" business).
Comments?
-J
---
-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
^ permalink raw reply
* Ethernet over GRE and vlans
From: Jonathan Thibault @ 2011-01-29 5:16 UTC (permalink / raw)
To: netdev; +Cc: Herbert Xu
In-Reply-To: <4D2F77A8.1010700@navigue.com>
As per one of my previous posts, imagine a setup like this:
Three linux hosts connected to their individual 802.1Q network via
eth0 interface linked by a L3 network through their eth1 interface.
(local network)
| (remote network 1)
| eth0.1 <--br1--> gre1.1 |
| eth0.3 <--br0--> gre1 -- (l3_to_host1) -- gre0 <--br0--> eth0-+
+-eth0
eth0.4 <--br3--> gre2 -- (l3-to_host2) -- gre0 <--br0--> eth0-+
eth0.2 <--br2--> gre2.2 |
(remote network 2)
Wanting only untagged packets from remote networks 1 and 2 requires
simple ebtables rules wich answers my original query. But I ran into
a strange issue where vlan1 and vlan2 tagged packets from their
respective remote networks do not appear on gre1.1 and gre2.2
interfaces at all.
I see the tagged packets on the gre1 and gre2 interfaces respectively
but cannot make their untagged equivalent (or anything else) show up
on gre2.2 and gre1.1 as they would on standard ethernet devices.
Is it wrong on my part to expect such behaviour from gretap devices
or is this simply not possible/implemented yet?
Please include me in replies, I am not currently subscribed to netdev.
Jonathan
P.S.: I CCed Mr. Xu as I believe he originally submitted gretap
patches.
^ permalink raw reply
* [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Balaji Venkatachalam @ 2011-01-29 8:03 UTC (permalink / raw)
To: netdev
Cc: mohan, blue.cube, lanconelli.claudio, Sriram Subramanian,
vbalaji.acs
From: Balaji Venkatachalam <balaji.v@thotakaa.com>
Updated patch for Microchip enc424j600 ethernet chip controlled via SPI.
I tested it on my custom board with ARM9 (Freescale i.MX233) with
Kernel 2.6.31.14.
Changes done since V1.27 to V1.28
1. did some code formatting at line no 110
Changes done since V1.24 to V1.27
1. Timeout Mechanism implemented for enc424j600_soft_reset function
2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
3. Window Naming changed to enum
4. Removed WRITEVERIFY functionality
Todo List:
1. Low Power Mode Functionality implementation
2. Provide Support for On-Chip DMA.
3. Remove mutex_lock wherever not required
Any comments are welcome.
Signed-off-by: Balaji Venkatachalam <balaji.v@thotakaa.com>
---
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600.c
b/drivers/net/enc424j600.c
--- a/drivers/net/enc424j600.c 1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600.c 2011-01-29 13:26:31.000000000 +0530
@@ -0,0 +1,1712 @@
+/*
+ * Microchip ENC424J600 ethernet driver (MAC + PHY) on SPI bus
+ *
+ * Copyright (C) 2011 Thotaka Technologies Pvt Ltd
+ * Author: Balaji Venkatachalam <balaji.v@thotakaa.com>
+ * based on enc424j600.c written by Kuba Marek
+ * based on enc28j60.c written by Claudio Lanconelli
+ *
+ * Changes done since V1.27 to V1.28
+ * 1. did some code formatting at line no 110
+ *
+ * Changes done since V1.24 to V1.27
+ * 1. Timeout Mechanism implemented for enc424j600_soft_reset function
+ * 2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
+ * 3. Window Naming changed to enum
+ * 4. Removed WRITEVERIFY functionality
+ *
+ * Todo List:
+ * 1. Low Power Mode Functionality implementation
+ * 2. Provide Support for On-Chip DMA
+ * 3. Remove mutex_lock wherever not required
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include "enc424j600_hw.h"
+
+#define DRV_NAME "enc424j600"
+#define DRV_VERSION "1.28"
+
+#define ENC424J600_MSG_DEFAULT \
+ (NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
+
+#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN)
+#define TX_TIMEOUT (4 * HZ)
+
+/* Max TX retries in case of collision as suggested by errata datasheet */
+#define MAX_TX_RETRYCOUNT 16
+
+#define SPI_OPLEN 1
+
+#define SRAMSIZE 0x6000
+#define TXSTART 0x0000
+#define RXSTART 0x1600
+
+static int enc424j600_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+
+enum {
+ RXFILTER_NORMAL,
+ RXFILTER_MULTI,
+ RXFILTER_PROMISC
+};
+enum {
+ RXWINDOW,
+ USERWINDOW,
+ GPWINDOW
+};
+
+/* Driver local data */
+struct enc424j600_net {
+ struct net_device *netdev;
+ struct spi_device *spi;
+ struct mutex lock;
+ struct sk_buff *tx_skb;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct work_struct setrx_work;
+ struct work_struct restart_work;
+ u8 bank; /* current register bank selected */
+ u16 next_pk_ptr; /* next packet pointer within FIFO */
+ u16 max_pk_counter; /* statistics: max packet counter */
+ u16 tx_retry_count;
+ bool hw_enable;
+ bool full_duplex;
+ bool autoneg;
+ bool speed100;
+ int rxfilter;
+ u32 msg_enable;
+
+ u8 *spi_rx_buf;
+ u8 *spi_tx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+};
+
+/* use ethtool to change the level for any given device */
+static struct {
+ u32 msg_enable;
+} debug = { -1 };
+
+static int enc424j600_spi_trans(struct enc424j600_net *priv, int len)
+{
+ /*modified to suit half duplexed spi */
+ struct spi_transfer tt = {
+ .tx_buf = priv->spi_tx_buf,
+ .len = SPI_OPLEN,
+ };
+ struct spi_transfer tr = {
+ .rx_buf = priv->spi_rx_buf,
+ .len = len,
+ };
+ struct spi_message m;
+ int ret;
+
+ spi_message_init(&m);
+
+ spi_message_add_tail(&tt, &m);
+ spi_message_add_tail(&tr, &m);
+
+ ret = spi_sync(priv->spi, &m);
+
+ if (ret == 0)
+ memcpy(priv->spi_rx_buf, tr.rx_buf, len);
+
+ if (ret)
+ dev_err(&priv->spi->dev,
+ "spi transfer failed: ret = %d\n", ret);
+ return ret;
+}
+
+/*
+ * Read data from chip SRAM.
+ * window = 0 for Receive Buffer
+ * = 1 for User Defined area
+ * = 2 for General Purpose area
+ */
+static int enc424j600_read_sram(struct enc424j600_net *priv,
+ u8 *dst, int len, u16 srcaddr, int window)
+{
+ int ret;
+
+ if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+ return -EINVAL;
+
+ /* First set the write pointer as per selected window */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXRDPT;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDARDPT;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPRDPT;
+
+ priv->spi_tx_buf[1] = srcaddr & 0xFF;
+ priv->spi_tx_buf[2] = srcaddr >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+ /* Transfer the data */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = RRXDATA;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = RUDADATA;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = RGPDATA;
+
+ ret = enc424j600_spi_trans(priv, len + 1);
+ /*READ*/
+ /* Copy the data from the rx buffer */
+ memcpy(dst, &priv->spi_rx_buf[0], len);
+
+ return ret;
+}
+
+/*
+ * Write data to chip SRAM.
+ * window = 1 for RX
+ * window = 2 for User Data
+ * window = 3 for GP
+ */
+static int enc424j600_write_sram(struct enc424j600_net *priv,
+ const u8 *src, int len, u16 dstaddr,
+ int window)
+{
+ int ret;
+
+ if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+ return -EINVAL;
+
+ /* First set the general purpose write pointer */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXWRPT;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDAWRPT;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPWRPT;
+
+ priv->spi_tx_buf[1] = dstaddr & 0xFF;
+ priv->spi_tx_buf[2] = dstaddr >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+ /* Copy the data to the tx buffer */
+ memcpy(&priv->spi_tx_buf[1], src, len);
+
+ /* Transfer the data */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXDATA;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDADATA;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPDATA;
+
+ ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1);
+
+ return ret;
+}
+
+/*
+ * Select the current register bank if necessary to be able to read @addr.
+ */
+static void enc424j600_set_bank(struct enc424j600_net *priv, u8 addr)
+{
+ u8 b = (addr & BANK_MASK) >> BANK_SHIFT;
+
+ /* These registers are present in all banks, no need to switch bank */
+ if (addr >= EUDASTL && addr <= ECON1H)
+ return;
+ if (priv->bank == b)
+ return;
+
+ priv->spi_tx_buf[0] = BXSEL(b);
+
+ enc424j600_spi_trans(priv, 1);
+ /*WRITE*/ priv->bank = b;
+}
+
+/*
+ * Set bits in an 8bit SFR.
+ */
+static void enc424j600_set_bits(struct enc424j600_net *priv, u8 addr, u8 mask)
+{
+ enc424j600_set_bank(priv, addr);
+ priv->spi_tx_buf[0] = BFS(addr);
+ priv->spi_tx_buf[1] = mask;
+ spi_write(priv->spi, priv->spi_tx_buf, 2);
+}
+
+/*
+ * Clear bits in an 8bit SFR.
+ */
+static void enc424j600_clear_bits(struct enc424j600_net *priv, u8
addr, u8 mask)
+{
+ enc424j600_set_bank(priv, addr);
+ priv->spi_tx_buf[0] = BFC(addr);
+ priv->spi_tx_buf[1] = mask;
+ spi_write(priv->spi, priv->spi_tx_buf, 2);
+}
+
+/*
+ * Write a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked write instruction.
+ */
+static int enc424j600_write_8b_sfr(struct enc424j600_net *priv, u8
sfr, u8 data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = data & 0xFF;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 2);
+
+ return ret;
+}
+
+/*
+ * Read a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_8b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u8 *data)
+{
+ int ret;
+
+ enc424j600_set_bank(priv, sfr);
+ priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+ ret = enc424j600_spi_trans(priv, 2);
+ /*READ*/ *data = priv->spi_rx_buf[0];
+
+ return ret;
+}
+
+/*
+ * Write a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked write instruction.
+ */
+
+static int enc424j600_write_16b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u16 data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = data & 0xFF;
+ priv->spi_tx_buf[2] = data >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+ if (ret && netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/*
+ * Read a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_16b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u16 *data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = 0;
+ priv->spi_tx_buf[2] = 0;
+ priv->spi_tx_buf[3] = 0;
+ ret = enc424j600_spi_trans(priv, 3);
+ /*READ*/ *data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8;
+
+ return ret;
+}
+
+static unsigned long msec20_to_jiffies;
+
+/*
+ * Wait for bits in register to become equal to @readyMask, but at most 20ms.
+ */
+static int checktimeout_16bit(struct enc424j600_net *priv,
+ u8 reg, u16 mask, u16 readyMask)
+{
+ unsigned long timeout = jiffies + msec20_to_jiffies;
+ u16 value;
+ /* 20 msec timeout read */
+ enc424j600_read_16b_sfr(priv, reg, &value);
+ while ((value & mask) != readyMask) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", reg);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_read_16b_sfr(priv, reg, &value);
+ }
+
+ return 0;
+}
+
+static int checktimeout_8bit(struct enc424j600_net *priv,
+ u8 reg, u8 mask, u8 readyMask)
+{
+ unsigned long timeout = jiffies + msec20_to_jiffies;
+ u8 value;
+ /* 20 msec timeout read */
+ enc424j600_read_8b_sfr(priv, reg, &value);
+ while ((value & mask) != readyMask) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", reg);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_read_8b_sfr(priv, reg, &value);
+ }
+
+ return 0;
+}
+
+/*
+ * Reset the enc424j600.
+ */
+static int enc424j600_soft_reset(struct enc424j600_net *priv)
+{
+ int ret;
+ u16 eudast;
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ enc424j600_write_16b_sfr(priv, EUDASTL, EUDAST_TEST_VAL);
+ ret = checktimeout_16bit(priv, EUDASTL, 0xFFFF, EUDAST_TEST_VAL);
+ if (ret != 0)
+ return ret;
+ ret = checktimeout_16bit(priv, ESTATH, CLKRDY, CLKRDY);
+ if (ret != 0)
+ return ret;
+
+ priv->spi_tx_buf[0] = SETETHRST;
+ enc424j600_spi_trans(priv, 1);
+ /*inline with the datasheet */
+ udelay(25);
+
+ enc424j600_read_16b_sfr(priv, EUDASTL, &eudast);
+ if (netif_msg_hw(priv) && eudast != 0)
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() EUDASTL is not zero!\n", __func__);
+ /*inline with the datasheet */
+ /*datasheet says to wait for 256 usec atleast */
+ udelay(300);
+ return 0;
+}
+
+/*
+ * PHY register read
+ * PHY registers are not accessed directly, but through the MII
+ */
+static int enc424j600_phy_read(struct enc424j600_net *priv,
+ u16 address, u16 *data)
+{
+ int ret;
+
+ enc424j600_write_16b_sfr(priv, MIREGADRL,
+ address | (MIREGADRH_VAL << 8));
+ enc424j600_write_16b_sfr(priv, MICMDL, MIIRD);
+ udelay(26);
+ ret = !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+ enc424j600_write_16b_sfr(priv, MICMDL, 0);
+ enc424j600_read_16b_sfr(priv, MIRDL, data);
+ return ret;
+}
+
+static int enc424j600_phy_write(struct enc424j600_net *priv, u16 address,
+ u16 data)
+{
+ enc424j600_write_16b_sfr(priv, MIREGADRL,
+ address | (MIREGADRH_VAL << 8));
+ enc424j600_write_16b_sfr(priv, MIWRL, data);
+ udelay(26);
+ return !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+}
+
+/*
+ * Read the hardware MAC address to dev->dev_addr.
+ */
+static int enc424j600_get_hw_macaddr(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ u16 maadr1, maadr2, maadr3;
+
+ mutex_lock(&priv->lock);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ enc424j600_read_16b_sfr(priv, MAADR3L, &maadr3);
+ ndev->dev_addr[5] = maadr3 >> 8;
+ ndev->dev_addr[4] = maadr3 & 0xff;
+ enc424j600_read_16b_sfr(priv, MAADR2L, &maadr2);
+ ndev->dev_addr[3] = maadr2 >> 8;
+ ndev->dev_addr[2] = maadr2 & 0xff;
+ enc424j600_read_16b_sfr(priv, MAADR1L, &maadr1);
+ ndev->dev_addr[1] = maadr1 >> 8;
+ ndev->dev_addr[0] = maadr1 & 0xff;
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * Program the hardware MAC address from dev->dev_addr.
+ */
+static int enc424j600_set_hw_macaddr(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+
+ mutex_lock(&priv->lock);
+
+ if (priv->hw_enable) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() Hardware must be disabled to set "
+ "Mac address\n", __func__);
+ mutex_unlock(&priv->lock);
+ return -EBUSY;
+ }
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ enc424j600_write_16b_sfr(priv, MAADR3L,
+ ndev->dev_addr[4] | ndev->dev_addr[5] << 8);
+ enc424j600_write_16b_sfr(priv, MAADR2L,
+ ndev->dev_addr[2] | ndev->dev_addr[3] << 8);
+ enc424j600_write_16b_sfr(priv, MAADR1L,
+ ndev->dev_addr[0] | ndev->dev_addr[1] << 8);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * Store the new hardware address in dev->dev_addr, and update the MAC.
+ */
+static int enc424j600_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ return enc424j600_set_hw_macaddr(dev);
+}
+
+u8 nolock_regb_read(struct enc424j600_net *priv, u8 address)
+{
+ u8 data;
+ enc424j600_read_8b_sfr(priv, address, &data);
+ return data;
+}
+
+u16 nolock_regw_read(struct enc424j600_net *priv, u8 address)
+{
+ u16 data;
+ enc424j600_read_16b_sfr(priv, address, &data);
+ return data;
+}
+
+/*Debug routine to dump useful register contents*/
+static void enc424j600_dump_regs(struct enc424j600_net *priv, const char *msg)
+{
+
+ mutex_lock(&priv->lock);
+ printk(KERN_DEBUG DRV_NAME " %s\n"
+ "Cntrl: ECON1H ECON1L ECON2H ECON2L ESTATH ESTATL EIRH "
+ "EIRL EIEH EIEL\n"
+ " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x 0x%02x 0x%02x\n"
+ "MAC : MACON1 MACON2\n"
+ " 0x%04x 0x%04x\n"
+ "Rx : ERXST ERXTAIL ERXHEAD ERXWRPT ERXRDPT ERXFCON MAMXFL\n"
+ " 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
+ "Tx : ETXST ETXLEN MACLCON1 \n"
+ " 0x%04x 0x%04x 0x%02x\n",
+ msg,
+ nolock_regb_read(priv, ECON1H), nolock_regb_read(priv, ECON1L),
+ nolock_regb_read(priv, ECON2H), nolock_regb_read(priv, ECON2L),
+ nolock_regb_read(priv, ESTATH), nolock_regb_read(priv, ESTATL),
+ nolock_regb_read(priv, EIRH), nolock_regb_read(priv, EIRL),
+ nolock_regb_read(priv, EIEH), nolock_regb_read(priv, EIEL),
+ nolock_regw_read(priv, MACON1L), nolock_regw_read(priv, MACON2L),
+ nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXTAILL),
+ nolock_regw_read(priv, ERXHEADL),
+ nolock_regw_read(priv, ERXWRPTL), nolock_regw_read(priv,
+ ERXRDPTL),
+ nolock_regw_read(priv, ERXFCONL), nolock_regw_read(priv,
+ MAMXFLL),
+ nolock_regw_read(priv, ETXSTL), nolock_regw_read(priv, ETXLENL),
+ nolock_regw_read(priv, MACLCONL));
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * TODO: Check the functionality
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive. (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc424j600_lowpower(struct enc424j600_net *priv, bool is_low)
+{
+
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev, "%s power...\n",
+ is_low ? "low" : "high");
+
+#if 0
+ mutex_lock(&priv->lock);
+ if (is_low) {
+ nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+ checktimeout_8bit(priv, ESTAT, ESTAT_RXBUSY, 0);
+ checktimeout_8bit(priv, ECON1, ECON1_TXRTS, 0);
+ /* ECON2_VRPS was set during initialization */
+ nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+ } else {
+ nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+ checktimeout_8bit(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+ /* caller sets ECON1_RXEN */
+ }
+ mutex_unlock(&priv->lock);
+#endif
+}
+
+static unsigned long msec2000_to_jiffies;
+/* Waits for autonegotiation to complete. */
+static int enc424j600_wait_for_autoneg(struct enc424j600_net *priv)
+{
+ unsigned long timeout = jiffies + msec2000_to_jiffies;
+ u16 value;
+ /* 20 msec timeout read */
+ enc424j600_phy_read(priv, PHSTAT1, &value);
+ while ((value & ANDONE) == 0) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", PHSTAT1);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_phy_read(priv, PHSTAT1, &value);
+ }
+ return 0;
+
+}
+
+/*
+ * Reset and initialize the chip, but don't enable interrupts and don't
+ * start receiving yet.
+ */
+static int enc424j600_hw_init(struct enc424j600_net *priv)
+{
+ u8 eidledl;
+ u16 phcon1;
+ u16 macon2;
+ u16 econ1l;
+ /*priv->autoneg = AUTONEG_ENABLE;*/
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() - %s\n", __func__,
+ priv->
+ autoneg ? "Autoneg" : (priv->full_duplex ? "FullDuplex" :
+ "HalfDuplex"));
+
+ mutex_lock(&priv->lock);
+
+ priv->bank = 0;
+ priv->hw_enable = false;
+ priv->tx_retry_count = 0;
+ priv->max_pk_counter = 0;
+ priv->rxfilter = RXFILTER_NORMAL;
+
+ if (enc424j600_soft_reset(priv) != 0)
+ return 0;
+
+ /*
+ * Check the device id and silicon revision id.
+ */
+ enc424j600_read_8b_sfr(priv, EIDLEDL, &eidledl);
+
+ if ((eidledl & DEVID_MASK) >> DEVID_SHIFT != ENC424J600_DEV_ID) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() Invalid device ID: %d\n", __func__,
+ (eidledl & DEVID_MASK) >> DEVID_SHIFT);
+ return 0;
+ }
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME ": Silicon revision ID: 0x%02x\n",
+ (eidledl & REVID_MASK) >> REVID_SHIFT);
+
+ enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+ enc424j600_write_16b_sfr(priv, ERXSTL, RXSTART);
+
+ priv->next_pk_ptr = RXSTART;
+ enc424j600_write_16b_sfr(priv, ERXTAILL, SRAMSIZE - 2);
+ enc424j600_write_16b_sfr(priv, ERXFCONL, UCEN | BCEN | CRCEN | RUNTEN);
+
+ enc424j600_phy_write(priv, PHANA, PHANA_DEFAULT);
+
+ /* PHCON1 */
+ phcon1 = 0;
+ if (priv->autoneg) {
+ /* Enable autonegotiation and renegotiate */
+ phcon1 |= ANEN | RENEG;
+ } else {
+ if (priv->speed100)
+ phcon1 |= SPD100;
+ if (priv->full_duplex)
+ phcon1 |= PFULDPX;
+ }
+ enc424j600_phy_write(priv, PHCON1, phcon1);
+
+ /* MACON2
+ * defer transmission if collision occurs (only for half duplex)
+ * pad to 60 or 64 bytes and append CRC
+ * enable receiving huge frames (instead of limiting packet size) */
+ macon2 = MACON2_DEFER | PADCFG2 | PADCFG0 | TXCRCEN | HFRMEN;
+
+ /* If autonegotiation is enabled, we have to wait untill it finishes
+ * and set the PHYDPX bit in MACON2 correctly */
+ if (priv->autoneg) {
+ u8 estath;
+ if (!enc424j600_wait_for_autoneg(priv)) {
+ /* read the PHYDPX bit in ESTAT and set FULDPX in
+ MACON2 accordingly */
+ enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+ if (estath & PHYDPX)
+ macon2 |= FULDPX;
+ } else /*if timedout, just disable autoneg */
+ priv->autoneg = AUTONEG_DISABLE;
+ } else if (priv->full_duplex)
+ macon2 |= FULDPX;
+
+ enc424j600_write_16b_sfr(priv, MACON2L, macon2);
+
+ /* MAIPGL
+ * Recomended values for inter packet gaps */
+ if (!priv->autoneg) {
+ enc424j600_write_16b_sfr(priv, MAIPGL,
+ MAIPGL_VAL | (MAIPGH_VAL << 8));
+ }
+
+ /*
+ * Select enabled interrupts, but don't set the global
+ * interrupt enable flag.
+ */
+
+ enc424j600_write_16b_sfr(priv, EIEL,
+ LINKIE << 8 | PKTIE | DMAIE | TXIE | TXABTIE |
+ RXABTIE);
+
+ enc424j600_read_16b_sfr(priv, ECON1L, &econ1l);
+ econ1l |= (RXEN);
+ enc424j600_write_16b_sfr(priv, ECON1L, econ1l);
+
+ mutex_unlock(&priv->lock);
+
+ if (netif_msg_hw(priv))
+ enc424j600_dump_regs(priv, "Hw initialized.");
+
+ return 1;
+}
+
+static void enc424j600_hw_enable(struct enc424j600_net *priv)
+{
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
+ __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* Clear any pending interrupts */
+ enc424j600_write_16b_sfr(priv, EIRL, 0);
+
+ /* Enable global interrupt flag */
+ enc424j600_set_bits(priv, EIEH, INTIE);
+
+ /* enable receive logic */
+ enc424j600_set_bits(priv, ECON1L, RXEN);
+ priv->hw_enable = true;
+ mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_hw_disable(struct enc424j600_net *priv)
+{
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() disabling interrupts.\n",
+ __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* disable receive logic */
+ enc424j600_clear_bits(priv, ECON1L, RXEN);
+
+ /* Disable global interrupt flag */
+ enc424j600_clear_bits(priv, EIEH, INTIE);
+
+ priv->hw_enable = false;
+
+ mutex_unlock(&priv->lock);
+}
+
+static int
+enc424j600_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ int ret = 0;
+ if (!priv->hw_enable) {
+ /* link is in low power mode now; duplex setting
+ * will take effect on next enc424j600_hw_init().
+ */
+ if (speed == SPEED_10 || speed == SPEED_100) {
+ priv->autoneg = (autoneg == AUTONEG_ENABLE);
+ priv->full_duplex = (duplex == DUPLEX_FULL);
+ priv->speed100 = (speed == SPEED_100);
+ } else {
+ if (netif_msg_link(priv))
+ dev_warn(&ndev->dev,
+ "unsupported link setting\n");
+ /*speeds other than SPEED_10 and SPEED_100 */
+ /*are not supported by chip */
+ ret = -EOPNOTSUPP;
+ }
+ } else {
+ if (netif_msg_link(priv))
+ dev_warn(&ndev->dev, "Warning: hw must be disabled "
+ "to set link mode\n");
+ ret = -EBUSY;
+ }
+ return ret;
+}
+
+/*
+ * Receive Status vector
+ */
+static void enc424j600_dump_rsv(struct enc424j600_net *priv, const char *msg,
+ u16 pk_ptr, int len, u16 sts)
+{
+ printk(KERN_DEBUG DRV_NAME ": %s - NextPk: 0x%04x - RSV:\n",
+ msg, pk_ptr);
+ printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, DribbleNibble: %d\n", len,
+ RSV_GETBIT(sts, RSV_DRIBBLENIBBLE));
+ printk(KERN_DEBUG DRV_NAME ": RxOK: %d, CRCErr:%d, LenChkErr: %d,"
+ " LenOutOfRange: %d\n", RSV_GETBIT(sts, RSV_RXOK),
+ RSV_GETBIT(sts, RSV_CRCERROR),
+ RSV_GETBIT(sts, RSV_LENCHECKERR),
+ RSV_GETBIT(sts, RSV_LENOUTOFRANGE));
+ printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, "
+ "LongDropEvent: %d, CarrierEvent: %d\n",
+ RSV_GETBIT(sts, RSV_RXMULTICAST),
+ RSV_GETBIT(sts, RSV_RXBROADCAST),
+ RSV_GETBIT(sts, RSV_RXLONGEVDROPEV),
+ RSV_GETBIT(sts, RSV_CARRIEREV));
+ printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d,"
+ " UnknownOp: %d, VLanTagFrame: %d\n",
+ RSV_GETBIT(sts, RSV_RXCONTROLFRAME),
+ RSV_GETBIT(sts, RSV_RXPAUSEFRAME),
+ RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE),
+ RSV_GETBIT(sts, RSV_RXTYPEVLAN));
+}
+
+static void dump_packet(const char *msg, int len, const char *data)
+{
+
+ printk(KERN_ALERT ": %s - packet len:%d\n", msg, len);
+ print_hex_dump(KERN_ALERT, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1,
+ data, len, true);
+}
+
+/*
+ * Calculate wrap around when reading beyond the end of the RX buffer
+ */
+static u16 rx_packet_start(u16 ptr)
+{
+ if (ptr + RSV_SIZE > RXEND_INIT)
+ return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART + 1);
+ else
+ return ptr + RSV_SIZE;
+}
+
+/*
+ * ERXRDPT need to be set always at odd addresses, refer to errata datasheet
+ */
+static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end)
+{
+ u16 erxrdpt;
+ if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end))
+ erxrdpt = end;
+ else
+ erxrdpt = next_packet_ptr - 1;
+
+ return erxrdpt;
+}
+
+static void nolock_rxfifo_init(struct enc424j600_net *priv, u16 start, u16 end)
+{
+ u16 erxrdpt;
+ if (start > 0x5FFF || end > 0x5FFF || start > end) {
+ if (netif_msg_drv(priv))
+ printk(KERN_ERR DRV_NAME ": %s(%d, %d) RXFIFO "
+ "bad parameters!\n", __func__, start, end);
+ return;
+ }
+ /* set receive buffer start + end */
+ priv->next_pk_ptr = start;
+ enc424j600_write_16b_sfr(priv, ERXSTL, start);
+ erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end);
+ enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+ enc424j600_write_16b_sfr(priv, ERXTAILL, end);
+}
+
+/*
+ * Hardware receive function.
+ * Read the buffer memory, update the FIFO pointer to free the buffer,
+ * check the status vector and decrement the packet counter.
+ */
+static void enc424j600_hw_rx(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ struct sk_buff *skb = NULL;
+ u16 erxrdpt, next_packet, rxstat;
+ u8 pkcnt;
+ u16 head, tail;
+ u8 rsv[RSV_SIZE];
+ u16 newrxtail;
+ int len;
+
+ if (netif_msg_rx_status(priv))
+ printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
+ priv->next_pk_ptr);
+ if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev,
+ "%s() Invalid packet address!! 0x%04x\n",
+ __func__, priv->next_pk_ptr);
+ mutex_lock(&priv->lock);
+ enc424j600_clear_bits(priv, ECON1L, RXEN);
+ enc424j600_set_bits(priv, ECON2L, RXRST);
+ enc424j600_clear_bits(priv, ECON2L, RXRST);
+ nolock_rxfifo_init(priv, RXSTART, RXEND_INIT);
+ enc424j600_clear_bits(priv, EIRL, RXABTIF);
+ enc424j600_set_bits(priv, ECON1L, RXEN);
+ mutex_unlock(&priv->lock);
+ ndev->stats.rx_errors++;
+ return;
+ }
+
+ /* Read next packet pointer and rx status vector */
+ enc424j600_read_sram(priv, rsv, sizeof(rsv), priv->next_pk_ptr,
+ RXWINDOW);
+
+ next_packet = rsv[1];
+ next_packet <<= 8;
+ next_packet |= rsv[0];
+
+ len = rsv[3];
+ len <<= 8;
+ len |= rsv[2];
+
+ rxstat = rsv[5];
+ rxstat <<= 8;
+ rxstat |= rsv[4];
+
+ if (netif_msg_rx_status(priv))
+ enc424j600_dump_rsv(priv, __func__, next_packet, len, rxstat);
+
+ if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
+ ndev->stats.rx_errors++;
+ if (RSV_GETBIT(rxstat, RSV_CRCERROR))
+ ndev->stats.rx_crc_errors++;
+ if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
+ ndev->stats.rx_frame_errors++;
+ if (len > MAX_FRAMELEN)
+ ndev->stats.rx_over_errors++;
+ } else {
+ skb = dev_alloc_skb(len + NET_IP_ALIGN);
+ if (!skb) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev,
+ "out of memory for Rx'd frame\n");
+ ndev->stats.rx_dropped++;
+ } else {
+ skb->dev = ndev;
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ /* copy the packet from the receive buffer */
+ enc424j600_read_sram(priv, skb_put(skb, len), len,
+ rx_packet_start(priv->next_pk_ptr),
+ RXWINDOW);
+
+ if (netif_msg_pktdata(priv))
+ dump_packet(__func__, skb->len, skb->data);
+ skb->protocol = eth_type_trans(skb, ndev);
+ /* update statistics */
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += len;
+ netif_rx_ni(skb);
+ }
+ }
+ newrxtail = next_packet - 2;
+ if (next_packet == RXSTART)
+ newrxtail = SRAMSIZE - 2;
+
+ enc424j600_write_16b_sfr(priv, ERXTAILL, newrxtail);
+ /*
+ * Move the RX read pointer to the start of the next
+ * received packet.
+ * This frees the memory we just read out
+ */
+ erxrdpt = erxrdpt_workaround(next_packet, RXSTART, RXEND_INIT);
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", __func__,
+ erxrdpt);
+
+ /*TODO: remove mutex_lock wherever not required*/
+ mutex_lock(&priv->lock);
+ enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+
+ priv->next_pk_ptr = next_packet;
+ enc424j600_read_8b_sfr(priv, ESTATL, &pkcnt);
+ enc424j600_read_16b_sfr(priv, ERXHEADL, &head);
+ enc424j600_read_16b_sfr(priv, ERXTAILL, &tail);
+ /* we are done with this packet, decrement the packet counter */
+ enc424j600_set_bits(priv, ECON1H, PKTDEC);
+
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * Access the PHY to determine link status
+ */
+static void enc424j600_check_link_status(struct enc424j600_net *priv)
+{
+ u8 estath;
+ u16 macon2;
+
+ enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+ if (estath & PHYLNK) {
+ if (priv->autoneg) {
+ if (!enc424j600_wait_for_autoneg(priv)) {
+ if (estath & PHYDPX) {
+ printk(KERN_ALERT "Full Duplex");
+ enc424j600_read_16b_sfr(priv, MACON2L,
+ &macon2);
+ macon2 |= FULDPX;
+ enc424j600_write_16b_sfr(priv, MACON2L,
+ macon2);
+ }
+ } else /*if timed out, disable autoneg and continue */
+ priv->autoneg = AUTONEG_DISABLE;
+ }
+ netif_carrier_on(priv->netdev);
+ if (netif_msg_ifup(priv))
+ dev_info(&(priv->netdev->dev), "link up\n");
+ } else {
+ if (netif_msg_ifdown(priv))
+ dev_info(&(priv->netdev->dev), "link down\n");
+ netif_carrier_off(priv->netdev);
+ }
+}
+
+static void enc424j600_tx_clear(struct enc424j600_net *priv, bool err)
+{
+ struct net_device *ndev = priv->netdev;
+ if (err)
+ ndev->stats.tx_errors++;
+ else
+ ndev->stats.tx_packets++;
+
+ if (priv->tx_skb) {
+ if (!err)
+ ndev->stats.tx_bytes += priv->tx_skb->len;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ netif_wake_queue(ndev);
+}
+
+static int enc424j600_int_rx_abbort_handler(struct enc424j600_net *priv,
+ int loop)
+{
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intRXAbt(%d)\n", loop);
+ mutex_lock(&priv->lock);
+ priv->netdev->stats.rx_dropped++;
+ enc424j600_clear_bits(priv, EIRL, RXABTIF);
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_link_handler(struct enc424j600_net *priv, int loop)
+{
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intLINK(%d)\n", loop);
+
+ /* we check more than is necessary here --
+ * only PHYLNK would be needed. */
+ enc424j600_check_link_status(priv);
+
+ return loop;
+}
+
+static int enc424j600_int_tx_handler(struct enc424j600_net *priv, int loop)
+{
+ loop++;
+
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intTX(%d)\n", loop);
+
+ mutex_lock(&priv->lock);
+ enc424j600_tx_clear(priv, false);
+ enc424j600_clear_bits(priv, EIRL, TXIF);
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_tx_err_handler(struct enc424j600_net *priv, int loop)
+{
+ u8 etxstat;
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intTXErr(%d)\n", loop);
+
+ mutex_lock(&priv->lock);
+
+ enc424j600_read_8b_sfr(priv, ETXSTATH, &etxstat);
+
+ if (etxstat & LATECOL) {
+ if (netif_msg_tx_err(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": Late collision TXErr (%d)\n",
+ priv->tx_retry_count);
+ if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
+ enc424j600_set_bits(priv, ECON1L, TXRTS);
+ else
+ enc424j600_tx_clear(priv, true);
+ } else if (etxstat & MAXCOL) {
+ if (netif_msg_tx_err(priv))
+ printk(KERN_DEBUG DRV_NAME ": Max collisions TXErr\n");
+ enc424j600_tx_clear(priv, true);
+ } else {
+ enc424j600_tx_clear(priv, true);
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_received_packet_handler(struct enc424j600_net *priv)
+{
+ uint8_t pk_counter;
+ int ret;
+
+ enc424j600_read_8b_sfr(priv, ESTATL, &pk_counter);
+ if (pk_counter && netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter);
+ if (pk_counter > priv->max_pk_counter) {
+ /* update statistics */
+ priv->max_pk_counter = pk_counter;
+ if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
+ printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n",
+ priv->max_pk_counter);
+ }
+ ret = pk_counter;
+ while (pk_counter-- > 0)
+ enc424j600_hw_rx(priv->netdev);
+ return ret;
+}
+
+static void enc424j600_irq_work_handler(struct work_struct *work)
+{
+
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, irq_work);
+ int loop;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ /* disable further interrupts */
+ enc424j600_clear_bits(priv, EIEH, INTIE);
+
+ do {
+ u16 intflags;
+ enc424j600_read_16b_sfr(priv, EIRL, &intflags);
+ loop = 0;
+
+ /* LINK changed handler */
+ if ((intflags & LINKIF) != 0)
+ loop = enc424j600_int_link_handler(priv, loop);
+
+ /* TX complete handler */
+ if ((intflags & TXIF) != 0)
+ loop = enc424j600_int_tx_handler(priv, loop);
+
+ /* TX Error handler */
+ if ((intflags & TXABTIF) != 0) {
+ printk(KERN_ALERT "ABORTING TRANSMITTING PACKET");
+ loop = enc424j600_int_tx_err_handler(priv, loop);
+ }
+ /* RX Error handler */
+ if ((intflags & RXABTIF) != 0) {
+ printk(KERN_ALERT "ABORTING RECEIVED PACKET");
+ loop = enc424j600_int_rx_abbort_handler(priv, loop);
+ }
+ /* RX handler */
+ if ((intflags & PKTIF) != 0)
+ loop = enc424j600_int_received_packet_handler(priv);
+ enc424j600_clear_bits(priv, EIRL, intflags && 0xff);
+ enc424j600_clear_bits(priv, EIRH, intflags >> 8);
+ } while (loop);
+ /* re-enable interrupts */
+ enc424j600_set_bits(priv, EIEH, INTIE);
+
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__);
+}
+
+void locked_reg_bfset(struct enc424j600_net *priv, u8 addr, u8 mask)
+{
+ mutex_lock(&priv->lock);
+ enc424j600_set_bits(priv, addr, mask);
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * Hardware transmit function.
+ * Fill the buffer memory and send the contents of the transmit buffer
+ * onto the network
+ */
+static void enc424j600_hw_tx(struct enc424j600_net *priv)
+{
+ if (!priv->tx_skb) {
+ enc424j600_tx_clear(priv, false);
+ return;
+ }
+
+ if (netif_msg_tx_queued(priv))
+ printk(KERN_DEBUG DRV_NAME ": Tx Packet Len:%d\n",
+ priv->tx_skb->len);
+
+ if (netif_msg_pktdata(priv))
+ dump_packet(__func__, priv->tx_skb->len, priv->tx_skb->data);
+
+ enc424j600_write_sram(priv, priv->tx_skb->data, priv->tx_skb->len,
+ TXSTART, GPWINDOW);
+
+ /* Set the tx pointer to start of general purpose SRAM area */
+ enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+
+ /* Write the transfer length */
+ enc424j600_write_16b_sfr(priv, ETXLENL, priv->tx_skb->len);
+
+ /* set TX request flag */
+ locked_reg_bfset(priv, ECON1L, TXRTS);
+}
+
+static int enc424j600_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+ if (netif_msg_tx_queued(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ /* If some error occurs while trying to transmit this
+ * packet, you should return '1' from this function.
+ * In such a case you _may not_ do anything to the
+ * SKB, it is still owned by the network queueing
+ * layer when an error is returned. This means you
+ * may not modify any SKB fields, you may not free
+ * the SKB, etc.
+ */
+ netif_stop_queue(dev);
+
+ /* save the timestamp */
+ priv->netdev->trans_start = jiffies;
+ /* Remember the skb for deferred processing */
+ priv->tx_skb = skb;
+ schedule_work(&priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static void enc424j600_tx_work_handler(struct work_struct *work)
+{
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, tx_work);
+
+ /* actual delivery of data */
+ enc424j600_hw_tx(priv);
+}
+
+static irqreturn_t enc424j600_irq(int irq, void *dev_id)
+{
+ struct enc424j600_net *priv = dev_id;
+ /*
+ * Can't do anything in interrupt context because we need to
+ * block (spi_sync() is blocking) so fire of the interrupt
+ * handling workqueue.
+ * Remember that we access enc424j600 registers through SPI bus
+ * via spi_sync() call.
+ */
+ schedule_work(&priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void enc424j600_tx_timeout(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+
+ if (netif_msg_timer(priv))
+ dev_err(&ndev->dev, DRV_NAME " tx timeout\n");
+
+ ndev->stats.tx_errors++;
+ /* can't restart safely under softirq */
+ schedule_work(&priv->restart_work);
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int enc424j600_net_open(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ if (netif_msg_ifup(priv))
+ dev_err(&dev->dev, "invalid MAC address %pM\n",
+ dev->dev_addr);
+ return -EADDRNOTAVAIL;
+ }
+ /* Reset the hardware here (and take it out of low power mode) */
+ enc424j600_lowpower(priv, false);
+ enc424j600_hw_disable(priv);
+ if (!enc424j600_hw_init(priv)) {
+ if (netif_msg_ifup(priv))
+ dev_err(&dev->dev, "hw_reset() failed\n");
+ return -EINVAL;
+ }
+ /* Update the MAC address (in case user has changed it) */
+ enc424j600_set_hw_macaddr(dev);
+ /* Enable interrupts */
+ enc424j600_hw_enable(priv);
+ /* check link status */
+ enc424j600_check_link_status(priv);
+ /* We are now ready to accept transmit requests from
+ * the queueing layer of the networking.
+ */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/* The inverse routine to net_open(). */
+static int enc424j600_net_close(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ enc424j600_hw_disable(priv);
+ enc424j600_lowpower(priv, true);
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adapter
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, filter out multicast packets
+ * num_addrs > 0 Multicast mode, receive normal and MC packets
+ */
+static void enc424j600_set_multicast_list(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+ int oldfilter = priv->rxfilter;
+
+ if (dev->flags & IFF_PROMISC) {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "promiscuous mode\n");
+ priv->rxfilter = RXFILTER_PROMISC;
+ } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count) {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "%smulticast mode\n",
+ (dev->flags & IFF_ALLMULTI) ? "all-" : "");
+ priv->rxfilter = RXFILTER_MULTI;
+ } else {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "normal mode\n");
+ priv->rxfilter = RXFILTER_NORMAL;
+ }
+
+ if (oldfilter != priv->rxfilter)
+ schedule_work(&priv->setrx_work);
+}
+
+void locked_regb_write(struct enc424j600_net *priv, u8 address, u8 data)
+{
+ mutex_lock(&priv->lock);
+ enc424j600_write_8b_sfr(priv, address, data);
+ mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_setrx_work_handler(struct work_struct *work)
+{
+ u16 macon1;
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, setrx_work);
+
+ if (priv->rxfilter == RXFILTER_PROMISC) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n");
+ enc424j600_read_16b_sfr(priv, MACON1L, &macon1);
+ macon1 = macon1 | PASSALL;
+ enc424j600_write_16b_sfr(priv, MACON1L, macon1);
+ locked_regb_write(priv, ERXFCONL, UCEN | MCEN | NOTMEEN);
+ } else if (priv->rxfilter == RXFILTER_MULTI) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": multicast mode\n");
+ locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN | MCEN);
+
+ } else {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": normal mode\n");
+ locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN);
+
+ }
+}
+
+static void enc424j600_restart_work_handler(struct work_struct *work)
+{
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, restart_work);
+ struct net_device *ndev = priv->netdev;
+ int ret;
+
+ rtnl_lock();
+ if (netif_running(ndev)) {
+ enc424j600_net_close(ndev);
+ ret = enc424j600_net_open(ndev);
+ if (unlikely(ret)) {
+ dev_info(&ndev->dev, " could not restart %d\n", ret);
+ dev_close(ndev);
+ }
+ }
+ rtnl_unlock();
+}
+
+/* ......................... ETHTOOL SUPPORT ........................... */
+
+static void
+enc424j600_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info,
+ dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int
+enc424j600_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->supported = SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_TP;
+
+ cmd->speed = priv->speed100 ? SPEED_100 : SPEED_10;
+ cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+ cmd->port = PORT_TP;
+ cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+ return 0;
+}
+
+static int
+enc424j600_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ return enc424j600_setlink(dev, cmd->autoneg, cmd->speed, cmd->duplex);
+}
+
+static u32 enc424j600_get_msglevel(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ return priv->msg_enable;
+}
+
+static void enc424j600_set_msglevel(struct net_device *dev, u32 val)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ priv->msg_enable = val;
+}
+
+static const struct ethtool_ops enc424j600_ethtool_ops = {
+ .get_settings = enc424j600_get_settings,
+ .set_settings = enc424j600_set_settings,
+ .get_drvinfo = enc424j600_get_drvinfo,
+ .get_msglevel = enc424j600_get_msglevel,
+ .set_msglevel = enc424j600_set_msglevel,
+};
+
+static int enc424j600_chipset_init(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ enc424j600_get_hw_macaddr(dev);
+ return enc424j600_hw_init(priv);
+
+}
+
+static const struct net_device_ops enc424j600_netdev_ops = {
+ .ndo_open = enc424j600_net_open,
+ .ndo_stop = enc424j600_net_close,
+ .ndo_start_xmit = enc424j600_send_packet,
+ .ndo_set_multicast_list = enc424j600_set_multicast_list,
+ .ndo_set_mac_address = enc424j600_set_mac_address,
+ .ndo_tx_timeout = enc424j600_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __devinit enc424j600_probe(struct spi_device *spi)
+{
+ struct net_device *dev;
+ struct enc424j600_net *priv;
+ int ret = 0;
+
+ if (netif_msg_drv(&debug))
+ dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
+ DRV_VERSION);
+
+ dev = alloc_etherdev(sizeof(struct enc424j600_net));
+ if (!dev) {
+ if (netif_msg_drv(&debug))
+ dev_err(&spi->dev, DRV_NAME
+ ": unable to alloc new ethernet\n");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+ priv = netdev_priv(dev);
+
+ priv->netdev = dev; /* priv to netdev reference */
+ priv->spi = spi; /* priv to spi reference */
+ priv->msg_enable = netif_msg_init(debug.msg_enable,
+ ENC424J600_MSG_DEFAULT);
+ mutex_init(&priv->lock);
+ INIT_WORK(&priv->tx_work, enc424j600_tx_work_handler);
+ INIT_WORK(&priv->setrx_work, enc424j600_setrx_work_handler);
+ INIT_WORK(&priv->irq_work, enc424j600_irq_work_handler);
+ INIT_WORK(&priv->restart_work, enc424j600_restart_work_handler);
+ dev_set_drvdata(&spi->dev, priv); /* spi to priv reference */
+ SET_NETDEV_DEV(dev, &spi->dev);
+ /*TODO: chip DMA features to be utilized */
+ /* If requested, allocate DMA buffers */
+ if (enc424j600_enable_dma) {
+ spi->dev.coherent_dma_mask = ~0;
+
+ /*
+ * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ * that much and share it between Tx and Rx DMA buffers.
+ */
+#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2
+#error "A problem in DMA buffer allocation"
+#endif
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE,
+ &priv->spi_tx_dma,
+ GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *) (priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ enc424j600_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!enc424j600_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_rx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (!enc424j600_chipset_init(dev)) {
+ if (netif_msg_probe(priv))
+ dev_info(&spi->dev, DRV_NAME " chip not found\n");
+ ret = -EIO;
+ goto error_irq;
+ }
+
+ /* Board setup must set the relevant edge trigger type;
+ * level triggers won't currently work.
+ */
+ ret = request_irq(spi->irq, enc424j600_irq, 0, DRV_NAME, priv);
+ if (ret < 0) {
+ if (netif_msg_probe(priv))
+ dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
+ "(ret = %d)\n", spi->irq, ret);
+ goto error_irq;
+ }
+
+ dev->if_port = IF_PORT_10BASET;
+ dev->irq = spi->irq;
+ dev->netdev_ops = &enc424j600_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ SET_ETHTOOL_OPS(dev, &enc424j600_ethtool_ops);
+
+ enc424j600_lowpower(priv, true);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ if (netif_msg_probe(priv))
+ dev_err(&spi->dev, "register netdev " DRV_NAME
+ " failed (ret = %d)\n", ret);
+ goto error_register;
+ }
+ dev_info(&dev->dev, DRV_NAME " driver registered\n");
+
+ return 0;
+
+error_register:
+ free_irq(spi->irq, priv);
+error_irq:
+ free_netdev(dev);
+ if (!enc424j600_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!enc424j600_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ if (enc424j600_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ return ret;
+}
+
+static int __devexit enc424j600_remove(struct spi_device *spi)
+{
+ struct enc424j600_net *priv = dev_get_drvdata(&spi->dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": remove\n");
+
+ unregister_netdev(priv->netdev);
+ free_irq(spi->irq, priv);
+ free_netdev(priv->netdev);
+
+ return 0;
+}
+
+static struct spi_driver enc424j600_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = enc424j600_probe,
+ .remove = __devexit_p(enc424j600_remove),
+};
+
+static int __init enc424j600_init(void)
+{
+ msec20_to_jiffies = msecs_to_jiffies(20);
+ /*autoneg works from 1600ms */
+ msec2000_to_jiffies = msecs_to_jiffies(2000);
+
+ return spi_register_driver(&enc424j600_driver);
+}
+
+module_init(enc424j600_init);
+
+static void __exit enc424j600_exit(void)
+{
+ spi_unregister_driver(&enc424j600_driver);
+}
+
+module_exit(enc424j600_exit);
+
+MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
+MODULE_AUTHOR("Balaji Venkatachalam <balaji.v@thotakaa.com>");
+MODULE_LICENSE("GPL");
+module_param_named(debug, debug.msg_enable, int, 0);
+MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
+module_param(enc424j600_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(enc424j600_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+MODULE_ALIAS("spi:" DRV_NAME);
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600_hw.h
b/drivers/net/enc424j600_hw.h
--- a/drivers/net/enc424j600_hw.h 1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600_hw.h 2011-01-05 23:51:24.000000000 +0530
@@ -0,0 +1,460 @@
+/*
+* enc424j600_hw.h: Register definitions
+*
+*/
+
+#ifndef _ENC424J600_HW_H
+#define _ENC424J600_HW_H
+
+/*
+* ENC424J600 Control Registers
+* Control register definitions are a combination of address
+* and bank number
+* - Register address (bits 0-4)
+* - Bank number (bits 5-6)
+*/
+#define ADDR_MASK 0x1F
+#define BANK_MASK 0x60
+#define BANK_SHIFT 5
+
+/* All-bank registers */
+#define EUDASTL 0x16
+#define EUDASTH 0x17
+#define EUDANDL 0x18
+#define EUDANDH 0x19
+#define ESTATL 0x1A
+#define ESTATH 0x1B
+#define EIRL 0x1C
+#define EIRH 0x1D
+#define ECON1L 0x1E
+#define ECON1H 0x1F
+
+/* Bank 0 registers */
+#define ETXSTL (0x00 | 0x00)
+#define ETXSTH (0x01 | 0x00)
+#define ETXLENL (0x02 | 0x00)
+#define ETXLENH (0x03 | 0x00)
+#define ERXSTL (0x04 | 0x00)
+#define ERXSTH (0x05 | 0x00)
+#define ERXTAILL (0x06 | 0x00)
+#define ERXTAILH (0x07 | 0x00)
+#define ERXHEADL (0x08 | 0x00)
+#define ERXHEADH (0x09 | 0x00)
+#define EDMASTL (0x0A | 0x00)
+#define EDMASTH (0x0B | 0x00)
+#define EDMALENL (0x0C | 0x00)
+#define EDMALENH (0x0D | 0x00)
+#define EDMADSTL (0x0E | 0x00)
+#define EDMADSTH (0x0F | 0x00)
+#define EDMACSL (0x10 | 0x00)
+#define EDMACSH (0x11 | 0x00)
+#define ETXSTATL (0x12 | 0x00)
+#define ETXSTATH (0x13 | 0x00)
+#define ETXWIREL (0x14 | 0x00)
+#define ETXWIREH (0x15 | 0x00)
+
+/* Bank 1 registers */
+#define EHT1L (0x00 | 0x20)
+#define EHT1H (0x01 | 0x20)
+#define EHT2L (0x02 | 0x20)
+#define EHT2H (0x03 | 0x20)
+#define EHT3L (0x04 | 0x20)
+#define EHT3H (0x05 | 0x20)
+#define EHT4L (0x06 | 0x20)
+#define EHT4H (0x07 | 0x20)
+#define EPMM1L (0x08 | 0x20)
+#define EPMM1H (0x09 | 0x20)
+#define EPMM2L (0x0A | 0x20)
+#define EPMM2H (0x0B | 0x20)
+#define EPMM3L (0x0C | 0x20)
+#define EPMM3H (0x0D | 0x20)
+#define EPMM4L (0x0E | 0x20)
+#define EPMM4H (0x0F | 0x20)
+#define EPMCSL (0x10 | 0x20)
+#define EPMCSH (0x11 | 0x20)
+#define EPMOL (0x12 | 0x20)
+#define EPMOH (0x13 | 0x20)
+#define ERXFCONL (0x14 | 0x20)
+#define ERXFCONH (0x15 | 0x20)
+
+/* Bank 2 registers */
+#define MACON1L (0x00 | 0x40)
+#define MACON1H (0x01 | 0x40)
+#define MACON2L (0x02 | 0x40)
+#define MACON2H (0x03 | 0x40)
+#define MABBIPGL (0x04 | 0x40)
+#define MABBIPGH (0x05 | 0x40)
+#define MAIPGL (0x06 | 0x40)
+#define MAIPGH (0x07 | 0x40)
+#define MACLCONL (0x08 | 0x40)
+#define MACLCONH (0x09 | 0x40)
+#define MAMXFLL (0x0A | 0x40)
+#define MAMXFLH (0x0B | 0x40)
+#define MICMDL (0x12 | 0x40)
+#define MICMDH (0x13 | 0x40)
+#define MIREGADRL (0x14 | 0x40)
+#define MIREGADRH (0x15 | 0x40)
+
+/* Bank 3 registers */
+#define MAADR3L (0x00 | 0x60)
+#define MAADR3H (0x01 | 0x60)
+#define MAADR2L (0x02 | 0x60)
+#define MAADR2H (0x03 | 0x60)
+#define MAADR1L (0x04 | 0x60)
+#define MAADR1H (0x05 | 0x60)
+#define MIWRL (0x06 | 0x60)
+#define MIWRH (0x07 | 0x60)
+#define MIRDL (0x08 | 0x60)
+#define MIRDH (0x09 | 0x60)
+#define MISTATL (0x0A | 0x60)
+#define MISTATH (0x0B | 0x60)
+#define EPAUSL (0x0C | 0x60)
+#define EPAUSH (0x0D | 0x60)
+#define ECON2L (0x0E | 0x60)
+#define ECON2H (0x0F | 0x60)
+#define ERXWML (0x10 | 0x60)
+#define ERXWMH (0x11 | 0x60)
+#define EIEL (0x12 | 0x60)
+#define EIEH (0x13 | 0x60)
+#define EIDLEDL (0x14 | 0x60)
+#define EIDLEDH (0x15 | 0x60)
+
+/* Unbanked registers */
+#define EGPDATA (0x00 | 0x80)
+#define ERXDATA (0x02 | 0x80)
+#define EUDADATA (0x04 | 0x80)
+#define EGPRDPTL (0x06 | 0x80)
+#define EGPRDPTH (0x07 | 0x80)
+#define EGPWRPTL (0x08 | 0x80)
+#define EGPWRPTH (0x09 | 0x80)
+#define ERXRDPTL (0x0A | 0x80)
+#define ERXRDPTH (0x0B | 0x80)
+#define ERXWRPTL (0x0C | 0x80)
+#define ERXWRPTH (0x0D | 0x80)
+#define EUDARDPTL (0x0E | 0x80)
+#define EUDARDPTH (0x0F | 0x80)
+#define EUDAWRPTL (0x10 | 0x80)
+#define EUDAWRPTH (0x11 | 0x80)
+
+/* PHY registers */
+#define PHCON1 0x00
+#define PHSTAT1 0x01
+#define PHANA 0x04
+#define PHANLPA 0x05
+#define PHANE 0x06
+#define PHCON2 0x11
+#define PHSTAT2 0x1B
+#define PHSTAT3 0x1F
+
+/* Single-byte instructions */
+#define BXSEL(bank) (0xC0 + (bank & (BANK_MASK >> BANK_SHIFT)) * 2)
+/* Bank X Select */
+#define B0SEL 0xC0 /* Bank 0 Select */
+#define B1SEL 0xC2 /* Bank 1 Select */
+#define B2SEL 0xC4 /* Bank 2 Select */
+#define B3SEL 0xC6 /* Bank 3 Select */
+#define SETETHRST 0xCA /* System Reset */
+#define FCDISABLE 0xE0 /* Flow Control Disable */
+#define FCSINGLE 0xE2 /* Flow Control Single */
+#define FCMULTIPLE 0xE4 /* Flow Control Multiple */
+#define FCCLEAR 0xE6 /* Flow Control Clear */
+#define SETPKTDEC 0xCC /* Decrement Packet Counter */
+#define DMASTOP 0xD2 /* DMA Stop */
+#define DMACKSUM 0xD8 /* DMA Start Checksum */
+#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */
+#define DMACOPY 0xDC /* DMA Start Copy */
+#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */
+#define SETTXRTS 0xD4 /* Request Packet Transmission */
+#define ENABLERX 0xE8 /* Enable RX */
+#define DISABLERX 0xEA /* Disable RX */
+#define SETEIE 0xEC /* Enable Interrupts */
+#define CLREIE 0xEE /* Disable Interrupts */
+
+/* Two byte instructions */
+#define RBSEL 0xC8 /* Read Bank Select */
+
+/* Three byte instructions */
+#define WGPRDPT 0x60 /* Write EGPRDPT */
+#define RGPRDPT 0x62 /* Read EGPRDPT */
+#define WRXRDPT 0x64 /* Write ERXRDPT */
+#define RRXRDPT 0x66 /* Read ERXRDPT */
+#define WUDARDPT 0x68 /* Write EUDARDPT */
+#define RUDARDPT 0x6A /* Read EUDARDPT */
+#define WGPWRPT 0x6C /* Write EGPWRPT */
+#define RGPWRPT 0x6E /* Read EGPWRPT */
+#define WRXWRPT 0x70 /* Write ERXWRPT */
+#define RRXWRPT 0x72 /* Read ERXWRPT */
+#define WUDAWRPT 0x74 /* Write EUDAWRPT */
+#define RUDAWRPT 0x76 /* Read EUDAWRPT */
+
+/* n byte instructions */
+#define RCR(addr) (0x00 | (addr & ADDR_MASK)) /* Read Control Register */
+#define WCR(addr) (0x40 | (addr & ADDR_MASK)) /* Write Control Register */
+#define RCRU 0x20 /* Read Control Register Unbanked */
+#define WCRU 0x22 /* Write Control Register Unbanked */
+#define BFS(addr) (0x80 | (addr & ADDR_MASK)) /* Bit Field Set */
+#define BFC(addr) (0xA0 | (addr & ADDR_MASK)) /* Bit Field Clear */
+#define BFSU 0x24 /* Bit Field Set Unbanked */
+#define BFCU 0x26 /* Bit Field Clear Unbanked */
+#define RGPDATA 0x28 /* Read EGPDATA */
+#define WGPDATA 0x2A /* Write EGPDATA */
+#define RRXDATA 0x2C /* Read ERXDATA */
+#define WRXDATA 0x2E /* Write ERXDATA */
+#define RUDADATA 0x30 /* Read EUDADATA */
+#define WUDADATA 0x32 /* Write EUDADATA */
+
+/* Register bit definitions */
+/* ESTATH */
+#define INT (1 << 7)
+#define FCIDLE (1 << 6)
+#define RXBUSY (1 << 5)
+#define CLKRDY (1 << 4)
+#define PHYDPX (1 << 2)
+#define PHYLNK (1 << 0)
+
+/* EIRH */
+/*for ease of use lets access it as a word*/
+#define CRYPTEN (1 << 15)
+#define MODEXIF (1 << 14)
+#define HASHIF (1 << 13)
+#define AESIF (1 << 12)
+#define LINKIF (1 << 11)
+
+/* EIRL */
+#define PKTIF (1 << 6)
+#define DMAIF (1 << 5)
+#define TXIF (1 << 3)
+#define TXABTIF (1 << 2)
+#define RXABTIF (1 << 1)
+#define PCFULIF (1 << 0)
+
+/* ECON1H */
+#define MODEXST (1 << 7)
+#define HASHEN (1 << 6)
+#define HASHOP (1 << 5)
+#define HASHLST (1 << 4)
+#define AESST (1 << 3)
+#define AESOP1 (1 << 2)
+#define AESOP0 (1 << 1)
+#define PKTDEC (1 << 0)
+
+/* ECON1L */
+#define FCOP1 (1 << 7)
+#define FCOP0 (1 << 6)
+#define DMAST (1 << 5)
+#define DMACPY (1 << 4)
+#define DMACSSD (1 << 3)
+#define DMANOCS (1 << 2)
+#define TXRTS (1 << 1)
+#define RXEN (1 << 0)
+
+/* ETXSTATH */
+#define LATECOL (1 << 2)
+#define MAXCOL (1 << 1)
+#define EXDEFER (1 << 0)
+
+/* ETXSTATL */
+#define ETXSTATL_DEFER (1 << 7)
+#define CRCBAD (1 << 4)
+#define COLCNT_MASK 0xF
+
+/* ERXFCONH */
+#define HTEN (1 << 7)
+#define MPEN (1 << 6)
+#define NOTPM (1 << 4)
+#define PMEN3 (1 << 3)
+#define PMEN2 (1 << 2)
+#define PMEN1 (1 << 1)
+#define PMEN0 (1 << 0)
+
+/* ERXFCONL */
+#define CRCEEN (1 << 7)
+#define CRCEN (1 << 6)
+#define RUNTEEN (1 << 5)
+#define RUNTEN (1 << 4)
+#define UCEN (1 << 3)
+#define NOTMEEN (1 << 2)
+#define MCEN (1 << 1)
+#define BCEN (1 << 0)
+/*no bytewise access*/
+/* MACON1L */
+#define LOOPBK (1 << 4)
+#define RXPAUS (1 << 2)
+#define PASSALL (1 << 1)
+
+/* MACON2 */
+#define MACON2_DEFER (1 << 14)
+#define BPEN (1 << 13)
+#define NOBKOFF (1 << 12)
+#define PADCFG2 (1 << 7)
+#define PADCFG1 (1 << 6)
+#define PADCFG0 (1 << 5)
+#define TXCRCEN (1 << 4)
+#define PHDREN (1 << 3)
+#define HFRMEN (1 << 2)
+#define FULDPX (1 << 0)
+
+/* MAIPG */
+/* value of the high byte is given by the reserved bits,
+* value of the low byte is recomended setting of the
+* IPG parameter.
+*/
+#define MAIPGH_VAL 0x0C
+#define MAIPGL_VAL 0x12
+
+/* MIREGADRH */
+#define MIREGADRH_VAL 0x01
+
+/* MIREGADRL */
+#define PHREG_MASK 0x1F
+
+/* MICMDL */
+#define MIISCAN (1 << 1)
+#define MIIRD (1 << 0)
+
+/* MISTATL */
+#define NVALID (1 << 2)
+#define SCAN (1 << 1)
+#define BUSY (1 << 0)
+
+/* ECON2H */
+#define ETHEN (1 << 7)
+#define STRCH (1 << 6)
+#define TXMAC (1 << 5)
+#define SHA1MD5 (1 << 4)
+#define COCON3 (1 << 3)
+#define COCON2 (1 << 2)
+#define COCON1 (1 << 1)
+#define COCON0 (1 << 0)
+
+/* ECON2L */
+#define AUTOFC (1 << 7)
+#define TXRST (1 << 6)
+#define RXRST (1 << 5)
+#define ETHRST (1 << 4)
+#define MODLEN1 (1 << 3)
+#define MODLEN0 (1 << 2)
+#define AESLEN1 (1 << 1)
+#define AESLEN0 (1 << 0)
+
+/* EIEH */
+#define INTIE (1 << 7)
+#define MODEXIE (1 << 6)
+#define HASHIE (1 << 5)
+#define AESIE (1 << 4)
+#define LINKIE (1 << 3)
+
+/* EIEL */
+#define PKTIE (1 << 6)
+#define DMAIE (1 << 5)
+#define TXIE (1 << 3)
+#define TXABTIE (1 << 2)
+#define RXABTIE (1 << 1)
+#define PCFULIE (1 << 0)
+
+/* EIDLEDH */
+#define LACFG3 (1 << 7)
+#define LACFG2 (1 << 6)
+#define LACFG1 (1 << 5)
+#define LACFG0 (1 << 4)
+#define LBCFG3 (1 << 3)
+#define LBCFG2 (1 << 2)
+#define LBCFG1 (1 << 1)
+#define LBCFG0 (1 << 0)
+
+/* EIDLEDL */
+#define DEVID_SHIFT 5
+#define DEVID_MASK (0x7 << DEVID_SHIFT)
+#define REVID_SHIFT 0
+#define REVID_MASK (0x1F << REVID_SHIFT)
+
+/* PHANA */
+/* Default value for PHY initialization*/
+#define PHANA_DEFAULT 0x05E1
+
+/* PHCON1 */
+#define PRST (1 << 15)
+#define PLOOPBK (1 << 14)
+#define SPD100 (1 << 13)
+#define ANEN (1 << 12)
+#define PSLEEP (1 << 11)
+#define RENEG (1 << 9)
+#define PFULDPX (1 << 8)
+
+/* PHSTAT */
+#define FULL100 (1 << 14)
+#define HALF100 (1 << 13)
+#define FULL10 (1 << 12)
+#define HALF10 (1 << 11)
+#define ANDONE (1 << 5)
+#define LRFAULT (1 << 4)
+#define ANABLE (1 << 3)
+#define LLSTAT (1 << 2)
+#define EXTREGS (1 << 0)
+
+#define EUDAST_TEST_VAL 0x1234
+
+#define TSV_SIZE 7
+
+#define ENC424J600_DEV_ID 0x1
+
+/* Configuration */
+
+/* Led is on when the link is present and driven low
+* temporarily when packet is TX'd or RX'd */
+#define LED_A_SETTINGS 0xC
+
+/* Led is on if the link is in 100 Mbps mode */
+#define LED_B_SETTINGS 0x8
+
+/* maximum ethernet frame length
+* Currently not used as a limit anywhere
+* (we're using the "huge frame enable" feature of
+* enc424j600). */
+#define MAX_FRAMELEN 1518
+
+/* Size in bytes of the receive buffer in enc424j600.
+* Must be word aligned (even).
+*/
+#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN)
+
+/* Start of the general purpose area in sram */
+#define SRAM_GP_START 0x0
+
+/* SRAM size */
+#define SRAM_SIZE 0x6000
+
+/* Start of the receive buffer */
+#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE)
+
+#define RSV_RXLONGEVDROPEV 16
+#define RSV_CARRIEREV 18
+#define RSV_CRCERROR 20
+#define RSV_LENCHECKERR 21
+#define RSV_LENOUTOFRANGE 22
+#define RSV_RXOK 23
+#define RSV_RXMULTICAST 24
+#define RSV_RXBROADCAST 25
+#define RSV_DRIBBLENIBBLE 26
+#define RSV_RXCONTROLFRAME 27
+#define RSV_RXPAUSEFRAME 28
+#define RSV_RXUNKNOWNOPCODE 29
+#define RSV_RXTYPEVLAN 30
+
+#define RSV_RUNTFILTERMATCH 31
+#define RSV_NOTMEFILTERMATCH 32
+#define RSV_HASHFILTERMATCH 33
+#define RSV_MAGICPKTFILTERMATCH 34
+#define RSV_PTRNMTCHFILTERMATCH 35
+#define RSV_UNICASTFILTERMATCH 36
+
+#define RSV_SIZE 8
+#define RSV_BITMASK(x) (1 << ((x) - 16))
+#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0)
+
+/* Put RX buffer at 0 as suggested by the Errata datasheet */
+
+#define RXSTART_INIT ERXST_VAL
+#define RXEND_INIT 0x5FFF
+
+#endif
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Kconfig
b/drivers/net/Kconfig
--- a/drivers/net/Kconfig 2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Kconfig 2011-01-16 15:26:16.000000000 +0530
@@ -973,6 +973,16 @@ config ENC28J60_WRITEVERIFY
Enable the verify after the buffer write useful for debugging purpose.
If unsure, say N.
+config ENC424J600
+ tristate "ENC424J600 support"
+ depends on EXPERIMENTAL && SPI && NET_ETHERNET
+ select CRC32
+ ---help---
+ Support for the Microchip EN424J600 ethernet chip.
+
+ To compile this driver as a module, choose M here. The module will be
+ called enc424j600.
+
config ETHOC
tristate "OpenCores 10/100 Mbps Ethernet MAC support"
depends on NET_ETHERNET && HAS_IOMEM
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Makefile
b/drivers/net/Makefile
--- a/drivers/net/Makefile 2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Makefile 2011-01-05 21:46:57.000000000 +0530
@@ -240,6 +240,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_d
pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ENC424J600) += enc424j600.o
obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
^ permalink raw reply
* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Balaji Venkatachalam @ 2011-01-29 8:24 UTC (permalink / raw)
To: David Miller; +Cc: netdev, mirqus, mohan, blue.cube, lanconelli.claudio, sriram
In-Reply-To: <20110124.150450.48527021.davem@davemloft.net>
From: Balaji Venkatachalam <balaji.v@thotakaa.com>
Updated patch for Microchip enc424j600 ethernet chip controlled via SPI.
I tested it on my custom board with ARM9 (Freescale i.MX233) with
Kernel 2.6.31.14.
Changes done since V1.27 to V1.28
1. did some code formatting at line no 110
Changes done since V1.24 to V1.27
1. Timeout Mechanism implemented for enc424j600_soft_reset function
2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
3. Window Naming changed to enum
4. Removed WRITEVERIFY functionality
Todo List:
1. Low Power Mode Functionality implementation
2. Provide Support for On-Chip DMA.
3. Remove mutex_lock wherever not required
Any comments are welcome.
Signed-off-by: Balaji Venkatachalam <balaji.v@thotakaa.com>
---
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600.c
b/drivers/net/enc424j600.c
--- a/drivers/net/enc424j600.c 1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600.c 2011-01-29 13:26:31.000000000 +0530
@@ -0,0 +1,1712 @@
+/*
+ * Microchip ENC424J600 ethernet driver (MAC + PHY) on SPI bus
+ *
+ * Copyright (C) 2011 Thotaka Technologies Pvt Ltd
+ * Author: Balaji Venkatachalam <balaji.v@thotakaa.com>
+ * based on enc424j600.c written by Kuba Marek
+ * based on enc28j60.c written by Claudio Lanconelli
+ *
+ * Changes done since V1.27 to V1.28
+ * 1. did some code formatting at line no 110
+ *
+ * Changes done since V1.24 to V1.27
+ * 1. Timeout Mechanism implemented for enc424j600_soft_reset function
+ * 2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
+ * 3. Window Naming changed to enum
+ * 4. Removed WRITEVERIFY functionality
+ *
+ * Todo List:
+ * 1. Low Power Mode Functionality implementation
+ * 2. Provide Support for On-Chip DMA
+ * 3. Remove mutex_lock wherever not required
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include "enc424j600_hw.h"
+
+#define DRV_NAME "enc424j600"
+#define DRV_VERSION "1.28"
+
+#define ENC424J600_MSG_DEFAULT \
+ (NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
+
+#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN)
+#define TX_TIMEOUT (4 * HZ)
+
+/* Max TX retries in case of collision as suggested by errata datasheet */
+#define MAX_TX_RETRYCOUNT 16
+
+#define SPI_OPLEN 1
+
+#define SRAMSIZE 0x6000
+#define TXSTART 0x0000
+#define RXSTART 0x1600
+
+static int enc424j600_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+
+enum {
+ RXFILTER_NORMAL,
+ RXFILTER_MULTI,
+ RXFILTER_PROMISC
+};
+enum {
+ RXWINDOW,
+ USERWINDOW,
+ GPWINDOW
+};
+
+/* Driver local data */
+struct enc424j600_net {
+ struct net_device *netdev;
+ struct spi_device *spi;
+ struct mutex lock;
+ struct sk_buff *tx_skb;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct work_struct setrx_work;
+ struct work_struct restart_work;
+ u8 bank; /* current register bank selected */
+ u16 next_pk_ptr; /* next packet pointer within FIFO */
+ u16 max_pk_counter; /* statistics: max packet counter */
+ u16 tx_retry_count;
+ bool hw_enable;
+ bool full_duplex;
+ bool autoneg;
+ bool speed100;
+ int rxfilter;
+ u32 msg_enable;
+
+ u8 *spi_rx_buf;
+ u8 *spi_tx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+};
+
+/* use ethtool to change the level for any given device */
+static struct {
+ u32 msg_enable;
+} debug = { -1 };
+
+static int enc424j600_spi_trans(struct enc424j600_net *priv, int len)
+{
+ /*modified to suit half duplexed spi */
+ struct spi_transfer tt = {
+ .tx_buf = priv->spi_tx_buf,
+ .len = SPI_OPLEN,
+ };
+ struct spi_transfer tr = {
+ .rx_buf = priv->spi_rx_buf,
+ .len = len,
+ };
+ struct spi_message m;
+ int ret;
+
+ spi_message_init(&m);
+
+ spi_message_add_tail(&tt, &m);
+ spi_message_add_tail(&tr, &m);
+
+ ret = spi_sync(priv->spi, &m);
+
+ if (ret == 0)
+ memcpy(priv->spi_rx_buf, tr.rx_buf, len);
+
+ if (ret)
+ dev_err(&priv->spi->dev,
+ "spi transfer failed: ret = %d\n", ret);
+ return ret;
+}
+
+/*
+ * Read data from chip SRAM.
+ * window = 0 for Receive Buffer
+ * = 1 for User Defined area
+ * = 2 for General Purpose area
+ */
+static int enc424j600_read_sram(struct enc424j600_net *priv,
+ u8 *dst, int len, u16 srcaddr, int window)
+{
+ int ret;
+
+ if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+ return -EINVAL;
+
+ /* First set the write pointer as per selected window */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXRDPT;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDARDPT;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPRDPT;
+
+ priv->spi_tx_buf[1] = srcaddr & 0xFF;
+ priv->spi_tx_buf[2] = srcaddr >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+ /* Transfer the data */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = RRXDATA;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = RUDADATA;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = RGPDATA;
+
+ ret = enc424j600_spi_trans(priv, len + 1);
+ /*READ*/
+ /* Copy the data from the rx buffer */
+ memcpy(dst, &priv->spi_rx_buf[0], len);
+
+ return ret;
+}
+
+/*
+ * Write data to chip SRAM.
+ * window = 1 for RX
+ * window = 2 for User Data
+ * window = 3 for GP
+ */
+static int enc424j600_write_sram(struct enc424j600_net *priv,
+ const u8 *src, int len, u16 dstaddr,
+ int window)
+{
+ int ret;
+
+ if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+ return -EINVAL;
+
+ /* First set the general purpose write pointer */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXWRPT;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDAWRPT;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPWRPT;
+
+ priv->spi_tx_buf[1] = dstaddr & 0xFF;
+ priv->spi_tx_buf[2] = dstaddr >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+ /* Copy the data to the tx buffer */
+ memcpy(&priv->spi_tx_buf[1], src, len);
+
+ /* Transfer the data */
+ if (window == RXWINDOW)
+ priv->spi_tx_buf[0] = WRXDATA;
+ else if (window == USERWINDOW)
+ priv->spi_tx_buf[0] = WUDADATA;
+ else if (window == GPWINDOW)
+ priv->spi_tx_buf[0] = WGPDATA;
+
+ ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1);
+
+ return ret;
+}
+
+/*
+ * Select the current register bank if necessary to be able to read @addr.
+ */
+static void enc424j600_set_bank(struct enc424j600_net *priv, u8 addr)
+{
+ u8 b = (addr & BANK_MASK) >> BANK_SHIFT;
+
+ /* These registers are present in all banks, no need to switch bank */
+ if (addr >= EUDASTL && addr <= ECON1H)
+ return;
+ if (priv->bank == b)
+ return;
+
+ priv->spi_tx_buf[0] = BXSEL(b);
+
+ enc424j600_spi_trans(priv, 1);
+ /*WRITE*/ priv->bank = b;
+}
+
+/*
+ * Set bits in an 8bit SFR.
+ */
+static void enc424j600_set_bits(struct enc424j600_net *priv, u8 addr, u8 mask)
+{
+ enc424j600_set_bank(priv, addr);
+ priv->spi_tx_buf[0] = BFS(addr);
+ priv->spi_tx_buf[1] = mask;
+ spi_write(priv->spi, priv->spi_tx_buf, 2);
+}
+
+/*
+ * Clear bits in an 8bit SFR.
+ */
+static void enc424j600_clear_bits(struct enc424j600_net *priv, u8
addr, u8 mask)
+{
+ enc424j600_set_bank(priv, addr);
+ priv->spi_tx_buf[0] = BFC(addr);
+ priv->spi_tx_buf[1] = mask;
+ spi_write(priv->spi, priv->spi_tx_buf, 2);
+}
+
+/*
+ * Write a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked write instruction.
+ */
+static int enc424j600_write_8b_sfr(struct enc424j600_net *priv, u8
sfr, u8 data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = data & 0xFF;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 2);
+
+ return ret;
+}
+
+/*
+ * Read a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_8b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u8 *data)
+{
+ int ret;
+
+ enc424j600_set_bank(priv, sfr);
+ priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+ ret = enc424j600_spi_trans(priv, 2);
+ /*READ*/ *data = priv->spi_rx_buf[0];
+
+ return ret;
+}
+
+/*
+ * Write a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked write instruction.
+ */
+
+static int enc424j600_write_16b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u16 data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = data & 0xFF;
+ priv->spi_tx_buf[2] = data >> 8;
+ ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+ if (ret && netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/*
+ * Read a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_16b_sfr(struct enc424j600_net *priv,
+ u8 sfr, u16 *data)
+{
+ int ret;
+ enc424j600_set_bank(priv, sfr);
+
+ priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+ priv->spi_tx_buf[1] = 0;
+ priv->spi_tx_buf[2] = 0;
+ priv->spi_tx_buf[3] = 0;
+ ret = enc424j600_spi_trans(priv, 3);
+ /*READ*/ *data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8;
+
+ return ret;
+}
+
+static unsigned long msec20_to_jiffies;
+
+/*
+ * Wait for bits in register to become equal to @readyMask, but at most 20ms.
+ */
+static int checktimeout_16bit(struct enc424j600_net *priv,
+ u8 reg, u16 mask, u16 readyMask)
+{
+ unsigned long timeout = jiffies + msec20_to_jiffies;
+ u16 value;
+ /* 20 msec timeout read */
+ enc424j600_read_16b_sfr(priv, reg, &value);
+ while ((value & mask) != readyMask) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", reg);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_read_16b_sfr(priv, reg, &value);
+ }
+
+ return 0;
+}
+
+static int checktimeout_8bit(struct enc424j600_net *priv,
+ u8 reg, u8 mask, u8 readyMask)
+{
+ unsigned long timeout = jiffies + msec20_to_jiffies;
+ u8 value;
+ /* 20 msec timeout read */
+ enc424j600_read_8b_sfr(priv, reg, &value);
+ while ((value & mask) != readyMask) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", reg);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_read_8b_sfr(priv, reg, &value);
+ }
+
+ return 0;
+}
+
+/*
+ * Reset the enc424j600.
+ */
+static int enc424j600_soft_reset(struct enc424j600_net *priv)
+{
+ int ret;
+ u16 eudast;
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ enc424j600_write_16b_sfr(priv, EUDASTL, EUDAST_TEST_VAL);
+ ret = checktimeout_16bit(priv, EUDASTL, 0xFFFF, EUDAST_TEST_VAL);
+ if (ret != 0)
+ return ret;
+ ret = checktimeout_16bit(priv, ESTATH, CLKRDY, CLKRDY);
+ if (ret != 0)
+ return ret;
+
+ priv->spi_tx_buf[0] = SETETHRST;
+ enc424j600_spi_trans(priv, 1);
+ /*inline with the datasheet */
+ udelay(25);
+
+ enc424j600_read_16b_sfr(priv, EUDASTL, &eudast);
+ if (netif_msg_hw(priv) && eudast != 0)
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() EUDASTL is not zero!\n", __func__);
+ /*inline with the datasheet */
+ /*datasheet says to wait for 256 usec atleast */
+ udelay(300);
+ return 0;
+}
+
+/*
+ * PHY register read
+ * PHY registers are not accessed directly, but through the MII
+ */
+static int enc424j600_phy_read(struct enc424j600_net *priv,
+ u16 address, u16 *data)
+{
+ int ret;
+
+ enc424j600_write_16b_sfr(priv, MIREGADRL,
+ address | (MIREGADRH_VAL << 8));
+ enc424j600_write_16b_sfr(priv, MICMDL, MIIRD);
+ udelay(26);
+ ret = !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+ enc424j600_write_16b_sfr(priv, MICMDL, 0);
+ enc424j600_read_16b_sfr(priv, MIRDL, data);
+ return ret;
+}
+
+static int enc424j600_phy_write(struct enc424j600_net *priv, u16 address,
+ u16 data)
+{
+ enc424j600_write_16b_sfr(priv, MIREGADRL,
+ address | (MIREGADRH_VAL << 8));
+ enc424j600_write_16b_sfr(priv, MIWRL, data);
+ udelay(26);
+ return !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+}
+
+/*
+ * Read the hardware MAC address to dev->dev_addr.
+ */
+static int enc424j600_get_hw_macaddr(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ u16 maadr1, maadr2, maadr3;
+
+ mutex_lock(&priv->lock);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ enc424j600_read_16b_sfr(priv, MAADR3L, &maadr3);
+ ndev->dev_addr[5] = maadr3 >> 8;
+ ndev->dev_addr[4] = maadr3 & 0xff;
+ enc424j600_read_16b_sfr(priv, MAADR2L, &maadr2);
+ ndev->dev_addr[3] = maadr2 >> 8;
+ ndev->dev_addr[2] = maadr2 & 0xff;
+ enc424j600_read_16b_sfr(priv, MAADR1L, &maadr1);
+ ndev->dev_addr[1] = maadr1 >> 8;
+ ndev->dev_addr[0] = maadr1 & 0xff;
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * Program the hardware MAC address from dev->dev_addr.
+ */
+static int enc424j600_set_hw_macaddr(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+
+ mutex_lock(&priv->lock);
+
+ if (priv->hw_enable) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() Hardware must be disabled to set "
+ "Mac address\n", __func__);
+ mutex_unlock(&priv->lock);
+ return -EBUSY;
+ }
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ enc424j600_write_16b_sfr(priv, MAADR3L,
+ ndev->dev_addr[4] | ndev->dev_addr[5] << 8);
+ enc424j600_write_16b_sfr(priv, MAADR2L,
+ ndev->dev_addr[2] | ndev->dev_addr[3] << 8);
+ enc424j600_write_16b_sfr(priv, MAADR1L,
+ ndev->dev_addr[0] | ndev->dev_addr[1] << 8);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/*
+ * Store the new hardware address in dev->dev_addr, and update the MAC.
+ */
+static int enc424j600_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ return enc424j600_set_hw_macaddr(dev);
+}
+
+u8 nolock_regb_read(struct enc424j600_net *priv, u8 address)
+{
+ u8 data;
+ enc424j600_read_8b_sfr(priv, address, &data);
+ return data;
+}
+
+u16 nolock_regw_read(struct enc424j600_net *priv, u8 address)
+{
+ u16 data;
+ enc424j600_read_16b_sfr(priv, address, &data);
+ return data;
+}
+
+/*Debug routine to dump useful register contents*/
+static void enc424j600_dump_regs(struct enc424j600_net *priv, const char *msg)
+{
+
+ mutex_lock(&priv->lock);
+ printk(KERN_DEBUG DRV_NAME " %s\n"
+ "Cntrl: ECON1H ECON1L ECON2H ECON2L ESTATH ESTATL EIRH "
+ "EIRL EIEH EIEL\n"
+ " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x 0x%02x 0x%02x\n"
+ "MAC : MACON1 MACON2\n"
+ " 0x%04x 0x%04x\n"
+ "Rx : ERXST ERXTAIL ERXHEAD ERXWRPT ERXRDPT ERXFCON MAMXFL\n"
+ " 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
+ "Tx : ETXST ETXLEN MACLCON1 \n"
+ " 0x%04x 0x%04x 0x%02x\n",
+ msg,
+ nolock_regb_read(priv, ECON1H), nolock_regb_read(priv, ECON1L),
+ nolock_regb_read(priv, ECON2H), nolock_regb_read(priv, ECON2L),
+ nolock_regb_read(priv, ESTATH), nolock_regb_read(priv, ESTATL),
+ nolock_regb_read(priv, EIRH), nolock_regb_read(priv, EIRL),
+ nolock_regb_read(priv, EIEH), nolock_regb_read(priv, EIEL),
+ nolock_regw_read(priv, MACON1L), nolock_regw_read(priv, MACON2L),
+ nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXTAILL),
+ nolock_regw_read(priv, ERXHEADL),
+ nolock_regw_read(priv, ERXWRPTL), nolock_regw_read(priv,
+ ERXRDPTL),
+ nolock_regw_read(priv, ERXFCONL), nolock_regw_read(priv,
+ MAMXFLL),
+ nolock_regw_read(priv, ETXSTL), nolock_regw_read(priv, ETXLENL),
+ nolock_regw_read(priv, MACLCONL));
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * TODO: Check the functionality
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive. (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc424j600_lowpower(struct enc424j600_net *priv, bool is_low)
+{
+
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev, "%s power...\n",
+ is_low ? "low" : "high");
+
+#if 0
+ mutex_lock(&priv->lock);
+ if (is_low) {
+ nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+ checktimeout_8bit(priv, ESTAT, ESTAT_RXBUSY, 0);
+ checktimeout_8bit(priv, ECON1, ECON1_TXRTS, 0);
+ /* ECON2_VRPS was set during initialization */
+ nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+ } else {
+ nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+ checktimeout_8bit(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+ /* caller sets ECON1_RXEN */
+ }
+ mutex_unlock(&priv->lock);
+#endif
+}
+
+static unsigned long msec2000_to_jiffies;
+/* Waits for autonegotiation to complete. */
+static int enc424j600_wait_for_autoneg(struct enc424j600_net *priv)
+{
+ unsigned long timeout = jiffies + msec2000_to_jiffies;
+ u16 value;
+ /* 20 msec timeout read */
+ enc424j600_phy_read(priv, PHSTAT1, &value);
+ while ((value & ANDONE) == 0) {
+ if (time_after(jiffies, timeout)) {
+ if (netif_msg_drv(priv))
+ dev_dbg(&priv->spi->dev,
+ "reg %02x ready timeout!\n", PHSTAT1);
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ enc424j600_phy_read(priv, PHSTAT1, &value);
+ }
+ return 0;
+
+}
+
+/*
+ * Reset and initialize the chip, but don't enable interrupts and don't
+ * start receiving yet.
+ */
+static int enc424j600_hw_init(struct enc424j600_net *priv)
+{
+ u8 eidledl;
+ u16 phcon1;
+ u16 macon2;
+ u16 econ1l;
+ /*priv->autoneg = AUTONEG_ENABLE;*/
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() - %s\n", __func__,
+ priv->
+ autoneg ? "Autoneg" : (priv->full_duplex ? "FullDuplex" :
+ "HalfDuplex"));
+
+ mutex_lock(&priv->lock);
+
+ priv->bank = 0;
+ priv->hw_enable = false;
+ priv->tx_retry_count = 0;
+ priv->max_pk_counter = 0;
+ priv->rxfilter = RXFILTER_NORMAL;
+
+ if (enc424j600_soft_reset(priv) != 0)
+ return 0;
+
+ /*
+ * Check the device id and silicon revision id.
+ */
+ enc424j600_read_8b_sfr(priv, EIDLEDL, &eidledl);
+
+ if ((eidledl & DEVID_MASK) >> DEVID_SHIFT != ENC424J600_DEV_ID) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": %s() Invalid device ID: %d\n", __func__,
+ (eidledl & DEVID_MASK) >> DEVID_SHIFT);
+ return 0;
+ }
+
+ if (netif_msg_drv(priv))
+ printk(KERN_INFO DRV_NAME ": Silicon revision ID: 0x%02x\n",
+ (eidledl & REVID_MASK) >> REVID_SHIFT);
+
+ enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+ enc424j600_write_16b_sfr(priv, ERXSTL, RXSTART);
+
+ priv->next_pk_ptr = RXSTART;
+ enc424j600_write_16b_sfr(priv, ERXTAILL, SRAMSIZE - 2);
+ enc424j600_write_16b_sfr(priv, ERXFCONL, UCEN | BCEN | CRCEN | RUNTEN);
+
+ enc424j600_phy_write(priv, PHANA, PHANA_DEFAULT);
+
+ /* PHCON1 */
+ phcon1 = 0;
+ if (priv->autoneg) {
+ /* Enable autonegotiation and renegotiate */
+ phcon1 |= ANEN | RENEG;
+ } else {
+ if (priv->speed100)
+ phcon1 |= SPD100;
+ if (priv->full_duplex)
+ phcon1 |= PFULDPX;
+ }
+ enc424j600_phy_write(priv, PHCON1, phcon1);
+
+ /* MACON2
+ * defer transmission if collision occurs (only for half duplex)
+ * pad to 60 or 64 bytes and append CRC
+ * enable receiving huge frames (instead of limiting packet size) */
+ macon2 = MACON2_DEFER | PADCFG2 | PADCFG0 | TXCRCEN | HFRMEN;
+
+ /* If autonegotiation is enabled, we have to wait untill it finishes
+ * and set the PHYDPX bit in MACON2 correctly */
+ if (priv->autoneg) {
+ u8 estath;
+ if (!enc424j600_wait_for_autoneg(priv)) {
+ /* read the PHYDPX bit in ESTAT and set FULDPX in
+ MACON2 accordingly */
+ enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+ if (estath & PHYDPX)
+ macon2 |= FULDPX;
+ } else /*if timedout, just disable autoneg */
+ priv->autoneg = AUTONEG_DISABLE;
+ } else if (priv->full_duplex)
+ macon2 |= FULDPX;
+
+ enc424j600_write_16b_sfr(priv, MACON2L, macon2);
+
+ /* MAIPGL
+ * Recomended values for inter packet gaps */
+ if (!priv->autoneg) {
+ enc424j600_write_16b_sfr(priv, MAIPGL,
+ MAIPGL_VAL | (MAIPGH_VAL << 8));
+ }
+
+ /*
+ * Select enabled interrupts, but don't set the global
+ * interrupt enable flag.
+ */
+
+ enc424j600_write_16b_sfr(priv, EIEL,
+ LINKIE << 8 | PKTIE | DMAIE | TXIE | TXABTIE |
+ RXABTIE);
+
+ enc424j600_read_16b_sfr(priv, ECON1L, &econ1l);
+ econ1l |= (RXEN);
+ enc424j600_write_16b_sfr(priv, ECON1L, econ1l);
+
+ mutex_unlock(&priv->lock);
+
+ if (netif_msg_hw(priv))
+ enc424j600_dump_regs(priv, "Hw initialized.");
+
+ return 1;
+}
+
+static void enc424j600_hw_enable(struct enc424j600_net *priv)
+{
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
+ __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* Clear any pending interrupts */
+ enc424j600_write_16b_sfr(priv, EIRL, 0);
+
+ /* Enable global interrupt flag */
+ enc424j600_set_bits(priv, EIEH, INTIE);
+
+ /* enable receive logic */
+ enc424j600_set_bits(priv, ECON1L, RXEN);
+ priv->hw_enable = true;
+ mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_hw_disable(struct enc424j600_net *priv)
+{
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() disabling interrupts.\n",
+ __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* disable receive logic */
+ enc424j600_clear_bits(priv, ECON1L, RXEN);
+
+ /* Disable global interrupt flag */
+ enc424j600_clear_bits(priv, EIEH, INTIE);
+
+ priv->hw_enable = false;
+
+ mutex_unlock(&priv->lock);
+}
+
+static int
+enc424j600_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ int ret = 0;
+ if (!priv->hw_enable) {
+ /* link is in low power mode now; duplex setting
+ * will take effect on next enc424j600_hw_init().
+ */
+ if (speed == SPEED_10 || speed == SPEED_100) {
+ priv->autoneg = (autoneg == AUTONEG_ENABLE);
+ priv->full_duplex = (duplex == DUPLEX_FULL);
+ priv->speed100 = (speed == SPEED_100);
+ } else {
+ if (netif_msg_link(priv))
+ dev_warn(&ndev->dev,
+ "unsupported link setting\n");
+ /*speeds other than SPEED_10 and SPEED_100 */
+ /*are not supported by chip */
+ ret = -EOPNOTSUPP;
+ }
+ } else {
+ if (netif_msg_link(priv))
+ dev_warn(&ndev->dev, "Warning: hw must be disabled "
+ "to set link mode\n");
+ ret = -EBUSY;
+ }
+ return ret;
+}
+
+/*
+ * Receive Status vector
+ */
+static void enc424j600_dump_rsv(struct enc424j600_net *priv, const char *msg,
+ u16 pk_ptr, int len, u16 sts)
+{
+ printk(KERN_DEBUG DRV_NAME ": %s - NextPk: 0x%04x - RSV:\n",
+ msg, pk_ptr);
+ printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, DribbleNibble: %d\n", len,
+ RSV_GETBIT(sts, RSV_DRIBBLENIBBLE));
+ printk(KERN_DEBUG DRV_NAME ": RxOK: %d, CRCErr:%d, LenChkErr: %d,"
+ " LenOutOfRange: %d\n", RSV_GETBIT(sts, RSV_RXOK),
+ RSV_GETBIT(sts, RSV_CRCERROR),
+ RSV_GETBIT(sts, RSV_LENCHECKERR),
+ RSV_GETBIT(sts, RSV_LENOUTOFRANGE));
+ printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, "
+ "LongDropEvent: %d, CarrierEvent: %d\n",
+ RSV_GETBIT(sts, RSV_RXMULTICAST),
+ RSV_GETBIT(sts, RSV_RXBROADCAST),
+ RSV_GETBIT(sts, RSV_RXLONGEVDROPEV),
+ RSV_GETBIT(sts, RSV_CARRIEREV));
+ printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d,"
+ " UnknownOp: %d, VLanTagFrame: %d\n",
+ RSV_GETBIT(sts, RSV_RXCONTROLFRAME),
+ RSV_GETBIT(sts, RSV_RXPAUSEFRAME),
+ RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE),
+ RSV_GETBIT(sts, RSV_RXTYPEVLAN));
+}
+
+static void dump_packet(const char *msg, int len, const char *data)
+{
+
+ printk(KERN_ALERT ": %s - packet len:%d\n", msg, len);
+ print_hex_dump(KERN_ALERT, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1,
+ data, len, true);
+}
+
+/*
+ * Calculate wrap around when reading beyond the end of the RX buffer
+ */
+static u16 rx_packet_start(u16 ptr)
+{
+ if (ptr + RSV_SIZE > RXEND_INIT)
+ return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART + 1);
+ else
+ return ptr + RSV_SIZE;
+}
+
+/*
+ * ERXRDPT need to be set always at odd addresses, refer to errata datasheet
+ */
+static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end)
+{
+ u16 erxrdpt;
+ if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end))
+ erxrdpt = end;
+ else
+ erxrdpt = next_packet_ptr - 1;
+
+ return erxrdpt;
+}
+
+static void nolock_rxfifo_init(struct enc424j600_net *priv, u16 start, u16 end)
+{
+ u16 erxrdpt;
+ if (start > 0x5FFF || end > 0x5FFF || start > end) {
+ if (netif_msg_drv(priv))
+ printk(KERN_ERR DRV_NAME ": %s(%d, %d) RXFIFO "
+ "bad parameters!\n", __func__, start, end);
+ return;
+ }
+ /* set receive buffer start + end */
+ priv->next_pk_ptr = start;
+ enc424j600_write_16b_sfr(priv, ERXSTL, start);
+ erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end);
+ enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+ enc424j600_write_16b_sfr(priv, ERXTAILL, end);
+}
+
+/*
+ * Hardware receive function.
+ * Read the buffer memory, update the FIFO pointer to free the buffer,
+ * check the status vector and decrement the packet counter.
+ */
+static void enc424j600_hw_rx(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+ struct sk_buff *skb = NULL;
+ u16 erxrdpt, next_packet, rxstat;
+ u8 pkcnt;
+ u16 head, tail;
+ u8 rsv[RSV_SIZE];
+ u16 newrxtail;
+ int len;
+
+ if (netif_msg_rx_status(priv))
+ printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
+ priv->next_pk_ptr);
+ if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev,
+ "%s() Invalid packet address!! 0x%04x\n",
+ __func__, priv->next_pk_ptr);
+ mutex_lock(&priv->lock);
+ enc424j600_clear_bits(priv, ECON1L, RXEN);
+ enc424j600_set_bits(priv, ECON2L, RXRST);
+ enc424j600_clear_bits(priv, ECON2L, RXRST);
+ nolock_rxfifo_init(priv, RXSTART, RXEND_INIT);
+ enc424j600_clear_bits(priv, EIRL, RXABTIF);
+ enc424j600_set_bits(priv, ECON1L, RXEN);
+ mutex_unlock(&priv->lock);
+ ndev->stats.rx_errors++;
+ return;
+ }
+
+ /* Read next packet pointer and rx status vector */
+ enc424j600_read_sram(priv, rsv, sizeof(rsv), priv->next_pk_ptr,
+ RXWINDOW);
+
+ next_packet = rsv[1];
+ next_packet <<= 8;
+ next_packet |= rsv[0];
+
+ len = rsv[3];
+ len <<= 8;
+ len |= rsv[2];
+
+ rxstat = rsv[5];
+ rxstat <<= 8;
+ rxstat |= rsv[4];
+
+ if (netif_msg_rx_status(priv))
+ enc424j600_dump_rsv(priv, __func__, next_packet, len, rxstat);
+
+ if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
+ ndev->stats.rx_errors++;
+ if (RSV_GETBIT(rxstat, RSV_CRCERROR))
+ ndev->stats.rx_crc_errors++;
+ if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
+ ndev->stats.rx_frame_errors++;
+ if (len > MAX_FRAMELEN)
+ ndev->stats.rx_over_errors++;
+ } else {
+ skb = dev_alloc_skb(len + NET_IP_ALIGN);
+ if (!skb) {
+ if (netif_msg_rx_err(priv))
+ dev_err(&ndev->dev,
+ "out of memory for Rx'd frame\n");
+ ndev->stats.rx_dropped++;
+ } else {
+ skb->dev = ndev;
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ /* copy the packet from the receive buffer */
+ enc424j600_read_sram(priv, skb_put(skb, len), len,
+ rx_packet_start(priv->next_pk_ptr),
+ RXWINDOW);
+
+ if (netif_msg_pktdata(priv))
+ dump_packet(__func__, skb->len, skb->data);
+ skb->protocol = eth_type_trans(skb, ndev);
+ /* update statistics */
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += len;
+ netif_rx_ni(skb);
+ }
+ }
+ newrxtail = next_packet - 2;
+ if (next_packet == RXSTART)
+ newrxtail = SRAMSIZE - 2;
+
+ enc424j600_write_16b_sfr(priv, ERXTAILL, newrxtail);
+ /*
+ * Move the RX read pointer to the start of the next
+ * received packet.
+ * This frees the memory we just read out
+ */
+ erxrdpt = erxrdpt_workaround(next_packet, RXSTART, RXEND_INIT);
+ if (netif_msg_hw(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", __func__,
+ erxrdpt);
+
+ /*TODO: remove mutex_lock wherever not required*/
+ mutex_lock(&priv->lock);
+ enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+
+ priv->next_pk_ptr = next_packet;
+ enc424j600_read_8b_sfr(priv, ESTATL, &pkcnt);
+ enc424j600_read_16b_sfr(priv, ERXHEADL, &head);
+ enc424j600_read_16b_sfr(priv, ERXTAILL, &tail);
+ /* we are done with this packet, decrement the packet counter */
+ enc424j600_set_bits(priv, ECON1H, PKTDEC);
+
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * Access the PHY to determine link status
+ */
+static void enc424j600_check_link_status(struct enc424j600_net *priv)
+{
+ u8 estath;
+ u16 macon2;
+
+ enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+ if (estath & PHYLNK) {
+ if (priv->autoneg) {
+ if (!enc424j600_wait_for_autoneg(priv)) {
+ if (estath & PHYDPX) {
+ printk(KERN_ALERT "Full Duplex");
+ enc424j600_read_16b_sfr(priv, MACON2L,
+ &macon2);
+ macon2 |= FULDPX;
+ enc424j600_write_16b_sfr(priv, MACON2L,
+ macon2);
+ }
+ } else /*if timed out, disable autoneg and continue */
+ priv->autoneg = AUTONEG_DISABLE;
+ }
+ netif_carrier_on(priv->netdev);
+ if (netif_msg_ifup(priv))
+ dev_info(&(priv->netdev->dev), "link up\n");
+ } else {
+ if (netif_msg_ifdown(priv))
+ dev_info(&(priv->netdev->dev), "link down\n");
+ netif_carrier_off(priv->netdev);
+ }
+}
+
+static void enc424j600_tx_clear(struct enc424j600_net *priv, bool err)
+{
+ struct net_device *ndev = priv->netdev;
+ if (err)
+ ndev->stats.tx_errors++;
+ else
+ ndev->stats.tx_packets++;
+
+ if (priv->tx_skb) {
+ if (!err)
+ ndev->stats.tx_bytes += priv->tx_skb->len;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ netif_wake_queue(ndev);
+}
+
+static int enc424j600_int_rx_abbort_handler(struct enc424j600_net *priv,
+ int loop)
+{
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intRXAbt(%d)\n", loop);
+ mutex_lock(&priv->lock);
+ priv->netdev->stats.rx_dropped++;
+ enc424j600_clear_bits(priv, EIRL, RXABTIF);
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_link_handler(struct enc424j600_net *priv, int loop)
+{
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intLINK(%d)\n", loop);
+
+ /* we check more than is necessary here --
+ * only PHYLNK would be needed. */
+ enc424j600_check_link_status(priv);
+
+ return loop;
+}
+
+static int enc424j600_int_tx_handler(struct enc424j600_net *priv, int loop)
+{
+ loop++;
+
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intTX(%d)\n", loop);
+
+ mutex_lock(&priv->lock);
+ enc424j600_tx_clear(priv, false);
+ enc424j600_clear_bits(priv, EIRL, TXIF);
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_tx_err_handler(struct enc424j600_net *priv, int loop)
+{
+ u8 etxstat;
+ loop++;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intTXErr(%d)\n", loop);
+
+ mutex_lock(&priv->lock);
+
+ enc424j600_read_8b_sfr(priv, ETXSTATH, &etxstat);
+
+ if (etxstat & LATECOL) {
+ if (netif_msg_tx_err(priv))
+ printk(KERN_DEBUG DRV_NAME
+ ": Late collision TXErr (%d)\n",
+ priv->tx_retry_count);
+ if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
+ enc424j600_set_bits(priv, ECON1L, TXRTS);
+ else
+ enc424j600_tx_clear(priv, true);
+ } else if (etxstat & MAXCOL) {
+ if (netif_msg_tx_err(priv))
+ printk(KERN_DEBUG DRV_NAME ": Max collisions TXErr\n");
+ enc424j600_tx_clear(priv, true);
+ } else {
+ enc424j600_tx_clear(priv, true);
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return loop;
+}
+
+static int enc424j600_int_received_packet_handler(struct enc424j600_net *priv)
+{
+ uint8_t pk_counter;
+ int ret;
+
+ enc424j600_read_8b_sfr(priv, ESTATL, &pk_counter);
+ if (pk_counter && netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter);
+ if (pk_counter > priv->max_pk_counter) {
+ /* update statistics */
+ priv->max_pk_counter = pk_counter;
+ if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
+ printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n",
+ priv->max_pk_counter);
+ }
+ ret = pk_counter;
+ while (pk_counter-- > 0)
+ enc424j600_hw_rx(priv->netdev);
+ return ret;
+}
+
+static void enc424j600_irq_work_handler(struct work_struct *work)
+{
+
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, irq_work);
+ int loop;
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ /* disable further interrupts */
+ enc424j600_clear_bits(priv, EIEH, INTIE);
+
+ do {
+ u16 intflags;
+ enc424j600_read_16b_sfr(priv, EIRL, &intflags);
+ loop = 0;
+
+ /* LINK changed handler */
+ if ((intflags & LINKIF) != 0)
+ loop = enc424j600_int_link_handler(priv, loop);
+
+ /* TX complete handler */
+ if ((intflags & TXIF) != 0)
+ loop = enc424j600_int_tx_handler(priv, loop);
+
+ /* TX Error handler */
+ if ((intflags & TXABTIF) != 0) {
+ printk(KERN_ALERT "ABORTING TRANSMITTING PACKET");
+ loop = enc424j600_int_tx_err_handler(priv, loop);
+ }
+ /* RX Error handler */
+ if ((intflags & RXABTIF) != 0) {
+ printk(KERN_ALERT "ABORTING RECEIVED PACKET");
+ loop = enc424j600_int_rx_abbort_handler(priv, loop);
+ }
+ /* RX handler */
+ if ((intflags & PKTIF) != 0)
+ loop = enc424j600_int_received_packet_handler(priv);
+ enc424j600_clear_bits(priv, EIRL, intflags && 0xff);
+ enc424j600_clear_bits(priv, EIRH, intflags >> 8);
+ } while (loop);
+ /* re-enable interrupts */
+ enc424j600_set_bits(priv, EIEH, INTIE);
+
+ if (netif_msg_intr(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__);
+}
+
+void locked_reg_bfset(struct enc424j600_net *priv, u8 addr, u8 mask)
+{
+ mutex_lock(&priv->lock);
+ enc424j600_set_bits(priv, addr, mask);
+ mutex_unlock(&priv->lock);
+}
+
+/*
+ * Hardware transmit function.
+ * Fill the buffer memory and send the contents of the transmit buffer
+ * onto the network
+ */
+static void enc424j600_hw_tx(struct enc424j600_net *priv)
+{
+ if (!priv->tx_skb) {
+ enc424j600_tx_clear(priv, false);
+ return;
+ }
+
+ if (netif_msg_tx_queued(priv))
+ printk(KERN_DEBUG DRV_NAME ": Tx Packet Len:%d\n",
+ priv->tx_skb->len);
+
+ if (netif_msg_pktdata(priv))
+ dump_packet(__func__, priv->tx_skb->len, priv->tx_skb->data);
+
+ enc424j600_write_sram(priv, priv->tx_skb->data, priv->tx_skb->len,
+ TXSTART, GPWINDOW);
+
+ /* Set the tx pointer to start of general purpose SRAM area */
+ enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+
+ /* Write the transfer length */
+ enc424j600_write_16b_sfr(priv, ETXLENL, priv->tx_skb->len);
+
+ /* set TX request flag */
+ locked_reg_bfset(priv, ECON1L, TXRTS);
+}
+
+static int enc424j600_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+ if (netif_msg_tx_queued(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ /* If some error occurs while trying to transmit this
+ * packet, you should return '1' from this function.
+ * In such a case you _may not_ do anything to the
+ * SKB, it is still owned by the network queueing
+ * layer when an error is returned. This means you
+ * may not modify any SKB fields, you may not free
+ * the SKB, etc.
+ */
+ netif_stop_queue(dev);
+
+ /* save the timestamp */
+ priv->netdev->trans_start = jiffies;
+ /* Remember the skb for deferred processing */
+ priv->tx_skb = skb;
+ schedule_work(&priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static void enc424j600_tx_work_handler(struct work_struct *work)
+{
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, tx_work);
+
+ /* actual delivery of data */
+ enc424j600_hw_tx(priv);
+}
+
+static irqreturn_t enc424j600_irq(int irq, void *dev_id)
+{
+ struct enc424j600_net *priv = dev_id;
+ /*
+ * Can't do anything in interrupt context because we need to
+ * block (spi_sync() is blocking) so fire of the interrupt
+ * handling workqueue.
+ * Remember that we access enc424j600 registers through SPI bus
+ * via spi_sync() call.
+ */
+ schedule_work(&priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void enc424j600_tx_timeout(struct net_device *ndev)
+{
+ struct enc424j600_net *priv = netdev_priv(ndev);
+
+ if (netif_msg_timer(priv))
+ dev_err(&ndev->dev, DRV_NAME " tx timeout\n");
+
+ ndev->stats.tx_errors++;
+ /* can't restart safely under softirq */
+ schedule_work(&priv->restart_work);
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int enc424j600_net_open(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ if (netif_msg_ifup(priv))
+ dev_err(&dev->dev, "invalid MAC address %pM\n",
+ dev->dev_addr);
+ return -EADDRNOTAVAIL;
+ }
+ /* Reset the hardware here (and take it out of low power mode) */
+ enc424j600_lowpower(priv, false);
+ enc424j600_hw_disable(priv);
+ if (!enc424j600_hw_init(priv)) {
+ if (netif_msg_ifup(priv))
+ dev_err(&dev->dev, "hw_reset() failed\n");
+ return -EINVAL;
+ }
+ /* Update the MAC address (in case user has changed it) */
+ enc424j600_set_hw_macaddr(dev);
+ /* Enable interrupts */
+ enc424j600_hw_enable(priv);
+ /* check link status */
+ enc424j600_check_link_status(priv);
+ /* We are now ready to accept transmit requests from
+ * the queueing layer of the networking.
+ */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/* The inverse routine to net_open(). */
+static int enc424j600_net_close(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+ enc424j600_hw_disable(priv);
+ enc424j600_lowpower(priv, true);
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adapter
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, filter out multicast packets
+ * num_addrs > 0 Multicast mode, receive normal and MC packets
+ */
+static void enc424j600_set_multicast_list(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+ int oldfilter = priv->rxfilter;
+
+ if (dev->flags & IFF_PROMISC) {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "promiscuous mode\n");
+ priv->rxfilter = RXFILTER_PROMISC;
+ } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count) {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "%smulticast mode\n",
+ (dev->flags & IFF_ALLMULTI) ? "all-" : "");
+ priv->rxfilter = RXFILTER_MULTI;
+ } else {
+ if (netif_msg_link(priv))
+ dev_info(&dev->dev, "normal mode\n");
+ priv->rxfilter = RXFILTER_NORMAL;
+ }
+
+ if (oldfilter != priv->rxfilter)
+ schedule_work(&priv->setrx_work);
+}
+
+void locked_regb_write(struct enc424j600_net *priv, u8 address, u8 data)
+{
+ mutex_lock(&priv->lock);
+ enc424j600_write_8b_sfr(priv, address, data);
+ mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_setrx_work_handler(struct work_struct *work)
+{
+ u16 macon1;
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, setrx_work);
+
+ if (priv->rxfilter == RXFILTER_PROMISC) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n");
+ enc424j600_read_16b_sfr(priv, MACON1L, &macon1);
+ macon1 = macon1 | PASSALL;
+ enc424j600_write_16b_sfr(priv, MACON1L, macon1);
+ locked_regb_write(priv, ERXFCONL, UCEN | MCEN | NOTMEEN);
+ } else if (priv->rxfilter == RXFILTER_MULTI) {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": multicast mode\n");
+ locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN | MCEN);
+
+ } else {
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": normal mode\n");
+ locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN);
+
+ }
+}
+
+static void enc424j600_restart_work_handler(struct work_struct *work)
+{
+ struct enc424j600_net *priv =
+ container_of(work, struct enc424j600_net, restart_work);
+ struct net_device *ndev = priv->netdev;
+ int ret;
+
+ rtnl_lock();
+ if (netif_running(ndev)) {
+ enc424j600_net_close(ndev);
+ ret = enc424j600_net_open(ndev);
+ if (unlikely(ret)) {
+ dev_info(&ndev->dev, " could not restart %d\n", ret);
+ dev_close(ndev);
+ }
+ }
+ rtnl_unlock();
+}
+
+/* ......................... ETHTOOL SUPPORT ........................... */
+
+static void
+enc424j600_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info,
+ dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int
+enc424j600_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->supported = SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_TP;
+
+ cmd->speed = priv->speed100 ? SPEED_100 : SPEED_10;
+ cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+ cmd->port = PORT_TP;
+ cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+ return 0;
+}
+
+static int
+enc424j600_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ return enc424j600_setlink(dev, cmd->autoneg, cmd->speed, cmd->duplex);
+}
+
+static u32 enc424j600_get_msglevel(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ return priv->msg_enable;
+}
+
+static void enc424j600_set_msglevel(struct net_device *dev, u32 val)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ priv->msg_enable = val;
+}
+
+static const struct ethtool_ops enc424j600_ethtool_ops = {
+ .get_settings = enc424j600_get_settings,
+ .set_settings = enc424j600_set_settings,
+ .get_drvinfo = enc424j600_get_drvinfo,
+ .get_msglevel = enc424j600_get_msglevel,
+ .set_msglevel = enc424j600_set_msglevel,
+};
+
+static int enc424j600_chipset_init(struct net_device *dev)
+{
+ struct enc424j600_net *priv = netdev_priv(dev);
+
+ enc424j600_get_hw_macaddr(dev);
+ return enc424j600_hw_init(priv);
+
+}
+
+static const struct net_device_ops enc424j600_netdev_ops = {
+ .ndo_open = enc424j600_net_open,
+ .ndo_stop = enc424j600_net_close,
+ .ndo_start_xmit = enc424j600_send_packet,
+ .ndo_set_multicast_list = enc424j600_set_multicast_list,
+ .ndo_set_mac_address = enc424j600_set_mac_address,
+ .ndo_tx_timeout = enc424j600_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __devinit enc424j600_probe(struct spi_device *spi)
+{
+ struct net_device *dev;
+ struct enc424j600_net *priv;
+ int ret = 0;
+
+ if (netif_msg_drv(&debug))
+ dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
+ DRV_VERSION);
+
+ dev = alloc_etherdev(sizeof(struct enc424j600_net));
+ if (!dev) {
+ if (netif_msg_drv(&debug))
+ dev_err(&spi->dev, DRV_NAME
+ ": unable to alloc new ethernet\n");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+ priv = netdev_priv(dev);
+
+ priv->netdev = dev; /* priv to netdev reference */
+ priv->spi = spi; /* priv to spi reference */
+ priv->msg_enable = netif_msg_init(debug.msg_enable,
+ ENC424J600_MSG_DEFAULT);
+ mutex_init(&priv->lock);
+ INIT_WORK(&priv->tx_work, enc424j600_tx_work_handler);
+ INIT_WORK(&priv->setrx_work, enc424j600_setrx_work_handler);
+ INIT_WORK(&priv->irq_work, enc424j600_irq_work_handler);
+ INIT_WORK(&priv->restart_work, enc424j600_restart_work_handler);
+ dev_set_drvdata(&spi->dev, priv); /* spi to priv reference */
+ SET_NETDEV_DEV(dev, &spi->dev);
+ /*TODO: chip DMA features to be utilized */
+ /* If requested, allocate DMA buffers */
+ if (enc424j600_enable_dma) {
+ spi->dev.coherent_dma_mask = ~0;
+
+ /*
+ * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ * that much and share it between Tx and Rx DMA buffers.
+ */
+#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2
+#error "A problem in DMA buffer allocation"
+#endif
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE,
+ &priv->spi_tx_dma,
+ GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *) (priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ enc424j600_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!enc424j600_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_rx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (!enc424j600_chipset_init(dev)) {
+ if (netif_msg_probe(priv))
+ dev_info(&spi->dev, DRV_NAME " chip not found\n");
+ ret = -EIO;
+ goto error_irq;
+ }
+
+ /* Board setup must set the relevant edge trigger type;
+ * level triggers won't currently work.
+ */
+ ret = request_irq(spi->irq, enc424j600_irq, 0, DRV_NAME, priv);
+ if (ret < 0) {
+ if (netif_msg_probe(priv))
+ dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
+ "(ret = %d)\n", spi->irq, ret);
+ goto error_irq;
+ }
+
+ dev->if_port = IF_PORT_10BASET;
+ dev->irq = spi->irq;
+ dev->netdev_ops = &enc424j600_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ SET_ETHTOOL_OPS(dev, &enc424j600_ethtool_ops);
+
+ enc424j600_lowpower(priv, true);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ if (netif_msg_probe(priv))
+ dev_err(&spi->dev, "register netdev " DRV_NAME
+ " failed (ret = %d)\n", ret);
+ goto error_register;
+ }
+ dev_info(&dev->dev, DRV_NAME " driver registered\n");
+
+ return 0;
+
+error_register:
+ free_irq(spi->irq, priv);
+error_irq:
+ free_netdev(dev);
+ if (!enc424j600_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!enc424j600_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ if (enc424j600_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ return ret;
+}
+
+static int __devexit enc424j600_remove(struct spi_device *spi)
+{
+ struct enc424j600_net *priv = dev_get_drvdata(&spi->dev);
+
+ if (netif_msg_drv(priv))
+ printk(KERN_DEBUG DRV_NAME ": remove\n");
+
+ unregister_netdev(priv->netdev);
+ free_irq(spi->irq, priv);
+ free_netdev(priv->netdev);
+
+ return 0;
+}
+
+static struct spi_driver enc424j600_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = enc424j600_probe,
+ .remove = __devexit_p(enc424j600_remove),
+};
+
+static int __init enc424j600_init(void)
+{
+ msec20_to_jiffies = msecs_to_jiffies(20);
+ /*autoneg works from 1600ms */
+ msec2000_to_jiffies = msecs_to_jiffies(2000);
+
+ return spi_register_driver(&enc424j600_driver);
+}
+
+module_init(enc424j600_init);
+
+static void __exit enc424j600_exit(void)
+{
+ spi_unregister_driver(&enc424j600_driver);
+}
+
+module_exit(enc424j600_exit);
+
+MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
+MODULE_AUTHOR("Balaji Venkatachalam <balaji.v@thotakaa.com>");
+MODULE_LICENSE("GPL");
+module_param_named(debug, debug.msg_enable, int, 0);
+MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
+module_param(enc424j600_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(enc424j600_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+MODULE_ALIAS("spi:" DRV_NAME);
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600_hw.h
b/drivers/net/enc424j600_hw.h
--- a/drivers/net/enc424j600_hw.h 1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600_hw.h 2011-01-05 23:51:24.000000000 +0530
@@ -0,0 +1,460 @@
+/*
+* enc424j600_hw.h: Register definitions
+*
+*/
+
+#ifndef _ENC424J600_HW_H
+#define _ENC424J600_HW_H
+
+/*
+* ENC424J600 Control Registers
+* Control register definitions are a combination of address
+* and bank number
+* - Register address (bits 0-4)
+* - Bank number (bits 5-6)
+*/
+#define ADDR_MASK 0x1F
+#define BANK_MASK 0x60
+#define BANK_SHIFT 5
+
+/* All-bank registers */
+#define EUDASTL 0x16
+#define EUDASTH 0x17
+#define EUDANDL 0x18
+#define EUDANDH 0x19
+#define ESTATL 0x1A
+#define ESTATH 0x1B
+#define EIRL 0x1C
+#define EIRH 0x1D
+#define ECON1L 0x1E
+#define ECON1H 0x1F
+
+/* Bank 0 registers */
+#define ETXSTL (0x00 | 0x00)
+#define ETXSTH (0x01 | 0x00)
+#define ETXLENL (0x02 | 0x00)
+#define ETXLENH (0x03 | 0x00)
+#define ERXSTL (0x04 | 0x00)
+#define ERXSTH (0x05 | 0x00)
+#define ERXTAILL (0x06 | 0x00)
+#define ERXTAILH (0x07 | 0x00)
+#define ERXHEADL (0x08 | 0x00)
+#define ERXHEADH (0x09 | 0x00)
+#define EDMASTL (0x0A | 0x00)
+#define EDMASTH (0x0B | 0x00)
+#define EDMALENL (0x0C | 0x00)
+#define EDMALENH (0x0D | 0x00)
+#define EDMADSTL (0x0E | 0x00)
+#define EDMADSTH (0x0F | 0x00)
+#define EDMACSL (0x10 | 0x00)
+#define EDMACSH (0x11 | 0x00)
+#define ETXSTATL (0x12 | 0x00)
+#define ETXSTATH (0x13 | 0x00)
+#define ETXWIREL (0x14 | 0x00)
+#define ETXWIREH (0x15 | 0x00)
+
+/* Bank 1 registers */
+#define EHT1L (0x00 | 0x20)
+#define EHT1H (0x01 | 0x20)
+#define EHT2L (0x02 | 0x20)
+#define EHT2H (0x03 | 0x20)
+#define EHT3L (0x04 | 0x20)
+#define EHT3H (0x05 | 0x20)
+#define EHT4L (0x06 | 0x20)
+#define EHT4H (0x07 | 0x20)
+#define EPMM1L (0x08 | 0x20)
+#define EPMM1H (0x09 | 0x20)
+#define EPMM2L (0x0A | 0x20)
+#define EPMM2H (0x0B | 0x20)
+#define EPMM3L (0x0C | 0x20)
+#define EPMM3H (0x0D | 0x20)
+#define EPMM4L (0x0E | 0x20)
+#define EPMM4H (0x0F | 0x20)
+#define EPMCSL (0x10 | 0x20)
+#define EPMCSH (0x11 | 0x20)
+#define EPMOL (0x12 | 0x20)
+#define EPMOH (0x13 | 0x20)
+#define ERXFCONL (0x14 | 0x20)
+#define ERXFCONH (0x15 | 0x20)
+
+/* Bank 2 registers */
+#define MACON1L (0x00 | 0x40)
+#define MACON1H (0x01 | 0x40)
+#define MACON2L (0x02 | 0x40)
+#define MACON2H (0x03 | 0x40)
+#define MABBIPGL (0x04 | 0x40)
+#define MABBIPGH (0x05 | 0x40)
+#define MAIPGL (0x06 | 0x40)
+#define MAIPGH (0x07 | 0x40)
+#define MACLCONL (0x08 | 0x40)
+#define MACLCONH (0x09 | 0x40)
+#define MAMXFLL (0x0A | 0x40)
+#define MAMXFLH (0x0B | 0x40)
+#define MICMDL (0x12 | 0x40)
+#define MICMDH (0x13 | 0x40)
+#define MIREGADRL (0x14 | 0x40)
+#define MIREGADRH (0x15 | 0x40)
+
+/* Bank 3 registers */
+#define MAADR3L (0x00 | 0x60)
+#define MAADR3H (0x01 | 0x60)
+#define MAADR2L (0x02 | 0x60)
+#define MAADR2H (0x03 | 0x60)
+#define MAADR1L (0x04 | 0x60)
+#define MAADR1H (0x05 | 0x60)
+#define MIWRL (0x06 | 0x60)
+#define MIWRH (0x07 | 0x60)
+#define MIRDL (0x08 | 0x60)
+#define MIRDH (0x09 | 0x60)
+#define MISTATL (0x0A | 0x60)
+#define MISTATH (0x0B | 0x60)
+#define EPAUSL (0x0C | 0x60)
+#define EPAUSH (0x0D | 0x60)
+#define ECON2L (0x0E | 0x60)
+#define ECON2H (0x0F | 0x60)
+#define ERXWML (0x10 | 0x60)
+#define ERXWMH (0x11 | 0x60)
+#define EIEL (0x12 | 0x60)
+#define EIEH (0x13 | 0x60)
+#define EIDLEDL (0x14 | 0x60)
+#define EIDLEDH (0x15 | 0x60)
+
+/* Unbanked registers */
+#define EGPDATA (0x00 | 0x80)
+#define ERXDATA (0x02 | 0x80)
+#define EUDADATA (0x04 | 0x80)
+#define EGPRDPTL (0x06 | 0x80)
+#define EGPRDPTH (0x07 | 0x80)
+#define EGPWRPTL (0x08 | 0x80)
+#define EGPWRPTH (0x09 | 0x80)
+#define ERXRDPTL (0x0A | 0x80)
+#define ERXRDPTH (0x0B | 0x80)
+#define ERXWRPTL (0x0C | 0x80)
+#define ERXWRPTH (0x0D | 0x80)
+#define EUDARDPTL (0x0E | 0x80)
+#define EUDARDPTH (0x0F | 0x80)
+#define EUDAWRPTL (0x10 | 0x80)
+#define EUDAWRPTH (0x11 | 0x80)
+
+/* PHY registers */
+#define PHCON1 0x00
+#define PHSTAT1 0x01
+#define PHANA 0x04
+#define PHANLPA 0x05
+#define PHANE 0x06
+#define PHCON2 0x11
+#define PHSTAT2 0x1B
+#define PHSTAT3 0x1F
+
+/* Single-byte instructions */
+#define BXSEL(bank) (0xC0 + (bank & (BANK_MASK >> BANK_SHIFT)) * 2)
+/* Bank X Select */
+#define B0SEL 0xC0 /* Bank 0 Select */
+#define B1SEL 0xC2 /* Bank 1 Select */
+#define B2SEL 0xC4 /* Bank 2 Select */
+#define B3SEL 0xC6 /* Bank 3 Select */
+#define SETETHRST 0xCA /* System Reset */
+#define FCDISABLE 0xE0 /* Flow Control Disable */
+#define FCSINGLE 0xE2 /* Flow Control Single */
+#define FCMULTIPLE 0xE4 /* Flow Control Multiple */
+#define FCCLEAR 0xE6 /* Flow Control Clear */
+#define SETPKTDEC 0xCC /* Decrement Packet Counter */
+#define DMASTOP 0xD2 /* DMA Stop */
+#define DMACKSUM 0xD8 /* DMA Start Checksum */
+#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */
+#define DMACOPY 0xDC /* DMA Start Copy */
+#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */
+#define SETTXRTS 0xD4 /* Request Packet Transmission */
+#define ENABLERX 0xE8 /* Enable RX */
+#define DISABLERX 0xEA /* Disable RX */
+#define SETEIE 0xEC /* Enable Interrupts */
+#define CLREIE 0xEE /* Disable Interrupts */
+
+/* Two byte instructions */
+#define RBSEL 0xC8 /* Read Bank Select */
+
+/* Three byte instructions */
+#define WGPRDPT 0x60 /* Write EGPRDPT */
+#define RGPRDPT 0x62 /* Read EGPRDPT */
+#define WRXRDPT 0x64 /* Write ERXRDPT */
+#define RRXRDPT 0x66 /* Read ERXRDPT */
+#define WUDARDPT 0x68 /* Write EUDARDPT */
+#define RUDARDPT 0x6A /* Read EUDARDPT */
+#define WGPWRPT 0x6C /* Write EGPWRPT */
+#define RGPWRPT 0x6E /* Read EGPWRPT */
+#define WRXWRPT 0x70 /* Write ERXWRPT */
+#define RRXWRPT 0x72 /* Read ERXWRPT */
+#define WUDAWRPT 0x74 /* Write EUDAWRPT */
+#define RUDAWRPT 0x76 /* Read EUDAWRPT */
+
+/* n byte instructions */
+#define RCR(addr) (0x00 | (addr & ADDR_MASK)) /* Read Control Register */
+#define WCR(addr) (0x40 | (addr & ADDR_MASK)) /* Write Control Register */
+#define RCRU 0x20 /* Read Control Register Unbanked */
+#define WCRU 0x22 /* Write Control Register Unbanked */
+#define BFS(addr) (0x80 | (addr & ADDR_MASK)) /* Bit Field Set */
+#define BFC(addr) (0xA0 | (addr & ADDR_MASK)) /* Bit Field Clear */
+#define BFSU 0x24 /* Bit Field Set Unbanked */
+#define BFCU 0x26 /* Bit Field Clear Unbanked */
+#define RGPDATA 0x28 /* Read EGPDATA */
+#define WGPDATA 0x2A /* Write EGPDATA */
+#define RRXDATA 0x2C /* Read ERXDATA */
+#define WRXDATA 0x2E /* Write ERXDATA */
+#define RUDADATA 0x30 /* Read EUDADATA */
+#define WUDADATA 0x32 /* Write EUDADATA */
+
+/* Register bit definitions */
+/* ESTATH */
+#define INT (1 << 7)
+#define FCIDLE (1 << 6)
+#define RXBUSY (1 << 5)
+#define CLKRDY (1 << 4)
+#define PHYDPX (1 << 2)
+#define PHYLNK (1 << 0)
+
+/* EIRH */
+/*for ease of use lets access it as a word*/
+#define CRYPTEN (1 << 15)
+#define MODEXIF (1 << 14)
+#define HASHIF (1 << 13)
+#define AESIF (1 << 12)
+#define LINKIF (1 << 11)
+
+/* EIRL */
+#define PKTIF (1 << 6)
+#define DMAIF (1 << 5)
+#define TXIF (1 << 3)
+#define TXABTIF (1 << 2)
+#define RXABTIF (1 << 1)
+#define PCFULIF (1 << 0)
+
+/* ECON1H */
+#define MODEXST (1 << 7)
+#define HASHEN (1 << 6)
+#define HASHOP (1 << 5)
+#define HASHLST (1 << 4)
+#define AESST (1 << 3)
+#define AESOP1 (1 << 2)
+#define AESOP0 (1 << 1)
+#define PKTDEC (1 << 0)
+
+/* ECON1L */
+#define FCOP1 (1 << 7)
+#define FCOP0 (1 << 6)
+#define DMAST (1 << 5)
+#define DMACPY (1 << 4)
+#define DMACSSD (1 << 3)
+#define DMANOCS (1 << 2)
+#define TXRTS (1 << 1)
+#define RXEN (1 << 0)
+
+/* ETXSTATH */
+#define LATECOL (1 << 2)
+#define MAXCOL (1 << 1)
+#define EXDEFER (1 << 0)
+
+/* ETXSTATL */
+#define ETXSTATL_DEFER (1 << 7)
+#define CRCBAD (1 << 4)
+#define COLCNT_MASK 0xF
+
+/* ERXFCONH */
+#define HTEN (1 << 7)
+#define MPEN (1 << 6)
+#define NOTPM (1 << 4)
+#define PMEN3 (1 << 3)
+#define PMEN2 (1 << 2)
+#define PMEN1 (1 << 1)
+#define PMEN0 (1 << 0)
+
+/* ERXFCONL */
+#define CRCEEN (1 << 7)
+#define CRCEN (1 << 6)
+#define RUNTEEN (1 << 5)
+#define RUNTEN (1 << 4)
+#define UCEN (1 << 3)
+#define NOTMEEN (1 << 2)
+#define MCEN (1 << 1)
+#define BCEN (1 << 0)
+/*no bytewise access*/
+/* MACON1L */
+#define LOOPBK (1 << 4)
+#define RXPAUS (1 << 2)
+#define PASSALL (1 << 1)
+
+/* MACON2 */
+#define MACON2_DEFER (1 << 14)
+#define BPEN (1 << 13)
+#define NOBKOFF (1 << 12)
+#define PADCFG2 (1 << 7)
+#define PADCFG1 (1 << 6)
+#define PADCFG0 (1 << 5)
+#define TXCRCEN (1 << 4)
+#define PHDREN (1 << 3)
+#define HFRMEN (1 << 2)
+#define FULDPX (1 << 0)
+
+/* MAIPG */
+/* value of the high byte is given by the reserved bits,
+* value of the low byte is recomended setting of the
+* IPG parameter.
+*/
+#define MAIPGH_VAL 0x0C
+#define MAIPGL_VAL 0x12
+
+/* MIREGADRH */
+#define MIREGADRH_VAL 0x01
+
+/* MIREGADRL */
+#define PHREG_MASK 0x1F
+
+/* MICMDL */
+#define MIISCAN (1 << 1)
+#define MIIRD (1 << 0)
+
+/* MISTATL */
+#define NVALID (1 << 2)
+#define SCAN (1 << 1)
+#define BUSY (1 << 0)
+
+/* ECON2H */
+#define ETHEN (1 << 7)
+#define STRCH (1 << 6)
+#define TXMAC (1 << 5)
+#define SHA1MD5 (1 << 4)
+#define COCON3 (1 << 3)
+#define COCON2 (1 << 2)
+#define COCON1 (1 << 1)
+#define COCON0 (1 << 0)
+
+/* ECON2L */
+#define AUTOFC (1 << 7)
+#define TXRST (1 << 6)
+#define RXRST (1 << 5)
+#define ETHRST (1 << 4)
+#define MODLEN1 (1 << 3)
+#define MODLEN0 (1 << 2)
+#define AESLEN1 (1 << 1)
+#define AESLEN0 (1 << 0)
+
+/* EIEH */
+#define INTIE (1 << 7)
+#define MODEXIE (1 << 6)
+#define HASHIE (1 << 5)
+#define AESIE (1 << 4)
+#define LINKIE (1 << 3)
+
+/* EIEL */
+#define PKTIE (1 << 6)
+#define DMAIE (1 << 5)
+#define TXIE (1 << 3)
+#define TXABTIE (1 << 2)
+#define RXABTIE (1 << 1)
+#define PCFULIE (1 << 0)
+
+/* EIDLEDH */
+#define LACFG3 (1 << 7)
+#define LACFG2 (1 << 6)
+#define LACFG1 (1 << 5)
+#define LACFG0 (1 << 4)
+#define LBCFG3 (1 << 3)
+#define LBCFG2 (1 << 2)
+#define LBCFG1 (1 << 1)
+#define LBCFG0 (1 << 0)
+
+/* EIDLEDL */
+#define DEVID_SHIFT 5
+#define DEVID_MASK (0x7 << DEVID_SHIFT)
+#define REVID_SHIFT 0
+#define REVID_MASK (0x1F << REVID_SHIFT)
+
+/* PHANA */
+/* Default value for PHY initialization*/
+#define PHANA_DEFAULT 0x05E1
+
+/* PHCON1 */
+#define PRST (1 << 15)
+#define PLOOPBK (1 << 14)
+#define SPD100 (1 << 13)
+#define ANEN (1 << 12)
+#define PSLEEP (1 << 11)
+#define RENEG (1 << 9)
+#define PFULDPX (1 << 8)
+
+/* PHSTAT */
+#define FULL100 (1 << 14)
+#define HALF100 (1 << 13)
+#define FULL10 (1 << 12)
+#define HALF10 (1 << 11)
+#define ANDONE (1 << 5)
+#define LRFAULT (1 << 4)
+#define ANABLE (1 << 3)
+#define LLSTAT (1 << 2)
+#define EXTREGS (1 << 0)
+
+#define EUDAST_TEST_VAL 0x1234
+
+#define TSV_SIZE 7
+
+#define ENC424J600_DEV_ID 0x1
+
+/* Configuration */
+
+/* Led is on when the link is present and driven low
+* temporarily when packet is TX'd or RX'd */
+#define LED_A_SETTINGS 0xC
+
+/* Led is on if the link is in 100 Mbps mode */
+#define LED_B_SETTINGS 0x8
+
+/* maximum ethernet frame length
+* Currently not used as a limit anywhere
+* (we're using the "huge frame enable" feature of
+* enc424j600). */
+#define MAX_FRAMELEN 1518
+
+/* Size in bytes of the receive buffer in enc424j600.
+* Must be word aligned (even).
+*/
+#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN)
+
+/* Start of the general purpose area in sram */
+#define SRAM_GP_START 0x0
+
+/* SRAM size */
+#define SRAM_SIZE 0x6000
+
+/* Start of the receive buffer */
+#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE)
+
+#define RSV_RXLONGEVDROPEV 16
+#define RSV_CARRIEREV 18
+#define RSV_CRCERROR 20
+#define RSV_LENCHECKERR 21
+#define RSV_LENOUTOFRANGE 22
+#define RSV_RXOK 23
+#define RSV_RXMULTICAST 24
+#define RSV_RXBROADCAST 25
+#define RSV_DRIBBLENIBBLE 26
+#define RSV_RXCONTROLFRAME 27
+#define RSV_RXPAUSEFRAME 28
+#define RSV_RXUNKNOWNOPCODE 29
+#define RSV_RXTYPEVLAN 30
+
+#define RSV_RUNTFILTERMATCH 31
+#define RSV_NOTMEFILTERMATCH 32
+#define RSV_HASHFILTERMATCH 33
+#define RSV_MAGICPKTFILTERMATCH 34
+#define RSV_PTRNMTCHFILTERMATCH 35
+#define RSV_UNICASTFILTERMATCH 36
+
+#define RSV_SIZE 8
+#define RSV_BITMASK(x) (1 << ((x) - 16))
+#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0)
+
+/* Put RX buffer at 0 as suggested by the Errata datasheet */
+
+#define RXSTART_INIT ERXST_VAL
+#define RXEND_INIT 0x5FFF
+
+#endif
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Kconfig
b/drivers/net/Kconfig
--- a/drivers/net/Kconfig 2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Kconfig 2011-01-16 15:26:16.000000000 +0530
@@ -973,6 +973,16 @@ config ENC28J60_WRITEVERIFY
Enable the verify after the buffer write useful for debugging purpose.
If unsure, say N.
+config ENC424J600
+ tristate "ENC424J600 support"
+ depends on EXPERIMENTAL && SPI && NET_ETHERNET
+ select CRC32
+ ---help---
+ Support for the Microchip EN424J600 ethernet chip.
+
+ To compile this driver as a module, choose M here. The module will be
+ called enc424j600.
+
config ETHOC
tristate "OpenCores 10/100 Mbps Ethernet MAC support"
depends on NET_ETHERNET && HAS_IOMEM
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Makefile
b/drivers/net/Makefile
--- a/drivers/net/Makefile 2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Makefile 2011-01-05 21:46:57.000000000 +0530
@@ -240,6 +240,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_d
pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ENC424J600) += enc424j600.o
obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
^ permalink raw reply
* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Francois Romieu @ 2011-01-29 11:58 UTC (permalink / raw)
To: Balaji Venkatachalam
Cc: netdev, mohan, blue.cube, lanconelli.claudio, Sriram Subramanian,
vbalaji.acs
In-Reply-To: <AANLkTikSNjq7Ncp4=hLp7-_cayyMad0uQzuHehPr1kSf@mail.gmail.com>
Balaji Venkatachalam <balaji.v@thotakaa.com> :
[...]
> +static int enc424j600_spi_trans(struct enc424j600_net *priv, int len)
> +{
> + /*modified to suit half duplexed spi */
> + struct spi_transfer tt = {
> + .tx_buf = priv->spi_tx_buf,
> + .len = SPI_OPLEN,
> + };
> + struct spi_transfer tr = {
> + .rx_buf = priv->spi_rx_buf,
> + .len = len,
> + };
> + struct spi_message m;
> + int ret;
> +
> + spi_message_init(&m);
> +
> + spi_message_add_tail(&tt, &m);
> + spi_message_add_tail(&tr, &m);
> +
> + ret = spi_sync(priv->spi, &m);
> +
> + if (ret == 0)
> + memcpy(priv->spi_rx_buf, tr.rx_buf, len);
> +
> + if (ret)
> + dev_err(&priv->spi->dev,
> + "spi transfer failed: ret = %d\n", ret);
> + return ret;
if (ret) {
dev_err(&priv->spi->dev,
"spi transfer failed: ret = %d\n", ret);
goto out;
}
memcpy(priv->spi_rx_buf, tr.rx_buf, len);
out:
return ret;
> +}
> +
> +/*
> + * Read data from chip SRAM.
> + * window = 0 for Receive Buffer
> + * = 1 for User Defined area
> + * = 2 for General Purpose area
> + */
> +static int enc424j600_read_sram(struct enc424j600_net *priv,
> + u8 *dst, int len, u16 srcaddr, int window)
> +{
> + int ret;
> +
> + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
> + return -EINVAL;
> +
> + /* First set the write pointer as per selected window */
> + if (window == RXWINDOW)
> + priv->spi_tx_buf[0] = WRXRDPT;
> + else if (window == USERWINDOW)
> + priv->spi_tx_buf[0] = WUDARDPT;
> + else if (window == GPWINDOW)
> + priv->spi_tx_buf[0] = WGPRDPT;
> +
> + priv->spi_tx_buf[1] = srcaddr & 0xFF;
> + priv->spi_tx_buf[2] = srcaddr >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> +
> + /* Transfer the data */
> + if (window == RXWINDOW)
> + priv->spi_tx_buf[0] = RRXDATA;
> + else if (window == USERWINDOW)
> + priv->spi_tx_buf[0] = RUDADATA;
> + else if (window == GPWINDOW)
> + priv->spi_tx_buf[0] = RGPDATA;
May be a local :
u8 *tx_buf = priv->spi_tx_buf;
> +
> + ret = enc424j600_spi_trans(priv, len + 1);
> + /*READ*/
> + /* Copy the data from the rx buffer */
^^^^
> + memcpy(dst, &priv->spi_rx_buf[0], len);
^^^^
tab vs spaces
> +
> + return ret;
> +}
> +
> +/*
> + * Write data to chip SRAM.
> + * window = 1 for RX
> + * window = 2 for User Data
> + * window = 3 for GP
> + */
> +static int enc424j600_write_sram(struct enc424j600_net *priv,
> + const u8 *src, int len, u16 dstaddr,
> + int window)
> +{
> + int ret;
> +
> + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
> + return -EINVAL;
> +
> + /* First set the general purpose write pointer */
> + if (window == RXWINDOW)
> + priv->spi_tx_buf[0] = WRXWRPT;
> + else if (window == USERWINDOW)
> + priv->spi_tx_buf[0] = WUDAWRPT;
> + else if (window == GPWINDOW)
> + priv->spi_tx_buf[0] = WGPWRPT;
> +
> + priv->spi_tx_buf[1] = dstaddr & 0xFF;
> + priv->spi_tx_buf[2] = dstaddr >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> +
> + /* Copy the data to the tx buffer */
> + memcpy(&priv->spi_tx_buf[1], src, len);
> +
> + /* Transfer the data */
> + if (window == RXWINDOW)
> + priv->spi_tx_buf[0] = WRXDATA;
> + else if (window == USERWINDOW)
> + priv->spi_tx_buf[0] = WUDADATA;
> + else if (window == GPWINDOW)
> + priv->spi_tx_buf[0] = WGPDATA;
> +
> + ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1);
> +
> + return ret;
> +}
> +
> +/*
> + * Select the current register bank if necessary to be able to read @addr.
> + */
> +static void enc424j600_set_bank(struct enc424j600_net *priv, u8 addr)
> +{
> + u8 b = (addr & BANK_MASK) >> BANK_SHIFT;
> +
> + /* These registers are present in all banks, no need to switch bank */
> + if (addr >= EUDASTL && addr <= ECON1H)
> + return;
> + if (priv->bank == b)
> + return;
> +
> + priv->spi_tx_buf[0] = BXSEL(b);
> +
> + enc424j600_spi_trans(priv, 1);
> + /*WRITE*/ priv->bank = b;
Please put this comment on a separate line (or remove it completely ?).
> +}
> +
> +/*
> + * Set bits in an 8bit SFR.
> + */
> +static void enc424j600_set_bits(struct enc424j600_net *priv, u8 addr, u8 mask)
> +{
> + enc424j600_set_bank(priv, addr);
> + priv->spi_tx_buf[0] = BFS(addr);
> + priv->spi_tx_buf[1] = mask;
> + spi_write(priv->spi, priv->spi_tx_buf, 2);
> +}
static void enc424j600_write_bits(struct enc424j600_net *priv, u8 addr,
u8 bits, u8 mask)
{
enc424j600_set_bank(priv, addr);
priv->spi_tx_buf[0] = bits;
priv->spi_tx_buf[1] = mask;
spi_write(priv->spi, priv->spi_tx_buf, 2);
}
enc424j600_write_bits(priv, addr, BFS(addr), mask); ?
> +
> +/*
> + * Clear bits in an 8bit SFR.
> + */
> +static void enc424j600_clear_bits(struct enc424j600_net *priv, u8
> addr, u8 mask)
> +{
> + enc424j600_set_bank(priv, addr);
> + priv->spi_tx_buf[0] = BFC(addr);
> + priv->spi_tx_buf[1] = mask;
> + spi_write(priv->spi, priv->spi_tx_buf, 2);
> +}
enc424j600_write_bits(priv, addr, BFC(addr), mask); ?
> +
> +/*
> + * Write a 8bit special function register.
> + * The @sfr parameters takes address of the register.
> + * Uses banked write instruction.
> + */
> +static int enc424j600_write_8b_sfr(struct enc424j600_net *priv, u8
> sfr, u8 data)
> +{
> + int ret;
> + enc424j600_set_bank(priv, sfr);
Please add an empty line after 'int ret'.
> +
> + priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
> + priv->spi_tx_buf[1] = data & 0xFF;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 2);
> +
> + return ret;
> +}
> +
> +/*
> + * Read a 8bit special function register.
> + * The @sfr parameters takes address of the register.
> + * Uses banked read instruction.
> + */
> +static int enc424j600_read_8b_sfr(struct enc424j600_net *priv,
> + u8 sfr, u8 *data)
> +{
> + int ret;
> +
> + enc424j600_set_bank(priv, sfr);
> + priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
> + ret = enc424j600_spi_trans(priv, 2);
> + /*READ*/ *data = priv->spi_rx_buf[0];
Please put this comment on a separate line (or remove it completely ?).
> +
> + return ret;
> +}
> +
> +/*
> + * Write a 16bit special function register.
> + * The @sfr parameters takes address of the low byte of the register.
> + * Takes care of the endiannes & buffers.
> + * Uses banked write instruction.
> + */
> +
> +static int enc424j600_write_16b_sfr(struct enc424j600_net *priv,
> + u8 sfr, u16 data)
> +{
> + int ret;
> + enc424j600_set_bank(priv, sfr);
Please add an empty line after 'int ret'.
> +
> + priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
> + priv->spi_tx_buf[1] = data & 0xFF;
> + priv->spi_tx_buf[2] = data >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> + if (ret && netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
> + __func__, ret);
Use netif_err ?
> +
> + return ret;
> +}
> +
> +/*
> + * Read a 16bit special function register.
> + * The @sfr parameters takes address of the low byte of the register.
> + * Takes care of the endiannes & buffers.
> + * Uses banked read instruction.
> + */
> +static int enc424j600_read_16b_sfr(struct enc424j600_net *priv,
> + u8 sfr, u16 *data)
> +{
> + int ret;
> + enc424j600_set_bank(priv, sfr);
Please add an empty line after 'int ret'.
> +
> + priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
> + priv->spi_tx_buf[1] = 0;
> + priv->spi_tx_buf[2] = 0;
> + priv->spi_tx_buf[3] = 0;
> + ret = enc424j600_spi_trans(priv, 3);
> + /*READ*/ *data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8;
Please put this comment on a separate line (or remove it completely ?).
> +
> + return ret;
> +}
> +
> +static unsigned long msec20_to_jiffies;
> +
> +/*
> + * Wait for bits in register to become equal to @readyMask, but at most 20ms.
> + */
> +static int checktimeout_16bit(struct enc424j600_net *priv,
> + u8 reg, u16 mask, u16 readyMask)
> +{
> + unsigned long timeout = jiffies + msec20_to_jiffies;
> + u16 value;
Please add an empty line after 'u16 value'.
> + /* 20 msec timeout read */
> + enc424j600_read_16b_sfr(priv, reg, &value);
> + while ((value & mask) != readyMask) {
> + if (time_after(jiffies, timeout)) {
> + if (netif_msg_drv(priv))
> + dev_dbg(&priv->spi->dev,
> + "reg %02x ready timeout!\n", reg);
Use netif_err (or friend).
> + return -ETIMEDOUT;
> + }
> + cpu_relax();
> + enc424j600_read_16b_sfr(priv, reg, &value);
> + }
> +
> + return 0;
> +}
> +
> +static int checktimeout_8bit(struct enc424j600_net *priv,
> + u8 reg, u8 mask, u8 readyMask)
> +{
> + unsigned long timeout = jiffies + msec20_to_jiffies;
> + u8 value;
Please add an empty line after 'u8 value'.
> + /* 20 msec timeout read */
> + enc424j600_read_8b_sfr(priv, reg, &value);
> + while ((value & mask) != readyMask) {
> + if (time_after(jiffies, timeout)) {
> + if (netif_msg_drv(priv))
> + dev_dbg(&priv->spi->dev,
> + "reg %02x ready timeout!\n", reg);
> + return -ETIMEDOUT;
> + }
> + cpu_relax();
> + enc424j600_read_8b_sfr(priv, reg, &value);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Reset the enc424j600.
> + */
> +static int enc424j600_soft_reset(struct enc424j600_net *priv)
> +{
> + int ret;
> + u16 eudast;
> + if (netif_msg_hw(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
Please add an empty line after 'u16 eudast'.
[...]
> +/*Debug routine to dump useful register contents*/
> +static void enc424j600_dump_regs(struct enc424j600_net *priv, const char *msg)
> +{
[...]
Could the same goal be achieved with ethtool_ops.get_regs ?
Otherwise you can grep for debugfs.h below drivers/net.
[...]
> +static int enc424j600_net_open(struct net_device *dev)
> +{
> + struct enc424j600_net *priv = netdev_priv(dev);
> +
> + if (netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + if (netif_msg_ifup(priv))
> + dev_err(&dev->dev, "invalid MAC address %pM\n",
> + dev->dev_addr);
> + return -EADDRNOTAVAIL;
> + }
> + /* Reset the hardware here (and take it out of low power mode) */
> + enc424j600_lowpower(priv, false);
> + enc424j600_hw_disable(priv);
> + if (!enc424j600_hw_init(priv)) {
> + if (netif_msg_ifup(priv))
> + dev_err(&dev->dev, "hw_reset() failed\n");
> + return -EINVAL;
Propagate the return status code of enc424j600_hw_init ?
> + }
> + /* Update the MAC address (in case user has changed it) */
> + enc424j600_set_hw_macaddr(dev);
> + /* Enable interrupts */
> + enc424j600_hw_enable(priv);
> + /* check link status */
> + enc424j600_check_link_status(priv);
> + /* We are now ready to accept transmit requests from
> + * the queueing layer of the networking.
> + */
> + netif_start_queue(dev);
> +
> + return 0;
> +}
[...]
> +static const struct ethtool_ops enc424j600_ethtool_ops = {
> + .get_settings = enc424j600_get_settings,
> + .set_settings = enc424j600_set_settings,
> + .get_drvinfo = enc424j600_get_drvinfo,
> + .get_msglevel = enc424j600_get_msglevel,
> + .set_msglevel = enc424j600_set_msglevel,
Please <tab>=<space>
> +};
> +
> +static int enc424j600_chipset_init(struct net_device *dev)
> +{
> + struct enc424j600_net *priv = netdev_priv(dev);
> +
> + enc424j600_get_hw_macaddr(dev);
> + return enc424j600_hw_init(priv);
> +
> +}
Remove the empty line after the return statement.
> +
> +static const struct net_device_ops enc424j600_netdev_ops = {
> + .ndo_open = enc424j600_net_open,
> + .ndo_stop = enc424j600_net_close,
> + .ndo_start_xmit = enc424j600_send_packet,
> + .ndo_set_multicast_list = enc424j600_set_multicast_list,
> + .ndo_set_mac_address = enc424j600_set_mac_address,
> + .ndo_tx_timeout = enc424j600_tx_timeout,
> + .ndo_change_mtu = eth_change_mtu,
> + .ndo_validate_addr = eth_validate_addr,
> +};
> +
> +static int __devinit enc424j600_probe(struct spi_device *spi)
> +{
> + struct net_device *dev;
> + struct enc424j600_net *priv;
> + int ret = 0;
int ret = -ENOMEM;
Then simplify code below.
> +
> + if (netif_msg_drv(&debug))
> + dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
> + DRV_VERSION);
> +
> + dev = alloc_etherdev(sizeof(struct enc424j600_net));
> + if (!dev) {
> + if (netif_msg_drv(&debug))
> + dev_err(&spi->dev, DRV_NAME
> + ": unable to alloc new ethernet\n");
> + ret = -ENOMEM;
> + goto error_alloc;
> + }
> + priv = netdev_priv(dev);
> +
> + priv->netdev = dev; /* priv to netdev reference */
> + priv->spi = spi; /* priv to spi reference */
> + priv->msg_enable = netif_msg_init(debug.msg_enable,
> + ENC424J600_MSG_DEFAULT);
> + mutex_init(&priv->lock);
> + INIT_WORK(&priv->tx_work, enc424j600_tx_work_handler);
> + INIT_WORK(&priv->setrx_work, enc424j600_setrx_work_handler);
> + INIT_WORK(&priv->irq_work, enc424j600_irq_work_handler);
> + INIT_WORK(&priv->restart_work, enc424j600_restart_work_handler);
> + dev_set_drvdata(&spi->dev, priv); /* spi to priv reference */
The three comments above are useless.
> + SET_NETDEV_DEV(dev, &spi->dev);
> + /*TODO: chip DMA features to be utilized */
> + /* If requested, allocate DMA buffers */
> + if (enc424j600_enable_dma) {
> + spi->dev.coherent_dma_mask = ~0;
> +
> + /*
> + * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
> + * that much and share it between Tx and Rx DMA buffers.
> + */
> +#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2
> +#error "A problem in DMA buffer allocation"
> +#endif
> + priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
> + PAGE_SIZE,
> + &priv->spi_tx_dma,
> + GFP_DMA);
> +
> + if (priv->spi_tx_buf) {
> + priv->spi_rx_buf = (u8 *) (priv->spi_tx_buf +
> + (PAGE_SIZE / 2));
priv->spi_rx_buf =
(u8 *) (priv->spi_tx_buf + (PAGE_SIZE / 2));
> + priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma +
> + (PAGE_SIZE / 2));
> + } else {
> + /* Fall back to non-DMA */
> + enc424j600_enable_dma = 0;
> + }
> + }
> +
> + /* Allocate non-DMA buffers */
> + if (!enc424j600_enable_dma) {
> + priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
> + if (!priv->spi_tx_buf) {
> + ret = -ENOMEM;
> + goto error_tx_buf;
Please:
goto error_what_must_be_done;
instead of:
goto error_where_it_comes_from;
> + }
> + priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
> + if (!priv->spi_rx_buf) {
> + ret = -ENOMEM;
> + goto error_rx_buf;
> + }
> + }
> +
> + if (!enc424j600_chipset_init(dev)) {
> + if (netif_msg_probe(priv))
> + dev_info(&spi->dev, DRV_NAME " chip not found\n");
> + ret = -EIO;
> + goto error_irq;
> + }
> +
> + /* Board setup must set the relevant edge trigger type;
> + * level triggers won't currently work.
> + */
> + ret = request_irq(spi->irq, enc424j600_irq, 0, DRV_NAME, priv);
> + if (ret < 0) {
> + if (netif_msg_probe(priv))
> + dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
> + "(ret = %d)\n", spi->irq, ret);
> + goto error_irq;
> + }
> +
> + dev->if_port = IF_PORT_10BASET;
> + dev->irq = spi->irq;
> + dev->netdev_ops = &enc424j600_netdev_ops;
> + dev->watchdog_timeo = TX_TIMEOUT;
> + SET_ETHTOOL_OPS(dev, &enc424j600_ethtool_ops);
> +
> + enc424j600_lowpower(priv, true);
> +
> + ret = register_netdev(dev);
> + if (ret) {
> + if (netif_msg_probe(priv))
> + dev_err(&spi->dev, "register netdev " DRV_NAME
> + " failed (ret = %d)\n", ret);
> + goto error_register;
> + }
> + dev_info(&dev->dev, DRV_NAME " driver registered\n");
> +
> + return 0;
out:
return ret;
> +
> +error_register:
> + free_irq(spi->irq, priv);
> +error_irq:
> + free_netdev(dev);
> + if (!enc424j600_enable_dma)
> + kfree(priv->spi_rx_buf);
> +error_rx_buf:
> + if (!enc424j600_enable_dma)
> + kfree(priv->spi_tx_buf);
> +error_tx_buf:
> + if (enc424j600_enable_dma) {
> + dma_free_coherent(&spi->dev, PAGE_SIZE,
> + priv->spi_tx_buf, priv->spi_tx_dma);
> + }
> +error_alloc:
> + return ret;
> +}
--
Ueimor
^ permalink raw reply
* ipheth problem in 2.6.36 and 2.6.37
From: Piotr Isajew @ 2011-01-29 17:42 UTC (permalink / raw)
To: diego; +Cc: gregkh, netdev
[-- Attachment #1: Type: text/plain, Size: 1464 bytes --]
Hi,
It looks for me that there is something wrong with ipheth
driver. In 2.6.35 it worked fine. However it doesn't work after
upgrade to 2.6.36 or 2.6.37:
Driver detects the iPhone and attaches it as eth interface:
[856636.512167] usb 1-1: Product: iPhone
[856636.512172] usb 1-1: Manufacturer: Apple Inc.
[856636.512178] usb 1-1: SerialNumber: 48......
[856636.512647] usb 1-1: usb_probe_device
[856636.512658] usb 1-1: configuration #1 chosen from 4 choices
[856636.513769] usb 1-1: adding 1-1:1.0 (config #1, interface 0)
[856636.514051] drivers/usb/core/inode.c: creating file '014'
[856636.514130] hub 1-0:1.0: state 7 ports 6 chg 0000 evt 0002
[856636.645496] usb 1-1: unregistering interface 1-1:1.0
[856636.645865] usb 1-1: usb_disable_device nuking non-ep0 URBs
[856636.648389] usb 1-1: adding 1-1:4.0 (config #4, interface 0)
[856636.648715] usb 1-1: adding 1-1:4.1 (config #4, interface 1)
[856636.648956] usb 1-1: adding 1-1:4.2 (config #4, interface 2)
[856637.488748] ipheth 1-1:4.2: usb_probe_interface
[856637.488760] ipheth 1-1:4.2: usb_probe_interface - got id
[856637.513964] ipheth 1-1:4.2: Apple iPhone USB Ethernet device attached
[856637.515377] usbcore: registered new interface driver ipheth
I'm sure that device pairing goes well -- bringing eth interface
"up" activates Internet modem mode on the phone. However it's not
possible to get an IP address for the interface. It's supposed to
be assigned by DHCP, but requests time out.
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* [PATCH net-2.6] slcan: fix referenced website in Kconfig help text
From: Oliver Hartkopp @ 2011-01-29 18:08 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List
Fix the referenced project website to www.mictronics.de in the Kconfig help
text for the slcan driver.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 986195e..5dec456 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -23,7 +23,7 @@ config CAN_SLCAN
As only the sending and receiving of CAN frames is implemented, this
driver should work with the (serial/USB) CAN hardware from:
- www.canusb.com / www.can232.com / www.mictronic.com / www.canhack.de
+ www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
Userspace tools to attach the SLCAN line discipline (slcan_attach,
slcand) can be found in the can-utils at the SocketCAN SVN, see
^ permalink raw reply related
* [PATCH v3 3/5] net: use ndo_fix_features for ethtool_ops->set_flags
From: Michał Mirosław @ 2011-01-29 18:39 UTC (permalink / raw)
To: netdev; +Cc: Ben Hutchings
In-Reply-To: <cover.1296325509.git.mirq-linux@rere.qmqm.pl>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
net/core/ethtool.c | 22 ++++++++++++++++++++--
1 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 409aebb..17a689f4 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -240,6 +240,25 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
return ret;
}
+static int __ethtool_set_flags(struct net_device *dev, u32 data)
+{
+ if (data & ~flags_dup_features)
+ return -EINVAL;
+
+ if (!(dev->hw_features & flags_dup_features)) {
+ if (!dev->ethtool_ops->set_flags)
+ return -EOPNOTSUPP;
+ return dev->ethtool_ops->set_flags(dev, data);
+ }
+
+ dev->wanted_features =
+ (dev->wanted_features & ~flags_dup_features) | data;
+
+ netdev_update_features(dev);
+
+ return 0;
+}
+
static u32 ethtool_get_feature_mask(u32 eth_cmd)
{
/* feature masks of legacy discrete ethtool ops */
@@ -1733,8 +1752,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
ethtool_op_get_flags));
break;
case ETHTOOL_SFLAGS:
- rc = ethtool_set_value(dev, useraddr,
- dev->ethtool_ops->set_flags);
+ rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
break;
case ETHTOOL_GPFLAGS:
rc = ethtool_get_value(dev, useraddr, ethcmd,
--
1.7.2.3
^ permalink raw reply related
* [PATCH v3 0/5] net: Unified offload configuration
From: Michał Mirosław @ 2011-01-29 18:39 UTC (permalink / raw)
To: netdev; +Cc: Ben Hutchings
Here's a next version of the ethtool unification patch series.
What's in it?
1:
the patch - implement unified ethtool setting ops
2..3:
implement interoperation between old and new ethtool ops
4:
include RX checksum in features and plug it into new framework
5:
convert loopback pseudodevice to new framework
What is it good for?
- unifies driver behaviour wrt hardware offloads
- removes a lot of boilerplate code from drivers
- allows better fine-grained control over used offloads
I'm testing this on ARM Gemini arch now. Patch to ethtool userspace tool
will follow this series. I'm not fond of the GFEATURES output I implemented -
please throw some suggestions on it if you can.
Driver conversions stay the same as in v2 - I'll resend them Cc'ing their
maintainters after the core interfaces get accepted.
Best Regards,
Michał Mirosław
---
v1: http://marc.info/?l=linux-netdev&m=129245188832643&w=3
Changes from v2:
- rebase to net-next after merging v2 leading patches
- fix missing comma in feature name table
- force NETIF_F_SOFT_FEATURES in hw_features for simpler code
(fixes a bug that disallowed changing GSO and GRO state)
Changes from v1:
- split structures for GFEATURES/SFEATURES
- naming of feature bits using GSTRINGS ETH_SS_FEATURES
- strict checking of bits used in SFEATURES call
- more comments and kernel-doc
- rebased to net-next after 2.6.37
---
Michał Mirosław (5):
net: Introduce new feature setting ops
net: ethtool: use ndo_fix_features for offload setting
net: use ndo_fix_features for ethtool_ops->set_flags
net: introduce NETIF_F_RXCSUM
loopback: convert to hw_features
drivers/net/loopback.c | 9 +-
include/linux/ethtool.h | 87 ++++++++-
include/linux/netdevice.h | 47 +++++-
net/core/dev.c | 47 ++++-
net/core/ethtool.c | 472 ++++++++++++++++++++++++++++-----------------
5 files changed, 469 insertions(+), 193 deletions(-)
--
1.7.2.3
^ permalink raw reply
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