* [BUGFIX PATCH] net: ethernet: cpsw: fix erroneous condition in error check
From: Lothar Waßmann @ 2013-03-21 12:20 UTC (permalink / raw)
To: Richard Cochran
Cc: David S. Miller, netdev, linux-kernel, Mugunthan V N,
Lothar Waßmann
The error check in cpsw_probe_dt() has an '&&' where an '||' is
meant to be. This causes a NULL pointer dereference when incomplet DT
data is passed to the driver ('phy_id' property for cpsw_emac1
missing).
Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
drivers/net/ethernet/ti/cpsw.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 5ceaa4c3..7aebc0c 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1504,7 +1504,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
struct platform_device *mdio;
parp = of_get_property(slave_node, "phy_id", &lenp);
- if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) {
+ if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
pr_err("Missing slave[%d] phy_id property\n", i);
ret = -EINVAL;
goto error_ret;
--
1.7.2.5
^ permalink raw reply related
* Re: [PATCH 2/2] netlink: Diag core and basic socket info dumping
From: Pavel Emelyanov @ 2013-03-21 11:46 UTC (permalink / raw)
To: Andrey Vagin, David S. Miller
Cc: linux-kernel, netdev, Eric Dumazet, Pablo Neira Ayuso,
Eric W. Biederman, Gao feng
In-Reply-To: <1363857669-19990-3-git-send-email-avagin@openvz.org>
On 03/21/2013 01:21 PM, Andrey Vagin wrote:
> The netlink_diag can be built as a module, just like it's done in
> unix sockets.
>
> The core dumping message carries the basic info about netlink sockets:
> family, type and protocol, portis, dst_group, dst_portid, state.
>
> Groups can be received as an optional parameter NETLINK_DIAG_GROUPS.
>
> Netlink sockets cab be filtered by protocols.
>
> The socket inode number and cookie is reserved for future per-socket info
> retrieving. The per-protocol filtering is also reserved for future by
> requiring the sdiag_protocol to be zero.
>
> The file /proc/net/netlink doesn't provide enough information for
> dumping netlink sockets. It doesn't provide dst_group, dst_portid,
> groups above 32.
>
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: Pavel Emelyanov <xemul@parallels.com>
> Cc: Pablo Neira Ayuso <pablo@netfilter.org>
> Cc: "Eric W. Biederman" <ebiederm@xmission.com>
> Cc: Gao feng <gaofeng@cn.fujitsu.com>
> Signed-off-by: Andrey Vagin <avagin@openvz.org>
Acked-by: Pavel Emelyanov <xemul@parallels.com>
^ permalink raw reply
* Re: [PATCH v2 1/4] tcp: fix too short FIN_WAIT2 time out
From: Toshiaki Makita @ 2013-03-21 11:45 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, netdev, makita.toshiaki
In-Reply-To: <20130319.093257.1744500513624729643.davem@davemloft.net>
On Tue, 2013-03-19 at 09:32 -0400, David Miller wrote:
> From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
> Date: Mon, 18 Mar 2013 21:39:04 +0900
>
> > if (tp->linger2 >= 0) {
> > - const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN;
> > -
> > - if (tmo > 0) {
> > - tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
> > - goto out;
> > - }
> > + tcp_time_wait(sk, TCP_FIN_WAIT2, TCP_TIMEWAIT_LEN);
> > + goto out;
> > }
>
> Well, now you're completely ignoring the user's linger setting.
If you mention TCP_LINGER2, I don't think I'm ignoring it.
It is taken into account in tcp_rcv_state_process() or tcp_close().
If I'm misunderstanding, I'd be glad if you could point out it.
>
> I really can't take these patches seriously, and will not apply them,
> sorry.
I think, at least, too short timeout is harmful.
If tcp_fin_timeout is set to 61, it will expire in 2 seconds, which
cause peer to receive unexpected reset by sending fin.
Don't you think there is a problem?
Toshiaki Makita
^ permalink raw reply
* Re: [PATCH RFC 1/5] kconfig: implement weak reverse-dependencies
From: Richard Cochran @ 2013-03-21 11:43 UTC (permalink / raw)
To: Konstantin Khlebnikov
Cc: Michal Marek, Andrew Morton, linux-kernel, linux-kbuild,
Tejun Heo, Greg Kroah-Hartman, netdev
In-Reply-To: <514AE9A3.5090209@openvz.org>
On Thu, Mar 21, 2013 at 03:06:11PM +0400, Konstantin Khlebnikov wrote:
>
> As I see this technology requires special dedicated server in the local
> network, thus it's unusable in most situations. But it starts working
> without any actions from the user (please fix me if I'm wrong).
Perhaps you don't have a very clear picture of how this PTP stuff
works. Even when the PHC and time stamping code is compiled in, it
does *not* start working unless the end user turns it on, via the
SIOCSHWTSTAMP and SO_TIMESTAMPING options.
See: Documentation/networking/timestamping.txt
Documentation/ptp/ptp.txt
> Thus this code enables some rarely used parts of hardware.
> After seeing several weird bugs in ethernet devices I prefer to
> keep unused/unwanted features off.
Just compiling drivers into kernel does not really change the behavior
of the hardware. The only drawback I know of is that it adds (minimal)
overhead into the packet processing paths, but that is a software
issue.
I don't mean to start a big discussion here. The question of whether
to have PHC support a compile time option should be discussed on the
netdev list (added on CC just in case).
Thanks,
Richard
^ permalink raw reply
* Re: [PATCH] ath9k : Fix ieee80211 work while going to suspend
From: Stanislaw Gruszka @ 2013-03-21 11:42 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: John W. Linville, Parag Warudkar, Jouni Malinen,
Vasanthakumar Thiagarajan, linux-wireless, ath9k-devel, netdev,
LKML, senthilb
In-Reply-To: <20130318210308.GD32416@pogo>
On Mon, Mar 18, 2013 at 02:03:08PM -0700, Luis R. Rodriguez wrote:
> > > --- a/drivers/net/wireless/ath/ath9k/link.c
> > > +++ b/drivers/net/wireless/ath/ath9k/link.c
> > > @@ -158,7 +158,8 @@ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
> > > {
> > > if (!AR_SREV_9300(sc->sc_ah))
> > > return;
> > > -
> > > + if (sc->suspending)
> > > + return;
>
> Thanks for the patch! Please note the style issue here, you should
> use a tab, but other than that lets review what happened.
>
> > > if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
> > > return;
>
> Note that what this will do is call later mod_timer() for
> rx_poll_timer, the right thing to do then, which would
> be equivalent to your patch is to modify the ath_start_rx_poll()
> to instead use the new API mod_timer_pending() added on v2.6.30
> via commit 74019224. This would not re-arm the timer if it was
> previously removed.
[snip]
> - mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
> - (nbeacon * sc->cur_beacon_conf.beacon_interval));
> + mod_timer_pending(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
> + (nbeacon * sc->cur_beacon_conf.beacon_interval));
But isn't this prevent to run timer in case it was not running, but
we want to start it ?
> Looking at this makes me think we should review all usage of
> mod_timer all over our 802.11 drivers, and mac80211, cfg80211 as
> well.
I mac80211 we use local->suspended and local->quiesce booleans to
prevent reschedule of timers when going to suspend for example.
Works use ifmgd->associted to prevent reschedule when we are
disassociating.
I think on ath9k also some boolean variable should be used, not only
for rx_poll_timer but also for other works i.e. tx_complete_work.
Is possible to use SC_OP_INVALID flags, since mac80211 call ath9k_stop
on suspend and ath9k_start on resume.
Stanislaw
^ permalink raw reply
* [resent x2][iputils][patch 01-07] setuid/capabilities fixups
From: Yuriy Kaminskiy @ 2013-03-21 11:38 UTC (permalink / raw)
To: netdev
In-Reply-To: <khaapr$ipr$1@ger.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 301 bytes --]
Yuriy Kaminskiy wrote:
> Yuriy Kaminskiy wrote:
>> Run ping, look at /proc/`pidof ping`/status -> capabilities are not
>> [permanently] dropped, some of uids are not dropped, etc. Fix assorted issues
>> with setuid and capabilities drop. Limited testing only, please review/check
>> carefully.
ping.
[-- Attachment #2: 0001-arping-ping-ping6-traceroute2-clockdiff-drop-fsuid-a.patch --]
[-- Type: text/x-diff, Size: 2800 bytes --]
>From fd8a9b6fa39a7fa926b105e477fc9a133b6ac862 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 01:43:01 +0400
Subject: [PATCH 1/7] arping, ping, ping6, traceroute2, clockdiff: drop fsuid
at start
Prevent ignoring netfilter -m owner checks. With this patch ping
correctly blocked by iptables rules, e.g.:
$ sudo iptables -I OUTPUT -m owner --uid-owner $UID -j DROP
$ ping www.google.com
---
arping.c | 9 +++++++++
clockdiff.c | 8 ++++++++
ping_common.c | 9 +++++++++
traceroute6.c | 8 ++++++++
4 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/arping.c b/arping.c
index 35408c1..bdf81e9 100644
--- a/arping.c
+++ b/arping.c
@@ -27,6 +27,9 @@
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
+#ifdef __linux__
+#include <sys/fsuid.h>
+#endif
#include <netdb.h>
#include <unistd.h>
@@ -229,6 +232,12 @@ int modify_capability_raw(int on)
perror("arping: setuid");
return -1;
}
+#ifdef __linux__
+ if (on) {
+ /* FIXME: error handling? setfsuid() have weird return code */
+ setfsuid(getuid());
+ }
+#endif
#endif
return 0;
}
diff --git a/clockdiff.c b/clockdiff.c
index 7c1ea1b..f12da2d 100644
--- a/clockdiff.c
+++ b/clockdiff.c
@@ -3,6 +3,9 @@
#include <sys/param.h>
#include <stdio.h>
#include <unistd.h>
+#ifdef __linux__
+#include <sys/fsuid.h>
+#endif
#include <stdlib.h>
#include <math.h>
#include <string.h>
@@ -561,6 +564,11 @@ main(int argc, char *argv[])
usage();
}
+#ifdef __linux__
+ // FIXME: error handling? setfsuid() have weird return code
+ setfsuid(getuid());
+#endif
+
sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
s_errno = errno;
diff --git a/ping_common.c b/ping_common.c
index 8d6b145..1d0012a 100644
--- a/ping_common.c
+++ b/ping_common.c
@@ -2,6 +2,9 @@
#include <ctype.h>
#include <sched.h>
#include <math.h>
+#ifdef __linux__
+#include <sys/fsuid.h>
+#endif
int options;
@@ -175,6 +178,12 @@ int modify_capability(int on)
perror("seteuid");
return -1;
}
+#ifdef __linux__
+ if (on) {
+ /* FIXME: error handling? setfsuid() have weird return code */
+ setfsuid(uid);
+ }
+#endif
return 0;
}
diff --git a/traceroute6.c b/traceroute6.c
index 0538d4b..a14ddb6 100644
--- a/traceroute6.c
+++ b/traceroute6.c
@@ -266,6 +266,9 @@ char copyright[] =
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef __linux__
+#include <sys/fsuid.h>
+#endif
#include "SNAPSHOT.h"
@@ -343,6 +346,11 @@ int main(int argc, char *argv[])
int ch, i, on, probe, seq, tos, ttl;
int socket_errno;
+#ifdef __linux__
+ // FIXME: error handling? setfsuid() have weird return code
+ setfsuid(getuid());
+#endif
+
icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
socket_errno = errno;
--
1.7.6.3
[-- Attachment #3: 0002-ping-permanently-drop-capabilities-before-entering-m.patch --]
[-- Type: text/x-diff, Size: 554 bytes --]
>From 550366b0d5ba7498c6f102a0f7da08f6119028c7 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 01:44:49 +0400
Subject: [PATCH 2/7] ping: permanently drop capabilities before entering main
loop
---
ping.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/ping.c b/ping.c
index c0366cd..7ac1217 100644
--- a/ping.c
+++ b/ping.c
@@ -587,6 +587,8 @@ main(int argc, char **argv)
setup(icmp_sock);
+ drop_capabilities();
+
main_loop(icmp_sock, packet, packlen);
}
--
1.7.6.3
[-- Attachment #4: 0003-arping-use-seteuid-for-temporal-uid-changes.patch --]
[-- Type: text/x-diff, Size: 609 bytes --]
>From 0f1a605b1b7838749f2dd315c588b939380ccbd0 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 01:46:44 +0400
Subject: [PATCH 3/7] arping: use seteuid for temporal uid changes
---
arping.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arping.c b/arping.c
index bdf81e9..a35fafd 100644
--- a/arping.c
+++ b/arping.c
@@ -228,7 +228,7 @@ int modify_capability_raw(int on)
cap_free(cap_p);
#else
- if (setuid(on ? euid : getuid())) {
+ if (seteuid(on ? euid : getuid())) {
perror("arping: setuid");
return -1;
}
--
1.7.6.3
[-- Attachment #5: 0004-arping-ping_common-reset-euid-before-permanent-drop.patch --]
[-- Type: text/x-diff, Size: 1025 bytes --]
>From b0025d82ce5592f8e8da51cf4b94ceea51073cbe Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 01:53:39 +0400
Subject: [PATCH 4/7] arping, ping_common: reset euid before permanent drop
setuid drop saved uid only if euid is 0
---
arping.c | 4 ++++
ping_common.c | 4 ++++
2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/arping.c b/arping.c
index a35fafd..0033f33 100644
--- a/arping.c
+++ b/arping.c
@@ -269,6 +269,10 @@ void drop_capabilities(void)
cap_free(cap_p);
#else
+ if (seteuid(euid)) {
+ perror("arping: setuid");
+ return -1;
+ }
if (setuid(getuid()) < 0) {
perror("arping: setuid");
exit(-1);
diff --git a/ping_common.c b/ping_common.c
index 1d0012a..4a184ee 100644
--- a/ping_common.c
+++ b/ping_common.c
@@ -199,6 +199,10 @@ void drop_capabilities(void)
}
cap_free(cap);
#else
+ if (seteuid(euid)) {
+ perror("seteuid");
+ exit(-1);
+ }
if (setuid(getuid())) {
perror("ping: setuid");
exit(-1);
--
1.7.6.3
[-- Attachment #6: 0005-ping-ping6-arping-clockdiff-Fix-CAP_SETUID-setuid-in.patch --]
[-- Type: text/x-diff, Size: 2783 bytes --]
>From dcb8828342f2591ee29f1be2fda2f225aee5efa9 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 01:56:07 +0400
Subject: [PATCH 5/7] ping, ping6, arping, clockdiff: Fix CAP_SETUID <->
setuid() interaction
setuid() only drops saved uid if process have CAP_SETUID.
Drop capabilities only after setuid().
---
arping.c | 30 +++++++++++++++---------------
clockdiff.c | 8 +++++---
ping_common.c | 30 +++++++++++++++---------------
3 files changed, 35 insertions(+), 33 deletions(-)
diff --git a/arping.c b/arping.c
index 0033f33..3c02abf 100644
--- a/arping.c
+++ b/arping.c
@@ -161,6 +161,21 @@ void limit_capabilities(void)
#ifdef CAPABILITIES
cap_t cap_p;
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ perror("arping: prctl");
+ exit(-1);
+ }
+
+ if (setuid(getuid()) < 0) {
+ perror("arping: setuid");
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ perror("arping: prctl");
+ exit(-1);
+ }
+
cap_p = cap_get_proc();
if (!cap_p) {
perror("arping: cap_get_proc");
@@ -184,21 +199,6 @@ void limit_capabilities(void)
}
}
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- perror("arping: prctl");
- exit(-1);
- }
-
- if (setuid(getuid()) < 0) {
- perror("arping: setuid");
- exit(-1);
- }
-
- if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
- perror("arping: prctl");
- exit(-1);
- }
-
cap_free(cap_p);
#else
euid = geteuid();
diff --git a/clockdiff.c b/clockdiff.c
index f12da2d..540366d 100644
--- a/clockdiff.c
+++ b/clockdiff.c
@@ -536,6 +536,11 @@ usage() {
}
void drop_rights(void) {
+ if (setuid(getuid())) {
+ perror("clockdiff: setuid");
+ exit(-1);
+ }
+ {
#ifdef CAPABILITIES
cap_t caps = cap_init();
if (cap_set_proc(caps)) {
@@ -544,9 +549,6 @@ void drop_rights(void) {
}
cap_free(caps);
#endif
- if (setuid(getuid())) {
- perror("clockdiff: setuid");
- exit(-1);
}
}
diff --git a/ping_common.c b/ping_common.c
index 4a184ee..b2344a7 100644
--- a/ping_common.c
+++ b/ping_common.c
@@ -80,6 +80,21 @@ void limit_capabilities(void)
cap_t cap_p;
cap_flag_value_t cap_ok;
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ perror("ping: prctl");
+ exit(-1);
+ }
+
+ if (setuid(getuid()) < 0) {
+ perror("setuid");
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ perror("ping: prctl");
+ exit(-1);
+ }
+
cap_cur_p = cap_get_proc();
if (!cap_cur_p) {
perror("ping: cap_get_proc");
@@ -109,21 +124,6 @@ void limit_capabilities(void)
exit(-1);
}
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- perror("ping: prctl");
- exit(-1);
- }
-
- if (setuid(getuid()) < 0) {
- perror("setuid");
- exit(-1);
- }
-
- if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
- perror("ping: prctl");
- exit(-1);
- }
-
cap_free(cap_p);
cap_free(cap_cur_p);
#endif
--
1.7.6.3
[-- Attachment #7: 0006-ninfod-use-u-without-capabilities-too.patch --]
[-- Type: text/x-diff, Size: 662 bytes --]
>From fdabf4869f868fe8ea0994910cc4944ebfd0b098 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 02:57:15 +0400
Subject: [PATCH 6/7] ninfod: use -u without capabilities too
---
ninfod/ninfod.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ninfod/ninfod.c b/ninfod/ninfod.c
index 5ff6993..f1db977 100644
--- a/ninfod/ninfod.c
+++ b/ninfod/ninfod.c
@@ -589,7 +589,7 @@ static void drop_capabilities(void)
cap_free(cap_p);
#else
- if (setuid(getuid()) < 0) {
+ if (setuid(opt_u ? opt_u : getuid()) < 0) {
DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno));
exit(-1);
}
--
1.7.6.3
[-- Attachment #8: 0007-ninfod-fix-capabilities-setting.patch --]
[-- Type: text/x-diff, Size: 3184 bytes --]
>From 4298d3af86b881ef7abb61a36f83a610fc8bb772 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@gmail.com>
Date: Wed, 2 Jan 2013 03:08:30 +0400
Subject: [PATCH 7/7] ninfod: fix capabilities setting
1) -u option failed to change real uid too (likely leaving it as root);
2) it failed to drop saved uid;
---
ninfod/ninfod.c | 58 +++++++++++++++++-------------------------------------
1 files changed, 18 insertions(+), 40 deletions(-)
diff --git a/ninfod/ninfod.c b/ninfod/ninfod.c
index f1db977..7f6a2fa 100644
--- a/ninfod/ninfod.c
+++ b/ninfod/ninfod.c
@@ -497,16 +497,28 @@ static void do_daemonize(void)
/* --------- */
#ifdef HAVE_LIBCAP
static const cap_value_t cap_net_raw = CAP_NET_RAW;
-static const cap_value_t cap_setuid = CAP_SETUID;
-static cap_flag_value_t cap_ok;
-#else
-static uid_t euid;
#endif
static void limit_capabilities(void)
{
#ifdef HAVE_LIBCAP
cap_t cap_p, cap_cur_p;
+ cap_flag_value_t cap_ok;
+
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno));
+ exit(-1);
+ }
+
+ if (setuid(opt_u ? opt_u : getuid()) < 0) {
+ DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno));
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno));
+ exit(-1);
+ }
cap_p = cap_init();
if (!cap_p) {
@@ -520,32 +532,20 @@ static void limit_capabilities(void)
exit(-1);
}
- /* net_raw + setuid / net_raw */
cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok);
if (cap_ok != CAP_CLEAR) {
cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_net_raw, CAP_SET);
cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap_net_raw, CAP_SET);
}
- cap_get_flag(cap_cur_p, CAP_SETUID, CAP_PERMITTED, &cap_ok);
- if (cap_ok != CAP_CLEAR)
- cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_setuid, CAP_SET);
-
if (cap_set_proc(cap_p) < 0) {
DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno));
if (errno != EPERM)
exit(-1);
}
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno));
- exit(-1);
- }
-
cap_free(cap_cur_p);
cap_free(cap_p);
-#else
- euid = geteuid();
#endif
}
@@ -560,28 +560,6 @@ static void drop_capabilities(void)
exit(-1);
}
- /* setuid / setuid */
- if (cap_ok != CAP_CLEAR) {
- cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_setuid, CAP_SET);
- cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap_setuid, CAP_SET);
-
- if (cap_set_proc(cap_p) < 0) {
- DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno));
- exit(-1);
- }
- }
-
- if (seteuid(opt_u ? opt_u : getuid()) < 0) {
- DEBUG(LOG_ERR, "setuid: %s\n", strerror(errno));
- exit(-1);
- }
-
- if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
- DEBUG(LOG_ERR, "prctl: %s\n", strerror(errno));
- exit(-1);
- }
-
- cap_clear(cap_p);
if (cap_set_proc(cap_p) < 0) {
DEBUG(LOG_ERR, "cap_set_proc: %s\n", strerror(errno));
exit(-1);
@@ -667,14 +645,14 @@ int main (int argc, char **argv)
appname = argv[0];
set_logfile();
+ parse_args(argc, argv);
+
limit_capabilities();
sock = open_sock();
if (sock < 0)
sock_errno = errno;
- parse_args(argc, argv);
-
drop_capabilities();
if (opt_h || opt_v)
--
1.7.6.3
^ permalink raw reply related
* Re: [PATCH] net: remove redundant ifdef CONFIG_CGROUPS
From: Neil Horman @ 2013-03-21 11:22 UTC (permalink / raw)
To: Li Zefan; +Cc: David Miller, LKML, netdev
In-Reply-To: <514A767B.7000307@huawei.com>
On Thu, Mar 21, 2013 at 10:54:51AM +0800, Li Zefan wrote:
> The cgroup code has been surrounded by ifdef CONFIG_NET_CLS_CGROUP
> and CONFIG_NETPRIO_CGROUP.
>
> Signed-off-by: Li Zefan <lizefan@huawei.com>
> ---
> net/core/sock.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/net/core/sock.c b/net/core/sock.c
> index b261a79..a19e728 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1298,7 +1298,6 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
> module_put(owner);
> }
>
> -#ifdef CONFIG_CGROUPS
> #if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
> void sock_update_classid(struct sock *sk, struct task_struct *task)
> {
> @@ -1321,7 +1320,6 @@ void sock_update_netprioidx(struct sock *sk, struct task_struct *task)
> }
> EXPORT_SYMBOL_GPL(sock_update_netprioidx);
> #endif
> -#endif
>
> /**
> * sk_alloc - All socket objects are allocated here
> --
> 1.8.0.2
>
>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
^ permalink raw reply
* Re: [PATCH 1/1 net-next] net: fec: fix the build as module
From: Uwe Kleine-König @ 2013-03-21 11:08 UTC (permalink / raw)
To: Frank Li; +Cc: festevam, lznuaa, netdev, davem, linux-arm-kernel
In-Reply-To: <1363859763-11962-1-git-send-email-Frank.Li@freescale.com>
On Thu, Mar 21, 2013 at 05:56:03PM +0800, Frank Li wrote:
> Since commit ff43da86c69 (NET: FEC: dynamtic check DMA desc buff type) the
> following build error happens when CONFIG_FEC=m
>
> ERROR: "fec_ptp_init" [drivers/net/ethernet/freescale/fec.ko] undefined!
> ERROR: "fec_ptp_ioctl" [drivers/net/ethernet/freescale/fec.ko] undefined!
> ERROR: "fec_ptp_start_cyclecounter" [drivers/net/ethernet/freescale/fec.ko] undefined!
>
> rename fec.c to fec_main.c
> Build fec.o and fec_ptp.o into one fec.ko
>
> Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Frank Li <Frank.Li@freescale.com>
> ---
> drivers/net/ethernet/freescale/Makefile | 3 +-
> drivers/net/ethernet/freescale/fec.c | 1961 -----------------------------
> drivers/net/ethernet/freescale/fec_main.c | 1961 +++++++++++++++++++++++++++++
Do you know format-patch's -M switch?
Uwe
> 3 files changed, 1963 insertions(+), 1962 deletions(-)
> delete mode 100644 drivers/net/ethernet/freescale/fec.c
> create mode 100644 drivers/net/ethernet/freescale/fec_main.c
>
> diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
> index b7d58fe..549ce13 100644
> --- a/drivers/net/ethernet/freescale/Makefile
> +++ b/drivers/net/ethernet/freescale/Makefile
> @@ -2,7 +2,8 @@
> # Makefile for the Freescale network device drivers.
> #
>
> -obj-$(CONFIG_FEC) += fec.o fec_ptp.o
> +obj-$(CONFIG_FEC) += fec.o
> +fec-objs :=fec_main.o fec_ptp.o
> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
> ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
> diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
> deleted file mode 100644
> index 69a4ade..0000000
> --- a/drivers/net/ethernet/freescale/fec.c
> +++ /dev/null
> @@ -1,1961 +0,0 @@
> -/*
> - * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
> - * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
> - *
> - * Right now, I am very wasteful with the buffers. I allocate memory
> - * pages and then divide them into 2K frame buffers. This way I know I
> - * have buffers large enough to hold one frame within one buffer descriptor.
> - * Once I get this working, I will use 64 or 128 byte CPM buffers, which
> - * will be much more memory efficient and will easily handle lots of
> - * small packets.
> - *
> - * Much better multiple PHY support by Magnus Damm.
> - * Copyright (c) 2000 Ericsson Radio Systems AB.
> - *
> - * Support for FEC controller of ColdFire processors.
> - * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
> - *
> - * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
> - * Copyright (c) 2004-2006 Macq Electronique SA.
> - *
> - * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
> - */
> -
> -#include <linux/module.h>
> -#include <linux/kernel.h>
> -#include <linux/string.h>
> -#include <linux/ptrace.h>
> -#include <linux/errno.h>
> -#include <linux/ioport.h>
> -#include <linux/slab.h>
> -#include <linux/interrupt.h>
> -#include <linux/init.h>
> -#include <linux/delay.h>
> -#include <linux/netdevice.h>
> -#include <linux/etherdevice.h>
> -#include <linux/skbuff.h>
> -#include <linux/spinlock.h>
> -#include <linux/workqueue.h>
> -#include <linux/bitops.h>
> -#include <linux/io.h>
> -#include <linux/irq.h>
> -#include <linux/clk.h>
> -#include <linux/platform_device.h>
> -#include <linux/phy.h>
> -#include <linux/fec.h>
> -#include <linux/of.h>
> -#include <linux/of_device.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_net.h>
> -#include <linux/pinctrl/consumer.h>
> -#include <linux/regulator/consumer.h>
> -
> -#include <asm/cacheflush.h>
> -
> -#ifndef CONFIG_ARM
> -#include <asm/coldfire.h>
> -#include <asm/mcfsim.h>
> -#endif
> -
> -#include "fec.h"
> -
> -#if defined(CONFIG_ARM)
> -#define FEC_ALIGNMENT 0xf
> -#else
> -#define FEC_ALIGNMENT 0x3
> -#endif
> -
> -#define DRIVER_NAME "fec"
> -#define FEC_NAPI_WEIGHT 64
> -
> -/* Pause frame feild and FIFO threshold */
> -#define FEC_ENET_FCE (1 << 5)
> -#define FEC_ENET_RSEM_V 0x84
> -#define FEC_ENET_RSFL_V 16
> -#define FEC_ENET_RAEM_V 0x8
> -#define FEC_ENET_RAFL_V 0x8
> -#define FEC_ENET_OPD_V 0xFFF0
> -
> -/* Controller is ENET-MAC */
> -#define FEC_QUIRK_ENET_MAC (1 << 0)
> -/* Controller needs driver to swap frame */
> -#define FEC_QUIRK_SWAP_FRAME (1 << 1)
> -/* Controller uses gasket */
> -#define FEC_QUIRK_USE_GASKET (1 << 2)
> -/* Controller has GBIT support */
> -#define FEC_QUIRK_HAS_GBIT (1 << 3)
> -/* Controller has extend desc buffer */
> -#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4)
> -
> -static struct platform_device_id fec_devtype[] = {
> - {
> - /* keep it for coldfire */
> - .name = DRIVER_NAME,
> - .driver_data = 0,
> - }, {
> - .name = "imx25-fec",
> - .driver_data = FEC_QUIRK_USE_GASKET,
> - }, {
> - .name = "imx27-fec",
> - .driver_data = 0,
> - }, {
> - .name = "imx28-fec",
> - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
> - }, {
> - .name = "imx6q-fec",
> - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
> - FEC_QUIRK_HAS_BUFDESC_EX,
> - }, {
> - /* sentinel */
> - }
> -};
> -MODULE_DEVICE_TABLE(platform, fec_devtype);
> -
> -enum imx_fec_type {
> - IMX25_FEC = 1, /* runs on i.mx25/50/53 */
> - IMX27_FEC, /* runs on i.mx27/35/51 */
> - IMX28_FEC,
> - IMX6Q_FEC,
> -};
> -
> -static const struct of_device_id fec_dt_ids[] = {
> - { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
> - { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
> - { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
> - { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
> - { /* sentinel */ }
> -};
> -MODULE_DEVICE_TABLE(of, fec_dt_ids);
> -
> -static unsigned char macaddr[ETH_ALEN];
> -module_param_array(macaddr, byte, NULL, 0);
> -MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
> -
> -#if defined(CONFIG_M5272)
> -/*
> - * Some hardware gets it MAC address out of local flash memory.
> - * if this is non-zero then assume it is the address to get MAC from.
> - */
> -#if defined(CONFIG_NETtel)
> -#define FEC_FLASHMAC 0xf0006006
> -#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
> -#define FEC_FLASHMAC 0xf0006000
> -#elif defined(CONFIG_CANCam)
> -#define FEC_FLASHMAC 0xf0020000
> -#elif defined (CONFIG_M5272C3)
> -#define FEC_FLASHMAC (0xffe04000 + 4)
> -#elif defined(CONFIG_MOD5272)
> -#define FEC_FLASHMAC 0xffc0406b
> -#else
> -#define FEC_FLASHMAC 0
> -#endif
> -#endif /* CONFIG_M5272 */
> -
> -#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
> -#error "FEC: descriptor ring size constants too large"
> -#endif
> -
> -/* Interrupt events/masks. */
> -#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
> -#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
> -#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
> -#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
> -#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
> -#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
> -#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
> -#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
> -#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
> -#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
> -
> -#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
> -#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
> -
> -/* The FEC stores dest/src/type, data, and checksum for receive packets.
> - */
> -#define PKT_MAXBUF_SIZE 1518
> -#define PKT_MINBUF_SIZE 64
> -#define PKT_MAXBLR_SIZE 1520
> -
> -/*
> - * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
> - * size bits. Other FEC hardware does not, so we need to take that into
> - * account when setting it.
> - */
> -#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
> - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
> -#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
> -#else
> -#define OPT_FRAME_SIZE 0
> -#endif
> -
> -/* FEC MII MMFR bits definition */
> -#define FEC_MMFR_ST (1 << 30)
> -#define FEC_MMFR_OP_READ (2 << 28)
> -#define FEC_MMFR_OP_WRITE (1 << 28)
> -#define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
> -#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
> -#define FEC_MMFR_TA (2 << 16)
> -#define FEC_MMFR_DATA(v) (v & 0xffff)
> -
> -#define FEC_MII_TIMEOUT 30000 /* us */
> -
> -/* Transmitter timeout */
> -#define TX_TIMEOUT (2 * HZ)
> -
> -#define FEC_PAUSE_FLAG_AUTONEG 0x1
> -#define FEC_PAUSE_FLAG_ENABLE 0x2
> -
> -static int mii_cnt;
> -
> -static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
> -{
> - struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
> - if (is_ex)
> - return (struct bufdesc *)(ex + 1);
> - else
> - return bdp + 1;
> -}
> -
> -static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
> -{
> - struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
> - if (is_ex)
> - return (struct bufdesc *)(ex - 1);
> - else
> - return bdp - 1;
> -}
> -
> -static void *swap_buffer(void *bufaddr, int len)
> -{
> - int i;
> - unsigned int *buf = bufaddr;
> -
> - for (i = 0; i < (len + 3) / 4; i++, buf++)
> - *buf = cpu_to_be32(*buf);
> -
> - return bufaddr;
> -}
> -
> -static netdev_tx_t
> -fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - struct bufdesc *bdp;
> - void *bufaddr;
> - unsigned short status;
> - unsigned int index;
> -
> - if (!fep->link) {
> - /* Link is down or autonegotiation is in progress. */
> - return NETDEV_TX_BUSY;
> - }
> -
> - /* Fill in a Tx ring entry */
> - bdp = fep->cur_tx;
> -
> - status = bdp->cbd_sc;
> -
> - if (status & BD_ENET_TX_READY) {
> - /* Ooops. All transmit buffers are full. Bail out.
> - * This should not happen, since ndev->tbusy should be set.
> - */
> - printk("%s: tx queue full!.\n", ndev->name);
> - return NETDEV_TX_BUSY;
> - }
> -
> - /* Clear all of the status flags */
> - status &= ~BD_ENET_TX_STATS;
> -
> - /* Set buffer length and buffer pointer */
> - bufaddr = skb->data;
> - bdp->cbd_datlen = skb->len;
> -
> - /*
> - * On some FEC implementations data must be aligned on
> - * 4-byte boundaries. Use bounce buffers to copy data
> - * and get it aligned. Ugh.
> - */
> - if (fep->bufdesc_ex)
> - index = (struct bufdesc_ex *)bdp -
> - (struct bufdesc_ex *)fep->tx_bd_base;
> - else
> - index = bdp - fep->tx_bd_base;
> -
> - if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
> - memcpy(fep->tx_bounce[index], skb->data, skb->len);
> - bufaddr = fep->tx_bounce[index];
> - }
> -
> - /*
> - * Some design made an incorrect assumption on endian mode of
> - * the system that it's running on. As the result, driver has to
> - * swap every frame going to and coming from the controller.
> - */
> - if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> - swap_buffer(bufaddr, skb->len);
> -
> - /* Save skb pointer */
> - fep->tx_skbuff[index] = skb;
> -
> - /* Push the data cache so the CPM does not get stale memory
> - * data.
> - */
> - bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
> - FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
> -
> - /* Send it on its way. Tell FEC it's ready, interrupt when done,
> - * it's the last BD of the frame, and to put the CRC on the end.
> - */
> - status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
> - | BD_ENET_TX_LAST | BD_ENET_TX_TC);
> - bdp->cbd_sc = status;
> -
> - if (fep->bufdesc_ex) {
> -
> - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> - ebdp->cbd_bdu = 0;
> - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
> - fep->hwts_tx_en)) {
> - ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
> - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
> - } else {
> -
> - ebdp->cbd_esc = BD_ENET_TX_INT;
> - }
> - }
> - /* If this was the last BD in the ring, start at the beginning again. */
> - if (status & BD_ENET_TX_WRAP)
> - bdp = fep->tx_bd_base;
> - else
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> -
> - fep->cur_tx = bdp;
> -
> - if (fep->cur_tx == fep->dirty_tx)
> - netif_stop_queue(ndev);
> -
> - /* Trigger transmission start */
> - writel(0, fep->hwp + FEC_X_DES_ACTIVE);
> -
> - skb_tx_timestamp(skb);
> -
> - return NETDEV_TX_OK;
> -}
> -
> -/* This function is called to start or restart the FEC during a link
> - * change. This only happens when switching between half and full
> - * duplex.
> - */
> -static void
> -fec_restart(struct net_device *ndev, int duplex)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - int i;
> - u32 temp_mac[2];
> - u32 rcntl = OPT_FRAME_SIZE | 0x04;
> - u32 ecntl = 0x2; /* ETHEREN */
> -
> - /* Whack a reset. We should wait for this. */
> - writel(1, fep->hwp + FEC_ECNTRL);
> - udelay(10);
> -
> - /*
> - * enet-mac reset will reset mac address registers too,
> - * so need to reconfigure it.
> - */
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> - memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
> - writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
> - writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
> - }
> -
> - /* Clear any outstanding interrupt. */
> - writel(0xffc00000, fep->hwp + FEC_IEVENT);
> -
> - /* Reset all multicast. */
> - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> -#ifndef CONFIG_M5272
> - writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
> - writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
> -#endif
> -
> - /* Set maximum receive buffer size. */
> - writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
> -
> - /* Set receive and transmit descriptor base. */
> - writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
> - if (fep->bufdesc_ex)
> - writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
> - * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
> - else
> - writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
> - * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
> -
> - fep->cur_rx = fep->rx_bd_base;
> -
> - for (i = 0; i <= TX_RING_MOD_MASK; i++) {
> - if (fep->tx_skbuff[i]) {
> - dev_kfree_skb_any(fep->tx_skbuff[i]);
> - fep->tx_skbuff[i] = NULL;
> - }
> - }
> -
> - /* Enable MII mode */
> - if (duplex) {
> - /* FD enable */
> - writel(0x04, fep->hwp + FEC_X_CNTRL);
> - } else {
> - /* No Rcv on Xmit */
> - rcntl |= 0x02;
> - writel(0x0, fep->hwp + FEC_X_CNTRL);
> - }
> -
> - fep->full_duplex = duplex;
> -
> - /* Set MII speed */
> - writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> -
> - /*
> - * The phy interface and speed need to get configured
> - * differently on enet-mac.
> - */
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> - /* Enable flow control and length check */
> - rcntl |= 0x40000000 | 0x00000020;
> -
> - /* RGMII, RMII or MII */
> - if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
> - rcntl |= (1 << 6);
> - else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
> - rcntl |= (1 << 8);
> - else
> - rcntl &= ~(1 << 8);
> -
> - /* 1G, 100M or 10M */
> - if (fep->phy_dev) {
> - if (fep->phy_dev->speed == SPEED_1000)
> - ecntl |= (1 << 5);
> - else if (fep->phy_dev->speed == SPEED_100)
> - rcntl &= ~(1 << 9);
> - else
> - rcntl |= (1 << 9);
> - }
> - } else {
> -#ifdef FEC_MIIGSK_ENR
> - if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
> - u32 cfgr;
> - /* disable the gasket and wait */
> - writel(0, fep->hwp + FEC_MIIGSK_ENR);
> - while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
> - udelay(1);
> -
> - /*
> - * configure the gasket:
> - * RMII, 50 MHz, no loopback, no echo
> - * MII, 25 MHz, no loopback, no echo
> - */
> - cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
> - ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
> - if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
> - cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
> - writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
> -
> - /* re-enable the gasket */
> - writel(2, fep->hwp + FEC_MIIGSK_ENR);
> - }
> -#endif
> - }
> -
> - /* enable pause frame*/
> - if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
> - ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
> - fep->phy_dev && fep->phy_dev->pause)) {
> - rcntl |= FEC_ENET_FCE;
> -
> - /* set FIFO thresh hold parameter to reduce overrun */
> - writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
> - writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
> - writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
> - writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
> -
> - /* OPD */
> - writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
> - } else {
> - rcntl &= ~FEC_ENET_FCE;
> - }
> -
> - writel(rcntl, fep->hwp + FEC_R_CNTRL);
> -
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> - /* enable ENET endian swap */
> - ecntl |= (1 << 8);
> - /* enable ENET store and forward mode */
> - writel(1 << 8, fep->hwp + FEC_X_WMRK);
> - }
> -
> - if (fep->bufdesc_ex)
> - ecntl |= (1 << 4);
> -
> - /* And last, enable the transmit and receive processing */
> - writel(ecntl, fep->hwp + FEC_ECNTRL);
> - writel(0, fep->hwp + FEC_R_DES_ACTIVE);
> -
> - if (fep->bufdesc_ex)
> - fec_ptp_start_cyclecounter(ndev);
> -
> - /* Enable interrupts we wish to service */
> - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> -}
> -
> -static void
> -fec_stop(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
> -
> - /* We cannot expect a graceful transmit stop without link !!! */
> - if (fep->link) {
> - writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
> - udelay(10);
> - if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
> - printk("fec_stop : Graceful transmit stop did not complete !\n");
> - }
> -
> - /* Whack a reset. We should wait for this. */
> - writel(1, fep->hwp + FEC_ECNTRL);
> - udelay(10);
> - writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> -
> - /* We have to keep ENET enabled to have MII interrupt stay working */
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> - writel(2, fep->hwp + FEC_ECNTRL);
> - writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
> - }
> -}
> -
> -
> -static void
> -fec_timeout(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - ndev->stats.tx_errors++;
> -
> - fec_restart(ndev, fep->full_duplex);
> - netif_wake_queue(ndev);
> -}
> -
> -static void
> -fec_enet_tx(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep;
> - struct bufdesc *bdp;
> - unsigned short status;
> - struct sk_buff *skb;
> - int index = 0;
> -
> - fep = netdev_priv(ndev);
> - bdp = fep->dirty_tx;
> -
> - /* get next bdp of dirty_tx */
> - if (bdp->cbd_sc & BD_ENET_TX_WRAP)
> - bdp = fep->tx_bd_base;
> - else
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> -
> - while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
> -
> - /* current queue is empty */
> - if (bdp == fep->cur_tx)
> - break;
> -
> - if (fep->bufdesc_ex)
> - index = (struct bufdesc_ex *)bdp -
> - (struct bufdesc_ex *)fep->tx_bd_base;
> - else
> - index = bdp - fep->tx_bd_base;
> -
> - dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> - FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
> - bdp->cbd_bufaddr = 0;
> -
> - skb = fep->tx_skbuff[index];
> -
> - /* Check for errors. */
> - if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
> - BD_ENET_TX_RL | BD_ENET_TX_UN |
> - BD_ENET_TX_CSL)) {
> - ndev->stats.tx_errors++;
> - if (status & BD_ENET_TX_HB) /* No heartbeat */
> - ndev->stats.tx_heartbeat_errors++;
> - if (status & BD_ENET_TX_LC) /* Late collision */
> - ndev->stats.tx_window_errors++;
> - if (status & BD_ENET_TX_RL) /* Retrans limit */
> - ndev->stats.tx_aborted_errors++;
> - if (status & BD_ENET_TX_UN) /* Underrun */
> - ndev->stats.tx_fifo_errors++;
> - if (status & BD_ENET_TX_CSL) /* Carrier lost */
> - ndev->stats.tx_carrier_errors++;
> - } else {
> - ndev->stats.tx_packets++;
> - }
> -
> - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
> - fep->bufdesc_ex) {
> - struct skb_shared_hwtstamps shhwtstamps;
> - unsigned long flags;
> - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> -
> - memset(&shhwtstamps, 0, sizeof(shhwtstamps));
> - spin_lock_irqsave(&fep->tmreg_lock, flags);
> - shhwtstamps.hwtstamp = ns_to_ktime(
> - timecounter_cyc2time(&fep->tc, ebdp->ts));
> - spin_unlock_irqrestore(&fep->tmreg_lock, flags);
> - skb_tstamp_tx(skb, &shhwtstamps);
> - }
> -
> - if (status & BD_ENET_TX_READY)
> - printk("HEY! Enet xmit interrupt and TX_READY.\n");
> -
> - /* Deferred means some collisions occurred during transmit,
> - * but we eventually sent the packet OK.
> - */
> - if (status & BD_ENET_TX_DEF)
> - ndev->stats.collisions++;
> -
> - /* Free the sk buffer associated with this last transmit */
> - dev_kfree_skb_any(skb);
> - fep->tx_skbuff[index] = NULL;
> -
> - fep->dirty_tx = bdp;
> -
> - /* Update pointer to next buffer descriptor to be transmitted */
> - if (status & BD_ENET_TX_WRAP)
> - bdp = fep->tx_bd_base;
> - else
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> -
> - /* Since we have freed up a buffer, the ring is no longer full
> - */
> - if (fep->dirty_tx != fep->cur_tx) {
> - if (netif_queue_stopped(ndev))
> - netif_wake_queue(ndev);
> - }
> - }
> - return;
> -}
> -
> -
> -/* During a receive, the cur_rx points to the current incoming buffer.
> - * When we update through the ring, if the next incoming buffer has
> - * not been given to the system, we just set the empty indicator,
> - * effectively tossing the packet.
> - */
> -static int
> -fec_enet_rx(struct net_device *ndev, int budget)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - struct bufdesc *bdp;
> - unsigned short status;
> - struct sk_buff *skb;
> - ushort pkt_len;
> - __u8 *data;
> - int pkt_received = 0;
> -
> -#ifdef CONFIG_M532x
> - flush_cache_all();
> -#endif
> -
> - /* First, grab all of the stats for the incoming packet.
> - * These get messed up if we get called due to a busy condition.
> - */
> - bdp = fep->cur_rx;
> -
> - while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
> -
> - if (pkt_received >= budget)
> - break;
> - pkt_received++;
> -
> - /* Since we have allocated space to hold a complete frame,
> - * the last indicator should be set.
> - */
> - if ((status & BD_ENET_RX_LAST) == 0)
> - printk("FEC ENET: rcv is not +last\n");
> -
> - if (!fep->opened)
> - goto rx_processing_done;
> -
> - /* Check for errors. */
> - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
> - BD_ENET_RX_CR | BD_ENET_RX_OV)) {
> - ndev->stats.rx_errors++;
> - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
> - /* Frame too long or too short. */
> - ndev->stats.rx_length_errors++;
> - }
> - if (status & BD_ENET_RX_NO) /* Frame alignment */
> - ndev->stats.rx_frame_errors++;
> - if (status & BD_ENET_RX_CR) /* CRC Error */
> - ndev->stats.rx_crc_errors++;
> - if (status & BD_ENET_RX_OV) /* FIFO overrun */
> - ndev->stats.rx_fifo_errors++;
> - }
> -
> - /* Report late collisions as a frame error.
> - * On this error, the BD is closed, but we don't know what we
> - * have in the buffer. So, just drop this frame on the floor.
> - */
> - if (status & BD_ENET_RX_CL) {
> - ndev->stats.rx_errors++;
> - ndev->stats.rx_frame_errors++;
> - goto rx_processing_done;
> - }
> -
> - /* Process the incoming frame. */
> - ndev->stats.rx_packets++;
> - pkt_len = bdp->cbd_datlen;
> - ndev->stats.rx_bytes += pkt_len;
> - data = (__u8*)__va(bdp->cbd_bufaddr);
> -
> - dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> - FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
> -
> - if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> - swap_buffer(data, pkt_len);
> -
> - /* This does 16 byte alignment, exactly what we need.
> - * The packet length includes FCS, but we don't want to
> - * include that when passing upstream as it messes up
> - * bridging applications.
> - */
> - skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
> -
> - if (unlikely(!skb)) {
> - ndev->stats.rx_dropped++;
> - } else {
> - skb_reserve(skb, NET_IP_ALIGN);
> - skb_put(skb, pkt_len - 4); /* Make room */
> - skb_copy_to_linear_data(skb, data, pkt_len - 4);
> - skb->protocol = eth_type_trans(skb, ndev);
> -
> - /* Get receive timestamp from the skb */
> - if (fep->hwts_rx_en && fep->bufdesc_ex) {
> - struct skb_shared_hwtstamps *shhwtstamps =
> - skb_hwtstamps(skb);
> - unsigned long flags;
> - struct bufdesc_ex *ebdp =
> - (struct bufdesc_ex *)bdp;
> -
> - memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> -
> - spin_lock_irqsave(&fep->tmreg_lock, flags);
> - shhwtstamps->hwtstamp = ns_to_ktime(
> - timecounter_cyc2time(&fep->tc, ebdp->ts));
> - spin_unlock_irqrestore(&fep->tmreg_lock, flags);
> - }
> -
> - if (!skb_defer_rx_timestamp(skb))
> - napi_gro_receive(&fep->napi, skb);
> - }
> -
> - bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
> - FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
> -rx_processing_done:
> - /* Clear the status flags for this buffer */
> - status &= ~BD_ENET_RX_STATS;
> -
> - /* Mark the buffer empty */
> - status |= BD_ENET_RX_EMPTY;
> - bdp->cbd_sc = status;
> -
> - if (fep->bufdesc_ex) {
> - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> -
> - ebdp->cbd_esc = BD_ENET_RX_INT;
> - ebdp->cbd_prot = 0;
> - ebdp->cbd_bdu = 0;
> - }
> -
> - /* Update BD pointer to next entry */
> - if (status & BD_ENET_RX_WRAP)
> - bdp = fep->rx_bd_base;
> - else
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - /* Doing this here will keep the FEC running while we process
> - * incoming frames. On a heavily loaded network, we should be
> - * able to keep up at the expense of system resources.
> - */
> - writel(0, fep->hwp + FEC_R_DES_ACTIVE);
> - }
> - fep->cur_rx = bdp;
> -
> - return pkt_received;
> -}
> -
> -static irqreturn_t
> -fec_enet_interrupt(int irq, void *dev_id)
> -{
> - struct net_device *ndev = dev_id;
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - uint int_events;
> - irqreturn_t ret = IRQ_NONE;
> -
> - do {
> - int_events = readl(fep->hwp + FEC_IEVENT);
> - writel(int_events, fep->hwp + FEC_IEVENT);
> -
> - if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
> - ret = IRQ_HANDLED;
> -
> - /* Disable the RX interrupt */
> - if (napi_schedule_prep(&fep->napi)) {
> - writel(FEC_RX_DISABLED_IMASK,
> - fep->hwp + FEC_IMASK);
> - __napi_schedule(&fep->napi);
> - }
> - }
> -
> - if (int_events & FEC_ENET_MII) {
> - ret = IRQ_HANDLED;
> - complete(&fep->mdio_done);
> - }
> - } while (int_events);
> -
> - return ret;
> -}
> -
> -static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
> -{
> - struct net_device *ndev = napi->dev;
> - int pkts = fec_enet_rx(ndev, budget);
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - fec_enet_tx(ndev);
> -
> - if (pkts < budget) {
> - napi_complete(napi);
> - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> - }
> - return pkts;
> -}
> -
> -/* ------------------------------------------------------------------------- */
> -static void fec_get_mac(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
> - unsigned char *iap, tmpaddr[ETH_ALEN];
> -
> - /*
> - * try to get mac address in following order:
> - *
> - * 1) module parameter via kernel command line in form
> - * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
> - */
> - iap = macaddr;
> -
> -#ifdef CONFIG_OF
> - /*
> - * 2) from device tree data
> - */
> - if (!is_valid_ether_addr(iap)) {
> - struct device_node *np = fep->pdev->dev.of_node;
> - if (np) {
> - const char *mac = of_get_mac_address(np);
> - if (mac)
> - iap = (unsigned char *) mac;
> - }
> - }
> -#endif
> -
> - /*
> - * 3) from flash or fuse (via platform data)
> - */
> - if (!is_valid_ether_addr(iap)) {
> -#ifdef CONFIG_M5272
> - if (FEC_FLASHMAC)
> - iap = (unsigned char *)FEC_FLASHMAC;
> -#else
> - if (pdata)
> - iap = (unsigned char *)&pdata->mac;
> -#endif
> - }
> -
> - /*
> - * 4) FEC mac registers set by bootloader
> - */
> - if (!is_valid_ether_addr(iap)) {
> - *((unsigned long *) &tmpaddr[0]) =
> - be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
> - *((unsigned short *) &tmpaddr[4]) =
> - be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
> - iap = &tmpaddr[0];
> - }
> -
> - memcpy(ndev->dev_addr, iap, ETH_ALEN);
> -
> - /* Adjust MAC if using macaddr */
> - if (iap == macaddr)
> - ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
> -}
> -
> -/* ------------------------------------------------------------------------- */
> -
> -/*
> - * Phy section
> - */
> -static void fec_enet_adjust_link(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct phy_device *phy_dev = fep->phy_dev;
> - unsigned long flags;
> -
> - int status_change = 0;
> -
> - spin_lock_irqsave(&fep->hw_lock, flags);
> -
> - /* Prevent a state halted on mii error */
> - if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
> - phy_dev->state = PHY_RESUMING;
> - goto spin_unlock;
> - }
> -
> - /* Duplex link change */
> - if (phy_dev->link) {
> - if (fep->full_duplex != phy_dev->duplex) {
> - fec_restart(ndev, phy_dev->duplex);
> - /* prevent unnecessary second fec_restart() below */
> - fep->link = phy_dev->link;
> - status_change = 1;
> - }
> - }
> -
> - /* Link on or off change */
> - if (phy_dev->link != fep->link) {
> - fep->link = phy_dev->link;
> - if (phy_dev->link)
> - fec_restart(ndev, phy_dev->duplex);
> - else
> - fec_stop(ndev);
> - status_change = 1;
> - }
> -
> -spin_unlock:
> - spin_unlock_irqrestore(&fep->hw_lock, flags);
> -
> - if (status_change)
> - phy_print_status(phy_dev);
> -}
> -
> -static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
> -{
> - struct fec_enet_private *fep = bus->priv;
> - unsigned long time_left;
> -
> - fep->mii_timeout = 0;
> - init_completion(&fep->mdio_done);
> -
> - /* start a read op */
> - writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
> - FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> - FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
> -
> - /* wait for end of transfer */
> - time_left = wait_for_completion_timeout(&fep->mdio_done,
> - usecs_to_jiffies(FEC_MII_TIMEOUT));
> - if (time_left == 0) {
> - fep->mii_timeout = 1;
> - printk(KERN_ERR "FEC: MDIO read timeout\n");
> - return -ETIMEDOUT;
> - }
> -
> - /* return value */
> - return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
> -}
> -
> -static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
> - u16 value)
> -{
> - struct fec_enet_private *fep = bus->priv;
> - unsigned long time_left;
> -
> - fep->mii_timeout = 0;
> - init_completion(&fep->mdio_done);
> -
> - /* start a write op */
> - writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
> - FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> - FEC_MMFR_TA | FEC_MMFR_DATA(value),
> - fep->hwp + FEC_MII_DATA);
> -
> - /* wait for end of transfer */
> - time_left = wait_for_completion_timeout(&fep->mdio_done,
> - usecs_to_jiffies(FEC_MII_TIMEOUT));
> - if (time_left == 0) {
> - fep->mii_timeout = 1;
> - printk(KERN_ERR "FEC: MDIO write timeout\n");
> - return -ETIMEDOUT;
> - }
> -
> - return 0;
> -}
> -
> -static int fec_enet_mdio_reset(struct mii_bus *bus)
> -{
> - return 0;
> -}
> -
> -static int fec_enet_mii_probe(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - struct phy_device *phy_dev = NULL;
> - char mdio_bus_id[MII_BUS_ID_SIZE];
> - char phy_name[MII_BUS_ID_SIZE + 3];
> - int phy_id;
> - int dev_id = fep->dev_id;
> -
> - fep->phy_dev = NULL;
> -
> - /* check for attached phy */
> - for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
> - if ((fep->mii_bus->phy_mask & (1 << phy_id)))
> - continue;
> - if (fep->mii_bus->phy_map[phy_id] == NULL)
> - continue;
> - if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
> - continue;
> - if (dev_id--)
> - continue;
> - strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
> - break;
> - }
> -
> - if (phy_id >= PHY_MAX_ADDR) {
> - printk(KERN_INFO
> - "%s: no PHY, assuming direct connection to switch\n",
> - ndev->name);
> - strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
> - phy_id = 0;
> - }
> -
> - snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
> - phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
> - fep->phy_interface);
> - if (IS_ERR(phy_dev)) {
> - printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
> - return PTR_ERR(phy_dev);
> - }
> -
> - /* mask with MAC supported features */
> - if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
> - phy_dev->supported &= PHY_GBIT_FEATURES;
> - phy_dev->supported |= SUPPORTED_Pause;
> - }
> - else
> - phy_dev->supported &= PHY_BASIC_FEATURES;
> -
> - phy_dev->advertising = phy_dev->supported;
> -
> - fep->phy_dev = phy_dev;
> - fep->link = 0;
> - fep->full_duplex = 0;
> -
> - printk(KERN_INFO
> - "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
> - ndev->name,
> - fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
> - fep->phy_dev->irq);
> -
> - return 0;
> -}
> -
> -static int fec_enet_mii_init(struct platform_device *pdev)
> -{
> - static struct mii_bus *fec0_mii_bus;
> - struct net_device *ndev = platform_get_drvdata(pdev);
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - const struct platform_device_id *id_entry =
> - platform_get_device_id(fep->pdev);
> - int err = -ENXIO, i;
> -
> - /*
> - * The dual fec interfaces are not equivalent with enet-mac.
> - * Here are the differences:
> - *
> - * - fec0 supports MII & RMII modes while fec1 only supports RMII
> - * - fec0 acts as the 1588 time master while fec1 is slave
> - * - external phys can only be configured by fec0
> - *
> - * That is to say fec1 can not work independently. It only works
> - * when fec0 is working. The reason behind this design is that the
> - * second interface is added primarily for Switch mode.
> - *
> - * Because of the last point above, both phys are attached on fec0
> - * mdio interface in board design, and need to be configured by
> - * fec0 mii_bus.
> - */
> - if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
> - /* fec1 uses fec0 mii_bus */
> - if (mii_cnt && fec0_mii_bus) {
> - fep->mii_bus = fec0_mii_bus;
> - mii_cnt++;
> - return 0;
> - }
> - return -ENOENT;
> - }
> -
> - fep->mii_timeout = 0;
> -
> - /*
> - * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
> - *
> - * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
> - * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
> - * Reference Manual has an error on this, and gets fixed on i.MX6Q
> - * document.
> - */
> - fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
> - fep->phy_speed--;
> - fep->phy_speed <<= 1;
> - writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> -
> - fep->mii_bus = mdiobus_alloc();
> - if (fep->mii_bus == NULL) {
> - err = -ENOMEM;
> - goto err_out;
> - }
> -
> - fep->mii_bus->name = "fec_enet_mii_bus";
> - fep->mii_bus->read = fec_enet_mdio_read;
> - fep->mii_bus->write = fec_enet_mdio_write;
> - fep->mii_bus->reset = fec_enet_mdio_reset;
> - snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
> - pdev->name, fep->dev_id + 1);
> - fep->mii_bus->priv = fep;
> - fep->mii_bus->parent = &pdev->dev;
> -
> - fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
> - if (!fep->mii_bus->irq) {
> - err = -ENOMEM;
> - goto err_out_free_mdiobus;
> - }
> -
> - for (i = 0; i < PHY_MAX_ADDR; i++)
> - fep->mii_bus->irq[i] = PHY_POLL;
> -
> - if (mdiobus_register(fep->mii_bus))
> - goto err_out_free_mdio_irq;
> -
> - mii_cnt++;
> -
> - /* save fec0 mii_bus */
> - if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
> - fec0_mii_bus = fep->mii_bus;
> -
> - return 0;
> -
> -err_out_free_mdio_irq:
> - kfree(fep->mii_bus->irq);
> -err_out_free_mdiobus:
> - mdiobus_free(fep->mii_bus);
> -err_out:
> - return err;
> -}
> -
> -static void fec_enet_mii_remove(struct fec_enet_private *fep)
> -{
> - if (--mii_cnt == 0) {
> - mdiobus_unregister(fep->mii_bus);
> - kfree(fep->mii_bus->irq);
> - mdiobus_free(fep->mii_bus);
> - }
> -}
> -
> -static int fec_enet_get_settings(struct net_device *ndev,
> - struct ethtool_cmd *cmd)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct phy_device *phydev = fep->phy_dev;
> -
> - if (!phydev)
> - return -ENODEV;
> -
> - return phy_ethtool_gset(phydev, cmd);
> -}
> -
> -static int fec_enet_set_settings(struct net_device *ndev,
> - struct ethtool_cmd *cmd)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct phy_device *phydev = fep->phy_dev;
> -
> - if (!phydev)
> - return -ENODEV;
> -
> - return phy_ethtool_sset(phydev, cmd);
> -}
> -
> -static void fec_enet_get_drvinfo(struct net_device *ndev,
> - struct ethtool_drvinfo *info)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - strlcpy(info->driver, fep->pdev->dev.driver->name,
> - sizeof(info->driver));
> - strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
> - strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
> -}
> -
> -static int fec_enet_get_ts_info(struct net_device *ndev,
> - struct ethtool_ts_info *info)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - if (fep->bufdesc_ex) {
> -
> - info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
> - SOF_TIMESTAMPING_RX_SOFTWARE |
> - SOF_TIMESTAMPING_SOFTWARE |
> - SOF_TIMESTAMPING_TX_HARDWARE |
> - SOF_TIMESTAMPING_RX_HARDWARE |
> - SOF_TIMESTAMPING_RAW_HARDWARE;
> - if (fep->ptp_clock)
> - info->phc_index = ptp_clock_index(fep->ptp_clock);
> - else
> - info->phc_index = -1;
> -
> - info->tx_types = (1 << HWTSTAMP_TX_OFF) |
> - (1 << HWTSTAMP_TX_ON);
> -
> - info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
> - (1 << HWTSTAMP_FILTER_ALL);
> - return 0;
> - } else {
> - return ethtool_op_get_ts_info(ndev, info);
> - }
> -}
> -
> -static void fec_enet_get_pauseparam(struct net_device *ndev,
> - struct ethtool_pauseparam *pause)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
> - pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
> - pause->rx_pause = pause->tx_pause;
> -}
> -
> -static int fec_enet_set_pauseparam(struct net_device *ndev,
> - struct ethtool_pauseparam *pause)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - if (pause->tx_pause != pause->rx_pause) {
> - netdev_info(ndev,
> - "hardware only support enable/disable both tx and rx");
> - return -EINVAL;
> - }
> -
> - fep->pause_flag = 0;
> -
> - /* tx pause must be same as rx pause */
> - fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
> - fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
> -
> - if (pause->rx_pause || pause->autoneg) {
> - fep->phy_dev->supported |= ADVERTISED_Pause;
> - fep->phy_dev->advertising |= ADVERTISED_Pause;
> - } else {
> - fep->phy_dev->supported &= ~ADVERTISED_Pause;
> - fep->phy_dev->advertising &= ~ADVERTISED_Pause;
> - }
> -
> - if (pause->autoneg) {
> - if (netif_running(ndev))
> - fec_stop(ndev);
> - phy_start_aneg(fep->phy_dev);
> - }
> - if (netif_running(ndev))
> - fec_restart(ndev, 0);
> -
> - return 0;
> -}
> -
> -static const struct ethtool_ops fec_enet_ethtool_ops = {
> - .get_pauseparam = fec_enet_get_pauseparam,
> - .set_pauseparam = fec_enet_set_pauseparam,
> - .get_settings = fec_enet_get_settings,
> - .set_settings = fec_enet_set_settings,
> - .get_drvinfo = fec_enet_get_drvinfo,
> - .get_link = ethtool_op_get_link,
> - .get_ts_info = fec_enet_get_ts_info,
> -};
> -
> -static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct phy_device *phydev = fep->phy_dev;
> -
> - if (!netif_running(ndev))
> - return -EINVAL;
> -
> - if (!phydev)
> - return -ENODEV;
> -
> - if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
> - return fec_ptp_ioctl(ndev, rq, cmd);
> -
> - return phy_mii_ioctl(phydev, rq, cmd);
> -}
> -
> -static void fec_enet_free_buffers(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - int i;
> - struct sk_buff *skb;
> - struct bufdesc *bdp;
> -
> - bdp = fep->rx_bd_base;
> - for (i = 0; i < RX_RING_SIZE; i++) {
> - skb = fep->rx_skbuff[i];
> -
> - if (bdp->cbd_bufaddr)
> - dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> - FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
> - if (skb)
> - dev_kfree_skb(skb);
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - }
> -
> - bdp = fep->tx_bd_base;
> - for (i = 0; i < TX_RING_SIZE; i++)
> - kfree(fep->tx_bounce[i]);
> -}
> -
> -static int fec_enet_alloc_buffers(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - int i;
> - struct sk_buff *skb;
> - struct bufdesc *bdp;
> -
> - bdp = fep->rx_bd_base;
> - for (i = 0; i < RX_RING_SIZE; i++) {
> - skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
> - if (!skb) {
> - fec_enet_free_buffers(ndev);
> - return -ENOMEM;
> - }
> - fep->rx_skbuff[i] = skb;
> -
> - bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
> - FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
> - bdp->cbd_sc = BD_ENET_RX_EMPTY;
> -
> - if (fep->bufdesc_ex) {
> - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> - ebdp->cbd_esc = BD_ENET_RX_INT;
> - }
> -
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - }
> -
> - /* Set the last buffer to wrap. */
> - bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> - bdp->cbd_sc |= BD_SC_WRAP;
> -
> - bdp = fep->tx_bd_base;
> - for (i = 0; i < TX_RING_SIZE; i++) {
> - fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
> -
> - bdp->cbd_sc = 0;
> - bdp->cbd_bufaddr = 0;
> -
> - if (fep->bufdesc_ex) {
> - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> - ebdp->cbd_esc = BD_ENET_RX_INT;
> - }
> -
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - }
> -
> - /* Set the last buffer to wrap. */
> - bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> - bdp->cbd_sc |= BD_SC_WRAP;
> -
> - return 0;
> -}
> -
> -static int
> -fec_enet_open(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - int ret;
> -
> - napi_enable(&fep->napi);
> -
> - /* I should reset the ring buffers here, but I don't yet know
> - * a simple way to do that.
> - */
> -
> - ret = fec_enet_alloc_buffers(ndev);
> - if (ret)
> - return ret;
> -
> - /* Probe and connect to PHY when open the interface */
> - ret = fec_enet_mii_probe(ndev);
> - if (ret) {
> - fec_enet_free_buffers(ndev);
> - return ret;
> - }
> - phy_start(fep->phy_dev);
> - netif_start_queue(ndev);
> - fep->opened = 1;
> - return 0;
> -}
> -
> -static int
> -fec_enet_close(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - /* Don't know what to do yet. */
> - fep->opened = 0;
> - netif_stop_queue(ndev);
> - fec_stop(ndev);
> -
> - if (fep->phy_dev) {
> - phy_stop(fep->phy_dev);
> - phy_disconnect(fep->phy_dev);
> - }
> -
> - fec_enet_free_buffers(ndev);
> -
> - return 0;
> -}
> -
> -/* Set or clear the multicast filter for this adaptor.
> - * Skeleton taken from sunlance driver.
> - * The CPM Ethernet implementation allows Multicast as well as individual
> - * MAC address filtering. Some of the drivers check to make sure it is
> - * a group multicast address, and discard those that are not. I guess I
> - * will do the same for now, but just remove the test if you want
> - * individual filtering as well (do the upper net layers want or support
> - * this kind of feature?).
> - */
> -
> -#define HASH_BITS 6 /* #bits in hash */
> -#define CRC32_POLY 0xEDB88320
> -
> -static void set_multicast_list(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct netdev_hw_addr *ha;
> - unsigned int i, bit, data, crc, tmp;
> - unsigned char hash;
> -
> - if (ndev->flags & IFF_PROMISC) {
> - tmp = readl(fep->hwp + FEC_R_CNTRL);
> - tmp |= 0x8;
> - writel(tmp, fep->hwp + FEC_R_CNTRL);
> - return;
> - }
> -
> - tmp = readl(fep->hwp + FEC_R_CNTRL);
> - tmp &= ~0x8;
> - writel(tmp, fep->hwp + FEC_R_CNTRL);
> -
> - if (ndev->flags & IFF_ALLMULTI) {
> - /* Catch all multicast addresses, so set the
> - * filter to all 1's
> - */
> - writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> - writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> -
> - return;
> - }
> -
> - /* Clear filter and add the addresses in hash register
> - */
> - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> -
> - netdev_for_each_mc_addr(ha, ndev) {
> - /* calculate crc32 value of mac address */
> - crc = 0xffffffff;
> -
> - for (i = 0; i < ndev->addr_len; i++) {
> - data = ha->addr[i];
> - for (bit = 0; bit < 8; bit++, data >>= 1) {
> - crc = (crc >> 1) ^
> - (((crc ^ data) & 1) ? CRC32_POLY : 0);
> - }
> - }
> -
> - /* only upper 6 bits (HASH_BITS) are used
> - * which point to specific bit in he hash registers
> - */
> - hash = (crc >> (32 - HASH_BITS)) & 0x3f;
> -
> - if (hash > 31) {
> - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> - tmp |= 1 << (hash - 32);
> - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> - } else {
> - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> - tmp |= 1 << hash;
> - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> - }
> - }
> -}
> -
> -/* Set a MAC change in hardware. */
> -static int
> -fec_set_mac_address(struct net_device *ndev, void *p)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct sockaddr *addr = p;
> -
> - if (!is_valid_ether_addr(addr->sa_data))
> - return -EADDRNOTAVAIL;
> -
> - memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
> -
> - writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
> - (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
> - fep->hwp + FEC_ADDR_LOW);
> - writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
> - fep->hwp + FEC_ADDR_HIGH);
> - return 0;
> -}
> -
> -#ifdef CONFIG_NET_POLL_CONTROLLER
> -/**
> - * fec_poll_controller - FEC Poll controller function
> - * @dev: The FEC network adapter
> - *
> - * Polled functionality used by netconsole and others in non interrupt mode
> - *
> - */
> -void fec_poll_controller(struct net_device *dev)
> -{
> - int i;
> - struct fec_enet_private *fep = netdev_priv(dev);
> -
> - for (i = 0; i < FEC_IRQ_NUM; i++) {
> - if (fep->irq[i] > 0) {
> - disable_irq(fep->irq[i]);
> - fec_enet_interrupt(fep->irq[i], dev);
> - enable_irq(fep->irq[i]);
> - }
> - }
> -}
> -#endif
> -
> -static const struct net_device_ops fec_netdev_ops = {
> - .ndo_open = fec_enet_open,
> - .ndo_stop = fec_enet_close,
> - .ndo_start_xmit = fec_enet_start_xmit,
> - .ndo_set_rx_mode = set_multicast_list,
> - .ndo_change_mtu = eth_change_mtu,
> - .ndo_validate_addr = eth_validate_addr,
> - .ndo_tx_timeout = fec_timeout,
> - .ndo_set_mac_address = fec_set_mac_address,
> - .ndo_do_ioctl = fec_enet_ioctl,
> -#ifdef CONFIG_NET_POLL_CONTROLLER
> - .ndo_poll_controller = fec_poll_controller,
> -#endif
> -};
> -
> - /*
> - * XXX: We need to clean up on failure exits here.
> - *
> - */
> -static int fec_enet_init(struct net_device *ndev)
> -{
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - struct bufdesc *cbd_base;
> - struct bufdesc *bdp;
> - int i;
> -
> - /* Allocate memory for buffer descriptors. */
> - cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
> - GFP_KERNEL);
> - if (!cbd_base)
> - return -ENOMEM;
> -
> - spin_lock_init(&fep->hw_lock);
> -
> - fep->netdev = ndev;
> -
> - /* Get the Ethernet address */
> - fec_get_mac(ndev);
> -
> - /* Set receive and transmit descriptor base. */
> - fep->rx_bd_base = cbd_base;
> - if (fep->bufdesc_ex)
> - fep->tx_bd_base = (struct bufdesc *)
> - (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
> - else
> - fep->tx_bd_base = cbd_base + RX_RING_SIZE;
> -
> - /* The FEC Ethernet specific entries in the device structure */
> - ndev->watchdog_timeo = TX_TIMEOUT;
> - ndev->netdev_ops = &fec_netdev_ops;
> - ndev->ethtool_ops = &fec_enet_ethtool_ops;
> -
> - writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
> - netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
> -
> - /* Initialize the receive buffer descriptors. */
> - bdp = fep->rx_bd_base;
> - for (i = 0; i < RX_RING_SIZE; i++) {
> -
> - /* Initialize the BD for every fragment in the page. */
> - bdp->cbd_sc = 0;
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - }
> -
> - /* Set the last buffer to wrap */
> - bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> - bdp->cbd_sc |= BD_SC_WRAP;
> -
> - /* ...and the same for transmit */
> - bdp = fep->tx_bd_base;
> - fep->cur_tx = bdp;
> - for (i = 0; i < TX_RING_SIZE; i++) {
> -
> - /* Initialize the BD for every fragment in the page. */
> - bdp->cbd_sc = 0;
> - bdp->cbd_bufaddr = 0;
> - bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> - }
> -
> - /* Set the last buffer to wrap */
> - bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> - bdp->cbd_sc |= BD_SC_WRAP;
> - fep->dirty_tx = bdp;
> -
> - fec_restart(ndev, 0);
> -
> - return 0;
> -}
> -
> -#ifdef CONFIG_OF
> -static int fec_get_phy_mode_dt(struct platform_device *pdev)
> -{
> - struct device_node *np = pdev->dev.of_node;
> -
> - if (np)
> - return of_get_phy_mode(np);
> -
> - return -ENODEV;
> -}
> -
> -static void fec_reset_phy(struct platform_device *pdev)
> -{
> - int err, phy_reset;
> - int msec = 1;
> - struct device_node *np = pdev->dev.of_node;
> -
> - if (!np)
> - return;
> -
> - of_property_read_u32(np, "phy-reset-duration", &msec);
> - /* A sane reset duration should not be longer than 1s */
> - if (msec > 1000)
> - msec = 1;
> -
> - phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
> - if (!gpio_is_valid(phy_reset))
> - return;
> -
> - err = devm_gpio_request_one(&pdev->dev, phy_reset,
> - GPIOF_OUT_INIT_LOW, "phy-reset");
> - if (err) {
> - dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
> - return;
> - }
> - msleep(msec);
> - gpio_set_value(phy_reset, 1);
> -}
> -#else /* CONFIG_OF */
> -static int fec_get_phy_mode_dt(struct platform_device *pdev)
> -{
> - return -ENODEV;
> -}
> -
> -static void fec_reset_phy(struct platform_device *pdev)
> -{
> - /*
> - * In case of platform probe, the reset has been done
> - * by machine code.
> - */
> -}
> -#endif /* CONFIG_OF */
> -
> -static int
> -fec_probe(struct platform_device *pdev)
> -{
> - struct fec_enet_private *fep;
> - struct fec_platform_data *pdata;
> - struct net_device *ndev;
> - int i, irq, ret = 0;
> - struct resource *r;
> - const struct of_device_id *of_id;
> - static int dev_id;
> - struct pinctrl *pinctrl;
> - struct regulator *reg_phy;
> -
> - of_id = of_match_device(fec_dt_ids, &pdev->dev);
> - if (of_id)
> - pdev->id_entry = of_id->data;
> -
> - r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - if (!r)
> - return -ENXIO;
> -
> - /* Init network device */
> - ndev = alloc_etherdev(sizeof(struct fec_enet_private));
> - if (!ndev)
> - return -ENOMEM;
> -
> - SET_NETDEV_DEV(ndev, &pdev->dev);
> -
> - /* setup board info structure */
> - fep = netdev_priv(ndev);
> -
> - /* default enable pause frame auto negotiation */
> - if (pdev->id_entry &&
> - (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
> - fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
> -
> - fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
> - fep->pdev = pdev;
> - fep->dev_id = dev_id++;
> -
> - fep->bufdesc_ex = 0;
> -
> - if (!fep->hwp) {
> - ret = -ENOMEM;
> - goto failed_ioremap;
> - }
> -
> - platform_set_drvdata(pdev, ndev);
> -
> - ret = fec_get_phy_mode_dt(pdev);
> - if (ret < 0) {
> - pdata = pdev->dev.platform_data;
> - if (pdata)
> - fep->phy_interface = pdata->phy;
> - else
> - fep->phy_interface = PHY_INTERFACE_MODE_MII;
> - } else {
> - fep->phy_interface = ret;
> - }
> -
> - pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> - if (IS_ERR(pinctrl)) {
> - ret = PTR_ERR(pinctrl);
> - goto failed_pin;
> - }
> -
> - fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
> - if (IS_ERR(fep->clk_ipg)) {
> - ret = PTR_ERR(fep->clk_ipg);
> - goto failed_clk;
> - }
> -
> - fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
> - if (IS_ERR(fep->clk_ahb)) {
> - ret = PTR_ERR(fep->clk_ahb);
> - goto failed_clk;
> - }
> -
> - fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
> - fep->bufdesc_ex =
> - pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
> - if (IS_ERR(fep->clk_ptp)) {
> - ret = PTR_ERR(fep->clk_ptp);
> - fep->bufdesc_ex = 0;
> - }
> -
> - clk_prepare_enable(fep->clk_ahb);
> - clk_prepare_enable(fep->clk_ipg);
> - if (!IS_ERR(fep->clk_ptp))
> - clk_prepare_enable(fep->clk_ptp);
> -
> - reg_phy = devm_regulator_get(&pdev->dev, "phy");
> - if (!IS_ERR(reg_phy)) {
> - ret = regulator_enable(reg_phy);
> - if (ret) {
> - dev_err(&pdev->dev,
> - "Failed to enable phy regulator: %d\n", ret);
> - goto failed_regulator;
> - }
> - }
> -
> - fec_reset_phy(pdev);
> -
> - if (fep->bufdesc_ex)
> - fec_ptp_init(ndev, pdev);
> -
> - ret = fec_enet_init(ndev);
> - if (ret)
> - goto failed_init;
> -
> - for (i = 0; i < FEC_IRQ_NUM; i++) {
> - irq = platform_get_irq(pdev, i);
> - if (irq < 0) {
> - if (i)
> - break;
> - ret = irq;
> - goto failed_irq;
> - }
> - ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
> - if (ret) {
> - while (--i >= 0) {
> - irq = platform_get_irq(pdev, i);
> - free_irq(irq, ndev);
> - }
> - goto failed_irq;
> - }
> - }
> -
> - ret = fec_enet_mii_init(pdev);
> - if (ret)
> - goto failed_mii_init;
> -
> - /* Carrier starts down, phylib will bring it up */
> - netif_carrier_off(ndev);
> -
> - ret = register_netdev(ndev);
> - if (ret)
> - goto failed_register;
> -
> - return 0;
> -
> -failed_register:
> - fec_enet_mii_remove(fep);
> -failed_mii_init:
> -failed_init:
> - for (i = 0; i < FEC_IRQ_NUM; i++) {
> - irq = platform_get_irq(pdev, i);
> - if (irq > 0)
> - free_irq(irq, ndev);
> - }
> -failed_irq:
> -failed_regulator:
> - clk_disable_unprepare(fep->clk_ahb);
> - clk_disable_unprepare(fep->clk_ipg);
> - if (!IS_ERR(fep->clk_ptp))
> - clk_disable_unprepare(fep->clk_ptp);
> -failed_pin:
> -failed_clk:
> -failed_ioremap:
> - free_netdev(ndev);
> -
> - return ret;
> -}
> -
> -static int
> -fec_drv_remove(struct platform_device *pdev)
> -{
> - struct net_device *ndev = platform_get_drvdata(pdev);
> - struct fec_enet_private *fep = netdev_priv(ndev);
> - int i;
> -
> - unregister_netdev(ndev);
> - fec_enet_mii_remove(fep);
> - del_timer_sync(&fep->time_keep);
> - clk_disable_unprepare(fep->clk_ptp);
> - if (fep->ptp_clock)
> - ptp_clock_unregister(fep->ptp_clock);
> - clk_disable_unprepare(fep->clk_ahb);
> - clk_disable_unprepare(fep->clk_ipg);
> - for (i = 0; i < FEC_IRQ_NUM; i++) {
> - int irq = platform_get_irq(pdev, i);
> - if (irq > 0)
> - free_irq(irq, ndev);
> - }
> - free_netdev(ndev);
> -
> - platform_set_drvdata(pdev, NULL);
> -
> - return 0;
> -}
> -
> -#ifdef CONFIG_PM
> -static int
> -fec_suspend(struct device *dev)
> -{
> - struct net_device *ndev = dev_get_drvdata(dev);
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - if (netif_running(ndev)) {
> - fec_stop(ndev);
> - netif_device_detach(ndev);
> - }
> - clk_disable_unprepare(fep->clk_ahb);
> - clk_disable_unprepare(fep->clk_ipg);
> -
> - return 0;
> -}
> -
> -static int
> -fec_resume(struct device *dev)
> -{
> - struct net_device *ndev = dev_get_drvdata(dev);
> - struct fec_enet_private *fep = netdev_priv(ndev);
> -
> - clk_prepare_enable(fep->clk_ahb);
> - clk_prepare_enable(fep->clk_ipg);
> - if (netif_running(ndev)) {
> - fec_restart(ndev, fep->full_duplex);
> - netif_device_attach(ndev);
> - }
> -
> - return 0;
> -}
> -
> -static const struct dev_pm_ops fec_pm_ops = {
> - .suspend = fec_suspend,
> - .resume = fec_resume,
> - .freeze = fec_suspend,
> - .thaw = fec_resume,
> - .poweroff = fec_suspend,
> - .restore = fec_resume,
> -};
> -#endif
> -
> -static struct platform_driver fec_driver = {
> - .driver = {
> - .name = DRIVER_NAME,
> - .owner = THIS_MODULE,
> -#ifdef CONFIG_PM
> - .pm = &fec_pm_ops,
> -#endif
> - .of_match_table = fec_dt_ids,
> - },
> - .id_table = fec_devtype,
> - .probe = fec_probe,
> - .remove = fec_drv_remove,
> -};
> -
> -module_platform_driver(fec_driver);
> -
> -MODULE_LICENSE("GPL");
> diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
> new file mode 100644
> index 0000000..69a4ade
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -0,0 +1,1961 @@
> +/*
> + * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
> + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
> + *
> + * Right now, I am very wasteful with the buffers. I allocate memory
> + * pages and then divide them into 2K frame buffers. This way I know I
> + * have buffers large enough to hold one frame within one buffer descriptor.
> + * Once I get this working, I will use 64 or 128 byte CPM buffers, which
> + * will be much more memory efficient and will easily handle lots of
> + * small packets.
> + *
> + * Much better multiple PHY support by Magnus Damm.
> + * Copyright (c) 2000 Ericsson Radio Systems AB.
> + *
> + * Support for FEC controller of ColdFire processors.
> + * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
> + *
> + * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
> + * Copyright (c) 2004-2006 Macq Electronique SA.
> + *
> + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/ptrace.h>
> +#include <linux/errno.h>
> +#include <linux/ioport.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
> +#include <linux/bitops.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy.h>
> +#include <linux/fec.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_net.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#ifndef CONFIG_ARM
> +#include <asm/coldfire.h>
> +#include <asm/mcfsim.h>
> +#endif
> +
> +#include "fec.h"
> +
> +#if defined(CONFIG_ARM)
> +#define FEC_ALIGNMENT 0xf
> +#else
> +#define FEC_ALIGNMENT 0x3
> +#endif
> +
> +#define DRIVER_NAME "fec"
> +#define FEC_NAPI_WEIGHT 64
> +
> +/* Pause frame feild and FIFO threshold */
> +#define FEC_ENET_FCE (1 << 5)
> +#define FEC_ENET_RSEM_V 0x84
> +#define FEC_ENET_RSFL_V 16
> +#define FEC_ENET_RAEM_V 0x8
> +#define FEC_ENET_RAFL_V 0x8
> +#define FEC_ENET_OPD_V 0xFFF0
> +
> +/* Controller is ENET-MAC */
> +#define FEC_QUIRK_ENET_MAC (1 << 0)
> +/* Controller needs driver to swap frame */
> +#define FEC_QUIRK_SWAP_FRAME (1 << 1)
> +/* Controller uses gasket */
> +#define FEC_QUIRK_USE_GASKET (1 << 2)
> +/* Controller has GBIT support */
> +#define FEC_QUIRK_HAS_GBIT (1 << 3)
> +/* Controller has extend desc buffer */
> +#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4)
> +
> +static struct platform_device_id fec_devtype[] = {
> + {
> + /* keep it for coldfire */
> + .name = DRIVER_NAME,
> + .driver_data = 0,
> + }, {
> + .name = "imx25-fec",
> + .driver_data = FEC_QUIRK_USE_GASKET,
> + }, {
> + .name = "imx27-fec",
> + .driver_data = 0,
> + }, {
> + .name = "imx28-fec",
> + .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
> + }, {
> + .name = "imx6q-fec",
> + .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
> + FEC_QUIRK_HAS_BUFDESC_EX,
> + }, {
> + /* sentinel */
> + }
> +};
> +MODULE_DEVICE_TABLE(platform, fec_devtype);
> +
> +enum imx_fec_type {
> + IMX25_FEC = 1, /* runs on i.mx25/50/53 */
> + IMX27_FEC, /* runs on i.mx27/35/51 */
> + IMX28_FEC,
> + IMX6Q_FEC,
> +};
> +
> +static const struct of_device_id fec_dt_ids[] = {
> + { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
> + { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
> + { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
> + { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, fec_dt_ids);
> +
> +static unsigned char macaddr[ETH_ALEN];
> +module_param_array(macaddr, byte, NULL, 0);
> +MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
> +
> +#if defined(CONFIG_M5272)
> +/*
> + * Some hardware gets it MAC address out of local flash memory.
> + * if this is non-zero then assume it is the address to get MAC from.
> + */
> +#if defined(CONFIG_NETtel)
> +#define FEC_FLASHMAC 0xf0006006
> +#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
> +#define FEC_FLASHMAC 0xf0006000
> +#elif defined(CONFIG_CANCam)
> +#define FEC_FLASHMAC 0xf0020000
> +#elif defined (CONFIG_M5272C3)
> +#define FEC_FLASHMAC (0xffe04000 + 4)
> +#elif defined(CONFIG_MOD5272)
> +#define FEC_FLASHMAC 0xffc0406b
> +#else
> +#define FEC_FLASHMAC 0
> +#endif
> +#endif /* CONFIG_M5272 */
> +
> +#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
> +#error "FEC: descriptor ring size constants too large"
> +#endif
> +
> +/* Interrupt events/masks. */
> +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
> +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
> +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
> +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
> +#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
> +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
> +#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
> +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
> +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
> +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
> +
> +#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
> +#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
> +
> +/* The FEC stores dest/src/type, data, and checksum for receive packets.
> + */
> +#define PKT_MAXBUF_SIZE 1518
> +#define PKT_MINBUF_SIZE 64
> +#define PKT_MAXBLR_SIZE 1520
> +
> +/*
> + * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
> + * size bits. Other FEC hardware does not, so we need to take that into
> + * account when setting it.
> + */
> +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
> + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
> +#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
> +#else
> +#define OPT_FRAME_SIZE 0
> +#endif
> +
> +/* FEC MII MMFR bits definition */
> +#define FEC_MMFR_ST (1 << 30)
> +#define FEC_MMFR_OP_READ (2 << 28)
> +#define FEC_MMFR_OP_WRITE (1 << 28)
> +#define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
> +#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
> +#define FEC_MMFR_TA (2 << 16)
> +#define FEC_MMFR_DATA(v) (v & 0xffff)
> +
> +#define FEC_MII_TIMEOUT 30000 /* us */
> +
> +/* Transmitter timeout */
> +#define TX_TIMEOUT (2 * HZ)
> +
> +#define FEC_PAUSE_FLAG_AUTONEG 0x1
> +#define FEC_PAUSE_FLAG_ENABLE 0x2
> +
> +static int mii_cnt;
> +
> +static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
> +{
> + struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
> + if (is_ex)
> + return (struct bufdesc *)(ex + 1);
> + else
> + return bdp + 1;
> +}
> +
> +static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
> +{
> + struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
> + if (is_ex)
> + return (struct bufdesc *)(ex - 1);
> + else
> + return bdp - 1;
> +}
> +
> +static void *swap_buffer(void *bufaddr, int len)
> +{
> + int i;
> + unsigned int *buf = bufaddr;
> +
> + for (i = 0; i < (len + 3) / 4; i++, buf++)
> + *buf = cpu_to_be32(*buf);
> +
> + return bufaddr;
> +}
> +
> +static netdev_tx_t
> +fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + struct bufdesc *bdp;
> + void *bufaddr;
> + unsigned short status;
> + unsigned int index;
> +
> + if (!fep->link) {
> + /* Link is down or autonegotiation is in progress. */
> + return NETDEV_TX_BUSY;
> + }
> +
> + /* Fill in a Tx ring entry */
> + bdp = fep->cur_tx;
> +
> + status = bdp->cbd_sc;
> +
> + if (status & BD_ENET_TX_READY) {
> + /* Ooops. All transmit buffers are full. Bail out.
> + * This should not happen, since ndev->tbusy should be set.
> + */
> + printk("%s: tx queue full!.\n", ndev->name);
> + return NETDEV_TX_BUSY;
> + }
> +
> + /* Clear all of the status flags */
> + status &= ~BD_ENET_TX_STATS;
> +
> + /* Set buffer length and buffer pointer */
> + bufaddr = skb->data;
> + bdp->cbd_datlen = skb->len;
> +
> + /*
> + * On some FEC implementations data must be aligned on
> + * 4-byte boundaries. Use bounce buffers to copy data
> + * and get it aligned. Ugh.
> + */
> + if (fep->bufdesc_ex)
> + index = (struct bufdesc_ex *)bdp -
> + (struct bufdesc_ex *)fep->tx_bd_base;
> + else
> + index = bdp - fep->tx_bd_base;
> +
> + if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
> + memcpy(fep->tx_bounce[index], skb->data, skb->len);
> + bufaddr = fep->tx_bounce[index];
> + }
> +
> + /*
> + * Some design made an incorrect assumption on endian mode of
> + * the system that it's running on. As the result, driver has to
> + * swap every frame going to and coming from the controller.
> + */
> + if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> + swap_buffer(bufaddr, skb->len);
> +
> + /* Save skb pointer */
> + fep->tx_skbuff[index] = skb;
> +
> + /* Push the data cache so the CPM does not get stale memory
> + * data.
> + */
> + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
> + FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
> +
> + /* Send it on its way. Tell FEC it's ready, interrupt when done,
> + * it's the last BD of the frame, and to put the CRC on the end.
> + */
> + status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
> + | BD_ENET_TX_LAST | BD_ENET_TX_TC);
> + bdp->cbd_sc = status;
> +
> + if (fep->bufdesc_ex) {
> +
> + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> + ebdp->cbd_bdu = 0;
> + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
> + fep->hwts_tx_en)) {
> + ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
> + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
> + } else {
> +
> + ebdp->cbd_esc = BD_ENET_TX_INT;
> + }
> + }
> + /* If this was the last BD in the ring, start at the beginning again. */
> + if (status & BD_ENET_TX_WRAP)
> + bdp = fep->tx_bd_base;
> + else
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> +
> + fep->cur_tx = bdp;
> +
> + if (fep->cur_tx == fep->dirty_tx)
> + netif_stop_queue(ndev);
> +
> + /* Trigger transmission start */
> + writel(0, fep->hwp + FEC_X_DES_ACTIVE);
> +
> + skb_tx_timestamp(skb);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +/* This function is called to start or restart the FEC during a link
> + * change. This only happens when switching between half and full
> + * duplex.
> + */
> +static void
> +fec_restart(struct net_device *ndev, int duplex)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + int i;
> + u32 temp_mac[2];
> + u32 rcntl = OPT_FRAME_SIZE | 0x04;
> + u32 ecntl = 0x2; /* ETHEREN */
> +
> + /* Whack a reset. We should wait for this. */
> + writel(1, fep->hwp + FEC_ECNTRL);
> + udelay(10);
> +
> + /*
> + * enet-mac reset will reset mac address registers too,
> + * so need to reconfigure it.
> + */
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> + memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
> + writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
> + writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
> + }
> +
> + /* Clear any outstanding interrupt. */
> + writel(0xffc00000, fep->hwp + FEC_IEVENT);
> +
> + /* Reset all multicast. */
> + writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> + writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> +#ifndef CONFIG_M5272
> + writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
> + writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
> +#endif
> +
> + /* Set maximum receive buffer size. */
> + writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
> +
> + /* Set receive and transmit descriptor base. */
> + writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
> + if (fep->bufdesc_ex)
> + writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
> + * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
> + else
> + writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
> + * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
> +
> + fep->cur_rx = fep->rx_bd_base;
> +
> + for (i = 0; i <= TX_RING_MOD_MASK; i++) {
> + if (fep->tx_skbuff[i]) {
> + dev_kfree_skb_any(fep->tx_skbuff[i]);
> + fep->tx_skbuff[i] = NULL;
> + }
> + }
> +
> + /* Enable MII mode */
> + if (duplex) {
> + /* FD enable */
> + writel(0x04, fep->hwp + FEC_X_CNTRL);
> + } else {
> + /* No Rcv on Xmit */
> + rcntl |= 0x02;
> + writel(0x0, fep->hwp + FEC_X_CNTRL);
> + }
> +
> + fep->full_duplex = duplex;
> +
> + /* Set MII speed */
> + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> +
> + /*
> + * The phy interface and speed need to get configured
> + * differently on enet-mac.
> + */
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> + /* Enable flow control and length check */
> + rcntl |= 0x40000000 | 0x00000020;
> +
> + /* RGMII, RMII or MII */
> + if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
> + rcntl |= (1 << 6);
> + else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
> + rcntl |= (1 << 8);
> + else
> + rcntl &= ~(1 << 8);
> +
> + /* 1G, 100M or 10M */
> + if (fep->phy_dev) {
> + if (fep->phy_dev->speed == SPEED_1000)
> + ecntl |= (1 << 5);
> + else if (fep->phy_dev->speed == SPEED_100)
> + rcntl &= ~(1 << 9);
> + else
> + rcntl |= (1 << 9);
> + }
> + } else {
> +#ifdef FEC_MIIGSK_ENR
> + if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
> + u32 cfgr;
> + /* disable the gasket and wait */
> + writel(0, fep->hwp + FEC_MIIGSK_ENR);
> + while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
> + udelay(1);
> +
> + /*
> + * configure the gasket:
> + * RMII, 50 MHz, no loopback, no echo
> + * MII, 25 MHz, no loopback, no echo
> + */
> + cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
> + ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
> + if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
> + cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
> + writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
> +
> + /* re-enable the gasket */
> + writel(2, fep->hwp + FEC_MIIGSK_ENR);
> + }
> +#endif
> + }
> +
> + /* enable pause frame*/
> + if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
> + ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
> + fep->phy_dev && fep->phy_dev->pause)) {
> + rcntl |= FEC_ENET_FCE;
> +
> + /* set FIFO thresh hold parameter to reduce overrun */
> + writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
> + writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
> + writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
> + writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
> +
> + /* OPD */
> + writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
> + } else {
> + rcntl &= ~FEC_ENET_FCE;
> + }
> +
> + writel(rcntl, fep->hwp + FEC_R_CNTRL);
> +
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> + /* enable ENET endian swap */
> + ecntl |= (1 << 8);
> + /* enable ENET store and forward mode */
> + writel(1 << 8, fep->hwp + FEC_X_WMRK);
> + }
> +
> + if (fep->bufdesc_ex)
> + ecntl |= (1 << 4);
> +
> + /* And last, enable the transmit and receive processing */
> + writel(ecntl, fep->hwp + FEC_ECNTRL);
> + writel(0, fep->hwp + FEC_R_DES_ACTIVE);
> +
> + if (fep->bufdesc_ex)
> + fec_ptp_start_cyclecounter(ndev);
> +
> + /* Enable interrupts we wish to service */
> + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> +}
> +
> +static void
> +fec_stop(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
> +
> + /* We cannot expect a graceful transmit stop without link !!! */
> + if (fep->link) {
> + writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
> + udelay(10);
> + if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
> + printk("fec_stop : Graceful transmit stop did not complete !\n");
> + }
> +
> + /* Whack a reset. We should wait for this. */
> + writel(1, fep->hwp + FEC_ECNTRL);
> + udelay(10);
> + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> +
> + /* We have to keep ENET enabled to have MII interrupt stay working */
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
> + writel(2, fep->hwp + FEC_ECNTRL);
> + writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
> + }
> +}
> +
> +
> +static void
> +fec_timeout(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + ndev->stats.tx_errors++;
> +
> + fec_restart(ndev, fep->full_duplex);
> + netif_wake_queue(ndev);
> +}
> +
> +static void
> +fec_enet_tx(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep;
> + struct bufdesc *bdp;
> + unsigned short status;
> + struct sk_buff *skb;
> + int index = 0;
> +
> + fep = netdev_priv(ndev);
> + bdp = fep->dirty_tx;
> +
> + /* get next bdp of dirty_tx */
> + if (bdp->cbd_sc & BD_ENET_TX_WRAP)
> + bdp = fep->tx_bd_base;
> + else
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> +
> + while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
> +
> + /* current queue is empty */
> + if (bdp == fep->cur_tx)
> + break;
> +
> + if (fep->bufdesc_ex)
> + index = (struct bufdesc_ex *)bdp -
> + (struct bufdesc_ex *)fep->tx_bd_base;
> + else
> + index = bdp - fep->tx_bd_base;
> +
> + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> + FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
> + bdp->cbd_bufaddr = 0;
> +
> + skb = fep->tx_skbuff[index];
> +
> + /* Check for errors. */
> + if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
> + BD_ENET_TX_RL | BD_ENET_TX_UN |
> + BD_ENET_TX_CSL)) {
> + ndev->stats.tx_errors++;
> + if (status & BD_ENET_TX_HB) /* No heartbeat */
> + ndev->stats.tx_heartbeat_errors++;
> + if (status & BD_ENET_TX_LC) /* Late collision */
> + ndev->stats.tx_window_errors++;
> + if (status & BD_ENET_TX_RL) /* Retrans limit */
> + ndev->stats.tx_aborted_errors++;
> + if (status & BD_ENET_TX_UN) /* Underrun */
> + ndev->stats.tx_fifo_errors++;
> + if (status & BD_ENET_TX_CSL) /* Carrier lost */
> + ndev->stats.tx_carrier_errors++;
> + } else {
> + ndev->stats.tx_packets++;
> + }
> +
> + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
> + fep->bufdesc_ex) {
> + struct skb_shared_hwtstamps shhwtstamps;
> + unsigned long flags;
> + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> +
> + memset(&shhwtstamps, 0, sizeof(shhwtstamps));
> + spin_lock_irqsave(&fep->tmreg_lock, flags);
> + shhwtstamps.hwtstamp = ns_to_ktime(
> + timecounter_cyc2time(&fep->tc, ebdp->ts));
> + spin_unlock_irqrestore(&fep->tmreg_lock, flags);
> + skb_tstamp_tx(skb, &shhwtstamps);
> + }
> +
> + if (status & BD_ENET_TX_READY)
> + printk("HEY! Enet xmit interrupt and TX_READY.\n");
> +
> + /* Deferred means some collisions occurred during transmit,
> + * but we eventually sent the packet OK.
> + */
> + if (status & BD_ENET_TX_DEF)
> + ndev->stats.collisions++;
> +
> + /* Free the sk buffer associated with this last transmit */
> + dev_kfree_skb_any(skb);
> + fep->tx_skbuff[index] = NULL;
> +
> + fep->dirty_tx = bdp;
> +
> + /* Update pointer to next buffer descriptor to be transmitted */
> + if (status & BD_ENET_TX_WRAP)
> + bdp = fep->tx_bd_base;
> + else
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> +
> + /* Since we have freed up a buffer, the ring is no longer full
> + */
> + if (fep->dirty_tx != fep->cur_tx) {
> + if (netif_queue_stopped(ndev))
> + netif_wake_queue(ndev);
> + }
> + }
> + return;
> +}
> +
> +
> +/* During a receive, the cur_rx points to the current incoming buffer.
> + * When we update through the ring, if the next incoming buffer has
> + * not been given to the system, we just set the empty indicator,
> + * effectively tossing the packet.
> + */
> +static int
> +fec_enet_rx(struct net_device *ndev, int budget)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + struct bufdesc *bdp;
> + unsigned short status;
> + struct sk_buff *skb;
> + ushort pkt_len;
> + __u8 *data;
> + int pkt_received = 0;
> +
> +#ifdef CONFIG_M532x
> + flush_cache_all();
> +#endif
> +
> + /* First, grab all of the stats for the incoming packet.
> + * These get messed up if we get called due to a busy condition.
> + */
> + bdp = fep->cur_rx;
> +
> + while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
> +
> + if (pkt_received >= budget)
> + break;
> + pkt_received++;
> +
> + /* Since we have allocated space to hold a complete frame,
> + * the last indicator should be set.
> + */
> + if ((status & BD_ENET_RX_LAST) == 0)
> + printk("FEC ENET: rcv is not +last\n");
> +
> + if (!fep->opened)
> + goto rx_processing_done;
> +
> + /* Check for errors. */
> + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
> + BD_ENET_RX_CR | BD_ENET_RX_OV)) {
> + ndev->stats.rx_errors++;
> + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
> + /* Frame too long or too short. */
> + ndev->stats.rx_length_errors++;
> + }
> + if (status & BD_ENET_RX_NO) /* Frame alignment */
> + ndev->stats.rx_frame_errors++;
> + if (status & BD_ENET_RX_CR) /* CRC Error */
> + ndev->stats.rx_crc_errors++;
> + if (status & BD_ENET_RX_OV) /* FIFO overrun */
> + ndev->stats.rx_fifo_errors++;
> + }
> +
> + /* Report late collisions as a frame error.
> + * On this error, the BD is closed, but we don't know what we
> + * have in the buffer. So, just drop this frame on the floor.
> + */
> + if (status & BD_ENET_RX_CL) {
> + ndev->stats.rx_errors++;
> + ndev->stats.rx_frame_errors++;
> + goto rx_processing_done;
> + }
> +
> + /* Process the incoming frame. */
> + ndev->stats.rx_packets++;
> + pkt_len = bdp->cbd_datlen;
> + ndev->stats.rx_bytes += pkt_len;
> + data = (__u8*)__va(bdp->cbd_bufaddr);
> +
> + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> + FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
> +
> + if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
> + swap_buffer(data, pkt_len);
> +
> + /* This does 16 byte alignment, exactly what we need.
> + * The packet length includes FCS, but we don't want to
> + * include that when passing upstream as it messes up
> + * bridging applications.
> + */
> + skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
> +
> + if (unlikely(!skb)) {
> + ndev->stats.rx_dropped++;
> + } else {
> + skb_reserve(skb, NET_IP_ALIGN);
> + skb_put(skb, pkt_len - 4); /* Make room */
> + skb_copy_to_linear_data(skb, data, pkt_len - 4);
> + skb->protocol = eth_type_trans(skb, ndev);
> +
> + /* Get receive timestamp from the skb */
> + if (fep->hwts_rx_en && fep->bufdesc_ex) {
> + struct skb_shared_hwtstamps *shhwtstamps =
> + skb_hwtstamps(skb);
> + unsigned long flags;
> + struct bufdesc_ex *ebdp =
> + (struct bufdesc_ex *)bdp;
> +
> + memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> +
> + spin_lock_irqsave(&fep->tmreg_lock, flags);
> + shhwtstamps->hwtstamp = ns_to_ktime(
> + timecounter_cyc2time(&fep->tc, ebdp->ts));
> + spin_unlock_irqrestore(&fep->tmreg_lock, flags);
> + }
> +
> + if (!skb_defer_rx_timestamp(skb))
> + napi_gro_receive(&fep->napi, skb);
> + }
> +
> + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
> + FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
> +rx_processing_done:
> + /* Clear the status flags for this buffer */
> + status &= ~BD_ENET_RX_STATS;
> +
> + /* Mark the buffer empty */
> + status |= BD_ENET_RX_EMPTY;
> + bdp->cbd_sc = status;
> +
> + if (fep->bufdesc_ex) {
> + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> +
> + ebdp->cbd_esc = BD_ENET_RX_INT;
> + ebdp->cbd_prot = 0;
> + ebdp->cbd_bdu = 0;
> + }
> +
> + /* Update BD pointer to next entry */
> + if (status & BD_ENET_RX_WRAP)
> + bdp = fep->rx_bd_base;
> + else
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + /* Doing this here will keep the FEC running while we process
> + * incoming frames. On a heavily loaded network, we should be
> + * able to keep up at the expense of system resources.
> + */
> + writel(0, fep->hwp + FEC_R_DES_ACTIVE);
> + }
> + fep->cur_rx = bdp;
> +
> + return pkt_received;
> +}
> +
> +static irqreturn_t
> +fec_enet_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = dev_id;
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + uint int_events;
> + irqreturn_t ret = IRQ_NONE;
> +
> + do {
> + int_events = readl(fep->hwp + FEC_IEVENT);
> + writel(int_events, fep->hwp + FEC_IEVENT);
> +
> + if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
> + ret = IRQ_HANDLED;
> +
> + /* Disable the RX interrupt */
> + if (napi_schedule_prep(&fep->napi)) {
> + writel(FEC_RX_DISABLED_IMASK,
> + fep->hwp + FEC_IMASK);
> + __napi_schedule(&fep->napi);
> + }
> + }
> +
> + if (int_events & FEC_ENET_MII) {
> + ret = IRQ_HANDLED;
> + complete(&fep->mdio_done);
> + }
> + } while (int_events);
> +
> + return ret;
> +}
> +
> +static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
> +{
> + struct net_device *ndev = napi->dev;
> + int pkts = fec_enet_rx(ndev, budget);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + fec_enet_tx(ndev);
> +
> + if (pkts < budget) {
> + napi_complete(napi);
> + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> + }
> + return pkts;
> +}
> +
> +/* ------------------------------------------------------------------------- */
> +static void fec_get_mac(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
> + unsigned char *iap, tmpaddr[ETH_ALEN];
> +
> + /*
> + * try to get mac address in following order:
> + *
> + * 1) module parameter via kernel command line in form
> + * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
> + */
> + iap = macaddr;
> +
> +#ifdef CONFIG_OF
> + /*
> + * 2) from device tree data
> + */
> + if (!is_valid_ether_addr(iap)) {
> + struct device_node *np = fep->pdev->dev.of_node;
> + if (np) {
> + const char *mac = of_get_mac_address(np);
> + if (mac)
> + iap = (unsigned char *) mac;
> + }
> + }
> +#endif
> +
> + /*
> + * 3) from flash or fuse (via platform data)
> + */
> + if (!is_valid_ether_addr(iap)) {
> +#ifdef CONFIG_M5272
> + if (FEC_FLASHMAC)
> + iap = (unsigned char *)FEC_FLASHMAC;
> +#else
> + if (pdata)
> + iap = (unsigned char *)&pdata->mac;
> +#endif
> + }
> +
> + /*
> + * 4) FEC mac registers set by bootloader
> + */
> + if (!is_valid_ether_addr(iap)) {
> + *((unsigned long *) &tmpaddr[0]) =
> + be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
> + *((unsigned short *) &tmpaddr[4]) =
> + be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
> + iap = &tmpaddr[0];
> + }
> +
> + memcpy(ndev->dev_addr, iap, ETH_ALEN);
> +
> + /* Adjust MAC if using macaddr */
> + if (iap == macaddr)
> + ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
> +}
> +
> +/* ------------------------------------------------------------------------- */
> +
> +/*
> + * Phy section
> + */
> +static void fec_enet_adjust_link(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct phy_device *phy_dev = fep->phy_dev;
> + unsigned long flags;
> +
> + int status_change = 0;
> +
> + spin_lock_irqsave(&fep->hw_lock, flags);
> +
> + /* Prevent a state halted on mii error */
> + if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
> + phy_dev->state = PHY_RESUMING;
> + goto spin_unlock;
> + }
> +
> + /* Duplex link change */
> + if (phy_dev->link) {
> + if (fep->full_duplex != phy_dev->duplex) {
> + fec_restart(ndev, phy_dev->duplex);
> + /* prevent unnecessary second fec_restart() below */
> + fep->link = phy_dev->link;
> + status_change = 1;
> + }
> + }
> +
> + /* Link on or off change */
> + if (phy_dev->link != fep->link) {
> + fep->link = phy_dev->link;
> + if (phy_dev->link)
> + fec_restart(ndev, phy_dev->duplex);
> + else
> + fec_stop(ndev);
> + status_change = 1;
> + }
> +
> +spin_unlock:
> + spin_unlock_irqrestore(&fep->hw_lock, flags);
> +
> + if (status_change)
> + phy_print_status(phy_dev);
> +}
> +
> +static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
> +{
> + struct fec_enet_private *fep = bus->priv;
> + unsigned long time_left;
> +
> + fep->mii_timeout = 0;
> + init_completion(&fep->mdio_done);
> +
> + /* start a read op */
> + writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
> + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> + FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
> +
> + /* wait for end of transfer */
> + time_left = wait_for_completion_timeout(&fep->mdio_done,
> + usecs_to_jiffies(FEC_MII_TIMEOUT));
> + if (time_left == 0) {
> + fep->mii_timeout = 1;
> + printk(KERN_ERR "FEC: MDIO read timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + /* return value */
> + return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
> +}
> +
> +static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
> + u16 value)
> +{
> + struct fec_enet_private *fep = bus->priv;
> + unsigned long time_left;
> +
> + fep->mii_timeout = 0;
> + init_completion(&fep->mdio_done);
> +
> + /* start a write op */
> + writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
> + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> + FEC_MMFR_TA | FEC_MMFR_DATA(value),
> + fep->hwp + FEC_MII_DATA);
> +
> + /* wait for end of transfer */
> + time_left = wait_for_completion_timeout(&fep->mdio_done,
> + usecs_to_jiffies(FEC_MII_TIMEOUT));
> + if (time_left == 0) {
> + fep->mii_timeout = 1;
> + printk(KERN_ERR "FEC: MDIO write timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +static int fec_enet_mdio_reset(struct mii_bus *bus)
> +{
> + return 0;
> +}
> +
> +static int fec_enet_mii_probe(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + struct phy_device *phy_dev = NULL;
> + char mdio_bus_id[MII_BUS_ID_SIZE];
> + char phy_name[MII_BUS_ID_SIZE + 3];
> + int phy_id;
> + int dev_id = fep->dev_id;
> +
> + fep->phy_dev = NULL;
> +
> + /* check for attached phy */
> + for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
> + if ((fep->mii_bus->phy_mask & (1 << phy_id)))
> + continue;
> + if (fep->mii_bus->phy_map[phy_id] == NULL)
> + continue;
> + if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
> + continue;
> + if (dev_id--)
> + continue;
> + strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
> + break;
> + }
> +
> + if (phy_id >= PHY_MAX_ADDR) {
> + printk(KERN_INFO
> + "%s: no PHY, assuming direct connection to switch\n",
> + ndev->name);
> + strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
> + phy_id = 0;
> + }
> +
> + snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
> + phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
> + fep->phy_interface);
> + if (IS_ERR(phy_dev)) {
> + printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
> + return PTR_ERR(phy_dev);
> + }
> +
> + /* mask with MAC supported features */
> + if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
> + phy_dev->supported &= PHY_GBIT_FEATURES;
> + phy_dev->supported |= SUPPORTED_Pause;
> + }
> + else
> + phy_dev->supported &= PHY_BASIC_FEATURES;
> +
> + phy_dev->advertising = phy_dev->supported;
> +
> + fep->phy_dev = phy_dev;
> + fep->link = 0;
> + fep->full_duplex = 0;
> +
> + printk(KERN_INFO
> + "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
> + ndev->name,
> + fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
> + fep->phy_dev->irq);
> +
> + return 0;
> +}
> +
> +static int fec_enet_mii_init(struct platform_device *pdev)
> +{
> + static struct mii_bus *fec0_mii_bus;
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + const struct platform_device_id *id_entry =
> + platform_get_device_id(fep->pdev);
> + int err = -ENXIO, i;
> +
> + /*
> + * The dual fec interfaces are not equivalent with enet-mac.
> + * Here are the differences:
> + *
> + * - fec0 supports MII & RMII modes while fec1 only supports RMII
> + * - fec0 acts as the 1588 time master while fec1 is slave
> + * - external phys can only be configured by fec0
> + *
> + * That is to say fec1 can not work independently. It only works
> + * when fec0 is working. The reason behind this design is that the
> + * second interface is added primarily for Switch mode.
> + *
> + * Because of the last point above, both phys are attached on fec0
> + * mdio interface in board design, and need to be configured by
> + * fec0 mii_bus.
> + */
> + if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
> + /* fec1 uses fec0 mii_bus */
> + if (mii_cnt && fec0_mii_bus) {
> + fep->mii_bus = fec0_mii_bus;
> + mii_cnt++;
> + return 0;
> + }
> + return -ENOENT;
> + }
> +
> + fep->mii_timeout = 0;
> +
> + /*
> + * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
> + *
> + * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
> + * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
> + * Reference Manual has an error on this, and gets fixed on i.MX6Q
> + * document.
> + */
> + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
> + fep->phy_speed--;
> + fep->phy_speed <<= 1;
> + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
> +
> + fep->mii_bus = mdiobus_alloc();
> + if (fep->mii_bus == NULL) {
> + err = -ENOMEM;
> + goto err_out;
> + }
> +
> + fep->mii_bus->name = "fec_enet_mii_bus";
> + fep->mii_bus->read = fec_enet_mdio_read;
> + fep->mii_bus->write = fec_enet_mdio_write;
> + fep->mii_bus->reset = fec_enet_mdio_reset;
> + snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
> + pdev->name, fep->dev_id + 1);
> + fep->mii_bus->priv = fep;
> + fep->mii_bus->parent = &pdev->dev;
> +
> + fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
> + if (!fep->mii_bus->irq) {
> + err = -ENOMEM;
> + goto err_out_free_mdiobus;
> + }
> +
> + for (i = 0; i < PHY_MAX_ADDR; i++)
> + fep->mii_bus->irq[i] = PHY_POLL;
> +
> + if (mdiobus_register(fep->mii_bus))
> + goto err_out_free_mdio_irq;
> +
> + mii_cnt++;
> +
> + /* save fec0 mii_bus */
> + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
> + fec0_mii_bus = fep->mii_bus;
> +
> + return 0;
> +
> +err_out_free_mdio_irq:
> + kfree(fep->mii_bus->irq);
> +err_out_free_mdiobus:
> + mdiobus_free(fep->mii_bus);
> +err_out:
> + return err;
> +}
> +
> +static void fec_enet_mii_remove(struct fec_enet_private *fep)
> +{
> + if (--mii_cnt == 0) {
> + mdiobus_unregister(fep->mii_bus);
> + kfree(fep->mii_bus->irq);
> + mdiobus_free(fep->mii_bus);
> + }
> +}
> +
> +static int fec_enet_get_settings(struct net_device *ndev,
> + struct ethtool_cmd *cmd)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct phy_device *phydev = fep->phy_dev;
> +
> + if (!phydev)
> + return -ENODEV;
> +
> + return phy_ethtool_gset(phydev, cmd);
> +}
> +
> +static int fec_enet_set_settings(struct net_device *ndev,
> + struct ethtool_cmd *cmd)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct phy_device *phydev = fep->phy_dev;
> +
> + if (!phydev)
> + return -ENODEV;
> +
> + return phy_ethtool_sset(phydev, cmd);
> +}
> +
> +static void fec_enet_get_drvinfo(struct net_device *ndev,
> + struct ethtool_drvinfo *info)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + strlcpy(info->driver, fep->pdev->dev.driver->name,
> + sizeof(info->driver));
> + strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
> + strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
> +}
> +
> +static int fec_enet_get_ts_info(struct net_device *ndev,
> + struct ethtool_ts_info *info)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + if (fep->bufdesc_ex) {
> +
> + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
> + SOF_TIMESTAMPING_RX_SOFTWARE |
> + SOF_TIMESTAMPING_SOFTWARE |
> + SOF_TIMESTAMPING_TX_HARDWARE |
> + SOF_TIMESTAMPING_RX_HARDWARE |
> + SOF_TIMESTAMPING_RAW_HARDWARE;
> + if (fep->ptp_clock)
> + info->phc_index = ptp_clock_index(fep->ptp_clock);
> + else
> + info->phc_index = -1;
> +
> + info->tx_types = (1 << HWTSTAMP_TX_OFF) |
> + (1 << HWTSTAMP_TX_ON);
> +
> + info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
> + (1 << HWTSTAMP_FILTER_ALL);
> + return 0;
> + } else {
> + return ethtool_op_get_ts_info(ndev, info);
> + }
> +}
> +
> +static void fec_enet_get_pauseparam(struct net_device *ndev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
> + pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
> + pause->rx_pause = pause->tx_pause;
> +}
> +
> +static int fec_enet_set_pauseparam(struct net_device *ndev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + if (pause->tx_pause != pause->rx_pause) {
> + netdev_info(ndev,
> + "hardware only support enable/disable both tx and rx");
> + return -EINVAL;
> + }
> +
> + fep->pause_flag = 0;
> +
> + /* tx pause must be same as rx pause */
> + fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
> + fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
> +
> + if (pause->rx_pause || pause->autoneg) {
> + fep->phy_dev->supported |= ADVERTISED_Pause;
> + fep->phy_dev->advertising |= ADVERTISED_Pause;
> + } else {
> + fep->phy_dev->supported &= ~ADVERTISED_Pause;
> + fep->phy_dev->advertising &= ~ADVERTISED_Pause;
> + }
> +
> + if (pause->autoneg) {
> + if (netif_running(ndev))
> + fec_stop(ndev);
> + phy_start_aneg(fep->phy_dev);
> + }
> + if (netif_running(ndev))
> + fec_restart(ndev, 0);
> +
> + return 0;
> +}
> +
> +static const struct ethtool_ops fec_enet_ethtool_ops = {
> + .get_pauseparam = fec_enet_get_pauseparam,
> + .set_pauseparam = fec_enet_set_pauseparam,
> + .get_settings = fec_enet_get_settings,
> + .set_settings = fec_enet_set_settings,
> + .get_drvinfo = fec_enet_get_drvinfo,
> + .get_link = ethtool_op_get_link,
> + .get_ts_info = fec_enet_get_ts_info,
> +};
> +
> +static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct phy_device *phydev = fep->phy_dev;
> +
> + if (!netif_running(ndev))
> + return -EINVAL;
> +
> + if (!phydev)
> + return -ENODEV;
> +
> + if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
> + return fec_ptp_ioctl(ndev, rq, cmd);
> +
> + return phy_mii_ioctl(phydev, rq, cmd);
> +}
> +
> +static void fec_enet_free_buffers(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + int i;
> + struct sk_buff *skb;
> + struct bufdesc *bdp;
> +
> + bdp = fep->rx_bd_base;
> + for (i = 0; i < RX_RING_SIZE; i++) {
> + skb = fep->rx_skbuff[i];
> +
> + if (bdp->cbd_bufaddr)
> + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
> + FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
> + if (skb)
> + dev_kfree_skb(skb);
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + }
> +
> + bdp = fep->tx_bd_base;
> + for (i = 0; i < TX_RING_SIZE; i++)
> + kfree(fep->tx_bounce[i]);
> +}
> +
> +static int fec_enet_alloc_buffers(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + int i;
> + struct sk_buff *skb;
> + struct bufdesc *bdp;
> +
> + bdp = fep->rx_bd_base;
> + for (i = 0; i < RX_RING_SIZE; i++) {
> + skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
> + if (!skb) {
> + fec_enet_free_buffers(ndev);
> + return -ENOMEM;
> + }
> + fep->rx_skbuff[i] = skb;
> +
> + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
> + FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
> + bdp->cbd_sc = BD_ENET_RX_EMPTY;
> +
> + if (fep->bufdesc_ex) {
> + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> + ebdp->cbd_esc = BD_ENET_RX_INT;
> + }
> +
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + }
> +
> + /* Set the last buffer to wrap. */
> + bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> + bdp->cbd_sc |= BD_SC_WRAP;
> +
> + bdp = fep->tx_bd_base;
> + for (i = 0; i < TX_RING_SIZE; i++) {
> + fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
> +
> + bdp->cbd_sc = 0;
> + bdp->cbd_bufaddr = 0;
> +
> + if (fep->bufdesc_ex) {
> + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> + ebdp->cbd_esc = BD_ENET_RX_INT;
> + }
> +
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + }
> +
> + /* Set the last buffer to wrap. */
> + bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> + bdp->cbd_sc |= BD_SC_WRAP;
> +
> + return 0;
> +}
> +
> +static int
> +fec_enet_open(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + int ret;
> +
> + napi_enable(&fep->napi);
> +
> + /* I should reset the ring buffers here, but I don't yet know
> + * a simple way to do that.
> + */
> +
> + ret = fec_enet_alloc_buffers(ndev);
> + if (ret)
> + return ret;
> +
> + /* Probe and connect to PHY when open the interface */
> + ret = fec_enet_mii_probe(ndev);
> + if (ret) {
> + fec_enet_free_buffers(ndev);
> + return ret;
> + }
> + phy_start(fep->phy_dev);
> + netif_start_queue(ndev);
> + fep->opened = 1;
> + return 0;
> +}
> +
> +static int
> +fec_enet_close(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + /* Don't know what to do yet. */
> + fep->opened = 0;
> + netif_stop_queue(ndev);
> + fec_stop(ndev);
> +
> + if (fep->phy_dev) {
> + phy_stop(fep->phy_dev);
> + phy_disconnect(fep->phy_dev);
> + }
> +
> + fec_enet_free_buffers(ndev);
> +
> + return 0;
> +}
> +
> +/* Set or clear the multicast filter for this adaptor.
> + * Skeleton taken from sunlance driver.
> + * The CPM Ethernet implementation allows Multicast as well as individual
> + * MAC address filtering. Some of the drivers check to make sure it is
> + * a group multicast address, and discard those that are not. I guess I
> + * will do the same for now, but just remove the test if you want
> + * individual filtering as well (do the upper net layers want or support
> + * this kind of feature?).
> + */
> +
> +#define HASH_BITS 6 /* #bits in hash */
> +#define CRC32_POLY 0xEDB88320
> +
> +static void set_multicast_list(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct netdev_hw_addr *ha;
> + unsigned int i, bit, data, crc, tmp;
> + unsigned char hash;
> +
> + if (ndev->flags & IFF_PROMISC) {
> + tmp = readl(fep->hwp + FEC_R_CNTRL);
> + tmp |= 0x8;
> + writel(tmp, fep->hwp + FEC_R_CNTRL);
> + return;
> + }
> +
> + tmp = readl(fep->hwp + FEC_R_CNTRL);
> + tmp &= ~0x8;
> + writel(tmp, fep->hwp + FEC_R_CNTRL);
> +
> + if (ndev->flags & IFF_ALLMULTI) {
> + /* Catch all multicast addresses, so set the
> + * filter to all 1's
> + */
> + writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> + writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> +
> + return;
> + }
> +
> + /* Clear filter and add the addresses in hash register
> + */
> + writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> + writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> +
> + netdev_for_each_mc_addr(ha, ndev) {
> + /* calculate crc32 value of mac address */
> + crc = 0xffffffff;
> +
> + for (i = 0; i < ndev->addr_len; i++) {
> + data = ha->addr[i];
> + for (bit = 0; bit < 8; bit++, data >>= 1) {
> + crc = (crc >> 1) ^
> + (((crc ^ data) & 1) ? CRC32_POLY : 0);
> + }
> + }
> +
> + /* only upper 6 bits (HASH_BITS) are used
> + * which point to specific bit in he hash registers
> + */
> + hash = (crc >> (32 - HASH_BITS)) & 0x3f;
> +
> + if (hash > 31) {
> + tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> + tmp |= 1 << (hash - 32);
> + writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
> + } else {
> + tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> + tmp |= 1 << hash;
> + writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
> + }
> + }
> +}
> +
> +/* Set a MAC change in hardware. */
> +static int
> +fec_set_mac_address(struct net_device *ndev, void *p)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct sockaddr *addr = p;
> +
> + if (!is_valid_ether_addr(addr->sa_data))
> + return -EADDRNOTAVAIL;
> +
> + memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
> +
> + writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
> + (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
> + fep->hwp + FEC_ADDR_LOW);
> + writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
> + fep->hwp + FEC_ADDR_HIGH);
> + return 0;
> +}
> +
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +/**
> + * fec_poll_controller - FEC Poll controller function
> + * @dev: The FEC network adapter
> + *
> + * Polled functionality used by netconsole and others in non interrupt mode
> + *
> + */
> +void fec_poll_controller(struct net_device *dev)
> +{
> + int i;
> + struct fec_enet_private *fep = netdev_priv(dev);
> +
> + for (i = 0; i < FEC_IRQ_NUM; i++) {
> + if (fep->irq[i] > 0) {
> + disable_irq(fep->irq[i]);
> + fec_enet_interrupt(fep->irq[i], dev);
> + enable_irq(fep->irq[i]);
> + }
> + }
> +}
> +#endif
> +
> +static const struct net_device_ops fec_netdev_ops = {
> + .ndo_open = fec_enet_open,
> + .ndo_stop = fec_enet_close,
> + .ndo_start_xmit = fec_enet_start_xmit,
> + .ndo_set_rx_mode = set_multicast_list,
> + .ndo_change_mtu = eth_change_mtu,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_tx_timeout = fec_timeout,
> + .ndo_set_mac_address = fec_set_mac_address,
> + .ndo_do_ioctl = fec_enet_ioctl,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> + .ndo_poll_controller = fec_poll_controller,
> +#endif
> +};
> +
> + /*
> + * XXX: We need to clean up on failure exits here.
> + *
> + */
> +static int fec_enet_init(struct net_device *ndev)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + struct bufdesc *cbd_base;
> + struct bufdesc *bdp;
> + int i;
> +
> + /* Allocate memory for buffer descriptors. */
> + cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
> + GFP_KERNEL);
> + if (!cbd_base)
> + return -ENOMEM;
> +
> + spin_lock_init(&fep->hw_lock);
> +
> + fep->netdev = ndev;
> +
> + /* Get the Ethernet address */
> + fec_get_mac(ndev);
> +
> + /* Set receive and transmit descriptor base. */
> + fep->rx_bd_base = cbd_base;
> + if (fep->bufdesc_ex)
> + fep->tx_bd_base = (struct bufdesc *)
> + (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
> + else
> + fep->tx_bd_base = cbd_base + RX_RING_SIZE;
> +
> + /* The FEC Ethernet specific entries in the device structure */
> + ndev->watchdog_timeo = TX_TIMEOUT;
> + ndev->netdev_ops = &fec_netdev_ops;
> + ndev->ethtool_ops = &fec_enet_ethtool_ops;
> +
> + writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
> + netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
> +
> + /* Initialize the receive buffer descriptors. */
> + bdp = fep->rx_bd_base;
> + for (i = 0; i < RX_RING_SIZE; i++) {
> +
> + /* Initialize the BD for every fragment in the page. */
> + bdp->cbd_sc = 0;
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + }
> +
> + /* Set the last buffer to wrap */
> + bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> + bdp->cbd_sc |= BD_SC_WRAP;
> +
> + /* ...and the same for transmit */
> + bdp = fep->tx_bd_base;
> + fep->cur_tx = bdp;
> + for (i = 0; i < TX_RING_SIZE; i++) {
> +
> + /* Initialize the BD for every fragment in the page. */
> + bdp->cbd_sc = 0;
> + bdp->cbd_bufaddr = 0;
> + bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> + }
> +
> + /* Set the last buffer to wrap */
> + bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
> + bdp->cbd_sc |= BD_SC_WRAP;
> + fep->dirty_tx = bdp;
> +
> + fec_restart(ndev, 0);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static int fec_get_phy_mode_dt(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> +
> + if (np)
> + return of_get_phy_mode(np);
> +
> + return -ENODEV;
> +}
> +
> +static void fec_reset_phy(struct platform_device *pdev)
> +{
> + int err, phy_reset;
> + int msec = 1;
> + struct device_node *np = pdev->dev.of_node;
> +
> + if (!np)
> + return;
> +
> + of_property_read_u32(np, "phy-reset-duration", &msec);
> + /* A sane reset duration should not be longer than 1s */
> + if (msec > 1000)
> + msec = 1;
> +
> + phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
> + if (!gpio_is_valid(phy_reset))
> + return;
> +
> + err = devm_gpio_request_one(&pdev->dev, phy_reset,
> + GPIOF_OUT_INIT_LOW, "phy-reset");
> + if (err) {
> + dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
> + return;
> + }
> + msleep(msec);
> + gpio_set_value(phy_reset, 1);
> +}
> +#else /* CONFIG_OF */
> +static int fec_get_phy_mode_dt(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> +
> +static void fec_reset_phy(struct platform_device *pdev)
> +{
> + /*
> + * In case of platform probe, the reset has been done
> + * by machine code.
> + */
> +}
> +#endif /* CONFIG_OF */
> +
> +static int
> +fec_probe(struct platform_device *pdev)
> +{
> + struct fec_enet_private *fep;
> + struct fec_platform_data *pdata;
> + struct net_device *ndev;
> + int i, irq, ret = 0;
> + struct resource *r;
> + const struct of_device_id *of_id;
> + static int dev_id;
> + struct pinctrl *pinctrl;
> + struct regulator *reg_phy;
> +
> + of_id = of_match_device(fec_dt_ids, &pdev->dev);
> + if (of_id)
> + pdev->id_entry = of_id->data;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r)
> + return -ENXIO;
> +
> + /* Init network device */
> + ndev = alloc_etherdev(sizeof(struct fec_enet_private));
> + if (!ndev)
> + return -ENOMEM;
> +
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> + /* setup board info structure */
> + fep = netdev_priv(ndev);
> +
> + /* default enable pause frame auto negotiation */
> + if (pdev->id_entry &&
> + (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
> + fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
> +
> + fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
> + fep->pdev = pdev;
> + fep->dev_id = dev_id++;
> +
> + fep->bufdesc_ex = 0;
> +
> + if (!fep->hwp) {
> + ret = -ENOMEM;
> + goto failed_ioremap;
> + }
> +
> + platform_set_drvdata(pdev, ndev);
> +
> + ret = fec_get_phy_mode_dt(pdev);
> + if (ret < 0) {
> + pdata = pdev->dev.platform_data;
> + if (pdata)
> + fep->phy_interface = pdata->phy;
> + else
> + fep->phy_interface = PHY_INTERFACE_MODE_MII;
> + } else {
> + fep->phy_interface = ret;
> + }
> +
> + pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> + if (IS_ERR(pinctrl)) {
> + ret = PTR_ERR(pinctrl);
> + goto failed_pin;
> + }
> +
> + fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
> + if (IS_ERR(fep->clk_ipg)) {
> + ret = PTR_ERR(fep->clk_ipg);
> + goto failed_clk;
> + }
> +
> + fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
> + if (IS_ERR(fep->clk_ahb)) {
> + ret = PTR_ERR(fep->clk_ahb);
> + goto failed_clk;
> + }
> +
> + fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
> + fep->bufdesc_ex =
> + pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
> + if (IS_ERR(fep->clk_ptp)) {
> + ret = PTR_ERR(fep->clk_ptp);
> + fep->bufdesc_ex = 0;
> + }
> +
> + clk_prepare_enable(fep->clk_ahb);
> + clk_prepare_enable(fep->clk_ipg);
> + if (!IS_ERR(fep->clk_ptp))
> + clk_prepare_enable(fep->clk_ptp);
> +
> + reg_phy = devm_regulator_get(&pdev->dev, "phy");
> + if (!IS_ERR(reg_phy)) {
> + ret = regulator_enable(reg_phy);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to enable phy regulator: %d\n", ret);
> + goto failed_regulator;
> + }
> + }
> +
> + fec_reset_phy(pdev);
> +
> + if (fep->bufdesc_ex)
> + fec_ptp_init(ndev, pdev);
> +
> + ret = fec_enet_init(ndev);
> + if (ret)
> + goto failed_init;
> +
> + for (i = 0; i < FEC_IRQ_NUM; i++) {
> + irq = platform_get_irq(pdev, i);
> + if (irq < 0) {
> + if (i)
> + break;
> + ret = irq;
> + goto failed_irq;
> + }
> + ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
> + if (ret) {
> + while (--i >= 0) {
> + irq = platform_get_irq(pdev, i);
> + free_irq(irq, ndev);
> + }
> + goto failed_irq;
> + }
> + }
> +
> + ret = fec_enet_mii_init(pdev);
> + if (ret)
> + goto failed_mii_init;
> +
> + /* Carrier starts down, phylib will bring it up */
> + netif_carrier_off(ndev);
> +
> + ret = register_netdev(ndev);
> + if (ret)
> + goto failed_register;
> +
> + return 0;
> +
> +failed_register:
> + fec_enet_mii_remove(fep);
> +failed_mii_init:
> +failed_init:
> + for (i = 0; i < FEC_IRQ_NUM; i++) {
> + irq = platform_get_irq(pdev, i);
> + if (irq > 0)
> + free_irq(irq, ndev);
> + }
> +failed_irq:
> +failed_regulator:
> + clk_disable_unprepare(fep->clk_ahb);
> + clk_disable_unprepare(fep->clk_ipg);
> + if (!IS_ERR(fep->clk_ptp))
> + clk_disable_unprepare(fep->clk_ptp);
> +failed_pin:
> +failed_clk:
> +failed_ioremap:
> + free_netdev(ndev);
> +
> + return ret;
> +}
> +
> +static int
> +fec_drv_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + int i;
> +
> + unregister_netdev(ndev);
> + fec_enet_mii_remove(fep);
> + del_timer_sync(&fep->time_keep);
> + clk_disable_unprepare(fep->clk_ptp);
> + if (fep->ptp_clock)
> + ptp_clock_unregister(fep->ptp_clock);
> + clk_disable_unprepare(fep->clk_ahb);
> + clk_disable_unprepare(fep->clk_ipg);
> + for (i = 0; i < FEC_IRQ_NUM; i++) {
> + int irq = platform_get_irq(pdev, i);
> + if (irq > 0)
> + free_irq(irq, ndev);
> + }
> + free_netdev(ndev);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int
> +fec_suspend(struct device *dev)
> +{
> + struct net_device *ndev = dev_get_drvdata(dev);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + fec_stop(ndev);
> + netif_device_detach(ndev);
> + }
> + clk_disable_unprepare(fep->clk_ahb);
> + clk_disable_unprepare(fep->clk_ipg);
> +
> + return 0;
> +}
> +
> +static int
> +fec_resume(struct device *dev)
> +{
> + struct net_device *ndev = dev_get_drvdata(dev);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> +
> + clk_prepare_enable(fep->clk_ahb);
> + clk_prepare_enable(fep->clk_ipg);
> + if (netif_running(ndev)) {
> + fec_restart(ndev, fep->full_duplex);
> + netif_device_attach(ndev);
> + }
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops fec_pm_ops = {
> + .suspend = fec_suspend,
> + .resume = fec_resume,
> + .freeze = fec_suspend,
> + .thaw = fec_resume,
> + .poweroff = fec_suspend,
> + .restore = fec_resume,
> +};
> +#endif
> +
> +static struct platform_driver fec_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> +#ifdef CONFIG_PM
> + .pm = &fec_pm_ops,
> +#endif
> + .of_match_table = fec_dt_ids,
> + },
> + .id_table = fec_devtype,
> + .probe = fec_probe,
> + .remove = fec_drv_remove,
> +};
> +
> +module_platform_driver(fec_driver);
> +
> +MODULE_LICENSE("GPL");
> --
> 1.7.1
>
>
>
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH 1/1 net-next] net: fec: fix the build as module
From: Frank Li @ 2013-03-21 9:56 UTC (permalink / raw)
To: festevam, lznuaa, u.kleine-koenig, netdev, davem,
linux-arm-kernel; +Cc: Frank Li
Since commit ff43da86c69 (NET: FEC: dynamtic check DMA desc buff type) the
following build error happens when CONFIG_FEC=m
ERROR: "fec_ptp_init" [drivers/net/ethernet/freescale/fec.ko] undefined!
ERROR: "fec_ptp_ioctl" [drivers/net/ethernet/freescale/fec.ko] undefined!
ERROR: "fec_ptp_start_cyclecounter" [drivers/net/ethernet/freescale/fec.ko] undefined!
rename fec.c to fec_main.c
Build fec.o and fec_ptp.o into one fec.ko
Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
drivers/net/ethernet/freescale/Makefile | 3 +-
drivers/net/ethernet/freescale/fec.c | 1961 -----------------------------
drivers/net/ethernet/freescale/fec_main.c | 1961 +++++++++++++++++++++++++++++
3 files changed, 1963 insertions(+), 1962 deletions(-)
delete mode 100644 drivers/net/ethernet/freescale/fec.c
create mode 100644 drivers/net/ethernet/freescale/fec_main.c
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index b7d58fe..549ce13 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -2,7 +2,8 @@
# Makefile for the Freescale network device drivers.
#
-obj-$(CONFIG_FEC) += fec.o fec_ptp.o
+obj-$(CONFIG_FEC) += fec.o
+fec-objs :=fec_main.o fec_ptp.o
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
deleted file mode 100644
index 69a4ade..0000000
--- a/drivers/net/ethernet/freescale/fec.c
+++ /dev/null
@@ -1,1961 +0,0 @@
-/*
- * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
- * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
- *
- * Right now, I am very wasteful with the buffers. I allocate memory
- * pages and then divide them into 2K frame buffers. This way I know I
- * have buffers large enough to hold one frame within one buffer descriptor.
- * Once I get this working, I will use 64 or 128 byte CPM buffers, which
- * will be much more memory efficient and will easily handle lots of
- * small packets.
- *
- * Much better multiple PHY support by Magnus Damm.
- * Copyright (c) 2000 Ericsson Radio Systems AB.
- *
- * Support for FEC controller of ColdFire processors.
- * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
- *
- * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
- * Copyright (c) 2004-2006 Macq Electronique SA.
- *
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/phy.h>
-#include <linux/fec.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/of_net.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regulator/consumer.h>
-
-#include <asm/cacheflush.h>
-
-#ifndef CONFIG_ARM
-#include <asm/coldfire.h>
-#include <asm/mcfsim.h>
-#endif
-
-#include "fec.h"
-
-#if defined(CONFIG_ARM)
-#define FEC_ALIGNMENT 0xf
-#else
-#define FEC_ALIGNMENT 0x3
-#endif
-
-#define DRIVER_NAME "fec"
-#define FEC_NAPI_WEIGHT 64
-
-/* Pause frame feild and FIFO threshold */
-#define FEC_ENET_FCE (1 << 5)
-#define FEC_ENET_RSEM_V 0x84
-#define FEC_ENET_RSFL_V 16
-#define FEC_ENET_RAEM_V 0x8
-#define FEC_ENET_RAFL_V 0x8
-#define FEC_ENET_OPD_V 0xFFF0
-
-/* Controller is ENET-MAC */
-#define FEC_QUIRK_ENET_MAC (1 << 0)
-/* Controller needs driver to swap frame */
-#define FEC_QUIRK_SWAP_FRAME (1 << 1)
-/* Controller uses gasket */
-#define FEC_QUIRK_USE_GASKET (1 << 2)
-/* Controller has GBIT support */
-#define FEC_QUIRK_HAS_GBIT (1 << 3)
-/* Controller has extend desc buffer */
-#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4)
-
-static struct platform_device_id fec_devtype[] = {
- {
- /* keep it for coldfire */
- .name = DRIVER_NAME,
- .driver_data = 0,
- }, {
- .name = "imx25-fec",
- .driver_data = FEC_QUIRK_USE_GASKET,
- }, {
- .name = "imx27-fec",
- .driver_data = 0,
- }, {
- .name = "imx28-fec",
- .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
- }, {
- .name = "imx6q-fec",
- .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
- FEC_QUIRK_HAS_BUFDESC_EX,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, fec_devtype);
-
-enum imx_fec_type {
- IMX25_FEC = 1, /* runs on i.mx25/50/53 */
- IMX27_FEC, /* runs on i.mx27/35/51 */
- IMX28_FEC,
- IMX6Q_FEC,
-};
-
-static const struct of_device_id fec_dt_ids[] = {
- { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
- { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
- { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
- { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, fec_dt_ids);
-
-static unsigned char macaddr[ETH_ALEN];
-module_param_array(macaddr, byte, NULL, 0);
-MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
-
-#if defined(CONFIG_M5272)
-/*
- * Some hardware gets it MAC address out of local flash memory.
- * if this is non-zero then assume it is the address to get MAC from.
- */
-#if defined(CONFIG_NETtel)
-#define FEC_FLASHMAC 0xf0006006
-#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
-#define FEC_FLASHMAC 0xf0006000
-#elif defined(CONFIG_CANCam)
-#define FEC_FLASHMAC 0xf0020000
-#elif defined (CONFIG_M5272C3)
-#define FEC_FLASHMAC (0xffe04000 + 4)
-#elif defined(CONFIG_MOD5272)
-#define FEC_FLASHMAC 0xffc0406b
-#else
-#define FEC_FLASHMAC 0
-#endif
-#endif /* CONFIG_M5272 */
-
-#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
-#error "FEC: descriptor ring size constants too large"
-#endif
-
-/* Interrupt events/masks. */
-#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
-#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
-#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
-#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
-#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
-#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
-#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
-#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
-#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
-#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
-
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
-#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
-
-/* The FEC stores dest/src/type, data, and checksum for receive packets.
- */
-#define PKT_MAXBUF_SIZE 1518
-#define PKT_MINBUF_SIZE 64
-#define PKT_MAXBLR_SIZE 1520
-
-/*
- * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
- * size bits. Other FEC hardware does not, so we need to take that into
- * account when setting it.
- */
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
-#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
-#else
-#define OPT_FRAME_SIZE 0
-#endif
-
-/* FEC MII MMFR bits definition */
-#define FEC_MMFR_ST (1 << 30)
-#define FEC_MMFR_OP_READ (2 << 28)
-#define FEC_MMFR_OP_WRITE (1 << 28)
-#define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
-#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
-#define FEC_MMFR_TA (2 << 16)
-#define FEC_MMFR_DATA(v) (v & 0xffff)
-
-#define FEC_MII_TIMEOUT 30000 /* us */
-
-/* Transmitter timeout */
-#define TX_TIMEOUT (2 * HZ)
-
-#define FEC_PAUSE_FLAG_AUTONEG 0x1
-#define FEC_PAUSE_FLAG_ENABLE 0x2
-
-static int mii_cnt;
-
-static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
-{
- struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
- if (is_ex)
- return (struct bufdesc *)(ex + 1);
- else
- return bdp + 1;
-}
-
-static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
-{
- struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
- if (is_ex)
- return (struct bufdesc *)(ex - 1);
- else
- return bdp - 1;
-}
-
-static void *swap_buffer(void *bufaddr, int len)
-{
- int i;
- unsigned int *buf = bufaddr;
-
- for (i = 0; i < (len + 3) / 4; i++, buf++)
- *buf = cpu_to_be32(*buf);
-
- return bufaddr;
-}
-
-static netdev_tx_t
-fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- struct bufdesc *bdp;
- void *bufaddr;
- unsigned short status;
- unsigned int index;
-
- if (!fep->link) {
- /* Link is down or autonegotiation is in progress. */
- return NETDEV_TX_BUSY;
- }
-
- /* Fill in a Tx ring entry */
- bdp = fep->cur_tx;
-
- status = bdp->cbd_sc;
-
- if (status & BD_ENET_TX_READY) {
- /* Ooops. All transmit buffers are full. Bail out.
- * This should not happen, since ndev->tbusy should be set.
- */
- printk("%s: tx queue full!.\n", ndev->name);
- return NETDEV_TX_BUSY;
- }
-
- /* Clear all of the status flags */
- status &= ~BD_ENET_TX_STATS;
-
- /* Set buffer length and buffer pointer */
- bufaddr = skb->data;
- bdp->cbd_datlen = skb->len;
-
- /*
- * On some FEC implementations data must be aligned on
- * 4-byte boundaries. Use bounce buffers to copy data
- * and get it aligned. Ugh.
- */
- if (fep->bufdesc_ex)
- index = (struct bufdesc_ex *)bdp -
- (struct bufdesc_ex *)fep->tx_bd_base;
- else
- index = bdp - fep->tx_bd_base;
-
- if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
- memcpy(fep->tx_bounce[index], skb->data, skb->len);
- bufaddr = fep->tx_bounce[index];
- }
-
- /*
- * Some design made an incorrect assumption on endian mode of
- * the system that it's running on. As the result, driver has to
- * swap every frame going to and coming from the controller.
- */
- if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
- swap_buffer(bufaddr, skb->len);
-
- /* Save skb pointer */
- fep->tx_skbuff[index] = skb;
-
- /* Push the data cache so the CPM does not get stale memory
- * data.
- */
- bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
- FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
-
- /* Send it on its way. Tell FEC it's ready, interrupt when done,
- * it's the last BD of the frame, and to put the CRC on the end.
- */
- status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
- | BD_ENET_TX_LAST | BD_ENET_TX_TC);
- bdp->cbd_sc = status;
-
- if (fep->bufdesc_ex) {
-
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
- ebdp->cbd_bdu = 0;
- if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
- fep->hwts_tx_en)) {
- ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
- skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
- } else {
-
- ebdp->cbd_esc = BD_ENET_TX_INT;
- }
- }
- /* If this was the last BD in the ring, start at the beginning again. */
- if (status & BD_ENET_TX_WRAP)
- bdp = fep->tx_bd_base;
- else
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
- fep->cur_tx = bdp;
-
- if (fep->cur_tx == fep->dirty_tx)
- netif_stop_queue(ndev);
-
- /* Trigger transmission start */
- writel(0, fep->hwp + FEC_X_DES_ACTIVE);
-
- skb_tx_timestamp(skb);
-
- return NETDEV_TX_OK;
-}
-
-/* This function is called to start or restart the FEC during a link
- * change. This only happens when switching between half and full
- * duplex.
- */
-static void
-fec_restart(struct net_device *ndev, int duplex)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- int i;
- u32 temp_mac[2];
- u32 rcntl = OPT_FRAME_SIZE | 0x04;
- u32 ecntl = 0x2; /* ETHEREN */
-
- /* Whack a reset. We should wait for this. */
- writel(1, fep->hwp + FEC_ECNTRL);
- udelay(10);
-
- /*
- * enet-mac reset will reset mac address registers too,
- * so need to reconfigure it.
- */
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
- memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
- writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
- writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
- }
-
- /* Clear any outstanding interrupt. */
- writel(0xffc00000, fep->hwp + FEC_IEVENT);
-
- /* Reset all multicast. */
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-#ifndef CONFIG_M5272
- writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
- writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
-#endif
-
- /* Set maximum receive buffer size. */
- writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
-
- /* Set receive and transmit descriptor base. */
- writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
- if (fep->bufdesc_ex)
- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
- * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
- else
- writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
- * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
-
- fep->cur_rx = fep->rx_bd_base;
-
- for (i = 0; i <= TX_RING_MOD_MASK; i++) {
- if (fep->tx_skbuff[i]) {
- dev_kfree_skb_any(fep->tx_skbuff[i]);
- fep->tx_skbuff[i] = NULL;
- }
- }
-
- /* Enable MII mode */
- if (duplex) {
- /* FD enable */
- writel(0x04, fep->hwp + FEC_X_CNTRL);
- } else {
- /* No Rcv on Xmit */
- rcntl |= 0x02;
- writel(0x0, fep->hwp + FEC_X_CNTRL);
- }
-
- fep->full_duplex = duplex;
-
- /* Set MII speed */
- writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
-
- /*
- * The phy interface and speed need to get configured
- * differently on enet-mac.
- */
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
- /* Enable flow control and length check */
- rcntl |= 0x40000000 | 0x00000020;
-
- /* RGMII, RMII or MII */
- if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
- rcntl |= (1 << 6);
- else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
- rcntl |= (1 << 8);
- else
- rcntl &= ~(1 << 8);
-
- /* 1G, 100M or 10M */
- if (fep->phy_dev) {
- if (fep->phy_dev->speed == SPEED_1000)
- ecntl |= (1 << 5);
- else if (fep->phy_dev->speed == SPEED_100)
- rcntl &= ~(1 << 9);
- else
- rcntl |= (1 << 9);
- }
- } else {
-#ifdef FEC_MIIGSK_ENR
- if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
- u32 cfgr;
- /* disable the gasket and wait */
- writel(0, fep->hwp + FEC_MIIGSK_ENR);
- while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
- udelay(1);
-
- /*
- * configure the gasket:
- * RMII, 50 MHz, no loopback, no echo
- * MII, 25 MHz, no loopback, no echo
- */
- cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
- ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
- if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
- cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
- writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
-
- /* re-enable the gasket */
- writel(2, fep->hwp + FEC_MIIGSK_ENR);
- }
-#endif
- }
-
- /* enable pause frame*/
- if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
- ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
- fep->phy_dev && fep->phy_dev->pause)) {
- rcntl |= FEC_ENET_FCE;
-
- /* set FIFO thresh hold parameter to reduce overrun */
- writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
- writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
- writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
- writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
-
- /* OPD */
- writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
- } else {
- rcntl &= ~FEC_ENET_FCE;
- }
-
- writel(rcntl, fep->hwp + FEC_R_CNTRL);
-
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
- /* enable ENET endian swap */
- ecntl |= (1 << 8);
- /* enable ENET store and forward mode */
- writel(1 << 8, fep->hwp + FEC_X_WMRK);
- }
-
- if (fep->bufdesc_ex)
- ecntl |= (1 << 4);
-
- /* And last, enable the transmit and receive processing */
- writel(ecntl, fep->hwp + FEC_ECNTRL);
- writel(0, fep->hwp + FEC_R_DES_ACTIVE);
-
- if (fep->bufdesc_ex)
- fec_ptp_start_cyclecounter(ndev);
-
- /* Enable interrupts we wish to service */
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-}
-
-static void
-fec_stop(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
-
- /* We cannot expect a graceful transmit stop without link !!! */
- if (fep->link) {
- writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
- udelay(10);
- if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
- printk("fec_stop : Graceful transmit stop did not complete !\n");
- }
-
- /* Whack a reset. We should wait for this. */
- writel(1, fep->hwp + FEC_ECNTRL);
- udelay(10);
- writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-
- /* We have to keep ENET enabled to have MII interrupt stay working */
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
- writel(2, fep->hwp + FEC_ECNTRL);
- writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
- }
-}
-
-
-static void
-fec_timeout(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- ndev->stats.tx_errors++;
-
- fec_restart(ndev, fep->full_duplex);
- netif_wake_queue(ndev);
-}
-
-static void
-fec_enet_tx(struct net_device *ndev)
-{
- struct fec_enet_private *fep;
- struct bufdesc *bdp;
- unsigned short status;
- struct sk_buff *skb;
- int index = 0;
-
- fep = netdev_priv(ndev);
- bdp = fep->dirty_tx;
-
- /* get next bdp of dirty_tx */
- if (bdp->cbd_sc & BD_ENET_TX_WRAP)
- bdp = fep->tx_bd_base;
- else
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
- while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
-
- /* current queue is empty */
- if (bdp == fep->cur_tx)
- break;
-
- if (fep->bufdesc_ex)
- index = (struct bufdesc_ex *)bdp -
- (struct bufdesc_ex *)fep->tx_bd_base;
- else
- index = bdp - fep->tx_bd_base;
-
- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
- FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
- bdp->cbd_bufaddr = 0;
-
- skb = fep->tx_skbuff[index];
-
- /* Check for errors. */
- if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
- BD_ENET_TX_RL | BD_ENET_TX_UN |
- BD_ENET_TX_CSL)) {
- ndev->stats.tx_errors++;
- if (status & BD_ENET_TX_HB) /* No heartbeat */
- ndev->stats.tx_heartbeat_errors++;
- if (status & BD_ENET_TX_LC) /* Late collision */
- ndev->stats.tx_window_errors++;
- if (status & BD_ENET_TX_RL) /* Retrans limit */
- ndev->stats.tx_aborted_errors++;
- if (status & BD_ENET_TX_UN) /* Underrun */
- ndev->stats.tx_fifo_errors++;
- if (status & BD_ENET_TX_CSL) /* Carrier lost */
- ndev->stats.tx_carrier_errors++;
- } else {
- ndev->stats.tx_packets++;
- }
-
- if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
- fep->bufdesc_ex) {
- struct skb_shared_hwtstamps shhwtstamps;
- unsigned long flags;
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- spin_lock_irqsave(&fep->tmreg_lock, flags);
- shhwtstamps.hwtstamp = ns_to_ktime(
- timecounter_cyc2time(&fep->tc, ebdp->ts));
- spin_unlock_irqrestore(&fep->tmreg_lock, flags);
- skb_tstamp_tx(skb, &shhwtstamps);
- }
-
- if (status & BD_ENET_TX_READY)
- printk("HEY! Enet xmit interrupt and TX_READY.\n");
-
- /* Deferred means some collisions occurred during transmit,
- * but we eventually sent the packet OK.
- */
- if (status & BD_ENET_TX_DEF)
- ndev->stats.collisions++;
-
- /* Free the sk buffer associated with this last transmit */
- dev_kfree_skb_any(skb);
- fep->tx_skbuff[index] = NULL;
-
- fep->dirty_tx = bdp;
-
- /* Update pointer to next buffer descriptor to be transmitted */
- if (status & BD_ENET_TX_WRAP)
- bdp = fep->tx_bd_base;
- else
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
-
- /* Since we have freed up a buffer, the ring is no longer full
- */
- if (fep->dirty_tx != fep->cur_tx) {
- if (netif_queue_stopped(ndev))
- netif_wake_queue(ndev);
- }
- }
- return;
-}
-
-
-/* During a receive, the cur_rx points to the current incoming buffer.
- * When we update through the ring, if the next incoming buffer has
- * not been given to the system, we just set the empty indicator,
- * effectively tossing the packet.
- */
-static int
-fec_enet_rx(struct net_device *ndev, int budget)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- struct bufdesc *bdp;
- unsigned short status;
- struct sk_buff *skb;
- ushort pkt_len;
- __u8 *data;
- int pkt_received = 0;
-
-#ifdef CONFIG_M532x
- flush_cache_all();
-#endif
-
- /* First, grab all of the stats for the incoming packet.
- * These get messed up if we get called due to a busy condition.
- */
- bdp = fep->cur_rx;
-
- while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
-
- if (pkt_received >= budget)
- break;
- pkt_received++;
-
- /* Since we have allocated space to hold a complete frame,
- * the last indicator should be set.
- */
- if ((status & BD_ENET_RX_LAST) == 0)
- printk("FEC ENET: rcv is not +last\n");
-
- if (!fep->opened)
- goto rx_processing_done;
-
- /* Check for errors. */
- if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
- BD_ENET_RX_CR | BD_ENET_RX_OV)) {
- ndev->stats.rx_errors++;
- if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
- /* Frame too long or too short. */
- ndev->stats.rx_length_errors++;
- }
- if (status & BD_ENET_RX_NO) /* Frame alignment */
- ndev->stats.rx_frame_errors++;
- if (status & BD_ENET_RX_CR) /* CRC Error */
- ndev->stats.rx_crc_errors++;
- if (status & BD_ENET_RX_OV) /* FIFO overrun */
- ndev->stats.rx_fifo_errors++;
- }
-
- /* Report late collisions as a frame error.
- * On this error, the BD is closed, but we don't know what we
- * have in the buffer. So, just drop this frame on the floor.
- */
- if (status & BD_ENET_RX_CL) {
- ndev->stats.rx_errors++;
- ndev->stats.rx_frame_errors++;
- goto rx_processing_done;
- }
-
- /* Process the incoming frame. */
- ndev->stats.rx_packets++;
- pkt_len = bdp->cbd_datlen;
- ndev->stats.rx_bytes += pkt_len;
- data = (__u8*)__va(bdp->cbd_bufaddr);
-
- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
- FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
-
- if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
- swap_buffer(data, pkt_len);
-
- /* This does 16 byte alignment, exactly what we need.
- * The packet length includes FCS, but we don't want to
- * include that when passing upstream as it messes up
- * bridging applications.
- */
- skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
-
- if (unlikely(!skb)) {
- ndev->stats.rx_dropped++;
- } else {
- skb_reserve(skb, NET_IP_ALIGN);
- skb_put(skb, pkt_len - 4); /* Make room */
- skb_copy_to_linear_data(skb, data, pkt_len - 4);
- skb->protocol = eth_type_trans(skb, ndev);
-
- /* Get receive timestamp from the skb */
- if (fep->hwts_rx_en && fep->bufdesc_ex) {
- struct skb_shared_hwtstamps *shhwtstamps =
- skb_hwtstamps(skb);
- unsigned long flags;
- struct bufdesc_ex *ebdp =
- (struct bufdesc_ex *)bdp;
-
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
-
- spin_lock_irqsave(&fep->tmreg_lock, flags);
- shhwtstamps->hwtstamp = ns_to_ktime(
- timecounter_cyc2time(&fep->tc, ebdp->ts));
- spin_unlock_irqrestore(&fep->tmreg_lock, flags);
- }
-
- if (!skb_defer_rx_timestamp(skb))
- napi_gro_receive(&fep->napi, skb);
- }
-
- bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
- FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
-rx_processing_done:
- /* Clear the status flags for this buffer */
- status &= ~BD_ENET_RX_STATS;
-
- /* Mark the buffer empty */
- status |= BD_ENET_RX_EMPTY;
- bdp->cbd_sc = status;
-
- if (fep->bufdesc_ex) {
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-
- ebdp->cbd_esc = BD_ENET_RX_INT;
- ebdp->cbd_prot = 0;
- ebdp->cbd_bdu = 0;
- }
-
- /* Update BD pointer to next entry */
- if (status & BD_ENET_RX_WRAP)
- bdp = fep->rx_bd_base;
- else
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- /* Doing this here will keep the FEC running while we process
- * incoming frames. On a heavily loaded network, we should be
- * able to keep up at the expense of system resources.
- */
- writel(0, fep->hwp + FEC_R_DES_ACTIVE);
- }
- fep->cur_rx = bdp;
-
- return pkt_received;
-}
-
-static irqreturn_t
-fec_enet_interrupt(int irq, void *dev_id)
-{
- struct net_device *ndev = dev_id;
- struct fec_enet_private *fep = netdev_priv(ndev);
- uint int_events;
- irqreturn_t ret = IRQ_NONE;
-
- do {
- int_events = readl(fep->hwp + FEC_IEVENT);
- writel(int_events, fep->hwp + FEC_IEVENT);
-
- if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
- ret = IRQ_HANDLED;
-
- /* Disable the RX interrupt */
- if (napi_schedule_prep(&fep->napi)) {
- writel(FEC_RX_DISABLED_IMASK,
- fep->hwp + FEC_IMASK);
- __napi_schedule(&fep->napi);
- }
- }
-
- if (int_events & FEC_ENET_MII) {
- ret = IRQ_HANDLED;
- complete(&fep->mdio_done);
- }
- } while (int_events);
-
- return ret;
-}
-
-static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
-{
- struct net_device *ndev = napi->dev;
- int pkts = fec_enet_rx(ndev, budget);
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- fec_enet_tx(ndev);
-
- if (pkts < budget) {
- napi_complete(napi);
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
- }
- return pkts;
-}
-
-/* ------------------------------------------------------------------------- */
-static void fec_get_mac(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
- unsigned char *iap, tmpaddr[ETH_ALEN];
-
- /*
- * try to get mac address in following order:
- *
- * 1) module parameter via kernel command line in form
- * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
- */
- iap = macaddr;
-
-#ifdef CONFIG_OF
- /*
- * 2) from device tree data
- */
- if (!is_valid_ether_addr(iap)) {
- struct device_node *np = fep->pdev->dev.of_node;
- if (np) {
- const char *mac = of_get_mac_address(np);
- if (mac)
- iap = (unsigned char *) mac;
- }
- }
-#endif
-
- /*
- * 3) from flash or fuse (via platform data)
- */
- if (!is_valid_ether_addr(iap)) {
-#ifdef CONFIG_M5272
- if (FEC_FLASHMAC)
- iap = (unsigned char *)FEC_FLASHMAC;
-#else
- if (pdata)
- iap = (unsigned char *)&pdata->mac;
-#endif
- }
-
- /*
- * 4) FEC mac registers set by bootloader
- */
- if (!is_valid_ether_addr(iap)) {
- *((unsigned long *) &tmpaddr[0]) =
- be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
- *((unsigned short *) &tmpaddr[4]) =
- be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
- iap = &tmpaddr[0];
- }
-
- memcpy(ndev->dev_addr, iap, ETH_ALEN);
-
- /* Adjust MAC if using macaddr */
- if (iap == macaddr)
- ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
- * Phy section
- */
-static void fec_enet_adjust_link(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct phy_device *phy_dev = fep->phy_dev;
- unsigned long flags;
-
- int status_change = 0;
-
- spin_lock_irqsave(&fep->hw_lock, flags);
-
- /* Prevent a state halted on mii error */
- if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
- phy_dev->state = PHY_RESUMING;
- goto spin_unlock;
- }
-
- /* Duplex link change */
- if (phy_dev->link) {
- if (fep->full_duplex != phy_dev->duplex) {
- fec_restart(ndev, phy_dev->duplex);
- /* prevent unnecessary second fec_restart() below */
- fep->link = phy_dev->link;
- status_change = 1;
- }
- }
-
- /* Link on or off change */
- if (phy_dev->link != fep->link) {
- fep->link = phy_dev->link;
- if (phy_dev->link)
- fec_restart(ndev, phy_dev->duplex);
- else
- fec_stop(ndev);
- status_change = 1;
- }
-
-spin_unlock:
- spin_unlock_irqrestore(&fep->hw_lock, flags);
-
- if (status_change)
- phy_print_status(phy_dev);
-}
-
-static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-{
- struct fec_enet_private *fep = bus->priv;
- unsigned long time_left;
-
- fep->mii_timeout = 0;
- init_completion(&fep->mdio_done);
-
- /* start a read op */
- writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
- FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
- FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
-
- /* wait for end of transfer */
- time_left = wait_for_completion_timeout(&fep->mdio_done,
- usecs_to_jiffies(FEC_MII_TIMEOUT));
- if (time_left == 0) {
- fep->mii_timeout = 1;
- printk(KERN_ERR "FEC: MDIO read timeout\n");
- return -ETIMEDOUT;
- }
-
- /* return value */
- return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
-}
-
-static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
- u16 value)
-{
- struct fec_enet_private *fep = bus->priv;
- unsigned long time_left;
-
- fep->mii_timeout = 0;
- init_completion(&fep->mdio_done);
-
- /* start a write op */
- writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
- FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
- FEC_MMFR_TA | FEC_MMFR_DATA(value),
- fep->hwp + FEC_MII_DATA);
-
- /* wait for end of transfer */
- time_left = wait_for_completion_timeout(&fep->mdio_done,
- usecs_to_jiffies(FEC_MII_TIMEOUT));
- if (time_left == 0) {
- fep->mii_timeout = 1;
- printk(KERN_ERR "FEC: MDIO write timeout\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int fec_enet_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
-static int fec_enet_mii_probe(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- struct phy_device *phy_dev = NULL;
- char mdio_bus_id[MII_BUS_ID_SIZE];
- char phy_name[MII_BUS_ID_SIZE + 3];
- int phy_id;
- int dev_id = fep->dev_id;
-
- fep->phy_dev = NULL;
-
- /* check for attached phy */
- for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
- if ((fep->mii_bus->phy_mask & (1 << phy_id)))
- continue;
- if (fep->mii_bus->phy_map[phy_id] == NULL)
- continue;
- if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
- continue;
- if (dev_id--)
- continue;
- strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
- break;
- }
-
- if (phy_id >= PHY_MAX_ADDR) {
- printk(KERN_INFO
- "%s: no PHY, assuming direct connection to switch\n",
- ndev->name);
- strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
- phy_id = 0;
- }
-
- snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
- phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
- fep->phy_interface);
- if (IS_ERR(phy_dev)) {
- printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
- return PTR_ERR(phy_dev);
- }
-
- /* mask with MAC supported features */
- if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
- phy_dev->supported &= PHY_GBIT_FEATURES;
- phy_dev->supported |= SUPPORTED_Pause;
- }
- else
- phy_dev->supported &= PHY_BASIC_FEATURES;
-
- phy_dev->advertising = phy_dev->supported;
-
- fep->phy_dev = phy_dev;
- fep->link = 0;
- fep->full_duplex = 0;
-
- printk(KERN_INFO
- "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- ndev->name,
- fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
- fep->phy_dev->irq);
-
- return 0;
-}
-
-static int fec_enet_mii_init(struct platform_device *pdev)
-{
- static struct mii_bus *fec0_mii_bus;
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct fec_enet_private *fep = netdev_priv(ndev);
- const struct platform_device_id *id_entry =
- platform_get_device_id(fep->pdev);
- int err = -ENXIO, i;
-
- /*
- * The dual fec interfaces are not equivalent with enet-mac.
- * Here are the differences:
- *
- * - fec0 supports MII & RMII modes while fec1 only supports RMII
- * - fec0 acts as the 1588 time master while fec1 is slave
- * - external phys can only be configured by fec0
- *
- * That is to say fec1 can not work independently. It only works
- * when fec0 is working. The reason behind this design is that the
- * second interface is added primarily for Switch mode.
- *
- * Because of the last point above, both phys are attached on fec0
- * mdio interface in board design, and need to be configured by
- * fec0 mii_bus.
- */
- if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
- /* fec1 uses fec0 mii_bus */
- if (mii_cnt && fec0_mii_bus) {
- fep->mii_bus = fec0_mii_bus;
- mii_cnt++;
- return 0;
- }
- return -ENOENT;
- }
-
- fep->mii_timeout = 0;
-
- /*
- * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
- *
- * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
- * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
- * Reference Manual has an error on this, and gets fixed on i.MX6Q
- * document.
- */
- fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
- fep->phy_speed--;
- fep->phy_speed <<= 1;
- writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
-
- fep->mii_bus = mdiobus_alloc();
- if (fep->mii_bus == NULL) {
- err = -ENOMEM;
- goto err_out;
- }
-
- fep->mii_bus->name = "fec_enet_mii_bus";
- fep->mii_bus->read = fec_enet_mdio_read;
- fep->mii_bus->write = fec_enet_mdio_write;
- fep->mii_bus->reset = fec_enet_mdio_reset;
- snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
- pdev->name, fep->dev_id + 1);
- fep->mii_bus->priv = fep;
- fep->mii_bus->parent = &pdev->dev;
-
- fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!fep->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- fep->mii_bus->irq[i] = PHY_POLL;
-
- if (mdiobus_register(fep->mii_bus))
- goto err_out_free_mdio_irq;
-
- mii_cnt++;
-
- /* save fec0 mii_bus */
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
- fec0_mii_bus = fep->mii_bus;
-
- return 0;
-
-err_out_free_mdio_irq:
- kfree(fep->mii_bus->irq);
-err_out_free_mdiobus:
- mdiobus_free(fep->mii_bus);
-err_out:
- return err;
-}
-
-static void fec_enet_mii_remove(struct fec_enet_private *fep)
-{
- if (--mii_cnt == 0) {
- mdiobus_unregister(fep->mii_bus);
- kfree(fep->mii_bus->irq);
- mdiobus_free(fep->mii_bus);
- }
-}
-
-static int fec_enet_get_settings(struct net_device *ndev,
- struct ethtool_cmd *cmd)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct phy_device *phydev = fep->phy_dev;
-
- if (!phydev)
- return -ENODEV;
-
- return phy_ethtool_gset(phydev, cmd);
-}
-
-static int fec_enet_set_settings(struct net_device *ndev,
- struct ethtool_cmd *cmd)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct phy_device *phydev = fep->phy_dev;
-
- if (!phydev)
- return -ENODEV;
-
- return phy_ethtool_sset(phydev, cmd);
-}
-
-static void fec_enet_get_drvinfo(struct net_device *ndev,
- struct ethtool_drvinfo *info)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- strlcpy(info->driver, fep->pdev->dev.driver->name,
- sizeof(info->driver));
- strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
- strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
-}
-
-static int fec_enet_get_ts_info(struct net_device *ndev,
- struct ethtool_ts_info *info)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- if (fep->bufdesc_ex) {
-
- info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
- SOF_TIMESTAMPING_RX_SOFTWARE |
- SOF_TIMESTAMPING_SOFTWARE |
- SOF_TIMESTAMPING_TX_HARDWARE |
- SOF_TIMESTAMPING_RX_HARDWARE |
- SOF_TIMESTAMPING_RAW_HARDWARE;
- if (fep->ptp_clock)
- info->phc_index = ptp_clock_index(fep->ptp_clock);
- else
- info->phc_index = -1;
-
- info->tx_types = (1 << HWTSTAMP_TX_OFF) |
- (1 << HWTSTAMP_TX_ON);
-
- info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
- (1 << HWTSTAMP_FILTER_ALL);
- return 0;
- } else {
- return ethtool_op_get_ts_info(ndev, info);
- }
-}
-
-static void fec_enet_get_pauseparam(struct net_device *ndev,
- struct ethtool_pauseparam *pause)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
- pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
- pause->rx_pause = pause->tx_pause;
-}
-
-static int fec_enet_set_pauseparam(struct net_device *ndev,
- struct ethtool_pauseparam *pause)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- if (pause->tx_pause != pause->rx_pause) {
- netdev_info(ndev,
- "hardware only support enable/disable both tx and rx");
- return -EINVAL;
- }
-
- fep->pause_flag = 0;
-
- /* tx pause must be same as rx pause */
- fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
- fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
-
- if (pause->rx_pause || pause->autoneg) {
- fep->phy_dev->supported |= ADVERTISED_Pause;
- fep->phy_dev->advertising |= ADVERTISED_Pause;
- } else {
- fep->phy_dev->supported &= ~ADVERTISED_Pause;
- fep->phy_dev->advertising &= ~ADVERTISED_Pause;
- }
-
- if (pause->autoneg) {
- if (netif_running(ndev))
- fec_stop(ndev);
- phy_start_aneg(fep->phy_dev);
- }
- if (netif_running(ndev))
- fec_restart(ndev, 0);
-
- return 0;
-}
-
-static const struct ethtool_ops fec_enet_ethtool_ops = {
- .get_pauseparam = fec_enet_get_pauseparam,
- .set_pauseparam = fec_enet_set_pauseparam,
- .get_settings = fec_enet_get_settings,
- .set_settings = fec_enet_set_settings,
- .get_drvinfo = fec_enet_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_ts_info = fec_enet_get_ts_info,
-};
-
-static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct phy_device *phydev = fep->phy_dev;
-
- if (!netif_running(ndev))
- return -EINVAL;
-
- if (!phydev)
- return -ENODEV;
-
- if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
- return fec_ptp_ioctl(ndev, rq, cmd);
-
- return phy_mii_ioctl(phydev, rq, cmd);
-}
-
-static void fec_enet_free_buffers(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- int i;
- struct sk_buff *skb;
- struct bufdesc *bdp;
-
- bdp = fep->rx_bd_base;
- for (i = 0; i < RX_RING_SIZE; i++) {
- skb = fep->rx_skbuff[i];
-
- if (bdp->cbd_bufaddr)
- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
- if (skb)
- dev_kfree_skb(skb);
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- }
-
- bdp = fep->tx_bd_base;
- for (i = 0; i < TX_RING_SIZE; i++)
- kfree(fep->tx_bounce[i]);
-}
-
-static int fec_enet_alloc_buffers(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- int i;
- struct sk_buff *skb;
- struct bufdesc *bdp;
-
- bdp = fep->rx_bd_base;
- for (i = 0; i < RX_RING_SIZE; i++) {
- skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
- if (!skb) {
- fec_enet_free_buffers(ndev);
- return -ENOMEM;
- }
- fep->rx_skbuff[i] = skb;
-
- bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
- FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
- bdp->cbd_sc = BD_ENET_RX_EMPTY;
-
- if (fep->bufdesc_ex) {
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
- ebdp->cbd_esc = BD_ENET_RX_INT;
- }
-
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- }
-
- /* Set the last buffer to wrap. */
- bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
- bdp->cbd_sc |= BD_SC_WRAP;
-
- bdp = fep->tx_bd_base;
- for (i = 0; i < TX_RING_SIZE; i++) {
- fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
-
- bdp->cbd_sc = 0;
- bdp->cbd_bufaddr = 0;
-
- if (fep->bufdesc_ex) {
- struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
- ebdp->cbd_esc = BD_ENET_RX_INT;
- }
-
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- }
-
- /* Set the last buffer to wrap. */
- bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
- bdp->cbd_sc |= BD_SC_WRAP;
-
- return 0;
-}
-
-static int
-fec_enet_open(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- int ret;
-
- napi_enable(&fep->napi);
-
- /* I should reset the ring buffers here, but I don't yet know
- * a simple way to do that.
- */
-
- ret = fec_enet_alloc_buffers(ndev);
- if (ret)
- return ret;
-
- /* Probe and connect to PHY when open the interface */
- ret = fec_enet_mii_probe(ndev);
- if (ret) {
- fec_enet_free_buffers(ndev);
- return ret;
- }
- phy_start(fep->phy_dev);
- netif_start_queue(ndev);
- fep->opened = 1;
- return 0;
-}
-
-static int
-fec_enet_close(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- /* Don't know what to do yet. */
- fep->opened = 0;
- netif_stop_queue(ndev);
- fec_stop(ndev);
-
- if (fep->phy_dev) {
- phy_stop(fep->phy_dev);
- phy_disconnect(fep->phy_dev);
- }
-
- fec_enet_free_buffers(ndev);
-
- return 0;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- * Skeleton taken from sunlance driver.
- * The CPM Ethernet implementation allows Multicast as well as individual
- * MAC address filtering. Some of the drivers check to make sure it is
- * a group multicast address, and discard those that are not. I guess I
- * will do the same for now, but just remove the test if you want
- * individual filtering as well (do the upper net layers want or support
- * this kind of feature?).
- */
-
-#define HASH_BITS 6 /* #bits in hash */
-#define CRC32_POLY 0xEDB88320
-
-static void set_multicast_list(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct netdev_hw_addr *ha;
- unsigned int i, bit, data, crc, tmp;
- unsigned char hash;
-
- if (ndev->flags & IFF_PROMISC) {
- tmp = readl(fep->hwp + FEC_R_CNTRL);
- tmp |= 0x8;
- writel(tmp, fep->hwp + FEC_R_CNTRL);
- return;
- }
-
- tmp = readl(fep->hwp + FEC_R_CNTRL);
- tmp &= ~0x8;
- writel(tmp, fep->hwp + FEC_R_CNTRL);
-
- if (ndev->flags & IFF_ALLMULTI) {
- /* Catch all multicast addresses, so set the
- * filter to all 1's
- */
- writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-
- return;
- }
-
- /* Clear filter and add the addresses in hash register
- */
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-
- netdev_for_each_mc_addr(ha, ndev) {
- /* calculate crc32 value of mac address */
- crc = 0xffffffff;
-
- for (i = 0; i < ndev->addr_len; i++) {
- data = ha->addr[i];
- for (bit = 0; bit < 8; bit++, data >>= 1) {
- crc = (crc >> 1) ^
- (((crc ^ data) & 1) ? CRC32_POLY : 0);
- }
- }
-
- /* only upper 6 bits (HASH_BITS) are used
- * which point to specific bit in he hash registers
- */
- hash = (crc >> (32 - HASH_BITS)) & 0x3f;
-
- if (hash > 31) {
- tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- tmp |= 1 << (hash - 32);
- writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- } else {
- tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
- tmp |= 1 << hash;
- writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
- }
- }
-}
-
-/* Set a MAC change in hardware. */
-static int
-fec_set_mac_address(struct net_device *ndev, void *p)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct sockaddr *addr = p;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
-
- writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
- (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
- fep->hwp + FEC_ADDR_LOW);
- writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
- fep->hwp + FEC_ADDR_HIGH);
- return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/**
- * fec_poll_controller - FEC Poll controller function
- * @dev: The FEC network adapter
- *
- * Polled functionality used by netconsole and others in non interrupt mode
- *
- */
-void fec_poll_controller(struct net_device *dev)
-{
- int i;
- struct fec_enet_private *fep = netdev_priv(dev);
-
- for (i = 0; i < FEC_IRQ_NUM; i++) {
- if (fep->irq[i] > 0) {
- disable_irq(fep->irq[i]);
- fec_enet_interrupt(fep->irq[i], dev);
- enable_irq(fep->irq[i]);
- }
- }
-}
-#endif
-
-static const struct net_device_ops fec_netdev_ops = {
- .ndo_open = fec_enet_open,
- .ndo_stop = fec_enet_close,
- .ndo_start_xmit = fec_enet_start_xmit,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_tx_timeout = fec_timeout,
- .ndo_set_mac_address = fec_set_mac_address,
- .ndo_do_ioctl = fec_enet_ioctl,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = fec_poll_controller,
-#endif
-};
-
- /*
- * XXX: We need to clean up on failure exits here.
- *
- */
-static int fec_enet_init(struct net_device *ndev)
-{
- struct fec_enet_private *fep = netdev_priv(ndev);
- struct bufdesc *cbd_base;
- struct bufdesc *bdp;
- int i;
-
- /* Allocate memory for buffer descriptors. */
- cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
- GFP_KERNEL);
- if (!cbd_base)
- return -ENOMEM;
-
- spin_lock_init(&fep->hw_lock);
-
- fep->netdev = ndev;
-
- /* Get the Ethernet address */
- fec_get_mac(ndev);
-
- /* Set receive and transmit descriptor base. */
- fep->rx_bd_base = cbd_base;
- if (fep->bufdesc_ex)
- fep->tx_bd_base = (struct bufdesc *)
- (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
- else
- fep->tx_bd_base = cbd_base + RX_RING_SIZE;
-
- /* The FEC Ethernet specific entries in the device structure */
- ndev->watchdog_timeo = TX_TIMEOUT;
- ndev->netdev_ops = &fec_netdev_ops;
- ndev->ethtool_ops = &fec_enet_ethtool_ops;
-
- writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
- netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
-
- /* Initialize the receive buffer descriptors. */
- bdp = fep->rx_bd_base;
- for (i = 0; i < RX_RING_SIZE; i++) {
-
- /* Initialize the BD for every fragment in the page. */
- bdp->cbd_sc = 0;
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- }
-
- /* Set the last buffer to wrap */
- bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
- bdp->cbd_sc |= BD_SC_WRAP;
-
- /* ...and the same for transmit */
- bdp = fep->tx_bd_base;
- fep->cur_tx = bdp;
- for (i = 0; i < TX_RING_SIZE; i++) {
-
- /* Initialize the BD for every fragment in the page. */
- bdp->cbd_sc = 0;
- bdp->cbd_bufaddr = 0;
- bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
- }
-
- /* Set the last buffer to wrap */
- bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
- bdp->cbd_sc |= BD_SC_WRAP;
- fep->dirty_tx = bdp;
-
- fec_restart(ndev, 0);
-
- return 0;
-}
-
-#ifdef CONFIG_OF
-static int fec_get_phy_mode_dt(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
-
- if (np)
- return of_get_phy_mode(np);
-
- return -ENODEV;
-}
-
-static void fec_reset_phy(struct platform_device *pdev)
-{
- int err, phy_reset;
- int msec = 1;
- struct device_node *np = pdev->dev.of_node;
-
- if (!np)
- return;
-
- of_property_read_u32(np, "phy-reset-duration", &msec);
- /* A sane reset duration should not be longer than 1s */
- if (msec > 1000)
- msec = 1;
-
- phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
- if (!gpio_is_valid(phy_reset))
- return;
-
- err = devm_gpio_request_one(&pdev->dev, phy_reset,
- GPIOF_OUT_INIT_LOW, "phy-reset");
- if (err) {
- dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
- return;
- }
- msleep(msec);
- gpio_set_value(phy_reset, 1);
-}
-#else /* CONFIG_OF */
-static int fec_get_phy_mode_dt(struct platform_device *pdev)
-{
- return -ENODEV;
-}
-
-static void fec_reset_phy(struct platform_device *pdev)
-{
- /*
- * In case of platform probe, the reset has been done
- * by machine code.
- */
-}
-#endif /* CONFIG_OF */
-
-static int
-fec_probe(struct platform_device *pdev)
-{
- struct fec_enet_private *fep;
- struct fec_platform_data *pdata;
- struct net_device *ndev;
- int i, irq, ret = 0;
- struct resource *r;
- const struct of_device_id *of_id;
- static int dev_id;
- struct pinctrl *pinctrl;
- struct regulator *reg_phy;
-
- of_id = of_match_device(fec_dt_ids, &pdev->dev);
- if (of_id)
- pdev->id_entry = of_id->data;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENXIO;
-
- /* Init network device */
- ndev = alloc_etherdev(sizeof(struct fec_enet_private));
- if (!ndev)
- return -ENOMEM;
-
- SET_NETDEV_DEV(ndev, &pdev->dev);
-
- /* setup board info structure */
- fep = netdev_priv(ndev);
-
- /* default enable pause frame auto negotiation */
- if (pdev->id_entry &&
- (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
- fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
-
- fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
- fep->pdev = pdev;
- fep->dev_id = dev_id++;
-
- fep->bufdesc_ex = 0;
-
- if (!fep->hwp) {
- ret = -ENOMEM;
- goto failed_ioremap;
- }
-
- platform_set_drvdata(pdev, ndev);
-
- ret = fec_get_phy_mode_dt(pdev);
- if (ret < 0) {
- pdata = pdev->dev.platform_data;
- if (pdata)
- fep->phy_interface = pdata->phy;
- else
- fep->phy_interface = PHY_INTERFACE_MODE_MII;
- } else {
- fep->phy_interface = ret;
- }
-
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto failed_pin;
- }
-
- fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(fep->clk_ipg)) {
- ret = PTR_ERR(fep->clk_ipg);
- goto failed_clk;
- }
-
- fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
- if (IS_ERR(fep->clk_ahb)) {
- ret = PTR_ERR(fep->clk_ahb);
- goto failed_clk;
- }
-
- fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
- fep->bufdesc_ex =
- pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
- if (IS_ERR(fep->clk_ptp)) {
- ret = PTR_ERR(fep->clk_ptp);
- fep->bufdesc_ex = 0;
- }
-
- clk_prepare_enable(fep->clk_ahb);
- clk_prepare_enable(fep->clk_ipg);
- if (!IS_ERR(fep->clk_ptp))
- clk_prepare_enable(fep->clk_ptp);
-
- reg_phy = devm_regulator_get(&pdev->dev, "phy");
- if (!IS_ERR(reg_phy)) {
- ret = regulator_enable(reg_phy);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to enable phy regulator: %d\n", ret);
- goto failed_regulator;
- }
- }
-
- fec_reset_phy(pdev);
-
- if (fep->bufdesc_ex)
- fec_ptp_init(ndev, pdev);
-
- ret = fec_enet_init(ndev);
- if (ret)
- goto failed_init;
-
- for (i = 0; i < FEC_IRQ_NUM; i++) {
- irq = platform_get_irq(pdev, i);
- if (irq < 0) {
- if (i)
- break;
- ret = irq;
- goto failed_irq;
- }
- ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
- if (ret) {
- while (--i >= 0) {
- irq = platform_get_irq(pdev, i);
- free_irq(irq, ndev);
- }
- goto failed_irq;
- }
- }
-
- ret = fec_enet_mii_init(pdev);
- if (ret)
- goto failed_mii_init;
-
- /* Carrier starts down, phylib will bring it up */
- netif_carrier_off(ndev);
-
- ret = register_netdev(ndev);
- if (ret)
- goto failed_register;
-
- return 0;
-
-failed_register:
- fec_enet_mii_remove(fep);
-failed_mii_init:
-failed_init:
- for (i = 0; i < FEC_IRQ_NUM; i++) {
- irq = platform_get_irq(pdev, i);
- if (irq > 0)
- free_irq(irq, ndev);
- }
-failed_irq:
-failed_regulator:
- clk_disable_unprepare(fep->clk_ahb);
- clk_disable_unprepare(fep->clk_ipg);
- if (!IS_ERR(fep->clk_ptp))
- clk_disable_unprepare(fep->clk_ptp);
-failed_pin:
-failed_clk:
-failed_ioremap:
- free_netdev(ndev);
-
- return ret;
-}
-
-static int
-fec_drv_remove(struct platform_device *pdev)
-{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct fec_enet_private *fep = netdev_priv(ndev);
- int i;
-
- unregister_netdev(ndev);
- fec_enet_mii_remove(fep);
- del_timer_sync(&fep->time_keep);
- clk_disable_unprepare(fep->clk_ptp);
- if (fep->ptp_clock)
- ptp_clock_unregister(fep->ptp_clock);
- clk_disable_unprepare(fep->clk_ahb);
- clk_disable_unprepare(fep->clk_ipg);
- for (i = 0; i < FEC_IRQ_NUM; i++) {
- int irq = platform_get_irq(pdev, i);
- if (irq > 0)
- free_irq(irq, ndev);
- }
- free_netdev(ndev);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int
-fec_suspend(struct device *dev)
-{
- struct net_device *ndev = dev_get_drvdata(dev);
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- if (netif_running(ndev)) {
- fec_stop(ndev);
- netif_device_detach(ndev);
- }
- clk_disable_unprepare(fep->clk_ahb);
- clk_disable_unprepare(fep->clk_ipg);
-
- return 0;
-}
-
-static int
-fec_resume(struct device *dev)
-{
- struct net_device *ndev = dev_get_drvdata(dev);
- struct fec_enet_private *fep = netdev_priv(ndev);
-
- clk_prepare_enable(fep->clk_ahb);
- clk_prepare_enable(fep->clk_ipg);
- if (netif_running(ndev)) {
- fec_restart(ndev, fep->full_duplex);
- netif_device_attach(ndev);
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops fec_pm_ops = {
- .suspend = fec_suspend,
- .resume = fec_resume,
- .freeze = fec_suspend,
- .thaw = fec_resume,
- .poweroff = fec_suspend,
- .restore = fec_resume,
-};
-#endif
-
-static struct platform_driver fec_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &fec_pm_ops,
-#endif
- .of_match_table = fec_dt_ids,
- },
- .id_table = fec_devtype,
- .probe = fec_probe,
- .remove = fec_drv_remove,
-};
-
-module_platform_driver(fec_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
new file mode 100644
index 0000000..69a4ade
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -0,0 +1,1961 @@
+/*
+ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * Right now, I am very wasteful with the buffers. I allocate memory
+ * pages and then divide them into 2K frame buffers. This way I know I
+ * have buffers large enough to hold one frame within one buffer descriptor.
+ * Once I get this working, I will use 64 or 128 byte CPM buffers, which
+ * will be much more memory efficient and will easily handle lots of
+ * small packets.
+ *
+ * Much better multiple PHY support by Magnus Damm.
+ * Copyright (c) 2000 Ericsson Radio Systems AB.
+ *
+ * Support for FEC controller of ColdFire processors.
+ * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
+ *
+ * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
+ * Copyright (c) 2004-2006 Macq Electronique SA.
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/fec.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/cacheflush.h>
+
+#ifndef CONFIG_ARM
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+#endif
+
+#include "fec.h"
+
+#if defined(CONFIG_ARM)
+#define FEC_ALIGNMENT 0xf
+#else
+#define FEC_ALIGNMENT 0x3
+#endif
+
+#define DRIVER_NAME "fec"
+#define FEC_NAPI_WEIGHT 64
+
+/* Pause frame feild and FIFO threshold */
+#define FEC_ENET_FCE (1 << 5)
+#define FEC_ENET_RSEM_V 0x84
+#define FEC_ENET_RSFL_V 16
+#define FEC_ENET_RAEM_V 0x8
+#define FEC_ENET_RAFL_V 0x8
+#define FEC_ENET_OPD_V 0xFFF0
+
+/* Controller is ENET-MAC */
+#define FEC_QUIRK_ENET_MAC (1 << 0)
+/* Controller needs driver to swap frame */
+#define FEC_QUIRK_SWAP_FRAME (1 << 1)
+/* Controller uses gasket */
+#define FEC_QUIRK_USE_GASKET (1 << 2)
+/* Controller has GBIT support */
+#define FEC_QUIRK_HAS_GBIT (1 << 3)
+/* Controller has extend desc buffer */
+#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4)
+
+static struct platform_device_id fec_devtype[] = {
+ {
+ /* keep it for coldfire */
+ .name = DRIVER_NAME,
+ .driver_data = 0,
+ }, {
+ .name = "imx25-fec",
+ .driver_data = FEC_QUIRK_USE_GASKET,
+ }, {
+ .name = "imx27-fec",
+ .driver_data = 0,
+ }, {
+ .name = "imx28-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
+ }, {
+ .name = "imx6q-fec",
+ .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+ FEC_QUIRK_HAS_BUFDESC_EX,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, fec_devtype);
+
+enum imx_fec_type {
+ IMX25_FEC = 1, /* runs on i.mx25/50/53 */
+ IMX27_FEC, /* runs on i.mx27/35/51 */
+ IMX28_FEC,
+ IMX6Q_FEC,
+};
+
+static const struct of_device_id fec_dt_ids[] = {
+ { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
+ { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
+ { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
+ { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fec_dt_ids);
+
+static unsigned char macaddr[ETH_ALEN];
+module_param_array(macaddr, byte, NULL, 0);
+MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
+
+#if defined(CONFIG_M5272)
+/*
+ * Some hardware gets it MAC address out of local flash memory.
+ * if this is non-zero then assume it is the address to get MAC from.
+ */
+#if defined(CONFIG_NETtel)
+#define FEC_FLASHMAC 0xf0006006
+#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
+#define FEC_FLASHMAC 0xf0006000
+#elif defined(CONFIG_CANCam)
+#define FEC_FLASHMAC 0xf0020000
+#elif defined (CONFIG_M5272C3)
+#define FEC_FLASHMAC (0xffe04000 + 4)
+#elif defined(CONFIG_MOD5272)
+#define FEC_FLASHMAC 0xffc0406b
+#else
+#define FEC_FLASHMAC 0
+#endif
+#endif /* CONFIG_M5272 */
+
+#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
+#error "FEC: descriptor ring size constants too large"
+#endif
+
+/* Interrupt events/masks. */
+#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
+#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
+#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
+#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
+#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
+#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
+#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
+#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
+#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
+#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
+#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
+
+/* The FEC stores dest/src/type, data, and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE 1518
+#define PKT_MINBUF_SIZE 64
+#define PKT_MAXBLR_SIZE 1520
+
+/*
+ * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
+ * size bits. Other FEC hardware does not, so we need to take that into
+ * account when setting it.
+ */
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM)
+#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
+#else
+#define OPT_FRAME_SIZE 0
+#endif
+
+/* FEC MII MMFR bits definition */
+#define FEC_MMFR_ST (1 << 30)
+#define FEC_MMFR_OP_READ (2 << 28)
+#define FEC_MMFR_OP_WRITE (1 << 28)
+#define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
+#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
+#define FEC_MMFR_TA (2 << 16)
+#define FEC_MMFR_DATA(v) (v & 0xffff)
+
+#define FEC_MII_TIMEOUT 30000 /* us */
+
+/* Transmitter timeout */
+#define TX_TIMEOUT (2 * HZ)
+
+#define FEC_PAUSE_FLAG_AUTONEG 0x1
+#define FEC_PAUSE_FLAG_ENABLE 0x2
+
+static int mii_cnt;
+
+static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex)
+{
+ struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
+ if (is_ex)
+ return (struct bufdesc *)(ex + 1);
+ else
+ return bdp + 1;
+}
+
+static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex)
+{
+ struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp;
+ if (is_ex)
+ return (struct bufdesc *)(ex - 1);
+ else
+ return bdp - 1;
+}
+
+static void *swap_buffer(void *bufaddr, int len)
+{
+ int i;
+ unsigned int *buf = bufaddr;
+
+ for (i = 0; i < (len + 3) / 4; i++, buf++)
+ *buf = cpu_to_be32(*buf);
+
+ return bufaddr;
+}
+
+static netdev_tx_t
+fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ struct bufdesc *bdp;
+ void *bufaddr;
+ unsigned short status;
+ unsigned int index;
+
+ if (!fep->link) {
+ /* Link is down or autonegotiation is in progress. */
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Fill in a Tx ring entry */
+ bdp = fep->cur_tx;
+
+ status = bdp->cbd_sc;
+
+ if (status & BD_ENET_TX_READY) {
+ /* Ooops. All transmit buffers are full. Bail out.
+ * This should not happen, since ndev->tbusy should be set.
+ */
+ printk("%s: tx queue full!.\n", ndev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Clear all of the status flags */
+ status &= ~BD_ENET_TX_STATS;
+
+ /* Set buffer length and buffer pointer */
+ bufaddr = skb->data;
+ bdp->cbd_datlen = skb->len;
+
+ /*
+ * On some FEC implementations data must be aligned on
+ * 4-byte boundaries. Use bounce buffers to copy data
+ * and get it aligned. Ugh.
+ */
+ if (fep->bufdesc_ex)
+ index = (struct bufdesc_ex *)bdp -
+ (struct bufdesc_ex *)fep->tx_bd_base;
+ else
+ index = bdp - fep->tx_bd_base;
+
+ if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
+ memcpy(fep->tx_bounce[index], skb->data, skb->len);
+ bufaddr = fep->tx_bounce[index];
+ }
+
+ /*
+ * Some design made an incorrect assumption on endian mode of
+ * the system that it's running on. As the result, driver has to
+ * swap every frame going to and coming from the controller.
+ */
+ if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+ swap_buffer(bufaddr, skb->len);
+
+ /* Save skb pointer */
+ fep->tx_skbuff[index] = skb;
+
+ /* Push the data cache so the CPM does not get stale memory
+ * data.
+ */
+ bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+ FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+
+ /* Send it on its way. Tell FEC it's ready, interrupt when done,
+ * it's the last BD of the frame, and to put the CRC on the end.
+ */
+ status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
+ | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+ bdp->cbd_sc = status;
+
+ if (fep->bufdesc_ex) {
+
+ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+ ebdp->cbd_bdu = 0;
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
+ fep->hwts_tx_en)) {
+ ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ } else {
+
+ ebdp->cbd_esc = BD_ENET_TX_INT;
+ }
+ }
+ /* If this was the last BD in the ring, start at the beginning again. */
+ if (status & BD_ENET_TX_WRAP)
+ bdp = fep->tx_bd_base;
+ else
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+ fep->cur_tx = bdp;
+
+ if (fep->cur_tx == fep->dirty_tx)
+ netif_stop_queue(ndev);
+
+ /* Trigger transmission start */
+ writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+ skb_tx_timestamp(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change. This only happens when switching between half and full
+ * duplex.
+ */
+static void
+fec_restart(struct net_device *ndev, int duplex)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ int i;
+ u32 temp_mac[2];
+ u32 rcntl = OPT_FRAME_SIZE | 0x04;
+ u32 ecntl = 0x2; /* ETHEREN */
+
+ /* Whack a reset. We should wait for this. */
+ writel(1, fep->hwp + FEC_ECNTRL);
+ udelay(10);
+
+ /*
+ * enet-mac reset will reset mac address registers too,
+ * so need to reconfigure it.
+ */
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
+ writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
+ writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
+ }
+
+ /* Clear any outstanding interrupt. */
+ writel(0xffc00000, fep->hwp + FEC_IEVENT);
+
+ /* Reset all multicast. */
+ writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+#ifndef CONFIG_M5272
+ writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
+ writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
+#endif
+
+ /* Set maximum receive buffer size. */
+ writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
+
+ /* Set receive and transmit descriptor base. */
+ writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
+ if (fep->bufdesc_ex)
+ writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
+ * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
+ else
+ writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
+ * RX_RING_SIZE, fep->hwp + FEC_X_DES_START);
+
+ fep->cur_rx = fep->rx_bd_base;
+
+ for (i = 0; i <= TX_RING_MOD_MASK; i++) {
+ if (fep->tx_skbuff[i]) {
+ dev_kfree_skb_any(fep->tx_skbuff[i]);
+ fep->tx_skbuff[i] = NULL;
+ }
+ }
+
+ /* Enable MII mode */
+ if (duplex) {
+ /* FD enable */
+ writel(0x04, fep->hwp + FEC_X_CNTRL);
+ } else {
+ /* No Rcv on Xmit */
+ rcntl |= 0x02;
+ writel(0x0, fep->hwp + FEC_X_CNTRL);
+ }
+
+ fep->full_duplex = duplex;
+
+ /* Set MII speed */
+ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+
+ /*
+ * The phy interface and speed need to get configured
+ * differently on enet-mac.
+ */
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ /* Enable flow control and length check */
+ rcntl |= 0x40000000 | 0x00000020;
+
+ /* RGMII, RMII or MII */
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ rcntl |= (1 << 6);
+ else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+ rcntl |= (1 << 8);
+ else
+ rcntl &= ~(1 << 8);
+
+ /* 1G, 100M or 10M */
+ if (fep->phy_dev) {
+ if (fep->phy_dev->speed == SPEED_1000)
+ ecntl |= (1 << 5);
+ else if (fep->phy_dev->speed == SPEED_100)
+ rcntl &= ~(1 << 9);
+ else
+ rcntl |= (1 << 9);
+ }
+ } else {
+#ifdef FEC_MIIGSK_ENR
+ if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
+ u32 cfgr;
+ /* disable the gasket and wait */
+ writel(0, fep->hwp + FEC_MIIGSK_ENR);
+ while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
+ udelay(1);
+
+ /*
+ * configure the gasket:
+ * RMII, 50 MHz, no loopback, no echo
+ * MII, 25 MHz, no loopback, no echo
+ */
+ cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+ ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
+ if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
+ cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
+ writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
+
+ /* re-enable the gasket */
+ writel(2, fep->hwp + FEC_MIIGSK_ENR);
+ }
+#endif
+ }
+
+ /* enable pause frame*/
+ if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
+ ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
+ fep->phy_dev && fep->phy_dev->pause)) {
+ rcntl |= FEC_ENET_FCE;
+
+ /* set FIFO thresh hold parameter to reduce overrun */
+ writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
+ writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
+ writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
+ writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
+
+ /* OPD */
+ writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
+ } else {
+ rcntl &= ~FEC_ENET_FCE;
+ }
+
+ writel(rcntl, fep->hwp + FEC_R_CNTRL);
+
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ /* enable ENET endian swap */
+ ecntl |= (1 << 8);
+ /* enable ENET store and forward mode */
+ writel(1 << 8, fep->hwp + FEC_X_WMRK);
+ }
+
+ if (fep->bufdesc_ex)
+ ecntl |= (1 << 4);
+
+ /* And last, enable the transmit and receive processing */
+ writel(ecntl, fep->hwp + FEC_ECNTRL);
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+
+ if (fep->bufdesc_ex)
+ fec_ptp_start_cyclecounter(ndev);
+
+ /* Enable interrupts we wish to service */
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+}
+
+static void
+fec_stop(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
+
+ /* We cannot expect a graceful transmit stop without link !!! */
+ if (fep->link) {
+ writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
+ udelay(10);
+ if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
+ printk("fec_stop : Graceful transmit stop did not complete !\n");
+ }
+
+ /* Whack a reset. We should wait for this. */
+ writel(1, fep->hwp + FEC_ECNTRL);
+ udelay(10);
+ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+ /* We have to keep ENET enabled to have MII interrupt stay working */
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ writel(2, fep->hwp + FEC_ECNTRL);
+ writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
+ }
+}
+
+
+static void
+fec_timeout(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ ndev->stats.tx_errors++;
+
+ fec_restart(ndev, fep->full_duplex);
+ netif_wake_queue(ndev);
+}
+
+static void
+fec_enet_tx(struct net_device *ndev)
+{
+ struct fec_enet_private *fep;
+ struct bufdesc *bdp;
+ unsigned short status;
+ struct sk_buff *skb;
+ int index = 0;
+
+ fep = netdev_priv(ndev);
+ bdp = fep->dirty_tx;
+
+ /* get next bdp of dirty_tx */
+ if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+ bdp = fep->tx_bd_base;
+ else
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+ while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
+
+ /* current queue is empty */
+ if (bdp == fep->cur_tx)
+ break;
+
+ if (fep->bufdesc_ex)
+ index = (struct bufdesc_ex *)bdp -
+ (struct bufdesc_ex *)fep->tx_bd_base;
+ else
+ index = bdp - fep->tx_bd_base;
+
+ dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+ FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+ bdp->cbd_bufaddr = 0;
+
+ skb = fep->tx_skbuff[index];
+
+ /* Check for errors. */
+ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+ BD_ENET_TX_RL | BD_ENET_TX_UN |
+ BD_ENET_TX_CSL)) {
+ ndev->stats.tx_errors++;
+ if (status & BD_ENET_TX_HB) /* No heartbeat */
+ ndev->stats.tx_heartbeat_errors++;
+ if (status & BD_ENET_TX_LC) /* Late collision */
+ ndev->stats.tx_window_errors++;
+ if (status & BD_ENET_TX_RL) /* Retrans limit */
+ ndev->stats.tx_aborted_errors++;
+ if (status & BD_ENET_TX_UN) /* Underrun */
+ ndev->stats.tx_fifo_errors++;
+ if (status & BD_ENET_TX_CSL) /* Carrier lost */
+ ndev->stats.tx_carrier_errors++;
+ } else {
+ ndev->stats.tx_packets++;
+ }
+
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
+ fep->bufdesc_ex) {
+ struct skb_shared_hwtstamps shhwtstamps;
+ unsigned long flags;
+ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ spin_lock_irqsave(&fep->tmreg_lock, flags);
+ shhwtstamps.hwtstamp = ns_to_ktime(
+ timecounter_cyc2time(&fep->tc, ebdp->ts));
+ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+ skb_tstamp_tx(skb, &shhwtstamps);
+ }
+
+ if (status & BD_ENET_TX_READY)
+ printk("HEY! Enet xmit interrupt and TX_READY.\n");
+
+ /* Deferred means some collisions occurred during transmit,
+ * but we eventually sent the packet OK.
+ */
+ if (status & BD_ENET_TX_DEF)
+ ndev->stats.collisions++;
+
+ /* Free the sk buffer associated with this last transmit */
+ dev_kfree_skb_any(skb);
+ fep->tx_skbuff[index] = NULL;
+
+ fep->dirty_tx = bdp;
+
+ /* Update pointer to next buffer descriptor to be transmitted */
+ if (status & BD_ENET_TX_WRAP)
+ bdp = fep->tx_bd_base;
+ else
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+
+ /* Since we have freed up a buffer, the ring is no longer full
+ */
+ if (fep->dirty_tx != fep->cur_tx) {
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+ }
+ }
+ return;
+}
+
+
+/* During a receive, the cur_rx points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+ */
+static int
+fec_enet_rx(struct net_device *ndev, int budget)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ struct bufdesc *bdp;
+ unsigned short status;
+ struct sk_buff *skb;
+ ushort pkt_len;
+ __u8 *data;
+ int pkt_received = 0;
+
+#ifdef CONFIG_M532x
+ flush_cache_all();
+#endif
+
+ /* First, grab all of the stats for the incoming packet.
+ * These get messed up if we get called due to a busy condition.
+ */
+ bdp = fep->cur_rx;
+
+ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
+
+ if (pkt_received >= budget)
+ break;
+ pkt_received++;
+
+ /* Since we have allocated space to hold a complete frame,
+ * the last indicator should be set.
+ */
+ if ((status & BD_ENET_RX_LAST) == 0)
+ printk("FEC ENET: rcv is not +last\n");
+
+ if (!fep->opened)
+ goto rx_processing_done;
+
+ /* Check for errors. */
+ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+ BD_ENET_RX_CR | BD_ENET_RX_OV)) {
+ ndev->stats.rx_errors++;
+ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+ /* Frame too long or too short. */
+ ndev->stats.rx_length_errors++;
+ }
+ if (status & BD_ENET_RX_NO) /* Frame alignment */
+ ndev->stats.rx_frame_errors++;
+ if (status & BD_ENET_RX_CR) /* CRC Error */
+ ndev->stats.rx_crc_errors++;
+ if (status & BD_ENET_RX_OV) /* FIFO overrun */
+ ndev->stats.rx_fifo_errors++;
+ }
+
+ /* Report late collisions as a frame error.
+ * On this error, the BD is closed, but we don't know what we
+ * have in the buffer. So, just drop this frame on the floor.
+ */
+ if (status & BD_ENET_RX_CL) {
+ ndev->stats.rx_errors++;
+ ndev->stats.rx_frame_errors++;
+ goto rx_processing_done;
+ }
+
+ /* Process the incoming frame. */
+ ndev->stats.rx_packets++;
+ pkt_len = bdp->cbd_datlen;
+ ndev->stats.rx_bytes += pkt_len;
+ data = (__u8*)__va(bdp->cbd_bufaddr);
+
+ dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+ FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+
+ if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+ swap_buffer(data, pkt_len);
+
+ /* This does 16 byte alignment, exactly what we need.
+ * The packet length includes FCS, but we don't want to
+ * include that when passing upstream as it messes up
+ * bridging applications.
+ */
+ skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN);
+
+ if (unlikely(!skb)) {
+ ndev->stats.rx_dropped++;
+ } else {
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pkt_len - 4); /* Make room */
+ skb_copy_to_linear_data(skb, data, pkt_len - 4);
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ /* Get receive timestamp from the skb */
+ if (fep->hwts_rx_en && fep->bufdesc_ex) {
+ struct skb_shared_hwtstamps *shhwtstamps =
+ skb_hwtstamps(skb);
+ unsigned long flags;
+ struct bufdesc_ex *ebdp =
+ (struct bufdesc_ex *)bdp;
+
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+
+ spin_lock_irqsave(&fep->tmreg_lock, flags);
+ shhwtstamps->hwtstamp = ns_to_ktime(
+ timecounter_cyc2time(&fep->tc, ebdp->ts));
+ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+ }
+
+ if (!skb_defer_rx_timestamp(skb))
+ napi_gro_receive(&fep->napi, skb);
+ }
+
+ bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
+ FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE);
+rx_processing_done:
+ /* Clear the status flags for this buffer */
+ status &= ~BD_ENET_RX_STATS;
+
+ /* Mark the buffer empty */
+ status |= BD_ENET_RX_EMPTY;
+ bdp->cbd_sc = status;
+
+ if (fep->bufdesc_ex) {
+ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+
+ ebdp->cbd_esc = BD_ENET_RX_INT;
+ ebdp->cbd_prot = 0;
+ ebdp->cbd_bdu = 0;
+ }
+
+ /* Update BD pointer to next entry */
+ if (status & BD_ENET_RX_WRAP)
+ bdp = fep->rx_bd_base;
+ else
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ /* Doing this here will keep the FEC running while we process
+ * incoming frames. On a heavily loaded network, we should be
+ * able to keep up at the expense of system resources.
+ */
+ writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+ }
+ fep->cur_rx = bdp;
+
+ return pkt_received;
+}
+
+static irqreturn_t
+fec_enet_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = dev_id;
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ uint int_events;
+ irqreturn_t ret = IRQ_NONE;
+
+ do {
+ int_events = readl(fep->hwp + FEC_IEVENT);
+ writel(int_events, fep->hwp + FEC_IEVENT);
+
+ if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
+ ret = IRQ_HANDLED;
+
+ /* Disable the RX interrupt */
+ if (napi_schedule_prep(&fep->napi)) {
+ writel(FEC_RX_DISABLED_IMASK,
+ fep->hwp + FEC_IMASK);
+ __napi_schedule(&fep->napi);
+ }
+ }
+
+ if (int_events & FEC_ENET_MII) {
+ ret = IRQ_HANDLED;
+ complete(&fep->mdio_done);
+ }
+ } while (int_events);
+
+ return ret;
+}
+
+static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
+{
+ struct net_device *ndev = napi->dev;
+ int pkts = fec_enet_rx(ndev, budget);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ fec_enet_tx(ndev);
+
+ if (pkts < budget) {
+ napi_complete(napi);
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ }
+ return pkts;
+}
+
+/* ------------------------------------------------------------------------- */
+static void fec_get_mac(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+
+ /*
+ * try to get mac address in following order:
+ *
+ * 1) module parameter via kernel command line in form
+ * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
+ */
+ iap = macaddr;
+
+#ifdef CONFIG_OF
+ /*
+ * 2) from device tree data
+ */
+ if (!is_valid_ether_addr(iap)) {
+ struct device_node *np = fep->pdev->dev.of_node;
+ if (np) {
+ const char *mac = of_get_mac_address(np);
+ if (mac)
+ iap = (unsigned char *) mac;
+ }
+ }
+#endif
+
+ /*
+ * 3) from flash or fuse (via platform data)
+ */
+ if (!is_valid_ether_addr(iap)) {
+#ifdef CONFIG_M5272
+ if (FEC_FLASHMAC)
+ iap = (unsigned char *)FEC_FLASHMAC;
+#else
+ if (pdata)
+ iap = (unsigned char *)&pdata->mac;
+#endif
+ }
+
+ /*
+ * 4) FEC mac registers set by bootloader
+ */
+ if (!is_valid_ether_addr(iap)) {
+ *((unsigned long *) &tmpaddr[0]) =
+ be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
+ *((unsigned short *) &tmpaddr[4]) =
+ be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
+ iap = &tmpaddr[0];
+ }
+
+ memcpy(ndev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using macaddr */
+ if (iap == macaddr)
+ ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Phy section
+ */
+static void fec_enet_adjust_link(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct phy_device *phy_dev = fep->phy_dev;
+ unsigned long flags;
+
+ int status_change = 0;
+
+ spin_lock_irqsave(&fep->hw_lock, flags);
+
+ /* Prevent a state halted on mii error */
+ if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
+ phy_dev->state = PHY_RESUMING;
+ goto spin_unlock;
+ }
+
+ /* Duplex link change */
+ if (phy_dev->link) {
+ if (fep->full_duplex != phy_dev->duplex) {
+ fec_restart(ndev, phy_dev->duplex);
+ /* prevent unnecessary second fec_restart() below */
+ fep->link = phy_dev->link;
+ status_change = 1;
+ }
+ }
+
+ /* Link on or off change */
+ if (phy_dev->link != fep->link) {
+ fep->link = phy_dev->link;
+ if (phy_dev->link)
+ fec_restart(ndev, phy_dev->duplex);
+ else
+ fec_stop(ndev);
+ status_change = 1;
+ }
+
+spin_unlock:
+ spin_unlock_irqrestore(&fep->hw_lock, flags);
+
+ if (status_change)
+ phy_print_status(phy_dev);
+}
+
+static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct fec_enet_private *fep = bus->priv;
+ unsigned long time_left;
+
+ fep->mii_timeout = 0;
+ init_completion(&fep->mdio_done);
+
+ /* start a read op */
+ writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+ FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
+
+ /* wait for end of transfer */
+ time_left = wait_for_completion_timeout(&fep->mdio_done,
+ usecs_to_jiffies(FEC_MII_TIMEOUT));
+ if (time_left == 0) {
+ fep->mii_timeout = 1;
+ printk(KERN_ERR "FEC: MDIO read timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* return value */
+ return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
+}
+
+static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+ u16 value)
+{
+ struct fec_enet_private *fep = bus->priv;
+ unsigned long time_left;
+
+ fep->mii_timeout = 0;
+ init_completion(&fep->mdio_done);
+
+ /* start a write op */
+ writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
+ FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+ FEC_MMFR_TA | FEC_MMFR_DATA(value),
+ fep->hwp + FEC_MII_DATA);
+
+ /* wait for end of transfer */
+ time_left = wait_for_completion_timeout(&fep->mdio_done,
+ usecs_to_jiffies(FEC_MII_TIMEOUT));
+ if (time_left == 0) {
+ fep->mii_timeout = 1;
+ printk(KERN_ERR "FEC: MDIO write timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int fec_enet_mdio_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+static int fec_enet_mii_probe(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ struct phy_device *phy_dev = NULL;
+ char mdio_bus_id[MII_BUS_ID_SIZE];
+ char phy_name[MII_BUS_ID_SIZE + 3];
+ int phy_id;
+ int dev_id = fep->dev_id;
+
+ fep->phy_dev = NULL;
+
+ /* check for attached phy */
+ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
+ if ((fep->mii_bus->phy_mask & (1 << phy_id)))
+ continue;
+ if (fep->mii_bus->phy_map[phy_id] == NULL)
+ continue;
+ if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
+ continue;
+ if (dev_id--)
+ continue;
+ strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
+ break;
+ }
+
+ if (phy_id >= PHY_MAX_ADDR) {
+ printk(KERN_INFO
+ "%s: no PHY, assuming direct connection to switch\n",
+ ndev->name);
+ strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
+ phy_id = 0;
+ }
+
+ snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id);
+ phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
+ fep->phy_interface);
+ if (IS_ERR(phy_dev)) {
+ printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
+ return PTR_ERR(phy_dev);
+ }
+
+ /* mask with MAC supported features */
+ if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
+ phy_dev->supported &= PHY_GBIT_FEATURES;
+ phy_dev->supported |= SUPPORTED_Pause;
+ }
+ else
+ phy_dev->supported &= PHY_BASIC_FEATURES;
+
+ phy_dev->advertising = phy_dev->supported;
+
+ fep->phy_dev = phy_dev;
+ fep->link = 0;
+ fep->full_duplex = 0;
+
+ printk(KERN_INFO
+ "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ ndev->name,
+ fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
+ fep->phy_dev->irq);
+
+ return 0;
+}
+
+static int fec_enet_mii_init(struct platform_device *pdev)
+{
+ static struct mii_bus *fec0_mii_bus;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ int err = -ENXIO, i;
+
+ /*
+ * The dual fec interfaces are not equivalent with enet-mac.
+ * Here are the differences:
+ *
+ * - fec0 supports MII & RMII modes while fec1 only supports RMII
+ * - fec0 acts as the 1588 time master while fec1 is slave
+ * - external phys can only be configured by fec0
+ *
+ * That is to say fec1 can not work independently. It only works
+ * when fec0 is working. The reason behind this design is that the
+ * second interface is added primarily for Switch mode.
+ *
+ * Because of the last point above, both phys are attached on fec0
+ * mdio interface in board design, and need to be configured by
+ * fec0 mii_bus.
+ */
+ if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
+ /* fec1 uses fec0 mii_bus */
+ if (mii_cnt && fec0_mii_bus) {
+ fep->mii_bus = fec0_mii_bus;
+ mii_cnt++;
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ fep->mii_timeout = 0;
+
+ /*
+ * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
+ *
+ * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
+ * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
+ * Reference Manual has an error on this, and gets fixed on i.MX6Q
+ * document.
+ */
+ fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+ fep->phy_speed--;
+ fep->phy_speed <<= 1;
+ writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+
+ fep->mii_bus = mdiobus_alloc();
+ if (fep->mii_bus == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ fep->mii_bus->name = "fec_enet_mii_bus";
+ fep->mii_bus->read = fec_enet_mdio_read;
+ fep->mii_bus->write = fec_enet_mdio_write;
+ fep->mii_bus->reset = fec_enet_mdio_reset;
+ snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ pdev->name, fep->dev_id + 1);
+ fep->mii_bus->priv = fep;
+ fep->mii_bus->parent = &pdev->dev;
+
+ fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!fep->mii_bus->irq) {
+ err = -ENOMEM;
+ goto err_out_free_mdiobus;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ fep->mii_bus->irq[i] = PHY_POLL;
+
+ if (mdiobus_register(fep->mii_bus))
+ goto err_out_free_mdio_irq;
+
+ mii_cnt++;
+
+ /* save fec0 mii_bus */
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+ fec0_mii_bus = fep->mii_bus;
+
+ return 0;
+
+err_out_free_mdio_irq:
+ kfree(fep->mii_bus->irq);
+err_out_free_mdiobus:
+ mdiobus_free(fep->mii_bus);
+err_out:
+ return err;
+}
+
+static void fec_enet_mii_remove(struct fec_enet_private *fep)
+{
+ if (--mii_cnt == 0) {
+ mdiobus_unregister(fep->mii_bus);
+ kfree(fep->mii_bus->irq);
+ mdiobus_free(fep->mii_bus);
+ }
+}
+
+static int fec_enet_get_settings(struct net_device *ndev,
+ struct ethtool_cmd *cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct phy_device *phydev = fep->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+static int fec_enet_set_settings(struct net_device *ndev,
+ struct ethtool_cmd *cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct phy_device *phydev = fep->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+static void fec_enet_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ strlcpy(info->driver, fep->pdev->dev.driver->name,
+ sizeof(info->driver));
+ strlcpy(info->version, "Revision: 1.0", sizeof(info->version));
+ strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
+}
+
+static int fec_enet_get_ts_info(struct net_device *ndev,
+ struct ethtool_ts_info *info)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ if (fep->bufdesc_ex) {
+
+ info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ if (fep->ptp_clock)
+ info->phc_index = ptp_clock_index(fep->ptp_clock);
+ else
+ info->phc_index = -1;
+
+ info->tx_types = (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+
+ info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_ALL);
+ return 0;
+ } else {
+ return ethtool_op_get_ts_info(ndev, info);
+ }
+}
+
+static void fec_enet_get_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *pause)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
+ pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
+ pause->rx_pause = pause->tx_pause;
+}
+
+static int fec_enet_set_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *pause)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ if (pause->tx_pause != pause->rx_pause) {
+ netdev_info(ndev,
+ "hardware only support enable/disable both tx and rx");
+ return -EINVAL;
+ }
+
+ fep->pause_flag = 0;
+
+ /* tx pause must be same as rx pause */
+ fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
+ fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
+
+ if (pause->rx_pause || pause->autoneg) {
+ fep->phy_dev->supported |= ADVERTISED_Pause;
+ fep->phy_dev->advertising |= ADVERTISED_Pause;
+ } else {
+ fep->phy_dev->supported &= ~ADVERTISED_Pause;
+ fep->phy_dev->advertising &= ~ADVERTISED_Pause;
+ }
+
+ if (pause->autoneg) {
+ if (netif_running(ndev))
+ fec_stop(ndev);
+ phy_start_aneg(fep->phy_dev);
+ }
+ if (netif_running(ndev))
+ fec_restart(ndev, 0);
+
+ return 0;
+}
+
+static const struct ethtool_ops fec_enet_ethtool_ops = {
+ .get_pauseparam = fec_enet_get_pauseparam,
+ .set_pauseparam = fec_enet_set_pauseparam,
+ .get_settings = fec_enet_get_settings,
+ .set_settings = fec_enet_set_settings,
+ .get_drvinfo = fec_enet_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_ts_info = fec_enet_get_ts_info,
+};
+
+static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct phy_device *phydev = fep->phy_dev;
+
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex)
+ return fec_ptp_ioctl(ndev, rq, cmd);
+
+ return phy_mii_ioctl(phydev, rq, cmd);
+}
+
+static void fec_enet_free_buffers(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int i;
+ struct sk_buff *skb;
+ struct bufdesc *bdp;
+
+ bdp = fep->rx_bd_base;
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = fep->rx_skbuff[i];
+
+ if (bdp->cbd_bufaddr)
+ dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+ if (skb)
+ dev_kfree_skb(skb);
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ }
+
+ bdp = fep->tx_bd_base;
+ for (i = 0; i < TX_RING_SIZE; i++)
+ kfree(fep->tx_bounce[i]);
+}
+
+static int fec_enet_alloc_buffers(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int i;
+ struct sk_buff *skb;
+ struct bufdesc *bdp;
+
+ bdp = fep->rx_bd_base;
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
+ if (!skb) {
+ fec_enet_free_buffers(ndev);
+ return -ENOMEM;
+ }
+ fep->rx_skbuff[i] = skb;
+
+ bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
+ FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
+
+ if (fep->bufdesc_ex) {
+ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+ ebdp->cbd_esc = BD_ENET_RX_INT;
+ }
+
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ }
+
+ /* Set the last buffer to wrap. */
+ bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ bdp = fep->tx_bd_base;
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
+
+ bdp->cbd_sc = 0;
+ bdp->cbd_bufaddr = 0;
+
+ if (fep->bufdesc_ex) {
+ struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+ ebdp->cbd_esc = BD_ENET_RX_INT;
+ }
+
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ }
+
+ /* Set the last buffer to wrap. */
+ bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ return 0;
+}
+
+static int
+fec_enet_open(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int ret;
+
+ napi_enable(&fep->napi);
+
+ /* I should reset the ring buffers here, but I don't yet know
+ * a simple way to do that.
+ */
+
+ ret = fec_enet_alloc_buffers(ndev);
+ if (ret)
+ return ret;
+
+ /* Probe and connect to PHY when open the interface */
+ ret = fec_enet_mii_probe(ndev);
+ if (ret) {
+ fec_enet_free_buffers(ndev);
+ return ret;
+ }
+ phy_start(fep->phy_dev);
+ netif_start_queue(ndev);
+ fep->opened = 1;
+ return 0;
+}
+
+static int
+fec_enet_close(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ /* Don't know what to do yet. */
+ fep->opened = 0;
+ netif_stop_queue(ndev);
+ fec_stop(ndev);
+
+ if (fep->phy_dev) {
+ phy_stop(fep->phy_dev);
+ phy_disconnect(fep->phy_dev);
+ }
+
+ fec_enet_free_buffers(ndev);
+
+ return 0;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering. Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not. I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+
+#define HASH_BITS 6 /* #bits in hash */
+#define CRC32_POLY 0xEDB88320
+
+static void set_multicast_list(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ unsigned int i, bit, data, crc, tmp;
+ unsigned char hash;
+
+ if (ndev->flags & IFF_PROMISC) {
+ tmp = readl(fep->hwp + FEC_R_CNTRL);
+ tmp |= 0x8;
+ writel(tmp, fep->hwp + FEC_R_CNTRL);
+ return;
+ }
+
+ tmp = readl(fep->hwp + FEC_R_CNTRL);
+ tmp &= ~0x8;
+ writel(tmp, fep->hwp + FEC_R_CNTRL);
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* Catch all multicast addresses, so set the
+ * filter to all 1's
+ */
+ writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+
+ return;
+ }
+
+ /* Clear filter and add the addresses in hash register
+ */
+ writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ /* calculate crc32 value of mac address */
+ crc = 0xffffffff;
+
+ for (i = 0; i < ndev->addr_len; i++) {
+ data = ha->addr[i];
+ for (bit = 0; bit < 8; bit++, data >>= 1) {
+ crc = (crc >> 1) ^
+ (((crc ^ data) & 1) ? CRC32_POLY : 0);
+ }
+ }
+
+ /* only upper 6 bits (HASH_BITS) are used
+ * which point to specific bit in he hash registers
+ */
+ hash = (crc >> (32 - HASH_BITS)) & 0x3f;
+
+ if (hash > 31) {
+ tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ tmp |= 1 << (hash - 32);
+ writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ } else {
+ tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+ tmp |= 1 << hash;
+ writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+ }
+ }
+}
+
+/* Set a MAC change in hardware. */
+static int
+fec_set_mac_address(struct net_device *ndev, void *p)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+
+ writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
+ (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
+ fep->hwp + FEC_ADDR_LOW);
+ writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
+ fep->hwp + FEC_ADDR_HIGH);
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * fec_poll_controller - FEC Poll controller function
+ * @dev: The FEC network adapter
+ *
+ * Polled functionality used by netconsole and others in non interrupt mode
+ *
+ */
+void fec_poll_controller(struct net_device *dev)
+{
+ int i;
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ if (fep->irq[i] > 0) {
+ disable_irq(fep->irq[i]);
+ fec_enet_interrupt(fep->irq[i], dev);
+ enable_irq(fep->irq[i]);
+ }
+ }
+}
+#endif
+
+static const struct net_device_ops fec_netdev_ops = {
+ .ndo_open = fec_enet_open,
+ .ndo_stop = fec_enet_close,
+ .ndo_start_xmit = fec_enet_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = fec_timeout,
+ .ndo_set_mac_address = fec_set_mac_address,
+ .ndo_do_ioctl = fec_enet_ioctl,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = fec_poll_controller,
+#endif
+};
+
+ /*
+ * XXX: We need to clean up on failure exits here.
+ *
+ */
+static int fec_enet_init(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ struct bufdesc *cbd_base;
+ struct bufdesc *bdp;
+ int i;
+
+ /* Allocate memory for buffer descriptors. */
+ cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+ GFP_KERNEL);
+ if (!cbd_base)
+ return -ENOMEM;
+
+ spin_lock_init(&fep->hw_lock);
+
+ fep->netdev = ndev;
+
+ /* Get the Ethernet address */
+ fec_get_mac(ndev);
+
+ /* Set receive and transmit descriptor base. */
+ fep->rx_bd_base = cbd_base;
+ if (fep->bufdesc_ex)
+ fep->tx_bd_base = (struct bufdesc *)
+ (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE);
+ else
+ fep->tx_bd_base = cbd_base + RX_RING_SIZE;
+
+ /* The FEC Ethernet specific entries in the device structure */
+ ndev->watchdog_timeo = TX_TIMEOUT;
+ ndev->netdev_ops = &fec_netdev_ops;
+ ndev->ethtool_ops = &fec_enet_ethtool_ops;
+
+ writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
+ netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
+
+ /* Initialize the receive buffer descriptors. */
+ bdp = fep->rx_bd_base;
+ for (i = 0; i < RX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page. */
+ bdp->cbd_sc = 0;
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ }
+
+ /* Set the last buffer to wrap */
+ bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ /* ...and the same for transmit */
+ bdp = fep->tx_bd_base;
+ fep->cur_tx = bdp;
+ for (i = 0; i < TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page. */
+ bdp->cbd_sc = 0;
+ bdp->cbd_bufaddr = 0;
+ bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
+ }
+
+ /* Set the last buffer to wrap */
+ bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex);
+ bdp->cbd_sc |= BD_SC_WRAP;
+ fep->dirty_tx = bdp;
+
+ fec_restart(ndev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ if (np)
+ return of_get_phy_mode(np);
+
+ return -ENODEV;
+}
+
+static void fec_reset_phy(struct platform_device *pdev)
+{
+ int err, phy_reset;
+ int msec = 1;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return;
+
+ of_property_read_u32(np, "phy-reset-duration", &msec);
+ /* A sane reset duration should not be longer than 1s */
+ if (msec > 1000)
+ msec = 1;
+
+ phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
+ if (!gpio_is_valid(phy_reset))
+ return;
+
+ err = devm_gpio_request_one(&pdev->dev, phy_reset,
+ GPIOF_OUT_INIT_LOW, "phy-reset");
+ if (err) {
+ dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
+ return;
+ }
+ msleep(msec);
+ gpio_set_value(phy_reset, 1);
+}
+#else /* CONFIG_OF */
+static int fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+
+static void fec_reset_phy(struct platform_device *pdev)
+{
+ /*
+ * In case of platform probe, the reset has been done
+ * by machine code.
+ */
+}
+#endif /* CONFIG_OF */
+
+static int
+fec_probe(struct platform_device *pdev)
+{
+ struct fec_enet_private *fep;
+ struct fec_platform_data *pdata;
+ struct net_device *ndev;
+ int i, irq, ret = 0;
+ struct resource *r;
+ const struct of_device_id *of_id;
+ static int dev_id;
+ struct pinctrl *pinctrl;
+ struct regulator *reg_phy;
+
+ of_id = of_match_device(fec_dt_ids, &pdev->dev);
+ if (of_id)
+ pdev->id_entry = of_id->data;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -ENXIO;
+
+ /* Init network device */
+ ndev = alloc_etherdev(sizeof(struct fec_enet_private));
+ if (!ndev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ /* setup board info structure */
+ fep = netdev_priv(ndev);
+
+ /* default enable pause frame auto negotiation */
+ if (pdev->id_entry &&
+ (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
+ fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
+
+ fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
+ fep->pdev = pdev;
+ fep->dev_id = dev_id++;
+
+ fep->bufdesc_ex = 0;
+
+ if (!fep->hwp) {
+ ret = -ENOMEM;
+ goto failed_ioremap;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ ret = fec_get_phy_mode_dt(pdev);
+ if (ret < 0) {
+ pdata = pdev->dev.platform_data;
+ if (pdata)
+ fep->phy_interface = pdata->phy;
+ else
+ fep->phy_interface = PHY_INTERFACE_MODE_MII;
+ } else {
+ fep->phy_interface = ret;
+ }
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(pinctrl);
+ goto failed_pin;
+ }
+
+ fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(fep->clk_ipg)) {
+ ret = PTR_ERR(fep->clk_ipg);
+ goto failed_clk;
+ }
+
+ fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(fep->clk_ahb)) {
+ ret = PTR_ERR(fep->clk_ahb);
+ goto failed_clk;
+ }
+
+ fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
+ fep->bufdesc_ex =
+ pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
+ if (IS_ERR(fep->clk_ptp)) {
+ ret = PTR_ERR(fep->clk_ptp);
+ fep->bufdesc_ex = 0;
+ }
+
+ clk_prepare_enable(fep->clk_ahb);
+ clk_prepare_enable(fep->clk_ipg);
+ if (!IS_ERR(fep->clk_ptp))
+ clk_prepare_enable(fep->clk_ptp);
+
+ reg_phy = devm_regulator_get(&pdev->dev, "phy");
+ if (!IS_ERR(reg_phy)) {
+ ret = regulator_enable(reg_phy);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable phy regulator: %d\n", ret);
+ goto failed_regulator;
+ }
+ }
+
+ fec_reset_phy(pdev);
+
+ if (fep->bufdesc_ex)
+ fec_ptp_init(ndev, pdev);
+
+ ret = fec_enet_init(ndev);
+ if (ret)
+ goto failed_init;
+
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0) {
+ if (i)
+ break;
+ ret = irq;
+ goto failed_irq;
+ }
+ ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
+ if (ret) {
+ while (--i >= 0) {
+ irq = platform_get_irq(pdev, i);
+ free_irq(irq, ndev);
+ }
+ goto failed_irq;
+ }
+ }
+
+ ret = fec_enet_mii_init(pdev);
+ if (ret)
+ goto failed_mii_init;
+
+ /* Carrier starts down, phylib will bring it up */
+ netif_carrier_off(ndev);
+
+ ret = register_netdev(ndev);
+ if (ret)
+ goto failed_register;
+
+ return 0;
+
+failed_register:
+ fec_enet_mii_remove(fep);
+failed_mii_init:
+failed_init:
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ free_irq(irq, ndev);
+ }
+failed_irq:
+failed_regulator:
+ clk_disable_unprepare(fep->clk_ahb);
+ clk_disable_unprepare(fep->clk_ipg);
+ if (!IS_ERR(fep->clk_ptp))
+ clk_disable_unprepare(fep->clk_ptp);
+failed_pin:
+failed_clk:
+failed_ioremap:
+ free_netdev(ndev);
+
+ return ret;
+}
+
+static int
+fec_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int i;
+
+ unregister_netdev(ndev);
+ fec_enet_mii_remove(fep);
+ del_timer_sync(&fep->time_keep);
+ clk_disable_unprepare(fep->clk_ptp);
+ if (fep->ptp_clock)
+ ptp_clock_unregister(fep->ptp_clock);
+ clk_disable_unprepare(fep->clk_ahb);
+ clk_disable_unprepare(fep->clk_ipg);
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ int irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ free_irq(irq, ndev);
+ }
+ free_netdev(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+fec_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ fec_stop(ndev);
+ netif_device_detach(ndev);
+ }
+ clk_disable_unprepare(fep->clk_ahb);
+ clk_disable_unprepare(fep->clk_ipg);
+
+ return 0;
+}
+
+static int
+fec_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ clk_prepare_enable(fep->clk_ahb);
+ clk_prepare_enable(fep->clk_ipg);
+ if (netif_running(ndev)) {
+ fec_restart(ndev, fep->full_duplex);
+ netif_device_attach(ndev);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops fec_pm_ops = {
+ .suspend = fec_suspend,
+ .resume = fec_resume,
+ .freeze = fec_suspend,
+ .thaw = fec_resume,
+ .poweroff = fec_suspend,
+ .restore = fec_resume,
+};
+#endif
+
+static struct platform_driver fec_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &fec_pm_ops,
+#endif
+ .of_match_table = fec_dt_ids,
+ },
+ .id_table = fec_devtype,
+ .probe = fec_probe,
+ .remove = fec_drv_remove,
+};
+
+module_platform_driver(fec_driver);
+
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] fec: Unify fec_ptp.c and fec.c
From: Uwe Kleine-König @ 2013-03-21 10:31 UTC (permalink / raw)
To: Frank Li; +Cc: Fabio Estevam, davem, Frank.Li, netdev, Fabio Estevam
In-Reply-To: <CAHrpEqSKS66EK_0L-e3=kDGAAYH3uBKzs=4pkmzvrJoVZpBgyQ@mail.gmail.com>
Hello Frank,
On Thu, Mar 21, 2013 at 06:15:28PM +0800, Frank Li wrote:
> 2013/3/21 Uwe Kleine-König <u.kleine-koenig@pengutronix.de>:
> > What is missing from your patch is dropping the EXPORT_SYMBOLs. Other
> > than that I like it better than using a single .c file.
>
> There are not EXPORT_SYMBOL in my fec.c? which version do you use?
As David said he took Fabio's patch for stable I assume they are in
net-next. I didn't check though.
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* Re: virtio: remove obsolete virtqueue_get_queue_index()
From: Cornelia Huck @ 2013-03-21 9:01 UTC (permalink / raw)
To: Rusty Russell; +Cc: mst, netdev
In-Reply-To: <878v5hp6hc.fsf@rustcorp.com.au>
On Thu, 21 Mar 2013 17:47:03 +1030
Rusty Russell <rusty@rustcorp.com.au> wrote:
> You can access it directly now, since 3.8: v3.7-rc1-13-g06ca287
> 'virtio: move queue_index and num_free fields into core struct
> virtqueue.'
>
> Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
>
> diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
> index 2029b6c..a7eddc7 100644
> --- a/drivers/s390/kvm/virtio_ccw.c
> +++ b/drivers/s390/kvm/virtio_ccw.c
> @@ -166,7 +166,7 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq)
>
> vcdev = to_vc_device(info->vq->vdev);
> ccw_device_get_schid(vcdev->cdev, &schid);
> - do_kvm_notify(schid, virtqueue_get_queue_index(vq));
> + do_kvm_notify(schid, vq->index);
> }
>
> static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
> @@ -188,7 +188,7 @@ static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw)
> unsigned long flags;
> unsigned long size;
> int ret;
> - unsigned int index = virtqueue_get_queue_index(vq);
> + unsigned int index = vq->index;
>
> /* Remove from our list. */
> spin_lock_irqsave(&vcdev->lock, flags);
> @@ -610,7 +610,7 @@ static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
> vq = NULL;
> spin_lock_irqsave(&vcdev->lock, flags);
> list_for_each_entry(info, &vcdev->virtqueues, node) {
> - if (virtqueue_get_queue_index(info->vq) == index) {
> + if (vq->index == index) {
Should be
if (info->vq->index == index) {
> vq = info->vq;
> break;
> }
^ permalink raw reply
* Re: [PATCH] fec: Unify fec_ptp.c and fec.c
From: Frank Li @ 2013-03-21 10:15 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Fabio Estevam, davem, Frank.Li, netdev, Fabio Estevam
In-Reply-To: <20130321100949.GO20530@pengutronix.de>
2013/3/21 Uwe Kleine-König <u.kleine-koenig@pengutronix.de>:
> Hello,
>
> On Thu, Mar 21, 2013 at 06:02:02PM +0800, Frank Li wrote:
>> I suggest use below patch to fix this problem
>>
>> --- a/drivers/net/ethernet/freescale/Makefile
>> +++ b/drivers/net/ethernet/freescale/Makefile
>> @@ -2,7 +2,8 @@
>> # Makefile for the Freescale network device drivers.
>> #
>>
>> -obj-$(CONFIG_FEC) += fec.o fec_ptp.o
>> +obj-$(CONFIG_FEC) += fec_driver.o
>> +fec_driver-objs := fec.o fec_ptp.o
>> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
>> ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
>> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
> I thought about suggesting something similar, too. I would have renamed
> fec.c to (say) fec_base.c and then kept the module name fec.ko. Not sure
> keeping the module name is critical.
Okay, how about rename fec.c to fec_main.c?
>
> What is missing from your patch is dropping the EXPORT_SYMBOLs. Other
> than that I like it better than using a single .c file.
There are not EXPORT_SYMBOL in my fec.c? which version do you use?
>
> Best regards
> Uwe
>
> --
> Pengutronix e.K. | Uwe Kleine-König |
> Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* Re: [PATCH] fec: Unify fec_ptp.c and fec.c
From: Uwe Kleine-König @ 2013-03-21 10:09 UTC (permalink / raw)
To: Frank Li; +Cc: Fabio Estevam, davem, Frank.Li, netdev, Fabio Estevam
In-Reply-To: <CAHrpEqTd2mVaTW6pwJaHqYreDo6rT_6s_o=Z=OtU71PZzZCSVg@mail.gmail.com>
Hello,
On Thu, Mar 21, 2013 at 06:02:02PM +0800, Frank Li wrote:
> I suggest use below patch to fix this problem
>
> --- a/drivers/net/ethernet/freescale/Makefile
> +++ b/drivers/net/ethernet/freescale/Makefile
> @@ -2,7 +2,8 @@
> # Makefile for the Freescale network device drivers.
> #
>
> -obj-$(CONFIG_FEC) += fec.o fec_ptp.o
> +obj-$(CONFIG_FEC) += fec_driver.o
> +fec_driver-objs := fec.o fec_ptp.o
> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
> ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
> obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
I thought about suggesting something similar, too. I would have renamed
fec.c to (say) fec_base.c and then kept the module name fec.ko. Not sure
keeping the module name is critical.
What is missing from your patch is dropping the EXPORT_SYMBOLs. Other
than that I like it better than using a single .c file.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* Re: [PATCH net-next] gro: relax ID check in inet_gro_receive()
From: Maciej Żenczykowski @ 2013-03-21 10:05 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Miller, netdev, Dmitry Kravkov, Eilon Greenstein,
Pravin B Shelar, H.K. Jerry Chu
In-Reply-To: <CANP3RGfWh1PVavxd-5gy1KvocgRiTNcm-bYZ=aMG6N6vPgyWnw@mail.gmail.com>
> I've never understood the usefulness of the 'IP ID increments by one'
check in the GRO TCP path anyway.
I think the above logic holds for non-TCP GRO in exactly the same way.
Furthermore, there's nothing like it in IPv6 (due to the outright lack
of an IPv6 ID field).
(And I have to learn to pay attention to those stupid 3 little
gray-on-white dots that hide the entire email I'm replying to)
^ permalink raw reply
* Re: [PATCH] fec: Unify fec_ptp.c and fec.c
From: Frank Li @ 2013-03-21 10:02 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Fabio Estevam, davem, Frank.Li, netdev, Fabio Estevam
In-Reply-To: <20130321062146.GL20530@pengutronix.de>
I suggest use below patch to fix this problem
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -2,7 +2,8 @@
# Makefile for the Freescale network device drivers.
#
-obj-$(CONFIG_FEC) += fec.o fec_ptp.o
+obj-$(CONFIG_FEC) += fec_driver.o
+fec_driver-objs := fec.o fec_ptp.o
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
2013/3/21 Uwe Kleine-König <u.kleine-koenig@pengutronix.de>:
> Hello Fabio,
>
> On Wed, Mar 20, 2013 at 05:11:07PM -0300, Fabio Estevam wrote:
>> Currently CONFIG_FEC=y generates two separate modules: fec_ptp.ko and fec.ko.
> CONFIG_FEC=m
>
>> These modules cannot be used independently, so it is better to unify them.
>>
>> Move fec_ptp.c code into fec.c and its definitions into fec.h.
>>
>> Suggested-by: Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
> s/Koe/Kö/
>
>> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
>
> You could note that you dropped the EXPORT_SYMBOLs. Other than that I
> assume it's a 1:1 change?
>
> Best regards
> Uwe
>
> --
> Pengutronix e.K. | Uwe Kleine-König |
> Industrial Linux Solutions | http://www.pengutronix.de/ |
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH net-next] gro: relax ID check in inet_gro_receive()
From: Maciej Żenczykowski @ 2013-03-21 9:59 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Miller, netdev, Dmitry Kravkov, Eilon Greenstein,
Pravin B Shelar, H.K. Jerry Chu
In-Reply-To: <1363841553.3333.47.camel@edumazet-glaptop>
Ack.
I've never understood the usefulness of the 'IP ID increments by one'
check in the GRO TCP path anyway.
TCP packets are DF.
AFAICT, the IP identifier field does not really serve a useful purpose
for non-fragment-ed/able packets.
The only possible exception I can think of has to do with
broken/non-spec-compliant stuff which fragments DF packets, or removes
the DF flag.
But that stuff shouldn't really work (and often doesn't) anyway.
Maciej Żenczykowski, Kernel Networking Developer @ Google
On Wed, Mar 20, 2013 at 9:52 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> From: Eric Dumazet <edumazet@google.com>
>
> GRE TSO support doesn't increment the ID in the inner IP header.
>
> Remove the ID check in inet_gro_receive() so that GRO can properly
> aggregate GRE encapsulated TCP packets, instead of forcing
> a flush for every packet.
>
> Testing the IP ID is not really needed anyway for proper GRO operation.
>
> We can use more readable (and faster) code to access tot_len and
> frag_off fields.
>
> Tested on a bnx2x setup after commit a848ade408b6b
> (bnx2x: add CSUM and TSO support for encapsulation protocols)
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Cc: Dmitry Kravkov <dmitry@broadcom.com>
> Cc: Eilon Greenstein <eilong@broadcom.com>
> Cc: Pravin B Shelar <pshelar@nicira.com>
> Cc: H.K. Jerry Chu <hkchu@google.com>
> Cc: Maciej Żenczykowski <maze@google.com>
> ---
> net/ipv4/af_inet.c | 11 ++++-------
> 1 file changed, 4 insertions(+), 7 deletions(-)
>
> diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
> index 9e5882c..302a47e 100644
> --- a/net/ipv4/af_inet.c
> +++ b/net/ipv4/af_inet.c
> @@ -1355,7 +1355,6 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
> const struct iphdr *iph;
> unsigned int hlen;
> unsigned int off;
> - unsigned int id;
> int flush = 1;
> int proto;
>
> @@ -1381,9 +1380,9 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
> if (unlikely(ip_fast_csum((u8 *)iph, 5)))
> goto out_unlock;
>
> - id = ntohl(*(__be32 *)&iph->id);
> - flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id ^ IP_DF));
> - id >>= 16;
> + flush = ntohs(iph->tot_len) ^ skb_gro_len(skb);
> +
> + flush |= (__force u16)iph->frag_off ^ htons(IP_DF);
>
> for (p = *head; p; p = p->next) {
> struct iphdr *iph2;
> @@ -1400,11 +1399,9 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
> continue;
> }
>
> - /* All fields must match except length and checksum. */
> NAPI_GRO_CB(p)->flush |=
> (iph->ttl ^ iph2->ttl) |
> - (iph->tos ^ iph2->tos) |
> - ((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id);
> + (iph->tos ^ iph2->tos);
>
> NAPI_GRO_CB(p)->flush |= flush;
> }
>
>
^ permalink raw reply
* Re: [PATCH] fec: Unify fec_ptp.c and fec.c
From: Richard Cochran @ 2013-03-21 9:46 UTC (permalink / raw)
To: Fabio Estevam; +Cc: davem, Frank.Li, u.kleine-koenig, netdev, Fabio Estevam
In-Reply-To: <1363810268-24398-1-git-send-email-festevam@gmail.com>
On Wed, Mar 20, 2013 at 05:11:07PM -0300, Fabio Estevam wrote:
> Move fec_ptp.c code into fec.c and its definitions into fec.h.
...
> +/**
> + * fec_ptp_start_cyclecounter - create the cycle counter from hw
> + * @ndev: network device
> + *
> + * this function initializes the timecounter and cyclecounter
> + * structures for use in generated a ns counter from the arbitrary
> + * fixed point cycles registers in the hardware.
> + */
> +void fec_ptp_start_cyclecounter(struct net_device *ndev)
> +{
This function and the other, previously global functions should now be
changed to 'static'.
Thanks,
Richard
^ permalink raw reply
* [PATCH 4/4] ss: Get netlink sockets info via sock-diag
From: Andrey Vagin @ 2013-03-21 9:33 UTC (permalink / raw)
To: netdev; +Cc: Pavel Emelyanov, Andrey Vagin, Stephen Hemminger
In-Reply-To: <1363858406-1489-1-git-send-email-avagin@openvz.org>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
include/linux/netlink_diag.h | 40 +++++++++++++++++++++++++++++++
misc/ss.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 include/linux/netlink_diag.h
diff --git a/include/linux/netlink_diag.h b/include/linux/netlink_diag.h
new file mode 100644
index 0000000..9328866
--- /dev/null
+++ b/include/linux/netlink_diag.h
@@ -0,0 +1,40 @@
+#ifndef __NETLINK_DIAG_H__
+#define __NETLINK_DIAG_H__
+
+#include <linux/types.h>
+
+struct netlink_diag_req {
+ __u8 sdiag_family;
+ __u8 sdiag_protocol;
+ __u16 pad;
+ __u32 ndiag_ino;
+ __u32 ndiag_show;
+ __u32 ndiag_cookie[2];
+};
+
+struct netlink_diag_msg {
+ __u8 ndiag_family;
+ __u8 ndiag_type;
+ __u8 ndiag_protocol;
+ __u8 ndiag_state;
+
+ __u32 ndiag_portid;
+ __u32 ndiag_dst_portid;
+ __u32 ndiag_dst_group;
+ __u32 ndiag_ino;
+ __u32 ndiag_cookie[2];
+};
+
+enum {
+ NETLINK_DIAG_MEMINFO,
+ NETLINK_DIAG_GROUPS,
+
+ NETLINK_DIAG_MAX,
+};
+
+#define NDIAG_PROTO_ALL ((__u8) ~0)
+
+#define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */
+#define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */
+
+#endif
diff --git a/misc/ss.c b/misc/ss.c
index 1af677a..49312f1 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -36,6 +36,7 @@
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <linux/unix_diag.h>
+#include <linux/netlink_diag.h>
int resolve_hosts = 0;
int resolve_services = 1;
@@ -2548,6 +2549,61 @@ static void netlink_show_one(struct filter *f,
return;
}
+static int netlink_show_sock(struct nlmsghdr *nlh, struct filter *f)
+{
+ struct netlink_diag_msg *r = NLMSG_DATA(nlh);
+ struct rtattr *tb[NETLINK_DIAG_MAX+1];
+ int rq = 0, wq = 0;
+ unsigned long groups = 0;
+
+ parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr*)(r+1),
+ nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+ if (tb[NETLINK_DIAG_GROUPS] && RTA_PAYLOAD(tb[NETLINK_DIAG_GROUPS]))
+ groups = *(unsigned long *) RTA_DATA(tb[NETLINK_DIAG_GROUPS]);
+
+ if (tb[NETLINK_DIAG_MEMINFO]) {
+ const __u32 *skmeminfo;
+ skmeminfo = RTA_DATA(tb[NETLINK_DIAG_MEMINFO]);
+
+ rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC];
+ wq = skmeminfo[SK_MEMINFO_WMEM_ALLOC];
+ }
+
+ netlink_show_one(f, r->ndiag_protocol, r->ndiag_portid, groups,
+ r->ndiag_state, r->ndiag_dst_portid, r->ndiag_dst_group,
+ rq, wq, 0, 0);
+
+ if (show_mem) {
+ printf("\t");
+ print_skmeminfo(tb, NETLINK_DIAG_MEMINFO);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int netlink_show_netlink(struct filter *f, FILE *dump_fp)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct netlink_diag_req r;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_seq = 123456;
+
+ req.r.sdiag_family = AF_NETLINK;
+ req.r.sdiag_protocol = NDIAG_PROTO_ALL;
+ req.r.ndiag_show = NDIAG_SHOW_GROUPS | NDIAG_SHOW_MEMINFO;
+
+ return handle_netlink_request(f, dump_fp, &req.nlh,
+ sizeof(req), netlink_show_sock);
+}
+
static int netlink_show(struct filter *f)
{
FILE *fp;
--
1.7.11.7
^ permalink raw reply related
* [PATCH 3/4] ss: show destination address for netlink sockets
From: Andrey Vagin @ 2013-03-21 9:33 UTC (permalink / raw)
To: netdev; +Cc: Pavel Emelyanov, Andrey Vagin, Stephen Hemminger
In-Reply-To: <1363858406-1489-1-git-send-email-avagin@openvz.org>
A netlink socket may be connected to a specific group.
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
misc/ss.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/misc/ss.c b/misc/ss.c
index 962f304..1af677a 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -2476,6 +2476,7 @@ static int packet_show(struct filter *f)
static void netlink_show_one(struct filter *f,
int prot, int pid, unsigned groups,
+ int state, int dst_pid, unsigned dst_group,
int rq, int wq,
unsigned long long sk, unsigned long long cb)
{
@@ -2530,8 +2531,14 @@ static void netlink_show_one(struct filter *f,
} else {
printf("%-*d ", serv_width, pid);
}
- printf("%*s*%-*s",
- addr_width, "", serv_width, "");
+
+ if (state == NETLINK_CONNECTED) {
+ printf("%*d:%-*d",
+ addr_width, dst_group, serv_width, dst_pid);
+ } else {
+ printf("%*s*%-*s",
+ addr_width, "", serv_width, "");
+ }
if (show_details) {
printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups);
@@ -2566,7 +2573,7 @@ static int netlink_show(struct filter *f)
&sk,
&prot, &pid, &groups, &rq, &wq, &cb, &rc);
- netlink_show_one(f, prot, pid, groups, rq, wq, sk, cb);
+ netlink_show_one(f, prot, pid, groups, TCP_CLOSE, 0, 0, rq, wq, sk, cb);
}
return 0;
--
1.7.11.7
^ permalink raw reply related
* [PATCH 2/4] ss: create a function to print info about netlink sockets
From: Andrey Vagin @ 2013-03-21 9:33 UTC (permalink / raw)
To: netdev; +Cc: Pavel Emelyanov, Andrey Vagin, Stephen Hemminger
In-Reply-To: <1363858406-1489-1-git-send-email-avagin@openvz.org>
It will be reused for printing info about netlink sockets, when
socket diag is used for retrieving information.
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
misc/ss.c | 130 ++++++++++++++++++++++++++++++++++----------------------------
1 file changed, 72 insertions(+), 58 deletions(-)
diff --git a/misc/ss.c b/misc/ss.c
index bde344a..962f304 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -2474,6 +2474,73 @@ static int packet_show(struct filter *f)
return 0;
}
+static void netlink_show_one(struct filter *f,
+ int prot, int pid, unsigned groups,
+ int rq, int wq,
+ unsigned long long sk, unsigned long long cb)
+{
+ if (f->f) {
+ struct tcpstat tst;
+ tst.local.family = AF_NETLINK;
+ tst.remote.family = AF_NETLINK;
+ tst.rport = -1;
+ tst.lport = pid;
+ tst.local.data[0] = prot;
+ tst.remote.data[0] = 0;
+ if (run_ssfilter(f->f, &tst) == 0)
+ return;
+ }
+
+ if (netid_width)
+ printf("%-*s ", netid_width, "nl");
+ if (state_width)
+ printf("%-*s ", state_width, "UNCONN");
+ printf("%-6d %-6d ", rq, wq);
+ if (resolve_services && prot == 0)
+ printf("%*s:", addr_width, "rtnl");
+ else if (resolve_services && prot == 3)
+ printf("%*s:", addr_width, "fw");
+ else if (resolve_services && prot == 4)
+ printf("%*s:", addr_width, "tcpdiag");
+ else
+ printf("%*d:", addr_width, prot);
+ if (pid == -1) {
+ printf("%-*s ", serv_width, "*");
+ } else if (resolve_services) {
+ int done = 0;
+ if (!pid) {
+ done = 1;
+ printf("%-*s ", serv_width, "kernel");
+ } else if (pid > 0) {
+ char procname[64];
+ FILE *fp;
+ sprintf(procname, "%s/%d/stat",
+ getenv("PROC_ROOT") ? : "/proc", pid);
+ if ((fp = fopen(procname, "r")) != NULL) {
+ if (fscanf(fp, "%*d (%[^)])", procname) == 1) {
+ sprintf(procname+strlen(procname), "/%d", pid);
+ printf("%-*s ", serv_width, procname);
+ done = 1;
+ }
+ fclose(fp);
+ }
+ }
+ if (!done)
+ printf("%-*d ", serv_width, pid);
+ } else {
+ printf("%-*d ", serv_width, pid);
+ }
+ printf("%*s*%-*s",
+ addr_width, "", serv_width, "");
+
+ if (show_details) {
+ printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups);
+ }
+ printf("\n");
+
+ return;
+}
+
static int netlink_show(struct filter *f)
{
FILE *fp;
@@ -2486,6 +2553,10 @@ static int netlink_show(struct filter *f)
if (!(f->states & (1<<SS_CLOSE)))
return 0;
+ if (!getenv("PROC_NET_NETLINK") && !getenv("PROC_ROOT") &&
+ netlink_show_netlink(f, NULL) == 0)
+ return 0;
+
if ((fp = net_netlink_open()) == NULL)
return -1;
fgets(buf, sizeof(buf)-1, fp);
@@ -2495,64 +2566,7 @@ static int netlink_show(struct filter *f)
&sk,
&prot, &pid, &groups, &rq, &wq, &cb, &rc);
- if (f->f) {
- struct tcpstat tst;
- tst.local.family = AF_NETLINK;
- tst.remote.family = AF_NETLINK;
- tst.rport = -1;
- tst.lport = pid;
- tst.local.data[0] = prot;
- tst.remote.data[0] = 0;
- if (run_ssfilter(f->f, &tst) == 0)
- continue;
- }
-
- if (netid_width)
- printf("%-*s ", netid_width, "nl");
- if (state_width)
- printf("%-*s ", state_width, "UNCONN");
- printf("%-6d %-6d ", rq, wq);
- if (resolve_services && prot == 0)
- printf("%*s:", addr_width, "rtnl");
- else if (resolve_services && prot == 3)
- printf("%*s:", addr_width, "fw");
- else if (resolve_services && prot == 4)
- printf("%*s:", addr_width, "tcpdiag");
- else
- printf("%*d:", addr_width, prot);
- if (pid == -1) {
- printf("%-*s ", serv_width, "*");
- } else if (resolve_services) {
- int done = 0;
- if (!pid) {
- done = 1;
- printf("%-*s ", serv_width, "kernel");
- } else if (pid > 0) {
- char procname[64];
- FILE *fp;
- sprintf(procname, "%s/%d/stat",
- getenv("PROC_ROOT") ? : "/proc", pid);
- if ((fp = fopen(procname, "r")) != NULL) {
- if (fscanf(fp, "%*d (%[^)])", procname) == 1) {
- sprintf(procname+strlen(procname), "/%d", pid);
- printf("%-*s ", serv_width, procname);
- done = 1;
- }
- fclose(fp);
- }
- }
- if (!done)
- printf("%-*d ", serv_width, pid);
- } else {
- printf("%-*d ", serv_width, pid);
- }
- printf("%*s*%-*s",
- addr_width, "", serv_width, "");
-
- if (show_details) {
- printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups);
- }
- printf("\n");
+ netlink_show_one(f, prot, pid, groups, rq, wq, sk, cb);
}
return 0;
--
1.7.11.7
^ permalink raw reply related
* [PATCH 1/4] ss: handle socket diag request in a separate function
From: Andrey Vagin @ 2013-03-21 9:33 UTC (permalink / raw)
To: netdev; +Cc: Pavel Emelyanov, Andrey Vagin, Stephen Hemminger
In-Reply-To: <1363858406-1489-1-git-send-email-avagin@openvz.org>
It will be reused to show netlink sockets
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
misc/ss.c | 49 +++++++++++++++++++++++++++++--------------------
1 file changed, 29 insertions(+), 20 deletions(-)
diff --git a/misc/ss.c b/misc/ss.c
index da9d32c..bde344a 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -2206,31 +2206,17 @@ static int unix_show_sock(struct nlmsghdr *nlh, struct filter *f)
return 0;
}
-static int unix_show_netlink(struct filter *f, FILE *dump_fp)
+static int handle_netlink_request(struct filter *f, FILE *dump_fp,
+ struct nlmsghdr *req, size_t size,
+ int (* show_one_sock)(struct nlmsghdr *nlh, struct filter *f))
{
int fd;
- struct {
- struct nlmsghdr nlh;
- struct unix_diag_req r;
- } req;
char buf[8192];
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
return -1;
- memset(&req, 0, sizeof(req));
- req.nlh.nlmsg_len = sizeof(req);
- req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
- req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
- req.nlh.nlmsg_seq = 123456;
-
- req.r.sdiag_family = AF_UNIX;
- req.r.udiag_states = f->states;
- req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
- if (show_mem)
- req.r.udiag_show |= UDIAG_SHOW_MEMINFO;
-
- if (send(fd, &req, sizeof(req), 0) < 0) {
+ if (send(fd, req, size, 0) < 0) {
close(fd);
return -1;
}
@@ -2275,13 +2261,13 @@ static int unix_show_netlink(struct filter *f, FILE *dump_fp)
} else {
errno = -err->error;
if (errno != ENOENT)
- fprintf(stderr, "UDIAG answers %d\n", errno);
+ fprintf(stderr, "DIAG answers %d\n", errno);
}
close(fd);
return -1;
}
if (!dump_fp) {
- err = unix_show_sock(h, f);
+ err = show_one_sock(h, f);
if (err < 0) {
close(fd);
return err;
@@ -2303,6 +2289,29 @@ close_it:
return 0;
}
+static int unix_show_netlink(struct filter *f, FILE *dump_fp)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct unix_diag_req r;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_seq = 123456;
+
+ req.r.sdiag_family = AF_UNIX;
+ req.r.udiag_states = f->states;
+ req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
+ if (show_mem)
+ req.r.udiag_show |= UDIAG_SHOW_MEMINFO;
+
+ return handle_netlink_request(f, dump_fp, &req.nlh,
+ sizeof(req), unix_show_sock);
+}
+
static int unix_show(struct filter *f)
{
FILE *fp;
--
1.7.11.7
^ permalink raw reply related
* [PATCH 0/4] ss: Get netlink sockets info via sock-diag
From: Andrey Vagin @ 2013-03-21 9:33 UTC (permalink / raw)
To: netdev; +Cc: Pavel Emelyanov, Andrey Vagin, Stephen Hemminger
In-Reply-To: <1363857669-19990-1-git-send-email-avagin@openvz.org>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Andrey Vagin (4):
ss: handle socket diag request in a separate function
ss: create a frunction to print info about netlink sockets
ss: show destination address for netlink sockets
ss: Get netlink sockets info via sock-diag
include/linux/netlink_diag.h | 40 ++++++++
misc/ss.c | 235 +++++++++++++++++++++++++++++--------------
2 files changed, 198 insertions(+), 77 deletions(-)
create mode 100644 include/linux/netlink_diag.h
--
1.7.11.7
^ permalink raw reply
* [PATCH 2/2] netlink: Diag core and basic socket info dumping
From: Andrey Vagin @ 2013-03-21 9:21 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: Andrey Vagin, David S. Miller, Eric Dumazet, Pavel Emelyanov,
Pablo Neira Ayuso, Eric W. Biederman, Gao feng
In-Reply-To: <1363857669-19990-1-git-send-email-avagin@openvz.org>
The netlink_diag can be built as a module, just like it's done in
unix sockets.
The core dumping message carries the basic info about netlink sockets:
family, type and protocol, portis, dst_group, dst_portid, state.
Groups can be received as an optional parameter NETLINK_DIAG_GROUPS.
Netlink sockets cab be filtered by protocols.
The socket inode number and cookie is reserved for future per-socket info
retrieving. The per-protocol filtering is also reserved for future by
requiring the sdiag_protocol to be zero.
The file /proc/net/netlink doesn't provide enough information for
dumping netlink sockets. It doesn't provide dst_group, dst_portid,
groups above 32.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Gao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
include/uapi/linux/netlink_diag.h | 40 ++++++++
net/Kconfig | 1 +
net/netlink/Kconfig | 10 ++
net/netlink/Makefile | 3 +
net/netlink/diag.c | 188 ++++++++++++++++++++++++++++++++++++++
5 files changed, 242 insertions(+)
create mode 100644 include/uapi/linux/netlink_diag.h
create mode 100644 net/netlink/Kconfig
create mode 100644 net/netlink/diag.c
diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h
new file mode 100644
index 0000000..9328866
--- /dev/null
+++ b/include/uapi/linux/netlink_diag.h
@@ -0,0 +1,40 @@
+#ifndef __NETLINK_DIAG_H__
+#define __NETLINK_DIAG_H__
+
+#include <linux/types.h>
+
+struct netlink_diag_req {
+ __u8 sdiag_family;
+ __u8 sdiag_protocol;
+ __u16 pad;
+ __u32 ndiag_ino;
+ __u32 ndiag_show;
+ __u32 ndiag_cookie[2];
+};
+
+struct netlink_diag_msg {
+ __u8 ndiag_family;
+ __u8 ndiag_type;
+ __u8 ndiag_protocol;
+ __u8 ndiag_state;
+
+ __u32 ndiag_portid;
+ __u32 ndiag_dst_portid;
+ __u32 ndiag_dst_group;
+ __u32 ndiag_ino;
+ __u32 ndiag_cookie[2];
+};
+
+enum {
+ NETLINK_DIAG_MEMINFO,
+ NETLINK_DIAG_GROUPS,
+
+ NETLINK_DIAG_MAX,
+};
+
+#define NDIAG_PROTO_ALL ((__u8) ~0)
+
+#define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */
+#define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */
+
+#endif
diff --git a/net/Kconfig b/net/Kconfig
index 6f676ab..2ddc904 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -217,6 +217,7 @@ source "net/dns_resolver/Kconfig"
source "net/batman-adv/Kconfig"
source "net/openvswitch/Kconfig"
source "net/vmw_vsock/Kconfig"
+source "net/netlink/Kconfig"
config RPS
boolean
diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig
new file mode 100644
index 0000000..5d6e8c0
--- /dev/null
+++ b/net/netlink/Kconfig
@@ -0,0 +1,10 @@
+#
+# Netlink Sockets
+#
+
+config NETLINK_DIAG
+ tristate "NETLINK: socket monitoring interface"
+ default n
+ ---help---
+ Support for NETLINK socket monitoring interface used by the ss tool.
+ If unsure, say Y.
diff --git a/net/netlink/Makefile b/net/netlink/Makefile
index bdd6ddf..e837917 100644
--- a/net/netlink/Makefile
+++ b/net/netlink/Makefile
@@ -3,3 +3,6 @@
#
obj-y := af_netlink.o genetlink.o
+
+obj-$(CONFIG_NETLINK_DIAG) += netlink_diag.o
+netlink_diag-y := diag.o
diff --git a/net/netlink/diag.c b/net/netlink/diag.c
new file mode 100644
index 0000000..5ffb1d1
--- /dev/null
+++ b/net/netlink/diag.c
@@ -0,0 +1,188 @@
+#include <linux/module.h>
+
+#include <net/sock.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/netlink_diag.h>
+
+#include "af_netlink.h"
+
+static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
+{
+ struct netlink_sock *nlk = nlk_sk(sk);
+
+ if (nlk->groups == NULL)
+ return 0;
+
+ return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
+ nlk->groups);
+}
+
+static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
+ struct netlink_diag_req *req,
+ u32 portid, u32 seq, u32 flags, int sk_ino)
+{
+ struct nlmsghdr *nlh;
+ struct netlink_diag_msg *rep;
+ struct netlink_sock *nlk = nlk_sk(sk);
+
+ nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
+ flags);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ rep = nlmsg_data(nlh);
+ rep->ndiag_family = AF_NETLINK;
+ rep->ndiag_type = sk->sk_type;
+ rep->ndiag_protocol = sk->sk_protocol;
+ rep->ndiag_state = sk->sk_state;
+
+ rep->ndiag_ino = sk_ino;
+ rep->ndiag_portid = nlk->portid;
+ rep->ndiag_dst_portid = nlk->dst_portid;
+ rep->ndiag_dst_group = nlk->dst_group;
+ sock_diag_save_cookie(sk, rep->ndiag_cookie);
+
+ if ((req->ndiag_show & NDIAG_SHOW_GROUPS) &&
+ sk_diag_dump_groups(sk, skb))
+ goto out_nlmsg_trim;
+
+ if ((req->ndiag_show & NDIAG_SHOW_MEMINFO) &&
+ sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
+ goto out_nlmsg_trim;
+
+ return nlmsg_end(skb, nlh);
+
+out_nlmsg_trim:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
+ int protocol, int s_num)
+{
+ struct netlink_table *tbl = &nl_table[protocol];
+ struct nl_portid_hash *hash = &tbl->hash;
+ struct net *net = sock_net(skb->sk);
+ struct netlink_diag_req *req;
+ struct sock *sk;
+ int ret = 0, num = 0, i;
+
+ req = nlmsg_data(cb->nlh);
+
+ for (i = 0; i <= hash->mask; i++) {
+ sk_for_each(sk, &hash->table[i]) {
+ if (!net_eq(sock_net(sk), net))
+ continue;
+ if (num < s_num) {
+ num++;
+ continue;
+ }
+
+ if (sk_diag_fill(sk, skb, req,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI,
+ sock_i_ino(sk)) < 0) {
+ ret = 1;
+ goto done;
+ }
+
+ num++;
+ }
+ }
+
+ sk_for_each_bound(sk, &tbl->mc_list) {
+ if (sk_hashed(sk))
+ continue;
+ if (!net_eq(sock_net(sk), net))
+ continue;
+ if (num < s_num) {
+ num++;
+ continue;
+ }
+
+ if (sk_diag_fill(sk, skb, req,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI,
+ sock_i_ino(sk)) < 0) {
+ ret = 1;
+ goto done;
+ }
+ num++;
+ }
+done:
+ cb->args[0] = num;
+ cb->args[1] = protocol;
+
+ return ret;
+}
+
+static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct netlink_diag_req *req;
+ int s_num = cb->args[0];
+
+ req = nlmsg_data(cb->nlh);
+
+ read_lock(&nl_table_lock);
+
+ if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
+ int i;
+
+ for (i = cb->args[1]; i < MAX_LINKS; i++) {
+ if (__netlink_diag_dump(skb, cb, i, s_num))
+ break;
+ s_num = 0;
+ }
+ } else {
+ if (req->sdiag_protocol >= MAX_LINKS) {
+ read_unlock(&nl_table_lock);
+ return -ENOENT;
+ }
+
+ __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num);
+ }
+
+ read_unlock(&nl_table_lock);
+
+ return skb->len;
+}
+
+static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
+{
+ int hdrlen = sizeof(struct netlink_diag_req);
+ struct net *net = sock_net(skb->sk);
+
+ if (nlmsg_len(h) < hdrlen)
+ return -EINVAL;
+
+ if (h->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = netlink_diag_dump,
+ };
+ return netlink_dump_start(net->diag_nlsk, skb, h, &c);
+ } else
+ return -EOPNOTSUPP;
+}
+
+static const struct sock_diag_handler netlink_diag_handler = {
+ .family = AF_NETLINK,
+ .dump = netlink_diag_handler_dump,
+};
+
+static int __init netlink_diag_init(void)
+{
+ return sock_diag_register(&netlink_diag_handler);
+}
+
+static void __exit netlink_diag_exit(void)
+{
+ sock_diag_unregister(&netlink_diag_handler);
+}
+
+module_init(netlink_diag_init);
+module_exit(netlink_diag_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);
--
1.8.1.4
^ permalink raw reply related
* [PATCH 1/2] net: prepare netlink code for netlink diag
From: Andrey Vagin @ 2013-03-21 9:21 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: Andrey Vagin, David S. Miller, Eric Dumazet, Pavel Emelyanov,
Pablo Neira Ayuso, Eric W. Biederman, Gao feng
In-Reply-To: <1363857669-19990-1-git-send-email-avagin@openvz.org>
Move a few declarations in a header.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Gao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
---
net/netlink/af_netlink.c | 59 ++++-----------------------------------------
net/netlink/af_netlink.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 54 deletions(-)
create mode 100644 net/netlink/af_netlink.h
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 1e3fd5b..a500ce2 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -61,28 +61,7 @@
#include <net/scm.h>
#include <net/netlink.h>
-#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
-#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
-
-struct netlink_sock {
- /* struct sock has to be the first member of netlink_sock */
- struct sock sk;
- u32 portid;
- u32 dst_portid;
- u32 dst_group;
- u32 flags;
- u32 subscriptions;
- u32 ngroups;
- unsigned long *groups;
- unsigned long state;
- wait_queue_head_t wait;
- struct netlink_callback *cb;
- struct mutex *cb_mutex;
- struct mutex cb_def_mutex;
- void (*netlink_rcv)(struct sk_buff *skb);
- void (*netlink_bind)(int group);
- struct module *module;
-};
+#include "af_netlink.h"
struct listeners {
struct rcu_head rcu;
@@ -94,48 +73,20 @@ struct listeners {
#define NETLINK_BROADCAST_SEND_ERROR 0x4
#define NETLINK_RECV_NO_ENOBUFS 0x8
-static inline struct netlink_sock *nlk_sk(struct sock *sk)
-{
- return container_of(sk, struct netlink_sock, sk);
-}
-
static inline int netlink_is_kernel(struct sock *sk)
{
return nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET;
}
-struct nl_portid_hash {
- struct hlist_head *table;
- unsigned long rehash_time;
-
- unsigned int mask;
- unsigned int shift;
-
- unsigned int entries;
- unsigned int max_shift;
-
- u32 rnd;
-};
-
-struct netlink_table {
- struct nl_portid_hash hash;
- struct hlist_head mc_list;
- struct listeners __rcu *listeners;
- unsigned int flags;
- unsigned int groups;
- struct mutex *cb_mutex;
- struct module *module;
- void (*bind)(int group);
- int registered;
-};
-
-static struct netlink_table *nl_table;
+struct netlink_table *nl_table;
+EXPORT_SYMBOL_GPL(nl_table);
static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);
static int netlink_dump(struct sock *sk);
-static DEFINE_RWLOCK(nl_table_lock);
+DEFINE_RWLOCK(nl_table_lock);
+EXPORT_SYMBOL_GPL(nl_table_lock);
static atomic_t nl_table_users = ATOMIC_INIT(0);
#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
new file mode 100644
index 0000000..d9acb2a
--- /dev/null
+++ b/net/netlink/af_netlink.h
@@ -0,0 +1,62 @@
+#ifndef _AF_NETLINK_H
+#define _AF_NETLINK_H
+
+#include <net/sock.h>
+
+#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
+#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
+
+struct netlink_sock {
+ /* struct sock has to be the first member of netlink_sock */
+ struct sock sk;
+ u32 portid;
+ u32 dst_portid;
+ u32 dst_group;
+ u32 flags;
+ u32 subscriptions;
+ u32 ngroups;
+ unsigned long *groups;
+ unsigned long state;
+ wait_queue_head_t wait;
+ struct netlink_callback *cb;
+ struct mutex *cb_mutex;
+ struct mutex cb_def_mutex;
+ void (*netlink_rcv)(struct sk_buff *skb);
+ void (*netlink_bind)(int group);
+ struct module *module;
+};
+
+static inline struct netlink_sock *nlk_sk(struct sock *sk)
+{
+ return container_of(sk, struct netlink_sock, sk);
+}
+
+struct nl_portid_hash {
+ struct hlist_head *table;
+ unsigned long rehash_time;
+
+ unsigned int mask;
+ unsigned int shift;
+
+ unsigned int entries;
+ unsigned int max_shift;
+
+ u32 rnd;
+};
+
+struct netlink_table {
+ struct nl_portid_hash hash;
+ struct hlist_head mc_list;
+ struct listeners __rcu *listeners;
+ unsigned int flags;
+ unsigned int groups;
+ struct mutex *cb_mutex;
+ struct module *module;
+ void (*bind)(int group);
+ int registered;
+};
+
+extern struct netlink_table *nl_table;
+extern rwlock_t nl_table_lock;
+
+#endif
--
1.8.1.4
^ permalink raw reply related
* [PATCH 0/2] netlink: implement socket diag for netlink sockets
From: Andrey Vagin @ 2013-03-21 9:21 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: Andrey Vagin, David S. Miller, Eric Dumazet, Pavel Emelyanov,
Pablo Neira Ayuso, Eric W. Biederman, Gao feng
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Gao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Andrey Vagin (2):
net: prepare netlink code for netlink diag
netlink: Diag core and basic socket info dumping
include/uapi/linux/netlink_diag.h | 40 ++++++++
net/Kconfig | 1 +
net/netlink/Kconfig | 10 ++
net/netlink/Makefile | 3 +
net/netlink/af_netlink.c | 59 +-----------
net/netlink/af_netlink.h | 62 +++++++++++++
net/netlink/diag.c | 186 ++++++++++++++++++++++++++++++++++++++
7 files changed, 307 insertions(+), 54 deletions(-)
create mode 100644 include/uapi/linux/netlink_diag.h
create mode 100644 net/netlink/Kconfig
create mode 100644 net/netlink/af_netlink.h
create mode 100644 net/netlink/diag.c
--
1.8.1.4
^ 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