Netdev List
 help / color / mirror / Atom feed
* [PATCH 0/4] Diet struct sk_buff a bit
From: Pavel Emelyanov @ 2007-10-19  8:59 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Netdev List, devel

The __u16 queue_mapping field only makes sense in the
CONFIG_NETDEVICES_MULTIQUEUE=y case only.

Despite this field may be set explicitly to some non-zero 
value (in net/core/pktgen.c), the exact value affects 
nothing in case the config option in question is N (mainly 
it is used in netif_subqueue_stopped(), which will always
return 0 in this case).

So cleanup the code a bit and move this field under the
config option.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>

^ permalink raw reply

* [PATCH 1/4] Use the skb_set_queue_mapping where appropriate
From: Pavel Emelyanov @ 2007-10-19  9:00 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Netdev List, devel
In-Reply-To: <471871E5.7060900@openvz.org>

There's already such a helper to initialize this field.
Use it.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>

---

diff --git a/net/core/dev.c b/net/core/dev.c
index 38b03da..8726589 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1661,7 +1661,7 @@ gso:
 		q = dev->qdisc;
 		if (q->enqueue) {
 			/* reset queue_mapping to zero */
-			skb->queue_mapping = 0;
+			skb_set_queue_mapping(skb, 0);
 			rc = q->enqueue(skb, q);
 			qdisc_run(dev);
 			spin_unlock(&dev->queue_lock);
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8cae60c..d4d9a36 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2603,8 +2603,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 	skb->network_header = skb->tail;
 	skb->transport_header = skb->network_header + sizeof(struct iphdr);
 	skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr));
-	skb->queue_mapping = pkt_dev->cur_queue_map;
-
+	skb_set_queue_mapping(skb, pkt_dev->cur_queue_map);
 	iph = ip_hdr(skb);
 	udph = udp_hdr(skb);
 
@@ -2941,8 +2940,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
 	skb->network_header = skb->tail;
 	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
 	skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr));
-	skb->queue_mapping = pkt_dev->cur_queue_map;
-
+	skb_set_queue_mapping(skb, pkt_dev->cur_queue_map);
 	iph = ipv6_hdr(skb);
 	udph = udp_hdr(skb);
 


^ permalink raw reply related

* [PATCH 2/4] Make and use skb_get_queue_mapping
From: Pavel Emelyanov @ 2007-10-19  9:02 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Netdev List, devel
In-Reply-To: <471871E5.7060900@openvz.org>

Make the helper for getting the field, symmetrical to
the "set" one. Return 0 if CONFIG_NETDEVICES_MULTIQUEUE=n

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>

---

diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index ed53aaa..ae41973 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -471,7 +471,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	len = max(skb->len, ETH_ZLEN);
-	queue = skb->queue_mapping;
+	queue = skb_get_queue_mapping(skb);
 #ifdef CONFIG_NETDEVICES_MULTIQUEUE
 	netif_stop_subqueue(dev, queue);
 #else
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index f93f22b..580ed1f 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1770,6 +1771,15 @@ static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping)
 #endif
 }
 
+static inline u16 skb_get_queue_mapping(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETDEVICES_MULTIQUEUE
+	return skb->queue_mapping;
+#else
+	return 0;
+#endif
+}
+
 static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from)
 {
 #ifdef CONFIG_NETDEVICES_MULTIQUEUE
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index be57cf3..421281d 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -266,7 +266,7 @@ static int teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
 	int busy;
 	int nores;
 	int len = skb->len;
-	int subq = skb->queue_mapping;
+	int subq = skb_get_queue_mapping(skb);
 	struct sk_buff *skb_res = NULL;
 
 	start = master->slaves;


^ permalink raw reply related

* [PATCH 3/4] Hide the queue_mapping field inside netif_subqueue_stopped
From: Pavel Emelyanov @ 2007-10-19  9:04 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Netdev List, devel
In-Reply-To: <471871E5.7060900@openvz.org>

Many places get the queue_mapping field from skb to pass it
to the netif_subqueue_stopped() which will be 0 in any case.

Make the helper that works with sk_buff

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>

---

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6f85db3..4a3f54e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -996,7 +996,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index)
  *
  * Check individual transmit queue of a device with multiple transmit queues.
  */
-static inline int netif_subqueue_stopped(const struct net_device *dev,
+static inline int __netif_subqueue_stopped(const struct net_device *dev,
 					 u16 queue_index)
 {
 #ifdef CONFIG_NETDEVICES_MULTIQUEUE
@@ -1007,6 +1007,11 @@ static inline int netif_subqueue_stopped(const struct net_device *dev,
 #endif
 }
 
+static inline int netif_subqueue_stopped(const struct net_device *dev,
+					 struct sk_buff *skb)
+{
+	return __netif_subqueue_stopped(dev, skb_get_queue_mapping(skb));
+}
 
 /**
  *	netif_wake_subqueue - allow sending packets on subqueue
diff --git a/net/core/dev.c b/net/core/dev.c
index 38b03da..8726589 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1553,7 +1553,7 @@ gso:
 			return rc;
 		}
 		if (unlikely((netif_queue_stopped(dev) ||
-			     netif_subqueue_stopped(dev, skb->queue_mapping)) &&
+			     netif_subqueue_stopped(dev, skb)) &&
 			     skb->next))
 			return NETDEV_TX_BUSY;
 	} while (skb->next);
@@ -1692,7 +1692,7 @@ gso:
 			HARD_TX_LOCK(dev, cpu);
 
 			if (!netif_queue_stopped(dev) &&
-			    !netif_subqueue_stopped(dev, skb->queue_mapping)) {
+			    !netif_subqueue_stopped(dev, skb)) {
 				rc = 0;
 				if (!dev_hard_start_xmit(skb, dev)) {
 					HARD_TX_UNLOCK(dev);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 95daba6..bf8d18f 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -67,7 +67,7 @@ static void queue_process(struct work_struct *work)
 		local_irq_save(flags);
 		netif_tx_lock(dev);
 		if ((netif_queue_stopped(dev) ||
-		     netif_subqueue_stopped(dev, skb->queue_mapping)) ||
+		     netif_subqueue_stopped(dev, skb)) ||
 		     dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) {
 			skb_queue_head(&npinfo->txq, skb);
 			netif_tx_unlock(dev);
@@ -269,7 +269,7 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
 		     tries > 0; --tries) {
 			if (netif_tx_trylock(dev)) {
 				if (!netif_queue_stopped(dev) &&
-				    !netif_subqueue_stopped(dev, skb->queue_mapping))
+				    !netif_subqueue_stopped(dev, skb))
 					status = dev->hard_start_xmit(skb, dev);
 				netif_tx_unlock(dev);
 
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8cae60c..d4d9a36 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3385,7 +3383,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
 
 	if ((netif_queue_stopped(odev) ||
 	     (pkt_dev->skb &&
-	      netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping))) ||
+	      netif_subqueue_stopped(odev, pkt_dev->skb))) ||
 	    need_resched()) {
 		idle_start = getCurUs();
 
@@ -3402,7 +3400,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
 		pkt_dev->idle_acc += getCurUs() - idle_start;
 
 		if (netif_queue_stopped(odev) ||
-		    netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
+		    netif_subqueue_stopped(odev, pkt_dev->skb)) {
 			pkt_dev->next_tx_us = getCurUs();	/* TODO */
 			pkt_dev->next_tx_ns = 0;
 			goto out;	/* Try the next interface */
@@ -3431,7 +3429,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
 
 	netif_tx_lock_bh(odev);
 	if (!netif_queue_stopped(odev) &&
-	    !netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
+	    !netif_subqueue_stopped(odev, pkt_dev->skb)) {
 
 		atomic_inc(&(pkt_dev->skb->users));
 	      retry_now:
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index be57cf3..421281d 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -284,7 +284,7 @@ restart:
 		if (slave->qdisc_sleeping != q)
 			continue;
 		if (netif_queue_stopped(slave) ||
-		    netif_subqueue_stopped(slave, subq) ||
+		    __netif_subqueue_stopped(slave, subq) ||
 		    !netif_running(slave)) {
 			busy = 1;
 			continue;
@@ -294,7 +294,7 @@ restart:
 		case 0:
 			if (netif_tx_trylock(slave)) {
 				if (!netif_queue_stopped(slave) &&
-				    !netif_subqueue_stopped(slave, subq) &&
+				    !__netif_subqueue_stopped(slave, subq) &&
 				    slave->hard_start_xmit(skb, slave) == 0) {
 					netif_tx_unlock(slave);
 					master->slaves = NEXT_SLAVE(q);


^ permalink raw reply related

* [PATCH 4/4] Cut off the queue_mapping field from sk_buff
From: Pavel Emelyanov @ 2007-10-19  9:05 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Netdev List, devel
In-Reply-To: <471871E5.7060900@openvz.org>

Just hide it behind the #ifdef, because nobody wants
it now.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>

---

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index f93f22b..580ed1f 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -301,8 +301,9 @@ struct sk_buff {
 #endif
 
 	int			iif;
+#ifdef CONFIG_NETDEVICES_MULTIQUEUE
 	__u16			queue_mapping;
-
+#endif
 #ifdef CONFIG_NET_SCHED
 	__u16			tc_index;	/* traffic control index */
 #ifdef CONFIG_NET_CLS_ACT

^ permalink raw reply related

* Re: 2.6.23-mm1 s390 driver problem
From: Andrew Morton @ 2007-10-19  9:27 UTC (permalink / raw)
  To: Cedric Le Goater
  Cc: cornelia.huck-tA70FqPdS9bQT0dZR+AlfA,
	Christian-YRGon0hq2jhQetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	heiko.carstens-tA70FqPdS9bQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Borntraeger,
	schwidefsky-tA70FqPdS9bQT0dZR+AlfA, Linux Containers,
	sam-uyr5N9Q2VtJg9hUCZPvPmw, Serge E. Hallyn
In-Reply-To: <471875E0.8040308-NmTC/0ZBporQT0dZR+AlfA@public.gmane.org>

On Fri, 19 Oct 2007 11:16:16 +0200 Cedric Le Goater <clg-NmTC/0ZBporQT0dZR+AlfA@public.gmane.org> wrote:

> Martin Schwidefsky wrote:
> > On Thu, 2007-10-18 at 15:31 -0500, Serge E. Hallyn wrote:
> >> Quoting Christian Borntraeger (borntraeger-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org):
> >>> Am Donnerstag, 18. Oktober 2007 schrieb Serge E. Hallyn:
> >>>> Sigh, well this turned out less informative than I'd liked.
> >>>> After bisecting 2.6.23 to 2.6.23-mm1, I found that
> >>>> git-s390.patch is the one breaking my s390 boot :(
> >>>> (Frown bc it's a conglomeration of patches0
> >>>>
> >>>> Symptom is:
> >>>> 	"Cannot open root device "dasdd2" or unknown-block(94,14)"
> >>>> even though dasdd2 appeared to be found earlier in the boot.  I also
> >>>> get
> >>> Can you post the full console output from IPL to the unsuccessful end?
> >> Yeah, sorry, appended below.
> >>
> >> I had thought that the line
> >> 	sysctl table check failed: /sunrpc/transports .7249.14 Missing strategy
> >> meant that the fix referenced in http://lkml.org/lkml/2007/10/11/48
> >> would fix it, but it appeared to have no effect.
> > 
> > This is the vmlinux.lds.S problem. The cleanup patch from Sam Ravnborg
> > moved the __initramfs_start and __initramfs_end symbols into
> > the .init.ramfs section. This is in itself not a problem, but it
> > surfaced a bug: there is no *(.init.initramfs), that needs to be
> > *(init.ramfs). I corrected this in the upstream patch but 2.6.23-mm1 has
> > the older one that still causes the "Cannot open root device". For
> > 2.6.23-mm1 use the patch below.
> > 
> 
> thanks martin, 
> 
> that helped going a little further in the boot process but we then have 
> a network issue when bringing the network interface up :

please cc netdev on network issues.

> Bringing up interface eth0:  ------------Ý cut here ¨------------ 
> Kernel BUG at 0000000000000002 Ýverbose debug info unavailable¨ 
> illegal operation: 0001 Ý#1¨ 
> Modules linked in: 
> CPU:    0    Not tainted 
> Process ip (pid: 1167, task: 0000000001d46038, ksp: 00000000025efb28) 
> Krnl PSW : 0704200180000000 0000000000000002 (0x2) 
>            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3 
> Krnl GPRS: 0000000000000000 0000000000000000 000000000241f600 0000000001c8d
>            00000000000086dd 0000000001eb6d70 0000000000000000 0000000001c8d
>            0000000001eb6d40 00000000003abc28 0000000001eb6d00 00000000025ef
>            000000000241f600 00000000003b6d18 00000000002b33d2 00000000025ef
> Krnl Code:>0000000000000002: 0000               unknown 
>            0000000000000004: 0000               unknown 
>            0000000000000006: 0000               unknown 
>            0000000000000008: 0000               unknown 
>            000000000000000a: 0000               unknown 
>            000000000000000c: 0000               unknown 
>            000000000000000e: 0000               unknown 
>            0000000000000010: 0000               unknown 
> Call Trace: 
> (Ý<00000000002b3352>¨ neigh_connected_output+0x76/0x138) 
>  Ý<0000000000325402>¨ ip6_output2+0x2da/0x470 
>  Ý<0000000000326ea6>¨ ip6_output+0x816/0x1064 
>  Ý<0000000000338e46>¨ __ndisc_send+0x416/0x6a8 
>  Ý<0000000000339330>¨ ndisc_send_rs+0x58/0x68 
>  Ý<000000000032cbf4>¨ addrconf_dad_completed+0xbc/0x100 
>  Ý<000000000032d2de>¨ addrconf_dad_start+0xa2/0x14c 
>  Ý<000000000032d408>¨ addrconf_add_linklocal+0x80/0xa8 
>  Ý<000000000032fa7e>¨ addrconf_notify+0x2de/0x8d4 
>  Ý<0000000000383990>¨ notifier_call_chain+0x5c/0x98 
>  Ý<0000000000063bca>¨ __raw_notifier_call_chain+0x26/0x34 
>  Ý<0000000000063c06>¨ raw_notifier_call_chain+0x2e/0x3c 
>  Ý<00000000002aa54c>¨ call_netdevice_notifiers+0x34/0x44 
>  Ý<00000000002ad1aa>¨ dev_open+0x9e/0xe0 
>  Ý<00000000002ad80a>¨ dev_change_flags+0x9e/0x1cc 
>  Ý<0000000000302c74>¨ devinet_ioctl+0x650/0x73c 
>  Ý<00000000003050ba>¨ inet_ioctl+0xde/0xf4 
>  Ý<000000000029a8d0>¨ sock_ioctl+0x1cc/0x2dc 
>  Ý<00000000000cb844>¨ do_ioctl+0xb8/0xcc 
>  Ý<00000000000cb8f2>¨ vfs_ioctl+0x9a/0x3ec 
>  Ý<00000000000cbc96>¨ sys_ioctl+0x52/0x7c 
>  Ý<0000000000022484>¨ sysc_noemu+0x10/0x16 
>  Ý<000002000010df12>¨ 0x2000010df12 

that's a network issue ;)

> 00000000002b3352 gives : include/linux/netdevice.h:819
> 

I have a feeling that we fixed this.  But there's no BUG at 2.6.23-mm1's
include/linux/netdevice.h:819.  How about setting
CONFIG_DEBUG_BUGVERBOSE=y?

^ permalink raw reply

* Re: [NET]: Fix possible dev_deactivate race condition
From: Herbert Xu @ 2007-10-19  9:29 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Linux Kernel Mailing List, David S. Miller, linuxppc-dev,
	Thomas Gleixner, netdev, akpm, Linus Torvalds, Ingo Molnar
In-Reply-To: <1192779319.27435.163.camel@twins>

On Fri, Oct 19, 2007 at 09:35:19AM +0200, Peter Zijlstra wrote:
>
> >         /* Wait for outstanding qdisc_run calls. */
> > -       while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state))
> > -               yield();
> > +       do {
> > +               while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state))
> > +                       yield();
> > +
> 
> Ouch!, is there really no sane locking alternative? Hashed waitqueues
> like for the page lock come to mind.

Well if we ever moved the transmission to full process context
then we'll gladly accept your patch :)

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* [PATCH] netdev: Interfamily support for IPSEC BEET
From: Joakim Koskela @ 2007-10-19 10:07 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Patrick McHardy

Hi,

Here's an updated version of the patch adding support for ipv4/ipv6
interfamily addressing for the ipsec BEET (Bound End-to-End Tunnel)
mode, as specified by the ietf draft found at:

http://www.ietf.org/internet-drafts/draft-nikander-esp-beet-mode-07.txt

The previous implementation required that both address pairs in the SA
were of the same family. This patch enables mixing ipv4 and ipv6
addresses. All combinations (4-4, 4-6, 6-4, 6-6) have been tested.

The generic interfamily fixes have been chopped off from this into
separate patches.

Signed-off-by: Joakim Koskela <jookos@gmail.com>
Signed-off-by: Herbert Xu     <herbert@gondor.apana.org.au>
Signed-off-by: Diego Beltrami <diego.beltrami@gmail.com>
Signed-off-by: Miika Komu     <miika@iki.fi>
---

diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 6b1a31a..b41e68d 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -288,8 +288,6 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
 		mtu += min_t(u32, blksize - 4, rem);
 		break;
 	case XFRM_MODE_BEET:
-		/* The worst case. */
-		mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
 		break;
 	}
 
@@ -397,8 +395,11 @@ static int esp_init_state(struct xfrm_state *x)
 	x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
 	if (x->props.mode == XFRM_MODE_TUNNEL)
 		x->props.header_len += sizeof(struct iphdr);
-	else if (x->props.mode == XFRM_MODE_BEET)
-		x->props.header_len += IPV4_BEET_PHMAXLEN;
+	else if (x->props.mode == XFRM_MODE_BEET) {
+		if (x->sel.family == AF_INET) {
+			x->props.header_len += IPV4_BEET_PHMAXLEN;
+		}
+	}
 	if (x->encap) {
 		struct xfrm_encap_tmpl *encap = x->encap;
 
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 2f14745..be58f8e 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -656,3 +656,6 @@ int ip_options_rcv_srr(struct sk_buff *skb)
 	}
 	return 0;
 }
+
+EXPORT_SYMBOL(ip_options_compile);
+
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 5e95c8a..b2e9308 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -94,7 +94,9 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
 		if (x->outer_mode->input(x, skb))
 			goto drop;
 
-		if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
+		if ((x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+		    (x->props.mode != XFRM_MODE_BEET ||
+		     x->sel.family != AF_INET)) {
 			decaps = 1;
 			break;
 		}
diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c
index e42e122..1346efc 100644
--- a/net/ipv4/xfrm4_mode_beet.c
+++ b/net/ipv4/xfrm4_mode_beet.c
@@ -6,6 +6,7 @@
  *                    Herbert Xu     <herbert@gondor.apana.org.au>
  *                    Abhinav Pathak <abhinav.pathak@hiit.fi>
  *                    Jeff Ahrenholz <ahrenholz@gmail.com>
+ *                    Joakim Koskela <jookos@gmail.com>
  */
 
 #include <linux/init.h>
@@ -16,6 +17,7 @@
 #include <net/dst.h>
 #include <net/ip.h>
 #include <net/xfrm.h>
+#include <net/inet_ecn.h>
 
 /* Add encapsulation header.
  *
@@ -23,88 +25,172 @@
  */
 static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct ip_beet_phdr *ph;
-	struct iphdr *iph, *top_iph;
-	int hdrlen, optlen;
-
-	iph = ip_hdr(skb);
-
-	hdrlen = 0;
-	optlen = iph->ihl * 4 - sizeof(*iph);
-	if (unlikely(optlen))
-		hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
-
-	skb_set_network_header(skb, IPV4_BEET_PHMAXLEN - x->props.header_len -
-				    hdrlen);
-	skb->mac_header = skb->network_header +
-			  offsetof(struct iphdr, protocol);
-	skb->transport_header = skb->network_header + sizeof(*iph);
-
-	ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen);
-
-	top_iph = ip_hdr(skb);
-	memmove(top_iph, iph, sizeof(*iph));
-	if (unlikely(optlen)) {
-		BUG_ON(optlen < 0);
-
-		ph->padlen = 4 - (optlen & 4);
-		ph->hdrlen = optlen / 8;
-		ph->nexthdr = top_iph->protocol;
-		if (ph->padlen)
-			memset(ph + 1, IPOPT_NOP, ph->padlen);
-
-		top_iph->protocol = IPPROTO_BEETPH;
-		top_iph->ihl = sizeof(struct iphdr) / 4;
-	}
+	struct dst_entry *dst = skb->dst;
+	struct iphdr *iphv4, *top_iphv4;
+	int hdrlen;
+
+	if (ip_hdr(skb)->version == 4) {
+		int optlen;
+		struct ip_beet_phdr *ph;
 
-	top_iph->saddr = x->props.saddr.a4;
-	top_iph->daddr = x->id.daddr.a4;
+		/* 4-4 */
+		iphv4 = ip_hdr(skb);
+
+		hdrlen = 0;
+		optlen = iphv4->ihl * 4 - sizeof(*iphv4);
+		if (unlikely(optlen))
+			hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
+
+		skb_set_network_header(skb, IPV4_BEET_PHMAXLEN - x->props.header_len -
+				       hdrlen);
+		skb->mac_header = skb->network_header +
+			offsetof(struct iphdr, protocol);
+		skb->transport_header = skb->network_header + sizeof(*iphv4);
+
+		ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iphv4) - hdrlen);
+
+		top_iphv4 = ip_hdr(skb);
+		memmove(top_iphv4, iphv4, sizeof(*iphv4));
+		if (unlikely(optlen)) {
+
+			BUG_ON(optlen < 0);
+
+			ph->padlen = 4 - (optlen & 4);
+			ph->hdrlen = optlen / 8;
+			ph->nexthdr = iphv4->protocol;
+			if (ph->padlen)
+				memset(ph + 1, IPOPT_NOP, ph->padlen);
+
+			top_iphv4->protocol = IPPROTO_BEETPH;
+			top_iphv4->ihl = 5;
+		}
+
+		top_iphv4->saddr = x->props.saddr.a4;
+		top_iphv4->daddr = x->id.daddr.a4;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+	} else if (ip_hdr(skb)->version == 6) {
+		int delta = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+		u8 protocol = ipv6_hdr(skb)->nexthdr;
+
+		/* Inner = 6, Outer = 4 : changing the external IP hdr
+		 * to the outer addresses
+		 */
+		skb_set_network_header(skb, delta - x->props.header_len);
+		skb->transport_header = skb->network_header + sizeof(*iphv4);
+		skb->mac_header = skb->network_header +
+			offsetof(struct iphdr, protocol);
+		skb_pull(skb, sizeof(struct ipv6hdr));
+
+		top_iphv4 = ip_hdr(skb);
+		top_iphv4->ihl = 5;
+		top_iphv4->version = 4;
+		top_iphv4->id = 0;
+		top_iphv4->frag_off = htons(IP_DF);
+		top_iphv4->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
+		top_iphv4->protocol = protocol;
+
+		top_iphv4->saddr = x->props.saddr.a4;
+		top_iphv4->daddr = x->id.daddr.a4;
+		IPCB(skb)->flags = 0;
+#endif
+	}
 
+	skb->protocol = htons(ETH_P_IP);
+	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
 	return 0;
+
 }
 
 static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	struct iphdr *iph = ip_hdr(skb);
+	int hops = iph->ttl;
 	int phlen = 0;
 	int optlen = 0;
-	u8 ph_nexthdr = 0;
+	__u8 protocol = 0;
 	int err = -EINVAL;
 
-	if (unlikely(iph->protocol == IPPROTO_BEETPH)) {
-		struct ip_beet_phdr *ph;
+	protocol = iph->protocol;
+	if (x->sel.family == AF_INET) {
+		if (unlikely(protocol == IPPROTO_BEETPH)) {
+			struct ip_beet_phdr *ph;
 
-		if (!pskb_may_pull(skb, sizeof(*ph)))
-			goto out;
-		ph = (struct ip_beet_phdr *)(ipip_hdr(skb) + 1);
+			if (!pskb_may_pull(skb, sizeof(*ph)))
+				goto out;
 
-		phlen = sizeof(*ph) + ph->padlen;
-		optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen);
-		if (optlen < 0 || optlen & 3 || optlen > 250)
-			goto out;
+			ph = (struct ip_beet_phdr *)(ipip_hdr(skb) + 1);
+
+			phlen = sizeof(*ph) + ph->padlen;
+			optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen);
+			if (optlen < 0 || optlen & 3 || optlen > 250)
+				goto out;
+
+			if (!pskb_may_pull(skb, phlen + optlen))
+				goto out;
+
+			protocol = ph->nexthdr;
+		}
 
-		if (!pskb_may_pull(skb, phlen + optlen))
+		skb_push(skb, sizeof(*iph) - phlen);
+		skb_reset_network_header(skb);
+		memmove(skb_network_header(skb), iph, sizeof(*iph));
+
+		iph = ip_hdr(skb);
+		iph->ihl = (sizeof(*iph) + optlen) / 4;
+		iph->tot_len = htons(skb->len);
+		iph->daddr = x->sel.daddr.a4;
+		iph->saddr = x->sel.saddr.a4;
+		iph->protocol = protocol;
+
+		ip_send_check(iph);
+		if (ip_options_compile(NULL, skb))
 			goto out;
-		skb->len -= phlen + optlen;
 
-		ph_nexthdr = ph->nexthdr;
+		dst_release(skb->dst);
+		skb->dst = NULL;
+
+		pskb_pull(skb, sizeof(*iph) + optlen);
+		skb_reset_transport_header(skb);
+		err = 0;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+	} else if (x->sel.family == AF_INET6) {
+		int delta = sizeof(struct ipv6hdr) + skb->mac_len;
+		struct ipv6hdr *ip6h;
+		const unsigned char *old_mac;
+
+		/* Here, the inner family is 6, therefore I have to
+		 * substitute the IPhdr by enlarging it.
+		 */
+		if (skb_headroom(skb) < delta) {
+			if (pskb_expand_head(skb, delta, 0, GFP_ATOMIC))
+				goto out;
+		}
+
+		skb_push(skb, sizeof(*ip6h));
+
+		/* mac might have references to ipv4. shouldn't matter */
+		old_mac = skb_mac_header(skb);
+		skb_set_mac_header(skb, -skb->mac_len);
+		memmove(skb_mac_header(skb), old_mac, skb->mac_len);
+
+		skb_reset_network_header(skb);
+		skb_reset_transport_header(skb);
+
+		ip6h = ipv6_hdr(skb);
+		memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
+		ip6h->version = 6;
+		ip6h->priority = 0;
+		ip6h->nexthdr = protocol;
+		ip6h->hop_limit = hops;
+		ip6h->payload_len = htons(skb->len - sizeof(*ip6h));
+		ipv6_addr_copy(&ip6h->daddr,
+			       (struct in6_addr *)&x->sel.daddr.a6);
+		ipv6_addr_copy(&ip6h->saddr,
+			       (struct in6_addr *)&x->sel.saddr.a6);
+		skb->protocol = htons(ETH_P_IPV6);
+		err = 0;
+#endif
 	}
-
-	skb_set_network_header(skb, phlen - sizeof(*iph));
-	memmove(skb_network_header(skb), iph, sizeof(*iph));
-	skb_set_transport_header(skb, phlen + optlen);
-	skb->data = skb_transport_header(skb);
-
-	iph = ip_hdr(skb);
-	iph->ihl = (sizeof(*iph) + optlen) / 4;
-	iph->tot_len = htons(skb->len + iph->ihl * 4);
-	iph->daddr = x->sel.daddr.a4;
-	iph->saddr = x->sel.saddr.a4;
-	if (ph_nexthdr)
-		iph->protocol = ph_nexthdr;
-	iph->check = 0;
-	iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
-	err = 0;
 out:
 	return err;
 }
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index b071543..f6d5d3c 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -244,7 +244,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
 	rem = mtu & (align - 1);
 	mtu &= ~(align - 1);
 
-	if (x->props.mode != XFRM_MODE_TUNNEL) {
+	if (x->props.mode != XFRM_MODE_TUNNEL ||
+	    x->props.mode != XFRM_MODE_BEET) {
 		u32 padsize = ((blksize - 1) & 7) + 1;
 		mtu -= blksize - padsize;
 		mtu += min_t(u32, blksize - padsize, rem);
@@ -356,6 +357,11 @@ static int esp6_init_state(struct xfrm_state *x)
 	x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
 	switch (x->props.mode) {
 	case XFRM_MODE_BEET:
+ 		if (x->sel.family == AF_INET) {
+ 			x->props.header_len += IPV4_BEET_PHMAXLEN +
+ 				(sizeof(struct ipv6hdr) - sizeof(struct iphdr));
+ 		}
+		break;
 	case XFRM_MODE_TRANSPORT:
 		break;
 	case XFRM_MODE_TUNNEL:
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 5157837..55f2130 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -71,7 +71,9 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
 		if (x->outer_mode->input(x, skb))
 			goto drop;
 
-		if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
+		if ((x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+ 		    (x->props.mode != XFRM_MODE_BEET ||
+ 		     x->sel.family != AF_INET6)) {
 			decaps = 1;
 			break;
 		}
diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c
index 2bfb4f0..377e921 100644
--- a/net/ipv6/xfrm6_mode_beet.c
+++ b/net/ipv6/xfrm6_mode_beet.c
@@ -6,6 +6,7 @@
  *                    Herbert Xu     <herbert@gondor.apana.org.au>
  *                    Abhinav Pathak <abhinav.pathak@hiit.fi>
  *                    Jeff Ahrenholz <ahrenholz@gmail.com>
+ *                    Joakim Koskela <jookos@gmail.com>
  */
 
 #include <linux/init.h>
@@ -17,6 +18,7 @@
 #include <net/dst.h>
 #include <net/inet_ecn.h>
 #include <net/ipv6.h>
+#include <net/ip.h>
 #include <net/xfrm.h>
 
 /* Add encapsulation header.
@@ -25,25 +27,81 @@
  */
 static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb)
 {
-	struct ipv6hdr *iph, *top_iph;
-	u8 *prevhdr;
-	int hdr_len;
-
-	iph = ipv6_hdr(skb);
-
-	hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
-
-	skb_set_mac_header(skb, (prevhdr - x->props.header_len) - skb->data);
-	skb_set_network_header(skb, -x->props.header_len);
-	skb->transport_header = skb->network_header + hdr_len;
-	__skb_pull(skb, hdr_len);
-
-	top_iph = ipv6_hdr(skb);
-	memmove(top_iph, iph, hdr_len);
-
-	ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
-	ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
-
+	struct dst_entry *dst = skb->dst;
+	struct iphdr *iphv4;
+	struct ipv6hdr *iphv6, *top_iphv6;
+	int hdrlen;
+
+	if (ip_hdr(skb)->version == 6) {
+		u8 *prevhdr;
+
+		iphv6 = ipv6_hdr(skb);
+
+		hdrlen = ip6_find_1stfragopt(skb, &prevhdr);
+
+		skb_set_mac_header(skb, (prevhdr - x->props.header_len) - skb->data);
+		skb_set_network_header(skb, -x->props.header_len);
+		skb->transport_header = skb->network_header + hdrlen;
+		__skb_pull(skb, hdrlen);
+
+		top_iphv6 = ipv6_hdr(skb);
+		memmove(top_iphv6, iphv6, hdrlen);
+
+		ipv6_addr_copy(&top_iphv6->saddr, (struct in6_addr *)&x->props.saddr);
+		ipv6_addr_copy(&top_iphv6->daddr, (struct in6_addr *)&x->id.daddr);
+	} else if (ip_hdr(skb)->version == 4) {
+		int flags, optlen, dsfield;
+		struct ip_beet_phdr *ph;
+		u8 protocol;
+
+		iphv4 = ip_hdr(skb);
+		hdrlen = 0;
+		optlen = iphv4->ihl * 4 - sizeof(*iphv4);
+		if (unlikely(optlen))
+			hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
+
+		skb_set_network_header(skb, IPV4_BEET_PHMAXLEN - x->props.header_len -
+				       hdrlen);
+		skb->mac_header = skb->network_header + offsetof(struct ipv6hdr, nexthdr);
+		skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
+
+		ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iphv4) - hdrlen);
+		if (unlikely(optlen)) {
+
+			BUG_ON(optlen < 0);
+
+			ph->padlen = 4 - (optlen & 4);
+			ph->hdrlen = optlen / 8;
+			ph->nexthdr = iphv4->protocol;
+			if (ph->padlen)
+				memset(ph + 1, IPOPT_NOP, ph->padlen);
+
+			protocol = IPPROTO_BEETPH;
+		} else
+			protocol = iphv4->protocol;
+
+		top_iphv6 = ipv6_hdr(skb);
+
+		/* DS disclosed */
+		top_iphv6->version = 6;
+		top_iphv6->priority = 0;
+		top_iphv6->flow_lbl[0] = 0;
+		top_iphv6->flow_lbl[1] = 0;
+		top_iphv6->flow_lbl[2] = 0;
+		dsfield = ipv6_get_dsfield(top_iphv6);
+		dsfield = INET_ECN_encapsulate(dsfield, dsfield);
+		flags = x->props.flags;
+		if (flags & XFRM_STATE_NOECN)
+			dsfield &= ~INET_ECN_MASK;
+		ipv6_change_dsfield(top_iphv6, 0, dsfield);
+
+		top_iphv6->nexthdr = protocol;
+		top_iphv6->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
+		ipv6_addr_copy(&top_iphv6->saddr, (struct in6_addr *)&x->props.saddr);
+		ipv6_addr_copy(&top_iphv6->daddr, (struct in6_addr *)&x->id.daddr);
+	}
+
+	skb->protocol = htons(ETH_P_IPV6);
 	return 0;
 }
 
@@ -51,26 +109,82 @@ static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	struct ipv6hdr *ip6h;
 	const unsigned char *old_mac;
-	int size = sizeof(struct ipv6hdr);
+	int size = ((x->sel.family == AF_INET) ?
+		    sizeof(struct iphdr) :
+		    sizeof(struct ipv6hdr));
 	int err = -EINVAL;
 
-	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
-		goto out;
-
-	skb_push(skb, size);
-	memmove(skb->data, skb_network_header(skb), size);
-	skb_reset_network_header(skb);
-
-	old_mac = skb_mac_header(skb);
-	skb_set_mac_header(skb, -skb->mac_len);
-	memmove(skb_mac_header(skb), old_mac, skb->mac_len);
-
-	ip6h = ipv6_hdr(skb);
-	ip6h->payload_len = htons(skb->len - size);
-	ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6);
-	ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6);
-	err = 0;
-out:
+	if (x->sel.family == AF_INET6) {
+		if (skb_cloned(skb) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			goto out;
+
+		ip6h = ipv6_hdr(skb);
+		skb_set_network_header(skb, -size);
+		memmove(skb_network_header(skb), ip6h, size);
+
+		ip6h = ipv6_hdr(skb);
+		ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6);
+		ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6);
+
+		ip6h->payload_len = htons(skb->len);
+		skb_reset_transport_header(skb);
+		skb->protocol = htons(ETH_P_IPV6);
+		err = 0;
+	} else {
+		__u8 proto = ipv6_hdr(skb)->nexthdr;
+		__u8 hops = ipv6_hdr(skb)->hop_limit;
+		int phlen = 0;
+		int optlen = 0;
+		struct iphdr *iph;
+
+		/* Inner = IPv4, therefore the IPhdr must be shrunk */
+		/* Inner = 4, Outer = 6 */
+		if (unlikely(proto == IPPROTO_BEETPH)) {
+			struct ip_beet_phdr *ph = (struct ip_beet_phdr *)
+				(struct ip_beet_phdr *)skb->data;
+
+			if (!pskb_may_pull(skb, sizeof(*ph)))
+				goto out;
+
+			phlen = sizeof(*ph) + ph->padlen;
+			optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen);
+			if (optlen < 0 || optlen & 3 || optlen > 250)
+				goto out;
+
+			if (!pskb_may_pull(skb, phlen + optlen))
+				goto out;
+			proto = ph->nexthdr;
+		}
+
+		if (skb_cloned(skb) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			goto out;
+
+		skb_push(skb, size - phlen);
+		skb_reset_network_header(skb);
+		skb_reset_transport_header(skb);
+
+		old_mac = skb_mac_header(skb);
+		skb_set_mac_header(skb, -skb->mac_len);
+		memmove(skb_mac_header(skb), old_mac, skb->mac_len);
+
+		iph = ip_hdr(skb);
+		iph->ihl = (sizeof(*iph) + optlen) / 4;
+		iph->version = 4;
+		iph->tos = 0;
+		iph->id = 0;
+		iph->frag_off = 0;
+		iph->ttl = hops;
+		iph->protocol = proto;
+		iph->daddr = x->sel.daddr.a4;
+		iph->saddr = x->sel.saddr.a4;
+		iph->tot_len = htons(skb->len);
+		ip_send_check(iph);
+		skb->protocol = htons(ETH_P_IP);
+		err = 0;
+	}
+ out:
 	return err;
 }
 
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
index b392bee..4e4c5a9 100644
--- a/net/ipv6/xfrm6_state.c
+++ b/net/ipv6/xfrm6_state.c
@@ -99,6 +99,17 @@ __xfrm6_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n)
 			src[i] = NULL;
 		}
 	}
+	if (j == n)
+		goto end;
+
+	/* Rule 5: select IPsec BEET */
+	for (i = 0; i < n; i++) {
+		if (src[i] &&
+		    src[i]->props.mode == XFRM_MODE_BEET) {
+			dst[j++] = src[i];
+			src[i] = NULL;
+		}
+	}
 	if (likely(j == n))
 		goto end;
 

^ permalink raw reply related

* Re: 2.6.23-mm1 s390 driver problem
From: Cedric Le Goater @ 2007-10-19 11:06 UTC (permalink / raw)
  To: schwidefsky-tA70FqPdS9bQT0dZR+AlfA
  Cc: Netdev, heiko.carstens-tA70FqPdS9bQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Christian Borntraeger,
	cornelia.huck-tA70FqPdS9bQT0dZR+AlfA, Linux Containers,
	sam-uyr5N9Q2VtJg9hUCZPvPmw, Serge E. Hallyn
In-Reply-To: <1192785653.25150.9.camel@localhost>

Martin Schwidefsky wrote:
> On Fri, 2007-10-19 at 11:16 +0200, Cedric Le Goater wrote:
>>> This is the vmlinux.lds.S problem. The cleanup patch from Sam Ravnborg
>>> moved the __initramfs_start and __initramfs_end symbols into
>>> the .init.ramfs section. This is in itself not a problem, but it
>>> surfaced a bug: there is no *(.init.initramfs), that needs to be
>>> *(init.ramfs). I corrected this in the upstream patch but 2.6.23-mm1 has
>>> the older one that still causes the "Cannot open root device". For
>>> 2.6.23-mm1 use the patch below.
>>>
>> thanks martin, 
>>
>> that helped going a little further in the boot process but we then have 
>> a network issue when bringing the network interface up :
> 
> See http://marc.info/?l=linux-kernel&m=119270398931208&w=2

hmm, that doesn't fix the oops.

/me looking.

Thanks,

C.

^ permalink raw reply

* Re: [PATCH 5/8] [MV643XX_ETH] Remove SHARED_REGS register address bias
From: Lennert Buytenhek @ 2007-10-19 11:09 UTC (permalink / raw)
  To: Roland Dreier
  Cc: Dale Farnsworth, Nicolas Pitre, Tzachi Perelstein, Manas Saksena,
	netdev
In-Reply-To: <ada7iljepfx.fsf@cisco.com>

On Thu, Oct 18, 2007 at 08:46:58PM -0700, Roland Dreier wrote:

>  > +static void __iomem *mv643xx_eth_base;
> 
>  > +	return readl(((void __iomem *)mv643xx_eth_base) + offset);
> 
> Given the declaration of mv643xx_eth_base as void __iomem * already, I
> don't understand why you need the cast to the same type here (and
> elsewhere in the driver).

Makes sense, fixed.

^ permalink raw reply

* Re: [PATCH 2/8] [MV643XX_ETH] Move ethernet register definitions into private header
From: Lennert Buytenhek @ 2007-10-19 11:09 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Dale Farnsworth, Nicolas Pitre, Tzachi Perelstein, Manas Saksena,
	netdev
In-Reply-To: <20071019083048.GA16865@infradead.org>

On Fri, Oct 19, 2007 at 09:30:48AM +0100, Christoph Hellwig wrote:

> > Move the mv643xx's ethernet-related register definitions from
> > include/linux/mv643xx.h into drivers/net/mv643xx_eth.h, since
> > they aren't of any use outside the ethernet driver.
> > 
> > Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
> > Acked-by: Tzachi Perelstein <tzachi@marvell.com>
> > 
> > Index: linux-2.6/drivers/net/mv643xx_eth.h
> > ===================================================================
> > --- linux-2.6.orig/drivers/net/mv643xx_eth.h
> > +++ linux-2.6/drivers/net/mv643xx_eth.h
> > @@ -7,7 +7,7 @@
> >  #include <linux/workqueue.h>
> >  #include <linux/mii.h>
> >  
> > -#include <linux/mv643xx.h>
> > +#include <linux/mv643xx_eth.h>
> 
> Isn't it a little too confusing to have two headers with the same name,
> one in drivers/net and one in include/linux?

Perhaps we can fold the drivers/net one into drivers/net/mv643xx_eth.c?
Since nothing else includes drivers/net/mv643xx_eth.h anyway, there's
not much point in having it separate.

^ permalink raw reply

* Re: 2.6.23-mm1 s390 driver problem
From: Cedric Le Goater @ 2007-10-19 11:17 UTC (permalink / raw)
  To: Andrew Morton
  Cc: cornelia.huck-tA70FqPdS9bQT0dZR+AlfA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	heiko.carstens-tA70FqPdS9bQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Christian Borntraeger,
	schwidefsky-tA70FqPdS9bQT0dZR+AlfA, Linux Containers,
	sam-uyr5N9Q2VtJg9hUCZPvPmw, Serge E. Hallyn
In-Reply-To: <20071019022719.537ee71f.akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>


>> that helped going a little further in the boot process but we then have 
>> a network issue when bringing the network interface up :
> 
> please cc netdev on network issues.

yes.

>> Bringing up interface eth0:  ------------Ý cut here ¨------------ 
>> Kernel BUG at 0000000000000002 Ýverbose debug info unavailable¨ 
>> illegal operation: 0001 Ý#1¨ 
>> Modules linked in: 
>> CPU:    0    Not tainted 
>> Process ip (pid: 1167, task: 0000000001d46038, ksp: 00000000025efb28) 
>> Krnl PSW : 0704200180000000 0000000000000002 (0x2) 
>>            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3 
>> Krnl GPRS: 0000000000000000 0000000000000000 000000000241f600 0000000001c8d
>>            00000000000086dd 0000000001eb6d70 0000000000000000 0000000001c8d
>>            0000000001eb6d40 00000000003abc28 0000000001eb6d00 00000000025ef
>>            000000000241f600 00000000003b6d18 00000000002b33d2 00000000025ef
>> Krnl Code:>0000000000000002: 0000               unknown 
>>            0000000000000004: 0000               unknown 
>>            0000000000000006: 0000               unknown 
>>            0000000000000008: 0000               unknown 
>>            000000000000000a: 0000               unknown 
>>            000000000000000c: 0000               unknown 
>>            000000000000000e: 0000               unknown 
>>            0000000000000010: 0000               unknown 
>> Call Trace: 
>> (Ý<00000000002b3352>¨ neigh_connected_output+0x76/0x138) 
>>  Ý<0000000000325402>¨ ip6_output2+0x2da/0x470 
>>  Ý<0000000000326ea6>¨ ip6_output+0x816/0x1064 
>>  Ý<0000000000338e46>¨ __ndisc_send+0x416/0x6a8 
>>  Ý<0000000000339330>¨ ndisc_send_rs+0x58/0x68 
>>  Ý<000000000032cbf4>¨ addrconf_dad_completed+0xbc/0x100 
>>  Ý<000000000032d2de>¨ addrconf_dad_start+0xa2/0x14c 
>>  Ý<000000000032d408>¨ addrconf_add_linklocal+0x80/0xa8 
>>  Ý<000000000032fa7e>¨ addrconf_notify+0x2de/0x8d4 
>>  Ý<0000000000383990>¨ notifier_call_chain+0x5c/0x98 
>>  Ý<0000000000063bca>¨ __raw_notifier_call_chain+0x26/0x34 
>>  Ý<0000000000063c06>¨ raw_notifier_call_chain+0x2e/0x3c 
>>  Ý<00000000002aa54c>¨ call_netdevice_notifiers+0x34/0x44 
>>  Ý<00000000002ad1aa>¨ dev_open+0x9e/0xe0 
>>  Ý<00000000002ad80a>¨ dev_change_flags+0x9e/0x1cc 
>>  Ý<0000000000302c74>¨ devinet_ioctl+0x650/0x73c 
>>  Ý<00000000003050ba>¨ inet_ioctl+0xde/0xf4 
>>  Ý<000000000029a8d0>¨ sock_ioctl+0x1cc/0x2dc 
>>  Ý<00000000000cb844>¨ do_ioctl+0xb8/0xcc 
>>  Ý<00000000000cb8f2>¨ vfs_ioctl+0x9a/0x3ec 
>>  Ý<00000000000cbc96>¨ sys_ioctl+0x52/0x7c 
>>  Ý<0000000000022484>¨ sysc_noemu+0x10/0x16 
>>  Ý<000002000010df12>¨ 0x2000010df12 
> 
> that's a network issue ;)
> 
>> 00000000002b3352 gives : include/linux/netdevice.h:819
>>
> 
> I have a feeling that we fixed this.  But there's no BUG at 2.6.23-mm1's
> include/linux/netdevice.h:819.  

but dev->header_ops is bogus. right ?

> How about setting CONFIG_DEBUG_BUGVERBOSE=y?

it is set :(

C.
 

^ permalink raw reply

* Re: 2.6.23-mm1 s390 driver problem
From: Martin Schwidefsky @ 2007-10-19 11:25 UTC (permalink / raw)
  To: Cedric Le Goater
  Cc: Andrew Morton, Serge E. Hallyn, Christian Borntraeger,
	linux-kernel, cornelia.huck, heiko.carstens, sam,
	Linux Containers, netdev
In-Reply-To: <47189242.8090006@fr.ibm.com>


On Fri, 2007-10-19 at 13:17 +0200, Cedric Le Goater wrote:
> > please cc netdev on network issues.
> 
> yes.
> 
> >> Bringing up interface eth0:  ------------Ý cut here ¨------------ 
> >> Kernel BUG at 0000000000000002 Ýverbose debug info unavailable¨ 
> >> illegal operation: 0001 Ý#1¨ 
> > 
> > that's a network issue ;)
> > 
> >> 00000000002b3352 gives : include/linux/netdevice.h:819
> >>
> > 
> > I have a feeling that we fixed this.  But there's no BUG at 2.6.23-mm1's
> > include/linux/netdevice.h:819.  
> 
> but dev->header_ops is bogus. right ?
> 
> > How about setting CONFIG_DEBUG_BUGVERBOSE=y?
> 
> it is set :(

This is definitly a problem with the header_ops in the qeth network
driver. I've asked our network team to take care of it. Stay tuned..

-- 
blue skies,
  Martin.

"Reality continues to ruin my life." - Calvin.



^ permalink raw reply

* [PATCH v4] FEC - fast ethernet controller for mpc52xx
From: Domen Puncer @ 2007-10-19 11:27 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Grant Likely, galak, linuxppc-dev, tnt, netdev
In-Reply-To: <4717B09B.3060800@pobox.com>

Driver for ethernet on mpc5200/mpc5200b SoCs (FEC).


Signed-off-by: Domen Puncer <domen.puncer@telargo.com>

---
On 18/10/07 15:14 -0400, Jeff Garzik wrote:
> 
> except a resend combining patches 3 and 4 as requested :)

OK, here it goes. Sorry for the delay.


diffstat:
 drivers/net/Kconfig           |   24 
 drivers/net/Makefile          |    4 
 drivers/net/fec_mpc52xx.c     | 1107 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/fec_mpc52xx.h     |  315 +++++++++++
 drivers/net/fec_mpc52xx_phy.c |  198 +++++++
 5 files changed, 1648 insertions(+)

Index: linux.git/drivers/net/Kconfig
===================================================================
--- linux.git.orig/drivers/net/Kconfig
+++ linux.git/drivers/net/Kconfig
@@ -1895,6 +1895,30 @@ config FEC2
 	  Say Y here if you want to use the second built-in 10/100 Fast
 	  ethernet controller on some Motorola ColdFire processors.
 
+config FEC_MPC52xx
+	tristate "MPC52xx FEC driver"
+	depends on PPC_MPC52xx
+	select PPC_BESTCOMM
+	select PPC_BESTCOMM_FEC
+	select CRC32
+	select PHYLIB
+	---help---
+	  This option enables support for the MPC5200's on-chip
+	  Fast Ethernet Controller
+	  If compiled as module, it will be called 'fec_mpc52xx.ko'.
+
+config FEC_MPC52xx_MDIO
+	bool "MPC52xx FEC MDIO bus driver"
+	depends on FEC_MPC52xx
+	default y
+	---help---
+	  The MPC5200's FEC can connect to the Ethernet either with
+	  an external MII PHY chip or 10 Mbps 7-wire interface
+	  (Motorola? industry standard).
+	  If your board uses an external PHY connected to FEC, enable this.
+	  If not sure, enable.
+	  If compiled as module, it will be called 'fec_mpc52xx_phy.ko'.
+
 config NE_H8300
 	tristate "NE2000 compatible support for H8/300"
 	depends on H8300
Index: linux.git/drivers/net/Makefile
===================================================================
--- linux.git.orig/drivers/net/Makefile
+++ linux.git/drivers/net/Makefile
@@ -96,6 +96,10 @@ obj-$(CONFIG_SHAPER) += shaper.o
 obj-$(CONFIG_HP100) += hp100.o
 obj-$(CONFIG_SMC9194) += smc9194.o
 obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
+ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
+	obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
+endif
 obj-$(CONFIG_68360_ENET) += 68360enet.o
 obj-$(CONFIG_WD80x3) += wd.o 8390.o
 obj-$(CONFIG_EL2) += 3c503.o 8390.o
Index: linux.git/drivers/net/fec_mpc52xx.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.c
@@ -0,0 +1,1107 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and
+ * now maintained by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
+ * Copyright (C) 2007  Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003-2004  MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/fec.h>
+
+#include "fec_mpc52xx.h"
+
+#define DRIVER_NAME "mpc52xx-fec"
+
+static irqreturn_t mpc52xx_fec_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_rx_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_tx_interrupt(int, void *);
+static void mpc52xx_fec_stop(struct net_device *dev);
+static void mpc52xx_fec_start(struct net_device *dev);
+static void mpc52xx_fec_reset(struct net_device *dev);
+
+static u8 mpc52xx_fec_mac_addr[6];
+module_param_array_named(mac, mpc52xx_fec_mac_addr, byte, NULL, 0);
+MODULE_PARM_DESC(mac, "six hex digits, ie. 0x1,0x2,0xc0,0x01,0xba,0xbe");
+
+static void mpc52xx_fec_tx_timeout(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	dev_warn(&dev->dev, "transmit timed out\n");
+
+	mpc52xx_fec_reset(dev);
+
+	priv->stats.tx_errors++;
+
+	if (!priv->tx_full)
+		netif_wake_queue(dev);
+}
+
+static void mpc52xx_fec_set_paddr(struct net_device *dev, u8 *mac)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	out_be32(&fec->paddr1, *(u32 *)(&mac[0]));
+	out_be32(&fec->paddr2, (*(u16 *)(&mac[4]) << 16) | FEC_PADDR2_TYPE);
+}
+
+static void mpc52xx_fec_get_paddr(struct net_device *dev, u8 *mac)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	*(u32 *)(&mac[0]) = in_be32(&fec->paddr1);
+	*(u16 *)(&mac[4]) = in_be32(&fec->paddr2) >> 16;
+}
+
+static int mpc52xx_fec_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sock = addr;
+
+	memcpy(dev->dev_addr, sock->sa_data, dev->addr_len);
+
+	mpc52xx_fec_set_paddr(dev, sock->sa_data);
+	return 0;
+}
+
+static void mpc52xx_fec_free_rx_buffers(struct bcom_task *s)
+{
+	struct sk_buff *skb;
+
+	while (!bcom_queue_empty(s)) {
+		skb = bcom_retrieve_buffer(s, NULL, NULL);
+		kfree_skb(skb);
+	}
+}
+
+static int mpc52xx_fec_alloc_rx_buffers(struct bcom_task *rxtsk)
+{
+	while (!bcom_queue_full(rxtsk)) {
+		struct sk_buff *skb;
+		struct bcom_fec_bd *bd;
+
+		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+		if (skb == NULL)
+			return -EAGAIN;
+
+		/* zero out the initial receive buffers to aid debugging */
+		memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
+
+		bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk);
+
+		bd->status = FEC_RX_BUFFER_SIZE;
+		bd->skb_pa = virt_to_phys(skb->data);
+
+		bcom_submit_next_buffer(rxtsk, skb);
+	}
+
+	return 0;
+}
+
+/* based on generic_adjust_link from fs_enet-main.c */
+static void mpc52xx_fec_adjust_link(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct phy_device *phydev = priv->phydev;
+	int new_state = 0;
+
+	if (phydev->link != PHY_DOWN) {
+		if (phydev->duplex != priv->duplex) {
+			struct mpc52xx_fec __iomem *fec = priv->fec;
+			u32 rcntrl;
+			u32 tcntrl;
+
+			new_state = 1;
+			priv->duplex = phydev->duplex;
+
+			rcntrl = in_be32(&fec->r_cntrl);
+			tcntrl = in_be32(&fec->x_cntrl);
+
+			rcntrl &= ~FEC_RCNTRL_DRT;
+			tcntrl &= ~FEC_TCNTRL_FDEN;
+			if (phydev->duplex == DUPLEX_FULL)
+				tcntrl |= FEC_TCNTRL_FDEN;	/* FD enable */
+			else
+				rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
+
+			out_be32(&fec->r_cntrl, rcntrl);
+			out_be32(&fec->x_cntrl, tcntrl);
+		}
+
+		if (phydev->speed != priv->speed) {
+			new_state = 1;
+			priv->speed = phydev->speed;
+		}
+
+		if (priv->link == PHY_DOWN) {
+			new_state = 1;
+			priv->link = phydev->link;
+			netif_schedule(dev);
+			netif_carrier_on(dev);
+			netif_start_queue(dev);
+		}
+
+	} else if (priv->link) {
+		new_state = 1;
+		priv->link = PHY_DOWN;
+		priv->speed = 0;
+		priv->duplex = -1;
+		netif_stop_queue(dev);
+		netif_carrier_off(dev);
+	}
+
+	if (new_state && netif_msg_link(priv))
+		phy_print_status(phydev);
+}
+
+static int mpc52xx_fec_init_phy(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct phy_device *phydev;
+	char phy_id[BUS_ID_SIZE];
+
+	snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT,
+			(unsigned int)dev->base_addr, priv->phy_addr);
+
+	priv->link = PHY_DOWN;
+	priv->speed = 0;
+	priv->duplex = -1;
+
+	phydev = phy_connect(dev, phy_id, &mpc52xx_fec_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+	if (IS_ERR(phydev)) {
+		dev_err(&dev->dev, "phy_connect failed\n");
+		return PTR_ERR(phydev);
+	}
+	dev_info(&dev->dev, "attached phy %i to driver %s\n",
+			phydev->addr, phydev->drv->name);
+
+	priv->phydev = phydev;
+
+	return 0;
+}
+
+static int mpc52xx_fec_phy_start(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	int err;
+
+	if (!priv->has_phy)
+		return 0;
+
+	err = mpc52xx_fec_init_phy(dev);
+	if (err) {
+		dev_err(&dev->dev, "mpc52xx_fec_init_phy failed\n");
+		return err;
+	}
+
+	/* reset phy - this also wakes it from PDOWN */
+	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+	phy_start(priv->phydev);
+
+	return 0;
+}
+
+static void mpc52xx_fec_phy_stop(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	if (!priv->has_phy)
+		return;
+
+	phy_disconnect(priv->phydev);
+	/* power down phy */
+	phy_stop(priv->phydev);
+	phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN);
+}
+
+static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv,
+		struct mii_ioctl_data *mii_data, int cmd)
+{
+	if (!priv->has_phy)
+		return -ENOTSUPP;
+
+	return phy_mii_ioctl(priv->phydev, mii_data, cmd);
+}
+
+static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
+{
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	if (!priv->has_phy)
+		return;
+
+	out_be32(&fec->mii_speed, priv->phy_speed);
+}
+
+static int mpc52xx_fec_open(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	int err = -EBUSY;
+
+	if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_DISABLED | IRQF_SHARED,
+	                DRIVER_NAME "_ctrl", dev)) {
+		dev_err(&dev->dev, "ctrl interrupt request failed\n");
+		goto out;
+	}
+	if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, IRQF_DISABLED,
+	                DRIVER_NAME "_rx", dev)) {
+		dev_err(&dev->dev, "rx interrupt request failed\n");
+		goto free_ctrl_irq;
+	}
+	if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, IRQF_DISABLED,
+	                DRIVER_NAME "_tx", dev)) {
+		dev_err(&dev->dev, "tx interrupt request failed\n");
+		goto free_2irqs;
+	}
+
+	bcom_fec_rx_reset(priv->rx_dmatsk);
+	bcom_fec_tx_reset(priv->tx_dmatsk);
+
+	err = mpc52xx_fec_alloc_rx_buffers(priv->rx_dmatsk);
+	if (err) {
+		dev_err(&dev->dev, "mpc52xx_fec_alloc_rx_buffers failed\n");
+		goto free_irqs;
+	}
+
+	err = mpc52xx_fec_phy_start(dev);
+	if (err)
+		goto free_skbs;
+
+	bcom_enable(priv->rx_dmatsk);
+	bcom_enable(priv->tx_dmatsk);
+
+	mpc52xx_fec_start(dev);
+
+	netif_start_queue(dev);
+
+	return 0;
+
+ free_skbs:
+	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
+
+ free_irqs:
+	free_irq(priv->t_irq, dev);
+ free_2irqs:
+	free_irq(priv->r_irq, dev);
+ free_ctrl_irq:
+	free_irq(dev->irq, dev);
+ out:
+
+	return err;
+}
+
+static int mpc52xx_fec_close(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	netif_stop_queue(dev);
+
+	mpc52xx_fec_stop(dev);
+
+	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
+
+	free_irq(dev->irq, dev);
+	free_irq(priv->r_irq, dev);
+	free_irq(priv->t_irq, dev);
+
+	mpc52xx_fec_phy_stop(dev);
+
+	return 0;
+}
+
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+static int mpc52xx_fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct bcom_fec_bd *bd;
+
+	if (bcom_queue_full(priv->tx_dmatsk)) {
+		if (net_ratelimit())
+			dev_err(&dev->dev, "transmit queue overrun\n");
+		return 1;
+	}
+
+	spin_lock_irq(&priv->lock);
+	dev->trans_start = jiffies;
+
+	bd = (struct bcom_fec_bd *)
+		bcom_prepare_next_buffer(priv->tx_dmatsk);
+
+	bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC;
+	bd->skb_pa = virt_to_phys(skb->data);
+
+	bcom_submit_next_buffer(priv->tx_dmatsk, skb);
+
+	if (bcom_queue_full(priv->tx_dmatsk)) {
+		priv->tx_full = 1;
+		netif_stop_queue(dev);
+	}
+
+	spin_unlock_irq(&priv->lock);
+
+	return 0;
+}
+
+/* This handles BestComm transmit task interrupts
+ */
+static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	spin_lock(&priv->lock);
+
+	while (bcom_buffer_done(priv->tx_dmatsk)) {
+		struct sk_buff *skb;
+		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL);
+
+		priv->tx_full = 0;
+		dev_kfree_skb_irq(skb);
+	}
+
+	if (netif_queue_stopped(dev) && !priv->tx_full)
+		netif_wake_queue(dev);
+
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	while (bcom_buffer_done(priv->rx_dmatsk)) {
+		struct sk_buff *skb;
+		struct sk_buff *rskb;
+		struct bcom_fec_bd *bd;
+		u32 status;
+
+		rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL);
+
+		/* Test for errors in received frame */
+		if (status & BCOM_FEC_RX_BD_ERRORS) {
+			/* Drop packet and reuse the buffer */
+			bd = (struct bcom_fec_bd *)
+				bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+			bd->status = FEC_RX_BUFFER_SIZE;
+			bd->skb_pa = virt_to_phys(rskb->data);
+
+			bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
+
+			priv->stats.rx_dropped++;
+
+			continue;
+		}
+
+		/* skbs are allocated on open, so now we allocate a new one,
+		 * and remove the old (with the packet) */
+		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+		if (skb) {
+			/* Process the received skb */
+			int length = status & BCOM_FEC_RX_BD_LEN_MASK;
+
+			skb_put(rskb, length - 4);	/* length without CRC32 */
+
+			rskb->dev = dev;
+			rskb->protocol = eth_type_trans(rskb, dev);
+
+			netif_rx(rskb);
+			dev->last_rx = jiffies;
+		} else {
+			/* Can't get a new one : reuse the same & drop pkt */
+			dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
+			priv->stats.rx_dropped++;
+
+			skb = rskb;
+		}
+
+		bd = (struct bcom_fec_bd *)
+			bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+		bd->status = FEC_RX_BUFFER_SIZE;
+		bd->skb_pa = virt_to_phys(skb->data);
+
+		bcom_submit_next_buffer(priv->rx_dmatsk, skb);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 ievent;
+
+	ievent = in_be32(&fec->ievent);
+
+	ievent &= ~FEC_IEVENT_MII;	/* mii is handled separately */
+	if (!ievent)
+		return IRQ_NONE;
+
+	out_be32(&fec->ievent, ievent);		/* clear pending events */
+
+	if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
+		if (ievent & ~FEC_IEVENT_TFINT)
+			dev_dbg(&dev->dev, "ievent: %08x\n", ievent);
+		return IRQ_HANDLED;
+	}
+
+	if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
+		dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n");
+	if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
+		dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
+
+	mpc52xx_fec_reset(dev);
+
+	netif_wake_queue(dev);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *mpc52xx_fec_get_stats(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct net_device_stats *stats = &priv->stats;
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	stats->rx_bytes = in_be32(&fec->rmon_r_octets);
+	stats->rx_packets = in_be32(&fec->rmon_r_packets);
+	stats->rx_errors = in_be32(&fec->rmon_r_crc_align) +
+		in_be32(&fec->rmon_r_undersize) +
+		in_be32(&fec->rmon_r_oversize) +
+		in_be32(&fec->rmon_r_frag) +
+		in_be32(&fec->rmon_r_jab);
+
+	stats->tx_bytes = in_be32(&fec->rmon_t_octets);
+	stats->tx_packets = in_be32(&fec->rmon_t_packets);
+	stats->tx_errors = in_be32(&fec->rmon_t_crc_align) +
+		in_be32(&fec->rmon_t_undersize) +
+		in_be32(&fec->rmon_t_oversize) +
+		in_be32(&fec->rmon_t_frag) +
+		in_be32(&fec->rmon_t_jab);
+
+	stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
+	stats->collisions = in_be32(&fec->rmon_t_col);
+
+	/* detailed rx_errors: */
+	stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
+					+ in_be32(&fec->rmon_r_oversize)
+					+ in_be32(&fec->rmon_r_frag)
+					+ in_be32(&fec->rmon_r_jab);
+	stats->rx_over_errors = in_be32(&fec->r_macerr);
+	stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
+	stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
+	stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
+	stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
+
+	/* detailed tx_errors: */
+	stats->tx_aborted_errors = 0;
+	stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
+	stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
+	stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
+	stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
+
+	return stats;
+}
+
+/*
+ * Read MIB counters in order to reset them,
+ * then zero all the stats fields in memory
+ */
+static void mpc52xx_fec_reset_stats(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	out_be32(&fec->mib_control, FEC_MIB_DISABLE);
+	memset_io(&fec->rmon_t_drop, 0,	(__force u32)&fec->reserved10 -
+			(__force u32)&fec->rmon_t_drop);
+	out_be32(&fec->mib_control, 0);
+
+	memset(&priv->stats, 0, sizeof(priv->stats));
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void mpc52xx_fec_set_multicast_list(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 rx_control;
+
+	rx_control = in_be32(&fec->r_cntrl);
+
+	if (dev->flags & IFF_PROMISC) {
+		rx_control |= FEC_RCNTRL_PROM;
+		out_be32(&fec->r_cntrl, rx_control);
+	} else {
+		rx_control &= ~FEC_RCNTRL_PROM;
+		out_be32(&fec->r_cntrl, rx_control);
+
+		if (dev->flags & IFF_ALLMULTI) {
+			out_be32(&fec->gaddr1, 0xffffffff);
+			out_be32(&fec->gaddr2, 0xffffffff);
+		} else {
+			u32 crc;
+			int i;
+			struct dev_mc_list *dmi;
+			u32 gaddr1 = 0x00000000;
+			u32 gaddr2 = 0x00000000;
+
+			dmi = dev->mc_list;
+			for (i=0; i<dev->mc_count; i++) {
+				crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
+				if (crc >= 32)
+					gaddr1 |= 1 << (crc-32);
+				else
+					gaddr2 |= 1 << crc;
+				dmi = dmi->next;
+			}
+			out_be32(&fec->gaddr1, gaddr1);
+			out_be32(&fec->gaddr2, gaddr2);
+		}
+	}
+}
+
+/**
+ * mpc52xx_fec_hw_init
+ * @dev: network device
+ *
+ * Setup various hardware setting, only needed once on start
+ */
+static void mpc52xx_fec_hw_init(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	int i;
+
+	/* Whack a reset.  We should wait for this. */
+	out_be32(&fec->ecntrl, FEC_ECNTRL_RESET);
+	for (i = 0; i < FEC_RESET_DELAY; ++i) {
+		if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0)
+			break;
+		udelay(1);
+	}
+	if (i == FEC_RESET_DELAY)
+		dev_err(&dev->dev, "FEC Reset timeout!\n");
+
+	/* set pause to 0x20 frames */
+	out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20);
+
+	/* high service request will be deasserted when there's < 7 bytes in fifo
+	 * low service request will be deasserted when there's < 4*7 bytes in fifo
+	 */
+	out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+	out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+
+	/* alarm when <= x bytes in FIFO */
+	out_be32(&fec->rfifo_alarm, 0x0000030c);
+	out_be32(&fec->tfifo_alarm, 0x00000100);
+
+	/* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */
+	out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B);
+
+	/* enable crc generation */
+	out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC);
+	out_be32(&fec->iaddr1, 0x00000000);	/* No individual filter */
+	out_be32(&fec->iaddr2, 0x00000000);	/* No individual filter */
+
+	/* set phy speed.
+	 * this can't be done in phy driver, since it needs to be called
+	 * before fec stuff (even on resume) */
+	mpc52xx_fec_phy_hw_init(priv);
+}
+
+/**
+ * mpc52xx_fec_start
+ * @dev: network device
+ *
+ * This function is called to start or restart the FEC during a link
+ * change.  This happens on fifo errors or when switching between half
+ * and full duplex.
+ */
+static void mpc52xx_fec_start(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 rcntrl;
+	u32 tcntrl;
+	u32 tmp;
+
+	/* clear sticky error bits */
+	tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF;
+	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp);
+	out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp);
+
+	/* FIFOs will reset on mpc52xx_fec_enable */
+	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET);
+
+	/* Set station address. */
+	mpc52xx_fec_set_paddr(dev, dev->dev_addr);
+
+	mpc52xx_fec_set_multicast_list(dev);
+
+	/* set max frame len, enable flow control, select mii mode */
+	rcntrl = FEC_RX_BUFFER_SIZE << 16;	/* max frame length */
+	rcntrl |= FEC_RCNTRL_FCE;
+
+	if (priv->has_phy)
+		rcntrl |= FEC_RCNTRL_MII_MODE;
+
+	if (priv->duplex == DUPLEX_FULL)
+		tcntrl = FEC_TCNTRL_FDEN;	/* FD enable */
+	else {
+		rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
+		tcntrl = 0;
+	}
+	out_be32(&fec->r_cntrl, rcntrl);
+	out_be32(&fec->x_cntrl, tcntrl);
+
+	/* Clear any outstanding interrupt. */
+	out_be32(&fec->ievent, 0xffffffff);
+
+	/* Enable interrupts we wish to service. */
+	out_be32(&fec->imask, FEC_IMASK_ENABLE);
+
+	/* And last, enable the transmit and receive processing. */
+	out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN);
+	out_be32(&fec->r_des_active, 0x01000000);
+
+	priv->tx_full = 0;
+}
+
+/**
+ * mpc52xx_fec_stop
+ * @dev: network device
+ *
+ * stop all activity on fec and empty dma buffers
+ */
+static void mpc52xx_fec_stop(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	unsigned long timeout;
+
+	/* disable all interrupts */
+	out_be32(&fec->imask, 0);
+
+	/* Disable the rx task. */
+	bcom_disable(priv->rx_dmatsk);
+
+	/* Wait for tx queue to drain, but only if we're in process context */
+	if (!in_interrupt()) {
+		timeout = jiffies + msecs_to_jiffies(2000);
+		while (time_before(jiffies, timeout) &&
+				!bcom_queue_empty(priv->tx_dmatsk))
+			msleep(100);
+
+		if (time_after_eq(jiffies, timeout))
+			dev_err(&dev->dev, "queues didn't drain\n");
+#if 1
+		if (time_after_eq(jiffies, timeout)) {
+			dev_err(&dev->dev, "  tx: index: %i, outdex: %i\n",
+					priv->tx_dmatsk->index,
+					priv->tx_dmatsk->outdex);
+			dev_err(&dev->dev, "  rx: index: %i, outdex: %i\n",
+					priv->rx_dmatsk->index,
+					priv->rx_dmatsk->outdex);
+		}
+#endif
+	}
+
+	bcom_disable(priv->tx_dmatsk);
+
+	/* Stop FEC */
+	out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN);
+
+	return;
+}
+
+/* reset fec and bestcomm tasks */
+static void mpc52xx_fec_reset(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	mpc52xx_fec_stop(dev);
+
+	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status));
+	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_RESET_FIFO);
+
+	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
+
+	mpc52xx_fec_hw_init(dev);
+
+	phy_stop(priv->phydev);
+	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+	phy_start(priv->phydev);
+
+	bcom_fec_rx_reset(priv->rx_dmatsk);
+	bcom_fec_tx_reset(priv->tx_dmatsk);
+
+	mpc52xx_fec_alloc_rx_buffers(priv->rx_dmatsk);
+
+	bcom_enable(priv->rx_dmatsk);
+	bcom_enable(priv->tx_dmatsk);
+
+	mpc52xx_fec_start(dev);
+}
+
+
+/* ethtool interface */
+static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
+		struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRIVER_NAME);
+}
+
+static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int mpc52xx_fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static u32 mpc52xx_fec_get_msglevel(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return priv->msg_enable;
+}
+
+static void mpc52xx_fec_set_msglevel(struct net_device *dev, u32 level)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	priv->msg_enable = level;
+}
+
+static const struct ethtool_ops mpc52xx_fec_ethtool_ops = {
+	.get_drvinfo = mpc52xx_fec_get_drvinfo,
+	.get_settings = mpc52xx_fec_get_settings,
+	.set_settings = mpc52xx_fec_set_settings,
+	.get_link = ethtool_op_get_link,
+	.get_msglevel = mpc52xx_fec_get_msglevel,
+	.set_msglevel = mpc52xx_fec_set_msglevel,
+};
+
+
+static int mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	return mpc52xx_fec_phy_mii_ioctl(priv, if_mii(rq), cmd);
+}
+
+/* ======================================================================== */
+/* OF Driver                                                                */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+{
+	int rv;
+	struct net_device *ndev;
+	struct mpc52xx_fec_priv *priv = NULL;
+	struct resource mem;
+	const phandle *ph;
+
+	phys_addr_t rx_fifo;
+	phys_addr_t tx_fifo;
+
+	/* Get the ether ndev & it's private zone */
+	ndev = alloc_etherdev(sizeof(struct mpc52xx_fec_priv));
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+
+	/* Reserve FEC control zone */
+	rv = of_address_to_resource(op->node, 0, &mem);
+	if (rv) {
+		printk(KERN_ERR DRIVER_NAME ": "
+				"Error while parsing device node resource\n" );
+		return rv;
+	}
+	if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) {
+		printk(KERN_ERR DRIVER_NAME
+			" - invalid resource size (%lx != %x), check mpc52xx_devices.c\n",
+			(unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
+		return -EINVAL;
+	}
+
+	if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME))
+		return -EBUSY;
+
+	/* Init ether ndev with what we have */
+	ndev->open		= mpc52xx_fec_open;
+	ndev->stop		= mpc52xx_fec_close;
+	ndev->hard_start_xmit	= mpc52xx_fec_hard_start_xmit;
+	ndev->do_ioctl		= mpc52xx_fec_ioctl;
+	ndev->ethtool_ops	= &mpc52xx_fec_ethtool_ops;
+	ndev->get_stats		= mpc52xx_fec_get_stats;
+	ndev->set_mac_address	= mpc52xx_fec_set_mac_address;
+	ndev->set_multicast_list = mpc52xx_fec_set_multicast_list;
+	ndev->tx_timeout	= mpc52xx_fec_tx_timeout;
+	ndev->watchdog_timeo	= FEC_WATCHDOG_TIMEOUT;
+	ndev->flags &= ~IFF_RUNNING;
+	ndev->base_addr		= mem.start;
+
+	priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */
+
+	spin_lock_init(&priv->lock);
+
+	/* ioremap the zones */
+	priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec));
+
+	if (!priv->fec) {
+		rv = -ENOMEM;
+		goto probe_error;
+	}
+
+	/* Bestcomm init */
+	rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data);
+	tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data);
+
+	priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE);
+	priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
+
+	if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
+		printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" );
+		rv = -ENOMEM;
+		goto probe_error;
+	}
+
+	/* Get the IRQ we need one by one */
+		/* Control */
+	ndev->irq = irq_of_parse_and_map(op->node, 0);
+
+		/* RX */
+	priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk);
+
+		/* TX */
+	priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk);
+
+	/* MAC address init */
+	if (!is_zero_ether_addr(mpc52xx_fec_mac_addr))
+		memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6);
+	else
+		mpc52xx_fec_get_paddr(ndev, ndev->dev_addr);
+
+	priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1;
+	priv->duplex = DUPLEX_FULL;
+
+	/* is the phy present in device tree? */
+	ph = of_get_property(op->node, "phy-handle", NULL);
+	if (ph) {
+		const unsigned int *prop;
+		struct device_node *phy_dn;
+		priv->has_phy = 1;
+
+		phy_dn = of_find_node_by_phandle(*ph);
+		prop = of_get_property(phy_dn, "reg", NULL);
+		priv->phy_addr = *prop;
+
+		of_node_put(phy_dn);
+
+		/* Phy speed */
+		priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
+	} else {
+		dev_info(&ndev->dev, "can't find \"phy-handle\" in device"
+				" tree, using 7-wire mode\n");
+	}
+
+	/* Hardware init */
+	mpc52xx_fec_hw_init(ndev);
+
+	mpc52xx_fec_reset_stats(ndev);
+
+	/* Register the new network device */
+	rv = register_netdev(ndev);
+	if (rv < 0)
+		goto probe_error;
+
+	/* We're done ! */
+	dev_set_drvdata(&op->dev, ndev);
+
+	return 0;
+
+
+	/* Error handling - free everything that might be allocated */
+probe_error:
+
+	irq_dispose_mapping(ndev->irq);
+
+	if (priv->rx_dmatsk)
+		bcom_fec_rx_release(priv->rx_dmatsk);
+	if (priv->tx_dmatsk)
+		bcom_fec_tx_release(priv->tx_dmatsk);
+
+	if (priv->fec)
+		iounmap(priv->fec);
+
+	release_mem_region(mem.start, sizeof(struct mpc52xx_fec));
+
+	free_netdev(ndev);
+
+	return rv;
+}
+
+static int
+mpc52xx_fec_remove(struct of_device *op)
+{
+	struct net_device *ndev;
+	struct mpc52xx_fec_priv *priv;
+
+	ndev = dev_get_drvdata(&op->dev);
+	if (!ndev)
+		return 0;
+	priv = netdev_priv(ndev);
+
+	unregister_netdev(ndev);
+
+	irq_dispose_mapping(ndev->irq);
+
+	bcom_fec_rx_release(priv->rx_dmatsk);
+	bcom_fec_tx_release(priv->tx_dmatsk);
+
+	iounmap(priv->fec);
+
+	release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec));
+
+	free_netdev(ndev);
+
+	dev_set_drvdata(&op->dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
+{
+	struct net_device *dev = dev_get_drvdata(&op->dev);
+
+	if (netif_running(dev))
+		mpc52xx_fec_close(dev);
+
+	return 0;
+}
+
+static int mpc52xx_fec_of_resume(struct of_device *op)
+{
+	struct net_device *dev = dev_get_drvdata(&op->dev);
+
+	mpc52xx_fec_hw_init(dev);
+	mpc52xx_fec_reset_stats(dev);
+
+	if (netif_running(dev))
+		mpc52xx_fec_open(dev);
+
+	return 0;
+}
+#endif
+
+static struct of_device_id mpc52xx_fec_match[] = {
+	{
+		.type		= "network",
+		.compatible	= "mpc5200-fec",
+	},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, mpc52xx_fec_match);
+
+static struct of_platform_driver mpc52xx_fec_driver = {
+	.owner		= THIS_MODULE,
+	.name		= DRIVER_NAME,
+	.match_table	= mpc52xx_fec_match,
+	.probe		= mpc52xx_fec_probe,
+	.remove		= mpc52xx_fec_remove,
+#ifdef CONFIG_PM
+	.suspend	= mpc52xx_fec_of_suspend,
+	.resume		= mpc52xx_fec_of_resume,
+#endif
+};
+
+
+/* ======================================================================== */
+/* Module                                                                   */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_fec_init(void)
+{
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+	int ret;
+	ret = of_register_platform_driver(&mpc52xx_fec_mdio_driver);
+	if (ret) {
+		printk(KERN_ERR DRIVER_NAME ": failed to register mdio driver\n");
+		return ret;
+	}
+#endif
+	return of_register_platform_driver(&mpc52xx_fec_driver);
+}
+
+static void __exit
+mpc52xx_fec_exit(void)
+{
+	of_unregister_platform_driver(&mpc52xx_fec_driver);
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+	of_unregister_platform_driver(&mpc52xx_fec_mdio_driver);
+#endif
+}
+
+
+module_init(mpc52xx_fec_init);
+module_exit(mpc52xx_fec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC");
Index: linux.git/drivers/net/fec_mpc52xx.h
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.h
@@ -0,0 +1,315 @@
+/*
+ * drivers/drivers/net/fec_mpc52xx/fec.h
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
+#define __DRIVERS_NET_MPC52XX_FEC_H__
+
+#include <linux/phy.h>
+
+/* Tunable constant */
+/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
+#define FEC_RX_BUFFER_SIZE	1522	/* max receive packet size */
+#define FEC_RX_NUM_BD		256
+#define FEC_TX_NUM_BD		64
+
+#define FEC_RESET_DELAY		50 	/* uS */
+
+#define FEC_WATCHDOG_TIMEOUT	((400*HZ)/1000)
+
+struct mpc52xx_fec_priv {
+	int duplex;
+	int tx_full;
+	int r_irq;
+	int t_irq;
+	struct mpc52xx_fec __iomem *fec;
+	struct bcom_task *rx_dmatsk;
+	struct bcom_task *tx_dmatsk;
+	spinlock_t lock;
+	struct net_device_stats stats;
+	int msg_enable;
+
+	int has_phy;
+	unsigned int phy_speed;
+	unsigned int phy_addr;
+	struct phy_device *phydev;
+	enum phy_state link;
+	int speed;
+};
+
+
+/* ======================================================================== */
+/* Hardware register sets & bits                                            */
+/* ======================================================================== */
+
+struct mpc52xx_fec {
+	u32 fec_id;			/* FEC + 0x000 */
+	u32 ievent;			/* FEC + 0x004 */
+	u32 imask;			/* FEC + 0x008 */
+
+	u32 reserved0[1];		/* FEC + 0x00C */
+	u32 r_des_active;		/* FEC + 0x010 */
+	u32 x_des_active;		/* FEC + 0x014 */
+	u32 r_des_active_cl;		/* FEC + 0x018 */
+	u32 x_des_active_cl;		/* FEC + 0x01C */
+	u32 ivent_set;			/* FEC + 0x020 */
+	u32 ecntrl;			/* FEC + 0x024 */
+
+	u32 reserved1[6];		/* FEC + 0x028-03C */
+	u32 mii_data;			/* FEC + 0x040 */
+	u32 mii_speed;			/* FEC + 0x044 */
+	u32 mii_status;			/* FEC + 0x048 */
+
+	u32 reserved2[5];		/* FEC + 0x04C-05C */
+	u32 mib_data;			/* FEC + 0x060 */
+	u32 mib_control;		/* FEC + 0x064 */
+
+	u32 reserved3[6];		/* FEC + 0x068-7C */
+	u32 r_activate;			/* FEC + 0x080 */
+	u32 r_cntrl;			/* FEC + 0x084 */
+	u32 r_hash;			/* FEC + 0x088 */
+	u32 r_data;			/* FEC + 0x08C */
+	u32 ar_done;			/* FEC + 0x090 */
+	u32 r_test;			/* FEC + 0x094 */
+	u32 r_mib;			/* FEC + 0x098 */
+	u32 r_da_low;			/* FEC + 0x09C */
+	u32 r_da_high;			/* FEC + 0x0A0 */
+
+	u32 reserved4[7];		/* FEC + 0x0A4-0BC */
+	u32 x_activate;			/* FEC + 0x0C0 */
+	u32 x_cntrl;			/* FEC + 0x0C4 */
+	u32 backoff;			/* FEC + 0x0C8 */
+	u32 x_data;			/* FEC + 0x0CC */
+	u32 x_status;			/* FEC + 0x0D0 */
+	u32 x_mib;			/* FEC + 0x0D4 */
+	u32 x_test;			/* FEC + 0x0D8 */
+	u32 fdxfc_da1;			/* FEC + 0x0DC */
+	u32 fdxfc_da2;			/* FEC + 0x0E0 */
+	u32 paddr1;			/* FEC + 0x0E4 */
+	u32 paddr2;			/* FEC + 0x0E8 */
+	u32 op_pause;			/* FEC + 0x0EC */
+
+	u32 reserved5[4];		/* FEC + 0x0F0-0FC */
+	u32 instr_reg;			/* FEC + 0x100 */
+	u32 context_reg;		/* FEC + 0x104 */
+	u32 test_cntrl;			/* FEC + 0x108 */
+	u32 acc_reg;			/* FEC + 0x10C */
+	u32 ones;			/* FEC + 0x110 */
+	u32 zeros;			/* FEC + 0x114 */
+	u32 iaddr1;			/* FEC + 0x118 */
+	u32 iaddr2;			/* FEC + 0x11C */
+	u32 gaddr1;			/* FEC + 0x120 */
+	u32 gaddr2;			/* FEC + 0x124 */
+	u32 random;			/* FEC + 0x128 */
+	u32 rand1;			/* FEC + 0x12C */
+	u32 tmp;			/* FEC + 0x130 */
+
+	u32 reserved6[3];		/* FEC + 0x134-13C */
+	u32 fifo_id;			/* FEC + 0x140 */
+	u32 x_wmrk;			/* FEC + 0x144 */
+	u32 fcntrl;			/* FEC + 0x148 */
+	u32 r_bound;			/* FEC + 0x14C */
+	u32 r_fstart;			/* FEC + 0x150 */
+	u32 r_count;			/* FEC + 0x154 */
+	u32 r_lag;			/* FEC + 0x158 */
+	u32 r_read;			/* FEC + 0x15C */
+	u32 r_write;			/* FEC + 0x160 */
+	u32 x_count;			/* FEC + 0x164 */
+	u32 x_lag;			/* FEC + 0x168 */
+	u32 x_retry;			/* FEC + 0x16C */
+	u32 x_write;			/* FEC + 0x170 */
+	u32 x_read;			/* FEC + 0x174 */
+
+	u32 reserved7[2];		/* FEC + 0x178-17C */
+	u32 fm_cntrl;			/* FEC + 0x180 */
+	u32 rfifo_data;			/* FEC + 0x184 */
+	u32 rfifo_status;		/* FEC + 0x188 */
+	u32 rfifo_cntrl;		/* FEC + 0x18C */
+	u32 rfifo_lrf_ptr;		/* FEC + 0x190 */
+	u32 rfifo_lwf_ptr;		/* FEC + 0x194 */
+	u32 rfifo_alarm;		/* FEC + 0x198 */
+	u32 rfifo_rdptr;		/* FEC + 0x19C */
+	u32 rfifo_wrptr;		/* FEC + 0x1A0 */
+	u32 tfifo_data;			/* FEC + 0x1A4 */
+	u32 tfifo_status;		/* FEC + 0x1A8 */
+	u32 tfifo_cntrl;		/* FEC + 0x1AC */
+	u32 tfifo_lrf_ptr;		/* FEC + 0x1B0 */
+	u32 tfifo_lwf_ptr;		/* FEC + 0x1B4 */
+	u32 tfifo_alarm;		/* FEC + 0x1B8 */
+	u32 tfifo_rdptr;		/* FEC + 0x1BC */
+	u32 tfifo_wrptr;		/* FEC + 0x1C0 */
+
+	u32 reset_cntrl;		/* FEC + 0x1C4 */
+	u32 xmit_fsm;			/* FEC + 0x1C8 */
+
+	u32 reserved8[3];		/* FEC + 0x1CC-1D4 */
+	u32 rdes_data0;			/* FEC + 0x1D8 */
+	u32 rdes_data1;			/* FEC + 0x1DC */
+	u32 r_length;			/* FEC + 0x1E0 */
+	u32 x_length;			/* FEC + 0x1E4 */
+	u32 x_addr;			/* FEC + 0x1E8 */
+	u32 cdes_data;			/* FEC + 0x1EC */
+	u32 status;			/* FEC + 0x1F0 */
+	u32 dma_control;		/* FEC + 0x1F4 */
+	u32 des_cmnd;			/* FEC + 0x1F8 */
+	u32 data;			/* FEC + 0x1FC */
+
+	u32 rmon_t_drop;		/* FEC + 0x200 */
+	u32 rmon_t_packets;		/* FEC + 0x204 */
+	u32 rmon_t_bc_pkt;		/* FEC + 0x208 */
+	u32 rmon_t_mc_pkt;		/* FEC + 0x20C */
+	u32 rmon_t_crc_align;		/* FEC + 0x210 */
+	u32 rmon_t_undersize;		/* FEC + 0x214 */
+	u32 rmon_t_oversize;		/* FEC + 0x218 */
+	u32 rmon_t_frag;		/* FEC + 0x21C */
+	u32 rmon_t_jab;			/* FEC + 0x220 */
+	u32 rmon_t_col;			/* FEC + 0x224 */
+	u32 rmon_t_p64;			/* FEC + 0x228 */
+	u32 rmon_t_p65to127;		/* FEC + 0x22C */
+	u32 rmon_t_p128to255;		/* FEC + 0x230 */
+	u32 rmon_t_p256to511;		/* FEC + 0x234 */
+	u32 rmon_t_p512to1023;		/* FEC + 0x238 */
+	u32 rmon_t_p1024to2047;		/* FEC + 0x23C */
+	u32 rmon_t_p_gte2048;		/* FEC + 0x240 */
+	u32 rmon_t_octets;		/* FEC + 0x244 */
+	u32 ieee_t_drop;		/* FEC + 0x248 */
+	u32 ieee_t_frame_ok;		/* FEC + 0x24C */
+	u32 ieee_t_1col;		/* FEC + 0x250 */
+	u32 ieee_t_mcol;		/* FEC + 0x254 */
+	u32 ieee_t_def;			/* FEC + 0x258 */
+	u32 ieee_t_lcol;		/* FEC + 0x25C */
+	u32 ieee_t_excol;		/* FEC + 0x260 */
+	u32 ieee_t_macerr;		/* FEC + 0x264 */
+	u32 ieee_t_cserr;		/* FEC + 0x268 */
+	u32 ieee_t_sqe;			/* FEC + 0x26C */
+	u32 t_fdxfc;			/* FEC + 0x270 */
+	u32 ieee_t_octets_ok;		/* FEC + 0x274 */
+
+	u32 reserved9[2];		/* FEC + 0x278-27C */
+	u32 rmon_r_drop;		/* FEC + 0x280 */
+	u32 rmon_r_packets;		/* FEC + 0x284 */
+	u32 rmon_r_bc_pkt;		/* FEC + 0x288 */
+	u32 rmon_r_mc_pkt;		/* FEC + 0x28C */
+	u32 rmon_r_crc_align;		/* FEC + 0x290 */
+	u32 rmon_r_undersize;		/* FEC + 0x294 */
+	u32 rmon_r_oversize;		/* FEC + 0x298 */
+	u32 rmon_r_frag;		/* FEC + 0x29C */
+	u32 rmon_r_jab;			/* FEC + 0x2A0 */
+
+	u32 rmon_r_resvd_0;		/* FEC + 0x2A4 */
+
+	u32 rmon_r_p64;			/* FEC + 0x2A8 */
+	u32 rmon_r_p65to127;		/* FEC + 0x2AC */
+	u32 rmon_r_p128to255;		/* FEC + 0x2B0 */
+	u32 rmon_r_p256to511;		/* FEC + 0x2B4 */
+	u32 rmon_r_p512to1023;		/* FEC + 0x2B8 */
+	u32 rmon_r_p1024to2047;		/* FEC + 0x2BC */
+	u32 rmon_r_p_gte2048;		/* FEC + 0x2C0 */
+	u32 rmon_r_octets;		/* FEC + 0x2C4 */
+	u32 ieee_r_drop;		/* FEC + 0x2C8 */
+	u32 ieee_r_frame_ok;		/* FEC + 0x2CC */
+	u32 ieee_r_crc;			/* FEC + 0x2D0 */
+	u32 ieee_r_align;		/* FEC + 0x2D4 */
+	u32 r_macerr;			/* FEC + 0x2D8 */
+	u32 r_fdxfc;			/* FEC + 0x2DC */
+	u32 ieee_r_octets_ok;		/* FEC + 0x2E0 */
+
+	u32 reserved10[7];		/* FEC + 0x2E4-2FC */
+
+	u32 reserved11[64];		/* FEC + 0x300-3FF */
+};
+
+#define	FEC_MIB_DISABLE			0x80000000
+
+#define	FEC_IEVENT_HBERR		0x80000000
+#define	FEC_IEVENT_BABR			0x40000000
+#define	FEC_IEVENT_BABT			0x20000000
+#define	FEC_IEVENT_GRA			0x10000000
+#define	FEC_IEVENT_TFINT		0x08000000
+#define	FEC_IEVENT_MII			0x00800000
+#define	FEC_IEVENT_LATE_COL		0x00200000
+#define	FEC_IEVENT_COL_RETRY_LIM	0x00100000
+#define	FEC_IEVENT_XFIFO_UN		0x00080000
+#define	FEC_IEVENT_XFIFO_ERROR		0x00040000
+#define	FEC_IEVENT_RFIFO_ERROR		0x00020000
+
+#define	FEC_IMASK_HBERR			0x80000000
+#define	FEC_IMASK_BABR			0x40000000
+#define	FEC_IMASK_BABT			0x20000000
+#define	FEC_IMASK_GRA			0x10000000
+#define	FEC_IMASK_MII			0x00800000
+#define	FEC_IMASK_LATE_COL		0x00200000
+#define	FEC_IMASK_COL_RETRY_LIM		0x00100000
+#define	FEC_IMASK_XFIFO_UN		0x00080000
+#define	FEC_IMASK_XFIFO_ERROR		0x00040000
+#define	FEC_IMASK_RFIFO_ERROR		0x00020000
+
+/* all but MII, which is enabled separately */
+#define FEC_IMASK_ENABLE	(FEC_IMASK_HBERR | FEC_IMASK_BABR | \
+		FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \
+		FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \
+		FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR)
+
+#define	FEC_RCNTRL_MAX_FL_SHIFT		16
+#define	FEC_RCNTRL_LOOP			0x01
+#define	FEC_RCNTRL_DRT			0x02
+#define	FEC_RCNTRL_MII_MODE		0x04
+#define	FEC_RCNTRL_PROM			0x08
+#define	FEC_RCNTRL_BC_REJ		0x10
+#define	FEC_RCNTRL_FCE			0x20
+
+#define	FEC_TCNTRL_GTS			0x00000001
+#define	FEC_TCNTRL_HBC			0x00000002
+#define	FEC_TCNTRL_FDEN			0x00000004
+#define	FEC_TCNTRL_TFC_PAUSE		0x00000008
+#define	FEC_TCNTRL_RFC_PAUSE		0x00000010
+
+#define	FEC_ECNTRL_RESET		0x00000001
+#define	FEC_ECNTRL_ETHER_EN		0x00000002
+
+#define FEC_MII_DATA_ST			0x40000000	/* Start frame */
+#define FEC_MII_DATA_OP_RD		0x20000000	/* Perform read */
+#define FEC_MII_DATA_OP_WR		0x10000000	/* Perform write */
+#define FEC_MII_DATA_PA_MSK		0x0f800000	/* PHY Address mask */
+#define FEC_MII_DATA_RA_MSK		0x007c0000	/* PHY Register mask */
+#define FEC_MII_DATA_TA			0x00020000	/* Turnaround */
+#define FEC_MII_DATA_DATAMSK		0x0000ffff	/* PHY data mask */
+
+#define FEC_MII_READ_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA)
+#define FEC_MII_WRITE_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA)
+
+#define FEC_MII_DATA_RA_SHIFT		0x12		/* MII reg addr bits */
+#define FEC_MII_DATA_PA_SHIFT		0x17		/* MII PHY addr bits */
+
+#define FEC_PADDR2_TYPE			0x8808
+
+#define FEC_OP_PAUSE_OPCODE		0x00010000
+
+#define FEC_FIFO_WMRK_256B		0x3
+
+#define FEC_FIFO_STATUS_ERR		0x00400000
+#define FEC_FIFO_STATUS_UF		0x00200000
+#define FEC_FIFO_STATUS_OF		0x00100000
+
+#define FEC_FIFO_CNTRL_FRAME		0x08000000
+#define FEC_FIFO_CNTRL_LTG_7		0x07000000
+
+#define FEC_RESET_CNTRL_RESET_FIFO	0x02000000
+#define FEC_RESET_CNTRL_ENABLE_IS_RESET	0x01000000
+
+#define FEC_XMIT_FSM_APPEND_CRC		0x02000000
+#define FEC_XMIT_FSM_ENABLE_CRC		0x01000000
+
+
+extern struct of_platform_driver mpc52xx_fec_mdio_driver;
+
+#endif	/* __DRIVERS_NET_MPC52XX_FEC_H__ */
Index: linux.git/drivers/net/fec_mpc52xx_phy.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx_phy.c
@@ -0,0 +1,198 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
+ *
+ * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include "fec_mpc52xx.h"
+
+struct mpc52xx_fec_mdio_priv {
+	struct mpc52xx_fec __iomem *regs;
+};
+
+static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	struct mpc52xx_fec __iomem *fec;
+	int tries = 100;
+	u32 request = FEC_MII_READ_FRAME;
+
+	fec = priv->regs;
+	out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+	request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+	request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+	out_be32(&priv->regs->mii_data, request);
+
+	/* wait for it to finish, this takes about 23 us on lite5200b */
+	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+		udelay(5);
+
+	if (tries == 0)
+		return -ETIMEDOUT;
+
+	return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK;
+}
+
+static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
+{
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	struct mpc52xx_fec __iomem *fec;
+	u32 value = data;
+	int tries = 100;
+
+	fec = priv->regs;
+	out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+	value |= FEC_MII_WRITE_FRAME;
+	value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+	value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+	out_be32(&priv->regs->mii_data, value);
+
+	/* wait for request to finish */
+	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+		udelay(5);
+
+	if (tries == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_id *match)
+{
+	struct device *dev = &of->dev;
+	struct device_node *np = of->node;
+	struct device_node *child = NULL;
+	struct mii_bus *bus;
+	struct mpc52xx_fec_mdio_priv *priv;
+	struct resource res = {};
+	int err;
+	int i;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (bus == NULL)
+		return -ENOMEM;
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+
+	bus->name = "mpc52xx MII bus";
+	bus->read = mpc52xx_fec_mdio_read;
+	bus->write = mpc52xx_fec_mdio_write;
+
+	/* setup irqs */
+	bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL);
+	if (bus->irq == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		bus->irq[i] = PHY_POLL;
+
+	while ((child = of_get_next_child(np, child)) != NULL) {
+		int irq = irq_of_parse_and_map(child, 0);
+		if (irq != NO_IRQ) {
+			const u32 *id = of_get_property(child, "reg", NULL);
+			bus->irq[*id] = irq;
+		}
+	}
+
+	/* setup registers */
+	err = of_address_to_resource(np, 0, &res);
+	if (err)
+		goto out_free;
+	priv->regs = ioremap(res.start, res.end - res.start + 1);
+	if (priv->regs == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+
+	bus->id = res.start;
+	bus->priv = priv;
+
+	bus->dev = dev;
+	dev_set_drvdata(dev, bus);
+
+	/* set MII speed */
+	out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1);
+
+	/* enable MII interrupt */
+	out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII);
+
+	err = mdiobus_register(bus);
+	if (err)
+		goto out_unmap;
+
+	return 0;
+
+ out_unmap:
+	iounmap(priv->regs);
+ out_free:
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		if (bus->irq[i] != PHY_POLL)
+			irq_dispose_mapping(bus->irq[i]);
+	kfree(bus->irq);
+	kfree(priv);
+	kfree(bus);
+
+	return err;
+}
+
+static int mpc52xx_fec_mdio_remove(struct of_device *of)
+{
+	struct device *dev = &of->dev;
+	struct mii_bus *bus = dev_get_drvdata(dev);
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	int i;
+
+	mdiobus_unregister(bus);
+	dev_set_drvdata(dev, NULL);
+
+	iounmap(priv->regs);
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		if (bus->irq[i])
+			irq_dispose_mapping(bus->irq[i]);
+	kfree(priv);
+	kfree(bus->irq);
+	kfree(bus);
+
+	return 0;
+}
+
+
+static struct of_device_id mpc52xx_fec_mdio_match[] = {
+	{
+		.type = "mdio",
+		.compatible = "mpc5200b-fec-phy",
+	},
+	{},
+};
+
+struct of_platform_driver mpc52xx_fec_mdio_driver = {
+	.name = "mpc5200b-fec-phy",
+	.probe = mpc52xx_fec_mdio_probe,
+	.remove = mpc52xx_fec_mdio_remove,
+	.match_table = mpc52xx_fec_mdio_match,
+};
+
+/* let fec driver call it, since this has to be registered before it */
+EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
+
+
+MODULE_LICENSE("Dual BSD/GPL");

^ permalink raw reply

* Re: [PATCH] PHYLIB: IRQ event workqueue handling fixes
From: Maciej W. Rozycki @ 2007-10-19 11:38 UTC (permalink / raw)
  To: Jarek Poplawski
  Cc: Andy Fleming, Andrew Morton, Jeff Garzik, netdev, linux-kernel
In-Reply-To: <Pine.LNX.4.64N.0710171221510.28993@blysk.ds.pg.gda.pl>

On Thu, 18 Oct 2007, Maciej W. Rozycki wrote:

> > 1) phy_change() checks PHY_HALTED flag without lock; I think it's
> > racy: eg. if it's done during phy_stop() it can check just before
> > the flag is set and reenable interrupts just after phy_stop() ends.
> 
>  I remember having a look into it, but it was long ago and I cannot 
> immediately recall the conclusion.  Which means it is either broken or 
> deserves a comment as non-obvious.  I will have a look into it again, but 
> I am resource-starved a little at the moment, sorry.

 Well, I have now recalled what the issue is -- we just plainly and simply 
want to avoid a hardirq spinlock for the very reason we do not do all the 
processing in the hardirq handler.  The thing is we make accesses to the 
MDIO bus with the phydev lock held and it may take ages until these 
accesses will have completed.  And we cannot afford keeping interrupts 
disabled for so long.

 So the only way is to make the check for the HALTED state lockless and 
make sure any race condition is handled gracefully and does not lead to 
inconsistent behaviour.  Which I think as of what we have in the 
net-2.6.24 tree is the case, but there are never too many eyes to look at 
a piece of code, so if anybody feels like proving me wrong, then just go 
ahead!

  Maciej

^ permalink raw reply

* [PATCH] netdev: Netfilters on outgoing interfamily ipsec
From: Joakim Koskela @ 2007-10-19 11:37 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Patrick McHardy, Herbert Xu

Hi,

I understand that Herbert is in midst of cleaning up the output of
interfamily transformations, but I thought I'd post a couple of
patches related to that anyway, sort of to show what we've needed to
fix to get our systems working.

This one changes how the netfilters are applied during output to be
based on the current address family of the packet instead of what it
will be transformed to.

Signed-off-by: Joakim Koskela <jookos@gmail.com>
---

diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index c4a7156..8b0c6bd 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -13,6 +13,9 @@
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter_ipv4.h>
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+#include <linux/netfilter_ipv6.h>
+#endif
 #include <net/ip.h>
 #include <net/xfrm.h>
 #include <net/icmp.h>
@@ -139,7 +142,13 @@ static int xfrm4_output_finish(struct sk_buff *skb)
 
 int xfrm4_output(struct sk_buff *skb)
 {
-	return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
-			    xfrm4_output_finish,
-			    !(IPCB(skb)->flags & IPSKB_REROUTED));
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+	if (ip_hdr(skb)->version == 6)
+		return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev,
+			       xfrm4_output_finish);
+	else
+#endif
+		return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
+				    xfrm4_output_finish,
+				    !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 6569767..7624613 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -14,8 +14,10 @@
 #include <linux/skbuff.h>
 #include <linux/icmpv6.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_ipv4.h>
 #include <net/ipv6.h>
 #include <net/xfrm.h>
+#include <net/ip.h>
 
 int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
 			  u8 **prevhdr)
@@ -134,6 +136,11 @@ static int xfrm6_output_finish(struct sk_buff *skb)
 
 int xfrm6_output(struct sk_buff *skb)
 {
-	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev,
-		       xfrm6_output_finish);
+	if (ip_hdr(skb)->version == 4)
+		return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
+				    xfrm6_output_finish,
+				    !(IPCB(skb)->flags & IPSKB_REROUTED));
+	else
+		return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev,
+			       xfrm6_output_finish);
 }

^ permalink raw reply related

* Re: multicast: bug or "feature"
From: Herbert Xu @ 2007-10-19 11:43 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, brian.haley, dlstevens
In-Reply-To: <47178708.2040606@hp.com>

Vlad Yasevich <vladislav.yasevich@hp.com> wrote:
>
> Now, to figure out what IPv6 does different and why it works.
> Seems to me that the two should have the same behavior.

IPv6 on Linux uses a per-interface addressing model as opposed
to the per-host model used by IPv4.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* [PATCH] netdev: Reset ipv4 flags during bundle creation on interfamily ipsec
From: Joakim Koskela @ 2007-10-19 11:40 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Patrick McHardy, Herbert Xu
In-Reply-To: <200710191437.39449.joakim.koskela@hiit.fi>

This patch resets the ipv4-related flags in the new flow as their
content will otherwise depend on the bits of the ipv6 addresses the
struct was previously used for. For example, fl4_tos might have
RTO_ONLINK set, which usually prevents the right route from being
found.

This bit was chopped off the larger patch dealing with the problems
related to creating the bundles for inter-family tranformations.

Signed-off-by: Joakim Koskela <jookos@gmail.com>
--

diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 82e27b8..386a762 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -184,6 +184,8 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int
 			case AF_INET:
 				fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4;
 				fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4;
+				fl_tunnel.fl4_tos = 0;
+				fl_tunnel.fl4_scope = 0;
 				break;
 			case AF_INET6:
 				ipv6_addr_copy(&fl_tunnel.fl6_dst, __xfrm6_bundle_addr_remote(xfrm[i], &fl->fl6_dst));

^ permalink raw reply related

* iproute2: git pull request from debian repo.
From: Andreas Henriksson @ 2007-10-19 12:27 UTC (permalink / raw)
  To: shemminger; +Cc: netdev, andreas

Hello!

We now have our brand new git repo set up for the debian package. We have
created a branch called "patches" where we are going to put stuff that we
hope will get merged upstream.

I'm sending out this pull request to get confirmation about if we are on the
right track ...

The repo url is git://git.debian.org/git/collab-maint/pkg-iproute.git
and the branch you're supposed to look at is "patches", as I mentioned.

(gitweb available at:
http://git.debian.org/?p=collab-maint/pkg-iproute.git;a=summary )

It currently only contains two fixes:
- trivial syntax fix in the ss(8) manpage, by Justin Pryzby.
  (Should be a no-brainer.)
- ip addr flush loop fix (from ubuntu), by Daniel Silverstone
  (Equivalent to the treatment "ip neigh flush" previously got.
   This obsoletes the patch from Alexander Wirt which was dropped
   from the last patch submission.)

The patches branch is currently based on the v2.6.23-071016 release, and we
intend to rebase it when you do a new release.

Please notify me about anything I've set up wrong (and helpful hints on how
to do it right, would be deeply appreciated since I'm a complete git newbie).
I would also like to ask if this pull request should be followed up by
posting the patches to the mailing list or if a public git repo is considered
enough? If you want me to also post the individual patches, I'd be very happy
if you have some helpful hints on how to most easily export just the ones in
the tree which doesn't come from the upstream branch and send them out...

Have a nice weekend!

-- 
Regards,
Andreas Henriksson

^ permalink raw reply

* Re: [PATCH] netdev: Netfilters on outgoing interfamily ipsec
From: Herbert Xu @ 2007-10-19 12:55 UTC (permalink / raw)
  To: Joakim Koskela; +Cc: netdev, David S. Miller, Patrick McHardy
In-Reply-To: <200710191437.39449.joakim.koskela@hiit.fi>

On Fri, Oct 19, 2007 at 02:37:38PM +0300, Joakim Koskela wrote:
> Hi,
> 
> I understand that Herbert is in midst of cleaning up the output of
> interfamily transformations, but I thought I'd post a couple of
> patches related to that anyway, sort of to show what we've needed to
> fix to get our systems working.
> 
> This one changes how the netfilters are applied during output to be
> based on the current address family of the packet instead of what it
> will be transformed to.

While I agree that this is definitely a problem, I've already
got a solution for it which we happen to need for async crypto
anyway.

Basically xfrm_output will invoke a continuation function based
on the external mode/family which will then call the right hooks.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PATCH 2/8] [MV643XX_ETH] Move ethernet register definitions into private header
From: Dale Farnsworth @ 2007-10-19 12:56 UTC (permalink / raw)
  To: Lennert Buytenhek
  Cc: Christoph Hellwig, Nicolas Pitre, Tzachi Perelstein,
	Manas Saksena, netdev
In-Reply-To: <20071019110957.GL16037@xi.wantstofly.org>

On Fri, Oct 19, 2007 at 01:09:57PM +0200, Lennert Buytenhek wrote:
> On Fri, Oct 19, 2007 at 09:30:48AM +0100, Christoph Hellwig wrote:
> > Isn't it a little too confusing to have two headers with the same name,
> > one in drivers/net and one in include/linux?
> 
> Perhaps we can fold the drivers/net one into drivers/net/mv643xx_eth.c?
> Since nothing else includes drivers/net/mv643xx_eth.h anyway, there's
> not much point in having it separate.

Sounds good to me.  Please add a patch to do so.

-Dale

^ permalink raw reply

* Re: [PATCH] PHYLIB: IRQ event workqueue handling fixes
From: Maciej W. Rozycki @ 2007-10-19 12:57 UTC (permalink / raw)
  To: Jarek Poplawski
  Cc: Andy Fleming, Andrew Morton, Jeff Garzik, netdev, linux-kernel
In-Reply-To: <20071019081706.GA2598@ff.dom.local>

On Fri, 19 Oct 2007, Jarek Poplawski wrote:

> But then... your patch seems to make it possible, because it enables
> irq to the initial state of the counter. Of course, this could happen
> on closing only.

 That's because free_irq() does not disable the interrupt in the correct 
manner.  The scenario is more or less like this:

phy_interrupt()				[depth == 0]
	disable_irq()
		depth++; status |= IRQ_DISABLED;
...
free_irq()				[depth == 1]
	status |= IRQ_DISABLED;
...
phy_change()				[depth == 1]
	enable_irq()
		depth--; status &= ~IRQ_DISABLED;
		oops!

Now if free_irq() correctly incremented the depth counter, then the last
enable_irq() would still decrement it, but with its initial value of 2 it 
would not change the status to reenable the line.

  Maciej

^ permalink raw reply

* Re: iproute2: git pull request from debian repo.
From: maximilian attems @ 2007-10-19 13:06 UTC (permalink / raw)
  To: Andreas Henriksson; +Cc: shemminger, netdev
In-Reply-To: <20071019122753.GA30078@scream.fatal.se>

On Fri, Oct 19, 2007 at 02:27:53PM +0200, Andreas Henriksson wrote:
> Hello!
> 
> We now have our brand new git repo set up for the debian package. We have
> created a branch called "patches" where we are going to put stuff that we
> hope will get merged upstream.
> 
> I'm sending out this pull request to get confirmation about if we are on the
> right track ...
> 
> The repo url is git://git.debian.org/git/collab-maint/pkg-iproute.git
> and the branch you're supposed to look at is "patches", as I mentioned.

on klibc i'm asked to attach git log -p on a pull request so
that the patches can be reviewed, please do so too.
 
also apprecitated would be the output of
git log master..patches | git shortlog

> It currently only contains two fixes:
> - trivial syntax fix in the ss(8) manpage, by Justin Pryzby.
>   (Should be a no-brainer.)
> - ip addr flush loop fix (from ubuntu), by Daniel Silverstone
>   (Equivalent to the treatment "ip neigh flush" previously got.
>    This obsoletes the patch from Alexander Wirt which was dropped
>    from the last patch submission.)
> 

happy weekend

-- 
maks

^ permalink raw reply

* Re: [PATCH] netdev: Reset ipv4 flags during bundle creation on interfamily ipsec
From: Herbert Xu @ 2007-10-19 13:09 UTC (permalink / raw)
  To: Joakim Koskela; +Cc: netdev, David S. Miller, Patrick McHardy
In-Reply-To: <200710191440.17232.joakim.koskela@hiit.fi>

On Fri, Oct 19, 2007 at 02:40:16PM +0300, Joakim Koskela wrote:
> This patch resets the ipv4-related flags in the new flow as their
> content will otherwise depend on the bits of the ipv6 addresses the
> struct was previously used for. For example, fl4_tos might have
> RTO_ONLINK set, which usually prevents the right route from being
> found.
> 
> This bit was chopped off the larger patch dealing with the problems
> related to creating the bundles for inter-family tranformations.

This changes behaviour.  Previously the same TOS value would be
used all the way through.  With this it won't apply to the first
tunnel and every SA after it.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PATCH] netdev: Netfilters on outgoing interfamily ipsec
From: Joakim Koskela @ 2007-10-19 13:18 UTC (permalink / raw)
  To: Herbert Xu; +Cc: netdev, David S. Miller, Patrick McHardy
In-Reply-To: <20071019125555.GA13955@gondor.apana.org.au>

On Friday 19 October 2007 15:55:55 Herbert Xu wrote:
> While I agree that this is definitely a problem, I've already
> got a solution for it which we happen to need for async crypto
> anyway.
>
> Basically xfrm_output will invoke a continuation function based
> on the external mode/family which will then call the right hooks.
>
> Cheers,

Ok, great. Lets go with that.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox