Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Oliver Neukum @ 2012-10-30 16:30 UTC (permalink / raw)
  To: Ming Lei
  Cc: Alan Stern, linux-kernel, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVO5-UPNrWsySzDE5AfOv1TMqbyitQX9ViidSJPM36fqAQ@mail.gmail.com>

On Wednesday 31 October 2012 00:00:56 Ming Lei wrote:
> On Tue, Oct 30, 2012 at 11:38 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> 
> >
> > Okay, I see your point.  But acquiring the lock here doesn't solve the
> > problem.  Suppose a thread is about to reset a USB mass-storage device.
> > It acquires the lock and sees that the noio flag is clear.  But before
> > it can issue the reset, another thread sets the noio flag.
> 
> If the USB mass-storage device is being reseted, the flag should be set
> already generally.  If the flag is still unset, that means the disk/network
> device isn't added into system(or removed just now), so memory allocation
> with block I/O should be allowed during the reset. Looks it isn't one problem,
> isn't it?

I am afraid it is, because a disk may just have been probed as the deviceis being reset.

	Regards
		Oliver


^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Ming Lei @ 2012-10-30 16:15 UTC (permalink / raw)
  To: Alan Stern
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVO5-UPNrWsySzDE5AfOv1TMqbyitQX9ViidSJPM36fqAQ@mail.gmail.com>

On Wed, Oct 31, 2012 at 12:00 AM, Ming Lei <ming.lei@canonical.com> wrote:
>
> Looks the simplest approach is to handle the noio flag thing at the start and
> end of rpm_resume.

Sorry, that doesn't work, runtime_suspend need that too because memory
allocation with block I/O might deadlock when doing I/O on the same device.

Thanks,
--
Ming Lei

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Ming Lei @ 2012-10-30 16:00 UTC (permalink / raw)
  To: Alan Stern
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <Pine.LNX.4.44L0.1210301112270.1363-100000@iolanthe.rowland.org>

On Tue, Oct 30, 2012 at 11:38 PM, Alan Stern <stern@rowland.harvard.edu> wrote:

>
> Okay, I see your point.  But acquiring the lock here doesn't solve the
> problem.  Suppose a thread is about to reset a USB mass-storage device.
> It acquires the lock and sees that the noio flag is clear.  But before
> it can issue the reset, another thread sets the noio flag.

If the USB mass-storage device is being reseted, the flag should be set
already generally.  If the flag is still unset, that means the disk/network
device isn't added into system(or removed just now), so memory allocation
with block I/O should be allowed during the reset. Looks it isn't one problem,
isn't it?

> I'm not sure what the best solution is.
>
>> The lock needn't to be held when the function is called inside
>> pm_runtime_set_memalloc_noio(),  so the bitfield flag should
>> be checked directly without holding power lock in dev_memalloc_noio().
>
> Yes.
>
> A couple of other things...  Runtime resume can be blocked by runtime
> suspend, if a resume is requested while the suspend is in progress.
> Therefore the runtime suspend code also needs to save-set-restore the
> noio flag.

Looks the simplest approach is to handle the noio flag thing at the start and
end of rpm_resume.

> Also, we should set the noio flag at the start of
> usb_stor_control_thread, because everything that thread does can
> potentially block an I/O operation.

Yes, it should be done, and all GFP_NOIO in usbcore should be converted
into GFP_KERNEL together. And the work shouldn't be started until
the patchset is merged.

> Lastly, pm_runtime_get_memalloc_noio always returns false when
> CONFIG_PM_RUNTIME is disabled.  But we still need to prevent I/O during
> usb_reset_device even when there's no runtime PM.  Maybe the simplest
> answer is always to set noio during resets.  That would also help with
> the race described above.

I have thought about this. IMO, pm_runtime_get_memalloc_noio should
return true always if CONFIG_PM_RUNTIME is unset.

Thanks,
--
Ming Lei

^ permalink raw reply

* tcp_metrics: dumb bug
From: Stephen Hemminger @ 2012-10-30 15:59 UTC (permalink / raw)
  To: Julian Anastasov; +Cc: netdev

https://bugzilla.kernel.org/show_bug.cgi?id=49171

Description From David Binderman 2012-10-21 10:31:05 (-) [reply]
The source code is

        if (nla_len(a) != sizeof(sizeof(struct in6_addr)))

I'm surprised that compiles. Suggest remove one of the sizeofs.

^ permalink raw reply

* Re: [PATCH net-next 2/8] skb: api to report errors for zero copy skbs
From: Michael S. Tsirkin @ 2012-10-30 15:54 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Alexander Duyck, Ian Campbell, kvm, netdev, linux-kernel,
	virtualization, Eric Dumazet, Andrew Morton, David S. Miller
In-Reply-To: <508FF5D0.6080107@redhat.com>

On Tue, Oct 30, 2012 at 11:44:16AM -0400, Vlad Yasevich wrote:
> On 10/29/2012 11:49 AM, Michael S. Tsirkin wrote:
> >Orphaning frags for zero copy skbs needs to allocate data in atomic
> >context so is has a chance to fail. If it does we currently discard
> >the skb which is safe, but we don't report anything to the caller,
> >so it can not recover by e.g. disabling zero copy.
> >
> >Add an API to free skb reporting such errors: this is used
> >by tun in case orphaning frags fails.
> >
> >Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> >---
> >  include/linux/skbuff.h |  1 +
> >  net/core/skbuff.c      | 19 +++++++++++++++++++
> >  2 files changed, 20 insertions(+)
> >
> >diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> >index 8bac11b..0644432 100644
> >--- a/include/linux/skbuff.h
> >+++ b/include/linux/skbuff.h
> >@@ -568,6 +568,7 @@ static inline struct rtable *skb_rtable(const struct sk_buff *skb)
> >  }
> >
> >  extern void kfree_skb(struct sk_buff *skb);
> >+extern void skb_tx_error(struct sk_buff *skb, int err);
> >  extern void consume_skb(struct sk_buff *skb);
> >  extern void	       __kfree_skb(struct sk_buff *skb);
> >  extern struct kmem_cache *skbuff_head_cache;
> >diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> >index eb31f6e..ad99c64 100644
> >--- a/net/core/skbuff.c
> >+++ b/net/core/skbuff.c
> >@@ -635,6 +635,25 @@ void kfree_skb(struct sk_buff *skb)
> >  EXPORT_SYMBOL(kfree_skb);
> >
> >  /**
> >+ *	kfree_skb_on_error - report an sk_buff xmit error
> >+ *	@skb: buffer that triggered an error
> >+ *
> >+ *	Report xmit error if a device callback is tracking this skb.
> >+ */
> 
> Nit:  Comment doesn't match new function.
> 
> -vlad

Good catch, thanks.

> >+void skb_tx_error(struct sk_buff *skb, int err)
> >+{
> >+	if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
> >+		struct ubuf_info *uarg;
> >+
> >+		uarg = skb_shinfo(skb)->destructor_arg;
> >+		if (uarg->callback)
> >+			uarg->callback(uarg, err);
> >+		skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
> >+	}
> >+}
> >+EXPORT_SYMBOL(skb_tx_error);
> >+
> >+/**
> >   *	consume_skb - free an skbuff
> >   *	@skb: buffer to free
> >   *
> >

^ permalink raw reply

* Re: [PATCH net-next 8/8] vhost-net: reduce vq polling on tx zerocopy
From: Michael S. Tsirkin @ 2012-10-30 15:54 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Alexander Duyck, Ian Campbell, kvm, netdev, linux-kernel,
	virtualization, Eric Dumazet, Andrew Morton, David S. Miller
In-Reply-To: <508FF6A1.5060201@redhat.com>

On Tue, Oct 30, 2012 at 11:47:45AM -0400, Vlad Yasevich wrote:
> On 10/29/2012 11:49 AM, Michael S. Tsirkin wrote:
> >It seems that to avoid deadlocks it is enough to poll vq before
> >  we are going to use the last buffer.  This should be faster than
> >c70aa540c7a9f67add11ad3161096fb95233aa2e.
> >
> >Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> >---
> >  drivers/vhost/net.c | 12 ++++++++++--
> >  1 file changed, 10 insertions(+), 2 deletions(-)
> >
> >diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> >index 8e9de79..3967f82 100644
> >--- a/drivers/vhost/net.c
> >+++ b/drivers/vhost/net.c
> >@@ -197,8 +197,16 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, int status)
> >  {
> >  	struct vhost_ubuf_ref *ubufs = ubuf->ctx;
> >  	struct vhost_virtqueue *vq = ubufs->vq;
> >-
> >-	vhost_poll_queue(&vq->poll);
> >+	int cnt = atomic_read(&ubufs->kref.refcount);
> >+
> >+	/*
> >+	 * Trigger polling thread if guest stopped submitting new buffers:
> >+	 * in this case, the refcount after decrement will eventually reach 1
> >+	 * so here it is 2.
> >+	 * We also trigger polling periodically after each 16 packets.
> >+	 */
> >+	if (cnt <= 2 || !(cnt % 16))
> 
> Why 16?  Does it make sense to make it configurable?
> 
> -vlad

Probably not but I'll add a comment explaining why.

> >+		vhost_poll_queue(&vq->poll);
> >  	/* set len to mark this desc buffers done DMA */
> >  	vq->heads[ubuf->desc].len = status ?
> >  		VHOST_DMA_FAILED_LEN : VHOST_DMA_DONE_LEN;
> >

^ permalink raw reply

* Re: [PATCH net-next 8/8] vhost-net: reduce vq polling on tx zerocopy
From: Vlad Yasevich @ 2012-10-30 15:47 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: David S. Miller, Eric Dumazet, Andrew Morton, Alexander Duyck,
	Ian Campbell, kvm, virtualization, netdev, linux-kernel
In-Reply-To: <3e57e2cde71a4270a8fef395c31f5f0e8a130c28.1351524502.git.mst@redhat.com>

On 10/29/2012 11:49 AM, Michael S. Tsirkin wrote:
> It seems that to avoid deadlocks it is enough to poll vq before
>   we are going to use the last buffer.  This should be faster than
> c70aa540c7a9f67add11ad3161096fb95233aa2e.
>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>   drivers/vhost/net.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 8e9de79..3967f82 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -197,8 +197,16 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, int status)
>   {
>   	struct vhost_ubuf_ref *ubufs = ubuf->ctx;
>   	struct vhost_virtqueue *vq = ubufs->vq;
> -
> -	vhost_poll_queue(&vq->poll);
> +	int cnt = atomic_read(&ubufs->kref.refcount);
> +
> +	/*
> +	 * Trigger polling thread if guest stopped submitting new buffers:
> +	 * in this case, the refcount after decrement will eventually reach 1
> +	 * so here it is 2.
> +	 * We also trigger polling periodically after each 16 packets.
> +	 */
> +	if (cnt <= 2 || !(cnt % 16))

Why 16?  Does it make sense to make it configurable?

-vlad

> +		vhost_poll_queue(&vq->poll);
>   	/* set len to mark this desc buffers done DMA */
>   	vq->heads[ubuf->desc].len = status ?
>   		VHOST_DMA_FAILED_LEN : VHOST_DMA_DONE_LEN;
>

^ permalink raw reply

* Re: [PATCH net-next 2/8] skb: api to report errors for zero copy skbs
From: Vlad Yasevich @ 2012-10-30 15:44 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: David S. Miller, Eric Dumazet, Andrew Morton, Alexander Duyck,
	Ian Campbell, kvm, virtualization, netdev, linux-kernel
In-Reply-To: <ee85ad5053a929c90e0347b6ba33778d96690e9b.1351524501.git.mst@redhat.com>

On 10/29/2012 11:49 AM, Michael S. Tsirkin wrote:
> Orphaning frags for zero copy skbs needs to allocate data in atomic
> context so is has a chance to fail. If it does we currently discard
> the skb which is safe, but we don't report anything to the caller,
> so it can not recover by e.g. disabling zero copy.
>
> Add an API to free skb reporting such errors: this is used
> by tun in case orphaning frags fails.
>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>   include/linux/skbuff.h |  1 +
>   net/core/skbuff.c      | 19 +++++++++++++++++++
>   2 files changed, 20 insertions(+)
>
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 8bac11b..0644432 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -568,6 +568,7 @@ static inline struct rtable *skb_rtable(const struct sk_buff *skb)
>   }
>
>   extern void kfree_skb(struct sk_buff *skb);
> +extern void skb_tx_error(struct sk_buff *skb, int err);
>   extern void consume_skb(struct sk_buff *skb);
>   extern void	       __kfree_skb(struct sk_buff *skb);
>   extern struct kmem_cache *skbuff_head_cache;
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index eb31f6e..ad99c64 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -635,6 +635,25 @@ void kfree_skb(struct sk_buff *skb)
>   EXPORT_SYMBOL(kfree_skb);
>
>   /**
> + *	kfree_skb_on_error - report an sk_buff xmit error
> + *	@skb: buffer that triggered an error
> + *
> + *	Report xmit error if a device callback is tracking this skb.
> + */

Nit:  Comment doesn't match new function.

-vlad

> +void skb_tx_error(struct sk_buff *skb, int err)
> +{
> +	if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
> +		struct ubuf_info *uarg;
> +
> +		uarg = skb_shinfo(skb)->destructor_arg;
> +		if (uarg->callback)
> +			uarg->callback(uarg, err);
> +		skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
> +	}
> +}
> +EXPORT_SYMBOL(skb_tx_error);
> +
> +/**
>    *	consume_skb - free an skbuff
>    *	@skb: buffer to free
>    *
>

^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Alan Stern @ 2012-10-30 15:38 UTC (permalink / raw)
  To: Ming Lei
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVOPDu6wVgPmvtTkokn7VV41x3XVvL4g_E0pz0mikUbvUg@mail.gmail.com>

On Tue, 30 Oct 2012, Ming Lei wrote:

> >> +bool pm_runtime_get_memalloc_noio(struct device *dev)
> >> +{
> >> +     bool ret;
> >> +     spin_lock_irq(&dev->power.lock);
> >> +     ret = dev->power.memalloc_noio_resume;
> >> +     spin_unlock_irq(&dev->power.lock);
> >> +     return ret;
> >> +}
> >
> > You don't need to acquire and release a spinlock just to read the
> > value.  Reading bitfields _is_ SMP-safe; writing them is not.
> 
> Thanks for your review.
> 
> As you pointed out before, the flag need to be checked before
> resetting usb devices, so the lock should be held to make another
> context(CPU) see the updated value suppose one context(CPU)
> call pm_runtime_set_memalloc_noio() to change the flag at the
> same time.

Okay, I see your point.  But acquiring the lock here doesn't solve the 
problem.  Suppose a thread is about to reset a USB mass-storage device.  
It acquires the lock and sees that the noio flag is clear.  But before 
it can issue the reset, another thread sets the noio flag.

I'm not sure what the best solution is.

> The lock needn't to be held when the function is called inside
> pm_runtime_set_memalloc_noio(),  so the bitfield flag should
> be checked directly without holding power lock in dev_memalloc_noio().

Yes.

A couple of other things...  Runtime resume can be blocked by runtime
suspend, if a resume is requested while the suspend is in progress.  
Therefore the runtime suspend code also needs to save-set-restore the
noio flag.

Also, we should set the noio flag at the start of 
usb_stor_control_thread, because everything that thread does can 
potentially block an I/O operation.

Lastly, pm_runtime_get_memalloc_noio always returns false when 
CONFIG_PM_RUNTIME is disabled.  But we still need to prevent I/O during 
usb_reset_device even when there's no runtime PM.  Maybe the simplest 
answer is always to set noio during resets.  That would also help with 
the race described above.

Alan Stern


^ permalink raw reply

* [PATCH] ipv4/fib_trie: notify deleted routes on if down event
From: Nicolas Dichtel @ 2012-10-30 15:32 UTC (permalink / raw)
  To: netdev; +Cc: davem, Nicolas Dichtel

This notification is missing in IPv4 (in IPv6, deleted routes are
notified).

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
 net/ipv4/fib_trie.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 31d771c..5b1c7d0 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1718,15 +1718,20 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
 	return 0;
 }
 
-static int trie_flush_list(struct list_head *head)
+static int trie_flush_list(struct fib_table *tb, struct leaf *l,
+			   struct leaf_info *li)
 {
 	struct fib_alias *fa, *fa_node;
 	int found = 0;
 
-	list_for_each_entry_safe(fa, fa_node, head, fa_list) {
+	list_for_each_entry_safe(fa, fa_node, &li->falh, fa_list) {
 		struct fib_info *fi = fa->fa_info;
 
 		if (fi && (fi->fib_flags & RTNH_F_DEAD)) {
+			struct nl_info nlinfo = { .nl_net = fi->fib_net, };
+
+			rtmsg_fib(RTM_DELROUTE, htonl(l->key), fa, li->plen,
+				  tb->tb_id, &nlinfo, 0);
 			list_del_rcu(&fa->fa_list);
 			fib_release_info(fa->fa_info);
 			alias_free_mem_rcu(fa);
@@ -1736,7 +1741,7 @@ static int trie_flush_list(struct list_head *head)
 	return found;
 }
 
-static int trie_flush_leaf(struct leaf *l)
+static int trie_flush_leaf(struct fib_table *tb, struct leaf *l)
 {
 	int found = 0;
 	struct hlist_head *lih = &l->list;
@@ -1744,7 +1749,7 @@ static int trie_flush_leaf(struct leaf *l)
 	struct leaf_info *li = NULL;
 
 	hlist_for_each_entry_safe(li, node, tmp, lih, hlist) {
-		found += trie_flush_list(&li->falh);
+		found += trie_flush_list(tb, l, li);
 
 		if (list_empty(&li->falh)) {
 			hlist_del_rcu(&li->hlist);
@@ -1835,7 +1840,7 @@ int fib_table_flush(struct fib_table *tb)
 	int found = 0;
 
 	for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
-		found += trie_flush_leaf(l);
+		found += trie_flush_leaf(tb, l);
 
 		if (ll && hlist_empty(&ll->list))
 			trie_leaf_remove(t, ll);
-- 
1.7.12

^ permalink raw reply related

* Re: [PATCH net-next] sctp: support per-association stats via a new SCTP_GET_ASSOC_STATS call
From: Thomas Graf @ 2012-10-30 14:54 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Neil Horman, Michele Baldessari, linux-sctp, David S. Miller,
	netdev
In-Reply-To: <508FE256.9010205@gmail.com>

On 10/30/12 at 10:21am, Vlad Yasevich wrote:
> What if the message arrived across multiple transports?
> 
> Also, you can always see current rto, but that means that the
> application has to still poll for it with PEER_ADDR_INFO.  I think
> the idea, as Thomas pointed out, is to easily see what the max rto
> was.  I like that, but I don't like the fact that it's per
> association.   With that stat we really don't know which transport
> spiked.  So, it gives us a some useful information, but not really
> enough.
> 
> It might be a little more useful to keep track of the which
> transport has experienced the spike and then return info about it.
> Granted, we may be too late and the spike corrected itself already,
> but at least it will give the management application a little more
> information and allow admin to have a little more info to determine
> why the spike occurred.

Agreed and good point, a per transport max rto makes more sense.

^ permalink raw reply

* Re: [PATCH net-next] sctp: support per-association stats via a new SCTP_GET_ASSOC_STATS call
From: Thomas Graf @ 2012-10-30 14:51 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Neil Horman, Michele Baldessari, linux-sctp, David S. Miller,
	netdev
In-Reply-To: <508FE34D.8010402@gmail.com>

On 10/30/12 at 10:25am, Vlad Yasevich wrote:
> Can you give me some reasons why you prefer 0?
> 
> 0 seems a bit strange to me.  if someone was to construct a
> histogram of values, they would start with some initial value, then
> see 0s if there is no change, a spike for large rto, and if the
> spike is corrected, it would drop to 0 indicating no change...
> Seems odd.
> 
> I would rather see what the current observed max rto is for an
> application polling period.  Then a histogram can be correctly
> constructed.

0 would indicate that the rto has not been recalculated since
the last read and the app can decideitself whether to ignore
that read, replace it with rto_min or use the last known good
rto_max value.

Obviously this would mean that the patch needs to be changed
so it updates the max observed rto after applying rto_min in
sctp_transport_update_rto().

I'm perfectly fine with using rto_min as well though, we just lose
a bit of information that may be helpful to some users.

^ permalink raw reply

* Re: [PATCH v2 2/3] pppoatm: fix race condition with destroying of vcc
From: Chas Williams (CONTRACTOR) @ 2012-10-30 14:26 UTC (permalink / raw)
  To: Krzysztof Mazur; +Cc: davem, dwmw2, netdev, linux-kernel
In-Reply-To: <1350926091-12642-2-git-send-email-krzysiek@podlesie.net>

In message <1350926091-12642-2-git-send-email-krzysiek@podlesie.net>,Krzysztof Mazur writes:
>The pppoatm_send() calls vcc->send() and now also checks for
>some vcc flags that indicate destroyed vcc without proper locking.
...
>The vcc_sendmsg() uses lock_sock(sk). This lock is used by
>vcc_release(), so vcc_destroy_socket() will not be called between
>check and during ->send(). The vcc_release_async() sets ATM_VF_CLOSE,
>but it should be safe to call ->send() after it, because
>vcc->dev->ops->close() is not called.

as i recall from way back, this shouldnt be necessary.  closing a vcc
for an attached protocol isnt supposed to require addtional locking
or synchronization.

vcc_release() locks the socket and vcc_destroy_socket() calls the device's
vcc close routine and pushes a NULL skb to the attached protocol.
this NULL push is supposed to let the attached protocol that no more
sends and recvs can be handled. 

that said, the order for the device vcc close and push does seem
reversed.  since i imagine there could be a pending pppoatm_send()
during this interval.  the push of the NULL skb is allowed to wait for
the subprotocol to finish its cleanup/shutdown.

^ permalink raw reply

* Re: [PATCH net-next] sctp: support per-association stats via a new SCTP_GET_ASSOC_STATS call
From: Vlad Yasevich @ 2012-10-30 14:25 UTC (permalink / raw)
  To: Thomas Graf
  Cc: Neil Horman, Michele Baldessari, linux-sctp, David S. Miller,
	netdev
In-Reply-To: <20121030125230.GB13450@casper.infradead.org>

On 10/30/2012 08:52 AM, Thomas Graf wrote:
> On 10/29/12 at 04:22pm, Vlad Yasevich wrote:
>> On 10/29/2012 07:37 AM, Neil Horman wrote:
>>> Hm, ok, looking for the maximum rto seen is definately more efficient that a
>>> high polling rate on the remaddr file.  Still can't say I really like it as a
>>> statistic though.  While it helps in diagnosing a very specific type of problem
>>> (applications that have a maximum allowable latency), its really not useful, and
>>> potentially misleading, in the general case.  Specificaly it may show a very
>>> large RTO even if that RTO was an erroneous spike in behavior earlier in the
>>> lifetime of a given transport, even if that RTO is not representative of the
>>> current behavior of the association.  It seems to me like this stat might be
>>> better collected using a stap script or by adding a trace point to
>>> sctp_transport_update_rto.  If the application needs to know this information
>>> internally during its operation to take corrective action, you can already get
>>> it via the SCTP_GET_PEER_ADDR_INFO socket option on a per transport basis just
>>> as efficiently.
>
> SCTP_GET_PEER_ADDR_INFO doesn't help here as the whole point of this
> stat is to get max(rto) as seen by the SCTP stack.
>
>> The max_rto is reset after each getsockopt(), so in effect, the
>> application sets its own polling interval and gets the max rto
>> achieved during it.  If the rto hasn't changed, then the last value
>> is returned.  Not sure how much I like that.  I would rather get max
>> rto achieved per polling period and upon reset, max_rto is
>> accumulated again (easy way to do that is set to rto_min on reset).
>> This way an monitoring thread can truly represent the max rto
>> reported by association.  It should normally remain steady, but this
>> will show spikes, if any.
>
> I would still reset it to 0 but I agree that it makes more sense to
> return 0 if max(rto) remains unchanged within the observation period
> rather than returning the previous max(rto).
>

Can you give me some reasons why you prefer 0?

0 seems a bit strange to me.  if someone was to construct a histogram of 
values, they would start with some initial value, then see 0s if there 
is no change, a spike for large rto, and if the spike is corrected, it 
would drop to 0 indicating no change...  Seems odd.

I would rather see what the current observed max rto is for an 
application polling period.  Then a histogram can be correctly constructed.

-vlad

^ permalink raw reply

* Re: [PATCH RFC] pkt_sched: enable QFQ to support TSO/GSO
From: Stephen Hemminger @ 2012-10-30 14:24 UTC (permalink / raw)
  To: Paolo Valente; +Cc: jhs, davem, linux-kernel, netdev, rizzo, fchecconi
In-Reply-To: <20121030060056.GA3373@paolo-ThinkPad-W520>

On Tue, 30 Oct 2012 07:00:56 +0100
Paolo Valente <paolo.valente@unimore.it> wrote:

> Hi,
> if the max packet size for some class (configured through tc) is
> violated by the actual size of the packets of that class, then QFQ
> would not schedule classes correctly, and the data structures
> implementing the bucket lists may get corrupted. This problem occurs
> with TSO/GSO even if the max packet size is set to the MTU, and is,
> e.g., the cause of the failure reported in [1]. Two patches have been
> proposed to solve this problem in [2], one of them is a preliminary
> version of this patch.
> 
> This patch addresses the above issues by: 1) setting QFQ parameters to
> proper values for supporting TSO/GSO (in particular, setting the
> maximum possible packet size to 64KB), 2) automatically increasing the
> max packet size for a class, lmax, when a packet with a larger size
> than the current value of lmax arrives.
> 
> The drawback of the first point is that the maximum weight for a class
> is now limited to 4096, which is equal to 1/16 of the maximum weight
> sum.
> 
> Finally, this patch also forcibly caps the timestamps of a class if
> they are too high to be stored in the bucket list. This capping, taken
> from QFQ+ [3], handles the unfrequent case described in the comment to
> the function slot_insert.
> 
> [1] http://marc.info/?l=linux-netdev&m=134968777902077&w=2
> [2] http://marc.info/?l=linux-netdev&m=135096573507936&w=2
> [3] http://marc.info/?l=linux-netdev&m=134902691421670&w=2
> 
> Signed-off-by: Paolo Valente <paolo.valente@unimore.it>
> Tested-by: Cong Wang <amwang@redhat.com>

Acked-by: Stephen Hemminger <shemminger@vyatta.com>

^ permalink raw reply

* Re: [PATCH net-next] sctp: support per-association stats via a new SCTP_GET_ASSOC_STATS call
From: Vlad Yasevich @ 2012-10-30 14:21 UTC (permalink / raw)
  To: Neil Horman
  Cc: Thomas Graf, Michele Baldessari, linux-sctp, David S. Miller,
	netdev
In-Reply-To: <20121030111546.GA14013@hmsreliant.think-freely.org>

On 10/30/2012 07:15 AM, Neil Horman wrote:
> On Mon, Oct 29, 2012 at 04:22:30PM -0400, Vlad Yasevich wrote:
>> On 10/29/2012 07:37 AM, Neil Horman wrote:
>>> On Mon, Oct 29, 2012 at 08:41:43AM +0000, Thomas Graf wrote:
>>>> On 10/26/12 at 10:37am, Neil Horman wrote:
>>>>> We already have files in /proc/net/sctp to count snmp system-wide totals,
>>>>> per-endpoint totals, and per association totals.  Why do these stats differently
>>>>> instead of just adding them the per-association file?  I get that solaris does
>>>>> this, but its not codified in any of the RFC's or other standards.  I would
>>>>> really rather see something like this go into the interfaces we have, rather
>>>>> than creating a new one.
>>>>
>>>> I really dislike to grow the procfs interface. I would favour a
>>>> netlink inteface but we already export all the statistics via
>>>> the socket interface so this is the most consistent choice.
>>>>
>>> I'm not sure what statistics you are referring to here.  The only stats I see
>>> exported (save for association setting exported via SCTP_STATUS on the socket
>>> interface), are done via proc.  I agree that proc stinks out loud, but my first
>>> though when seeing stats exported via an interface in which you need to provide
>>> process specific data (in this case the socket and association id), is that the
>>> very next thing that someone will ask for is the ability for these stats to be
>>> visible from outside the process, so an external tool can monitor them.  I guess
>>> I'm ok with this approach, since its more efficient than polling proc (as per
>>> your needs below), but maybe its time to start looking at implementing a
>>> NETLINK_STATS interface so that we can have the best of both worlds.
>>>
>>
>> Yep.  That's been on the TODO list for a while, just always at a
>> lower priority then other things...
>>
> I'll start trying to look at it...when I get a spare moment :)
>
>>>
>>>>> And the max observed rto stat is just odd.  Each
>>>>> transport has an rto value, not each association, and you cal already see the
>>>>> individual transport rto values in /proc/net/sctp/remaddr.
>>>>
>>>> It's true that this is not found in any RFC and the request seems to
>>>> be based on the fact that Solaris provides this information already.
>>>> Recording the largest observed RTO is critical for latency sensitive
>>>> use cases. Looking at RTO in remaddr doesn't really help to figure out
>>>> the MAX(RTO) even with a very high polling frequency, something you
>>>> don't want to do on a procfs file.
>>>>
>>> Hm, ok, looking for the maximum rto seen is definately more efficient that a
>>> high polling rate on the remaddr file.  Still can't say I really like it as a
>>> statistic though.  While it helps in diagnosing a very specific type of problem
>>> (applications that have a maximum allowable latency), its really not useful, and
>>> potentially misleading, in the general case.  Specificaly it may show a very
>>> large RTO even if that RTO was an erroneous spike in behavior earlier in the
>>> lifetime of a given transport, even if that RTO is not representative of the
>>> current behavior of the association.  It seems to me like this stat might be
>>> better collected using a stap script or by adding a trace point to
>>> sctp_transport_update_rto.  If the application needs to know this information
>>> internally during its operation to take corrective action, you can already get
>>> it via the SCTP_GET_PEER_ADDR_INFO socket option on a per transport basis just
>>> as efficiently.
>>>
>>
>> The max_rto is reset after each getsockopt(), so in effect, the
>> application sets its own polling interval and gets the max rto
>> achieved during it.  If the rto hasn't changed, then the last value
>> is returned.  Not sure how much I like that.  I would rather get max
>> rto achieved per polling period and upon reset, max_rto is
>> accumulated again (easy way to do that is set to rto_min on reset).
>> This way an monitoring thread can truly represent the max rto
>> reported by association.  It should normally remain steady, but this
>> will show spikes, if any.
>>
> Agreed, that would be a better way to determine the maximum rto the association
> has seen.  My thought above was to, on every receive, to use the sndrcvinfo cmsg
> to detect the transport that a message arrived on, and then use
> SCTP_GET_PEER_ADDR_INFO to determine the current rto on that transport.  It
> should still show spikes, while avoiding unneeded polls, as you would only have
> to check the stat for every packet (or set of packets) received.

What if the message arrived across multiple transports?

Also, you can always see current rto, but that means that the 
application has to still poll for it with PEER_ADDR_INFO.  I think the 
idea, as Thomas pointed out, is to easily see what the max rto was.  I 
like that, but I don't like the fact that it's per association.   With 
that stat we really don't know which transport spiked.  So, it gives us 
a some useful information, but not really enough.

It might be a little more useful to keep track of the which transport 
has experienced the spike and then return info about it.    Granted, we 
may be too late and the spike corrected itself already, but at least it 
will give the management application a little more information and allow 
admin to have a little more info to determine why the spike occurred.

-vlad

^ permalink raw reply

* [PATCH] if_ether.h: add B.A.T.M.A.N.-Advanced Ethertype
From: Antonio Quartulli @ 2012-10-30 14:08 UTC (permalink / raw)
  To: David Miller
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r

Add Ethertype 0x4305 (not an officially registered id).
This Ethertype is used by every frame generated by B.A.T.M.A.N.-Advanced. Its
definition is currently batman-adv local only and since it is not officially
registered it is better to make its definition kernel-wide so that we avoid
collisions given by future unofficial uses of the same Ethertype.

Signed-off-by: Antonio Quartulli <ordex-GaUfNO9RBHfsrOwW+9ziJQ@public.gmane.org>
---
 include/uapi/linux/if_ether.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 0343e1f..67fb87c 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -48,6 +48,7 @@
 #define	ETH_P_BPQ	0x08FF		/* G8BPQ AX.25 Ethernet Packet	[ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_IEEEPUP	0x0a00		/* Xerox IEEE802.3 PUP packet */
 #define ETH_P_IEEEPUPAT	0x0a01		/* Xerox IEEE802.3 PUP Addr Trans packet */
+#define ETH_P_BATMAN	0x4305		/* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
 #define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
 #define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
-- 
1.7.12.4

^ permalink raw reply related

* Re: [PATCH 1/1] net: fix compile warning in af_unix.c
From: Stephen Hemminger @ 2012-10-30 14:00 UTC (permalink / raw)
  To: Jing Wang; +Cc: David S. Miller, Eric Dumazet, Al Viro, Pavel Emelyanov, netdev
In-Reply-To: <1351581396-20493-1-git-send-email-windsdaemon@gmail.com>

On Tue, 30 Oct 2012 15:16:36 +0800
Jing Wang <windsdaemon@gmail.com> wrote:

> This patch fix some compile warning as follows:
> net/unix/af_unix.c: In function 'unix_bind':
> net/unix/af_unix.c:895: warning: 'path.dentry' may be used uninitialized in this function
> net/unix/af_unix.c:895: warning: 'path.mnt' may be used uninitialized in this function
> 
> Signed-off-by: Jing Wang <windsdaemon@gmail.com>
> ---
>  net/unix/af_unix.c |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
> 
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index 5b5c876..9f710ac 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -895,6 +895,8 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
>  		struct path path;
>  		umode_t mode = S_IFSOCK |
>  		       (SOCK_INODE(sock)->i_mode & ~current_umask());
> +		path.mnt = NULL;
> +		path.dentry = NULL;
>  		err = unix_mknod(sun_path, mode, &path);
>  		if (err) {
>  			if (err == -EEXIST)

I appreciate the effort to silence warnings. But this warning is a false
positive that only occurs on some versions of Gcc. With gcc 4.7 the
warning does not occur.

The general consensus has been NOT to do initializations or other changes
if the problem is due to Gcc bug.

^ permalink raw reply

* Re: [uclinux-dist-devel] [PATCH RFC net-next 2/4] bfin_mac: replace sys time stamps with raw ones instead.
From: Richard Cochran @ 2012-10-30 13:41 UTC (permalink / raw)
  To: Bob Liu
  Cc: netdev, Miroslav Lichvar, John Ronciak, John Stultz, Jeff Kirsher,
	device-drivers-devel, Jacob Keller, uclinux-dist-devel,
	Patrick Ohly, David Miller
In-Reply-To: <CAA_GA1dWgav0fP3wJNFQW=VE9Qh-V8NBHqsmNoXaRny+5dnewg@mail.gmail.com>

On Tue, Oct 30, 2012 at 05:17:51PM +0800, Bob Liu wrote:

> > @@ -892,51 +879,25 @@ static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
> >
> >         regval = bfin_read_EMAC_PTP_RXSNAPLO();
> >         regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32;
> > -       ns = timecounter_cyc2time(&lp->clock, regval);
> > -       timecompare_update(&lp->compare, ns);
> > +       ns = regval >> lp->shift;
> >         memset(shhwtstamps, 0, sizeof(*shhwtstamps));
> >         shhwtstamps->hwtstamp = ns_to_ktime(ns);
> > -       shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns);
> > -
> > -       bfin_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare);
> > -}
> 
> In my test,  system time in slave side can't be updated although here
> have set shhwtstamps->hwtstamp = ns_to_ktime(ns).
> Any idea?

So the "system time" in shhwtstamps->syststamp has been removed. Only
the raw value remains.

Or what do you mean by "can't be updated"?

How did you test this?

I would suggest the following:

1. test using Documentation/ptp/testptp to make sure the clock is
   working reasconably.

2. try ptp4l from http://linuxptp.sourceforge.net/ to make sure the
   time stamping is working

Thanks,
Richard

^ permalink raw reply

* Re: [PATCH net-next] sctp: support per-association stats via a new SCTP_GET_ASSOC_STATS call
From: Thomas Graf @ 2012-10-30 12:52 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Neil Horman, Michele Baldessari, linux-sctp, David S. Miller,
	netdev
In-Reply-To: <508EE586.9060907@gmail.com>

On 10/29/12 at 04:22pm, Vlad Yasevich wrote:
> On 10/29/2012 07:37 AM, Neil Horman wrote:
> >Hm, ok, looking for the maximum rto seen is definately more efficient that a
> >high polling rate on the remaddr file.  Still can't say I really like it as a
> >statistic though.  While it helps in diagnosing a very specific type of problem
> >(applications that have a maximum allowable latency), its really not useful, and
> >potentially misleading, in the general case.  Specificaly it may show a very
> >large RTO even if that RTO was an erroneous spike in behavior earlier in the
> >lifetime of a given transport, even if that RTO is not representative of the
> >current behavior of the association.  It seems to me like this stat might be
> >better collected using a stap script or by adding a trace point to
> >sctp_transport_update_rto.  If the application needs to know this information
> >internally during its operation to take corrective action, you can already get
> >it via the SCTP_GET_PEER_ADDR_INFO socket option on a per transport basis just
> >as efficiently.

SCTP_GET_PEER_ADDR_INFO doesn't help here as the whole point of this
stat is to get max(rto) as seen by the SCTP stack.

> The max_rto is reset after each getsockopt(), so in effect, the
> application sets its own polling interval and gets the max rto
> achieved during it.  If the rto hasn't changed, then the last value
> is returned.  Not sure how much I like that.  I would rather get max
> rto achieved per polling period and upon reset, max_rto is
> accumulated again (easy way to do that is set to rto_min on reset).
> This way an monitoring thread can truly represent the max rto
> reported by association.  It should normally remain steady, but this
> will show spikes, if any.

I would still reset it to 0 but I agree that it makes more sense to
return 0 if max(rto) remains unchanged within the observation period
rather than returning the previous max(rto).

^ permalink raw reply

* Re: [PATCH 1/4] net: mvneta: driver for Marvell Armada 370/XP network unit
From: Thomas Petazzoni @ 2012-10-30 12:28 UTC (permalink / raw)
  To: Nobuhiro Iwamatsu
  Cc: David S. Miller, Lennert Buytenhek, Lior Amsalem, Andrew Lunn,
	Jason Cooper, netdev, Maen Suleiman, Gregory Clement,
	linux-arm-kernel
In-Reply-To: <CABMQnVLBCU-2C6DbKsXAVPanWx=oQnjE7Tpi2q5bgpYJ9SQt-A@mail.gmail.com>

Dear Nobuhiro Iwamatsu,

On Tue, 30 Oct 2012 21:07:34 +0900, Nobuhiro Iwamatsu wrote:

> I tested this driver on OpenBlocks AX3 and worked fine.

Thanks.

> I will send a patch which support DT for OpenBlocks AX3.

I was also about to prepare a patch doing that, but I was waiting for
the network driver itself to be merged, and also the basic OpenBlocks
AX3 support as well.


> Tested-by: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>

Thanks!

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH 1/4] net: mvneta: driver for Marvell Armada 370/XP network unit
From: Nobuhiro Iwamatsu @ 2012-10-30 12:07 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: David S. Miller, Lennert Buytenhek, Lior Amsalem, Andrew Lunn,
	Jason Cooper, netdev, Maen Suleiman, Gregory Clement,
	linux-arm-kernel
In-Reply-To: <1351245804-31478-2-git-send-email-thomas.petazzoni@free-electrons.com>

Hi,

I tested this driver on OpenBlocks AX3 and worked fine.
I will send a patch which support DT for OpenBlocks AX3.

On Fri, Oct 26, 2012 at 7:03 PM, Thomas Petazzoni
<thomas.petazzoni@free-electrons.com> wrote:
> This patch contains a new network driver for the network unit of the
> ARM Marvell Armada 370 and the Armada XP. Both SoCs use the PJ4B
> processor, a Marvell-developed ARM core that implements the ARMv7
> instruction set.
>
> Compared to previous ARM Marvell SoCs (Kirkwood, Orion, Discovery),
> the network unit in Armada 370 and Armada XP is highly different. This
> is the reason why this new 'mvneta' driver is needed, while the older
> ARM Marvell SoCs use the 'mv643xx_eth' driver.
>
> Here is an overview of the most important hardware changes that
> require a new, specific, driver for the network unit of Armada 370/XP:
>
>  - The new network unit has a completely different design and layout
>    for the RX and TX descriptors. They are now organized as a simple
>    array (each RX and TX queue has base address and size of this
>    array) rather than a linked list as in the old SoCs.
>
>  - The new network unit has a different RXQ and TXQ management: this
>    management is done using special read/write counter registers,
>    while in the Old SocS, it was done using the Ownership bit in RX
>    and TX descriptors.
>
>  - The new network unit has different interrupt registers
>
>  - The new network unit way of cleaning of interrupts is not done by
>    writing to the cause register, but by updating per-queue counters
>
>  - The new network unit has different GMAC registers (link, speed,
>    duplex configuration) and different WRR registers.
>
>  - The new network unit has lots of new units like PnC (Parser and
>    Classifier), PMT, BM (Memory Buffer Management), xPON, and more.
>
> The driver proposed in the current patch only handles the basic
> features. Additional hardware features will progressively be supported
> as needed.
>
> This code has originally been written by Rami Rosen
> <rosenr@marvell.com>, and then reviewed and cleaned up by Thomas
> Petazzoni <thomas.petazzoni@free-electrons.com>.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Tested-by: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>

> ---
>  .../bindings/net/marvell-armada-370-neta.txt       |   22 +
>  drivers/net/ethernet/marvell/Kconfig               |   12 +
>  drivers/net/ethernet/marvell/Makefile              |    1 +
>  drivers/net/ethernet/marvell/mvneta.c              | 3006 ++++++++++++++++++++
>  4 files changed, 3041 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
>  create mode 100644 drivers/net/ethernet/marvell/mvneta.c
>
> diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
> new file mode 100644
> index 0000000..b094c79
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
> @@ -0,0 +1,22 @@
> +* Marvell Armada 370 / Armada XP Ethernet Controller (NETA)
> +
> +Required properties:
> +- compatible: should be "marvell,armada-370-neta".
> +- reg: address and length of the register set for the device.
> +- interrupts: interrupt for the device
> +- phy-mode: String, operation mode of the PHY interface. Supported
> +  values are the ones handled by of_get_phy_mode().
> +- phy-addr: Integer, address of the PHY.
> +- clock-frequency: frequency of the peripheral clock of the SoC.
> +
> +Example:
> +
> +ethernet@d0070000 {
> +               compatible = "marvell,armada-370-neta";
> +               reg = <0xd0070000 0x2500>;
> +               interrupts = <8>;
> +               clock-frequency = <250000000>;
> +               status = "okay";
> +               phy-mode = "rgmii-id";
> +               phy-addr = <25>;
> +};
> diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
> index 0029934..7bdc5da 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -18,6 +18,18 @@ config NET_VENDOR_MARVELL
>
>  if NET_VENDOR_MARVELL
>
> +config MVNETA
> +       tristate "Marvell Armada 370/XP network interface support"
> +       depends on MACH_ARMADA_370_XP
> +       select PHYLIB
> +       ---help---
> +         This driver supports the network interface units in the
> +         Marvell ARMADA XP and ARMADA 370 SoC family.
> +
> +         Note that this driver is distinct from the mv643xx_eth
> +         driver, which should be used for the older Marvell SoCs
> +         (Dove, Orion, Discovery, Kirkwood).
> +
>  config MV643XX_ETH
>         tristate "Marvell Discovery (643XX) and Orion ethernet support"
>         depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
> diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
> index 57e3234..a13f9b9 100644
> --- a/drivers/net/ethernet/marvell/Makefile
> +++ b/drivers/net/ethernet/marvell/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>  obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
>  obj-$(CONFIG_SKGE) += skge.o
>  obj-$(CONFIG_SKY2) += sky2.o
> +obj-$(CONFIG_MVNETA) += mvneta.o
> diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
> new file mode 100644
> index 0000000..1eef8bc
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/mvneta.c
> @@ -0,0 +1,3006 @@
> +/*
> + * Driver for Marvell NETA network card for Armada XP and Armada 370 SoCs.
> + *
> + * Copyright (C) 2012 Marvell
> + *
> + * Rami Rosen <rosenr@marvell.com>
> + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> + *
> + * 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/version.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/platform_device.h>
> +#include <linux/skbuff.h>
> +#include <linux/inetdevice.h>
> +#include <linux/mbus.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <net/ip.h>
> +#include <net/ipv6.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_net.h>
> +#include <linux/of_address.h>
> +#include <linux/phy.h>
> +
> +/* Registers */
> +#define MVNETA_RXQ_CONFIG_REG(q)                (0x1400 + ((q) << 2))
> +#define      MVNETA_RXQ_HW_BUF_ALLOC            BIT(1)
> +#define      MVNETA_RXQ_PKT_OFFSET_ALL_MASK     (0xf    << 8)
> +#define      MVNETA_RXQ_PKT_OFFSET_MASK(offs)   ((offs) << 8)
> +#define MVNETA_RXQ_THRESHOLD_REG(q)             (0x14c0 + ((q) << 2))
> +#define      MVNETA_RXQ_NON_OCCUPIED(v)         ((v) << 16)
> +#define MVNETA_RXQ_BASE_ADDR_REG(q)             (0x1480 + ((q) << 2))
> +#define MVNETA_RXQ_SIZE_REG(q)                  (0x14a0 + ((q) << 2))
> +#define      MVNETA_RXQ_BUF_SIZE_SHIFT          19
> +#define      MVNETA_RXQ_BUF_SIZE_MASK           (0x1fff << 19)
> +#define MVNETA_RXQ_STATUS_REG(q)                (0x14e0 + ((q) << 2))
> +#define      MVNETA_RXQ_OCCUPIED_ALL_MASK       0x3fff
> +#define MVNETA_RXQ_STATUS_UPDATE_REG(q)         (0x1500 + ((q) << 2))
> +#define      MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT  16
> +#define      MVNETA_RXQ_ADD_NON_OCCUPIED_MAX    255
> +#define MVNETA_PORT_RX_RESET                    0x1cc0
> +#define      MVNETA_PORT_RX_DMA_RESET           BIT(0)
> +#define MVNETA_PHY_ADDR                         0x2000
> +#define      MVNETA_PHY_ADDR_MASK               0x1f
> +#define MVNETA_SMI                              0x2004
> +#define      MVNETA_SMI_DATA_SHIFT              0
> +#define      MVNETA_SMI_PHY_ADDR_SHIFT          16
> +#define      MVNETA_SMI_PHY_REG_SHIFT           21
> +#define      MVNETA_SMI_READ_OPERATION          BIT(26)
> +#define      MVNETA_SMI_WRITE_OPERATION         0
> +#define      MVNETA_SMI_READ_VALID              BIT(27)
> +#define      MVNETA_SMI_BUSY                    BIT(28)
> +#define MVNETA_MBUS_RETRY                       0x2010
> +#define MVNETA_UNIT_INTR_CAUSE                  0x2080
> +#define MVNETA_UNIT_CONTROL                     0x20B0
> +#define      MVNETA_PHY_POLLING_ENABLE          BIT(1)
> +#define MVNETA_WIN_BASE(w)                      (0x2200 + ((w) << 3))
> +#define MVNETA_WIN_SIZE(w)                      (0x2204 + ((w) << 3))
> +#define MVNETA_WIN_REMAP(w)                     (0x2280 + ((w) << 2))
> +#define MVNETA_BASE_ADDR_ENABLE                 0x2290
> +#define MVNETA_PORT_CONFIG                      0x2400
> +#define      MVNETA_UNI_PROMISC_MODE            BIT(0)
> +#define      MVNETA_DEF_RXQ(q)                  ((q) << 1)
> +#define      MVNETA_DEF_RXQ_ARP(q)              ((q) << 4)
> +#define      MVNETA_TX_UNSET_ERR_SUM            BIT(12)
> +#define      MVNETA_DEF_RXQ_TCP(q)              ((q) << 16)
> +#define      MVNETA_DEF_RXQ_UDP(q)              ((q) << 19)
> +#define      MVNETA_DEF_RXQ_BPDU(q)             ((q) << 22)
> +#define      MVNETA_RX_CSUM_WITH_PSEUDO_HDR     BIT(25)
> +#define      MVNETA_PORT_CONFIG_DEFL_VALUE(q)   (MVNETA_DEF_RXQ(q)       | \
> +                                                MVNETA_DEF_RXQ_ARP(q)   | \
> +                                                MVNETA_DEF_RXQ_TCP(q)   | \
> +                                                MVNETA_DEF_RXQ_UDP(q)   | \
> +                                                MVNETA_DEF_RXQ_BPDU(q)  | \
> +                                                MVNETA_TX_UNSET_ERR_SUM | \
> +                                                MVNETA_RX_CSUM_WITH_PSEUDO_HDR)
> +#define MVNETA_PORT_CONFIG_EXTEND                0x2404
> +#define MVNETA_MAC_ADDR_LOW                      0x2414
> +#define MVNETA_MAC_ADDR_HIGH                     0x2418
> +#define MVNETA_SDMA_CONFIG                       0x241c
> +#define      MVNETA_SDMA_BRST_SIZE_16            4
> +#define      MVNETA_NO_DESC_SWAP                 0x0
> +#define      MVNETA_RX_BRST_SZ_MASK(burst)       ((burst) << 1)
> +#define      MVNETA_RX_NO_DATA_SWAP              BIT(4)
> +#define      MVNETA_TX_NO_DATA_SWAP              BIT(5)
> +#define      MVNETA_TX_BRST_SZ_MASK(burst)       ((burst) << 22)
> +#define MVNETA_PORT_STATUS                       0x2444
> +#define      MVNETA_TX_IN_PRGRS                  BIT(1)
> +#define      MVNETA_TX_FIFO_EMPTY                BIT(8)
> +#define MVNETA_RX_MIN_FRAME_SIZE                 0x247c
> +#define MVNETA_TYPE_PRIO                         0x24bc
> +#define      MVNETA_FORCE_UNI                    BIT(21)
> +#define MVNETA_TXQ_CMD_1                         0x24e4
> +#define MVNETA_TXQ_CMD                           0x2448
> +#define      MVNETA_TXQ_DISABLE_SHIFT            8
> +#define      MVNETA_TXQ_ENABLE_MASK              0x000000ff
> +#define MVNETA_ACC_MODE                          0x2500
> +#define MVNETA_CPU_MAP(cpu)                      (0x2540 + ((cpu) << 2))
> +#define      MVNETA_CPU_RXQ_ACCESS_ALL_MASK      0x000000ff
> +#define      MVNETA_CPU_TXQ_ACCESS_ALL_MASK      0x0000ff00
> +#define MVNETA_RXQ_TIME_COAL_REG(q)              (0x2580 + ((q) << 2))
> +#define MVNETA_INTR_NEW_CAUSE                    0x25a0
> +#define      MVNETA_RX_INTR_MASK(nr_rxqs)        (((1 << nr_rxqs) - 1) << 8)
> +#define MVNETA_INTR_NEW_MASK                     0x25a4
> +#define MVNETA_INTR_OLD_CAUSE                    0x25a8
> +#define MVNETA_INTR_OLD_MASK                     0x25ac
> +#define MVNETA_INTR_MISC_CAUSE                   0x25b0
> +#define MVNETA_INTR_MISC_MASK                    0x25b4
> +#define MVNETA_INTR_ENABLE                       0x25b8
> +#define      MVNETA_TXQ_INTR_ENABLE_ALL_MASK     0x0000ff00
> +#define      MVNETA_RXQ_INTR_ENABLE_ALL_MASK     0xff000000
> +#define MVNETA_RXQ_CMD                           0x2680
> +#define      MVNETA_RXQ_DISABLE_SHIFT            8
> +#define      MVNETA_RXQ_ENABLE_MASK              0x000000ff
> +#define MVETH_TXQ_TOKEN_COUNT_REG(q)             (0x2700 + ((q) << 4))
> +#define MVETH_TXQ_TOKEN_CFG_REG(q)               (0x2704 + ((q) << 4))
> +#define MVNETA_GMAC_CTRL_0                       0x2c00
> +#define      MVNETA_GMAC_MAX_RX_SIZE_SHIFT       2
> +#define      MVNETA_GMAC_MAX_RX_SIZE_MASK        0x7ffc
> +#define      MVNETA_GMAC0_PORT_ENABLE            BIT(0)
> +#define MVNETA_GMAC_CTRL_2                       0x2c08
> +#define      MVNETA_GMAC2_PSC_ENABLE             BIT(3)
> +#define      MVNETA_GMAC2_PORT_RGMII             BIT(4)
> +#define      MVNETA_GMAC2_PORT_RESET             BIT(6)
> +#define MVNETA_GMAC_STATUS                       0x2c10
> +#define      MVNETA_GMAC_LINK_UP                 BIT(0)
> +#define      MVNETA_GMAC_SPEED_1000              BIT(1)
> +#define      MVNETA_GMAC_SPEED_100               BIT(2)
> +#define      MVNETA_GMAC_FULL_DUPLEX             BIT(3)
> +#define      MVNETA_GMAC_RX_FLOW_CTRL_ENABLE     BIT(4)
> +#define      MVNETA_GMAC_TX_FLOW_CTRL_ENABLE     BIT(5)
> +#define      MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE     BIT(6)
> +#define      MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE     BIT(7)
> +#define MVNETA_GMAC_AUTONEG_CONFIG               0x2c0c
> +#define      MVNETA_GMAC_FORCE_LINK_DOWN         BIT(0)
> +#define      MVNETA_GMAC_FORCE_LINK_PASS         BIT(1)
> +#define      MVNETA_GMAC_CONFIG_MII_SPEED        BIT(5)
> +#define      MVNETA_GMAC_CONFIG_GMII_SPEED       BIT(6)
> +#define      MVNETA_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
> +#define MVNETA_MIB_COUNTERS_BASE                 0x3080
> +#define      MVNETA_MIB_LATE_COLLISION           0x7c
> +#define MVNETA_DA_FILT_SPEC_MCAST                0x3400
> +#define MVNETA_DA_FILT_OTH_MCAST                 0x3500
> +#define MVNETA_DA_FILT_UCAST_BASE                0x3600
> +#define MVNETA_TXQ_BASE_ADDR_REG(q)              (0x3c00 + ((q) << 2))
> +#define MVNETA_TXQ_SIZE_REG(q)                   (0x3c20 + ((q) << 2))
> +#define      MVNETA_TXQ_SENT_THRESH_ALL_MASK     0x3fff0000
> +#define      MVNETA_TXQ_SENT_THRESH_MASK(coal)   ((coal) << 16)
> +#define MVNETA_TXQ_UPDATE_REG(q)                 (0x3c60 + ((q) << 2))
> +#define      MVNETA_TXQ_DEC_SENT_SHIFT           16
> +#define MVNETA_TXQ_STATUS_REG(q)                 (0x3c40 + ((q) << 2))
> +#define      MVNETA_TXQ_SENT_DESC_SHIFT          16
> +#define      MVNETA_TXQ_SENT_DESC_MASK           0x3fff0000
> +#define MVNETA_PORT_TX_RESET                     0x3cf0
> +#define      MVNETA_PORT_TX_DMA_RESET            BIT(0)
> +#define MVNETA_TX_MTU                            0x3e0c
> +#define MVNETA_TX_TOKEN_SIZE                     0x3e14
> +#define      MVNETA_TX_TOKEN_SIZE_MAX            0xffffffff
> +#define MVNETA_TXQ_TOKEN_SIZE_REG(q)             (0x3e40 + ((q) << 2))
> +#define      MVNETA_TXQ_TOKEN_SIZE_MAX           0x7fffffff
> +
> +#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK     0xff
> +
> +/* Descriptor ring Macros */
> +#define MVNETA_QUEUE_NEXT_DESC(q, index)       \
> +       (((index) < (q)->last_desc) ? ((index) + 1) : 0)
> +
> +/* Various constants */
> +
> +/* Coalescing */
> +#define MVNETA_TXDONE_COAL_PKTS                16
> +#define MVNETA_RX_COAL_PKTS            32
> +#define MVNETA_RX_COAL_USEC            100
> +
> +/* Timer */
> +#define MVNETA_TX_DONE_TIMER_PERIOD    10
> +
> +/* Napi polling weight */
> +#define MVNETA_RX_POLL_WEIGHT          64
> +
> +#define MVNETA_MH_SIZE                 2
> +
> +#define MVNETA_CPU_D_CACHE_LINE_SIZE    32
> +#define MVNETA_ETH_CRC_SIZE            4
> +#define MVNETA_TX_CSUM_MAX_SIZE                9800
> +#define MVNETA_ACC_MODE_EXT            1
> +
> +/* Timeout constants */
> +#define MVNETA_TX_DISABLE_TIMEOUT_MSEC 1000
> +#define MVNETA_RX_DISABLE_TIMEOUT_MSEC 1000
> +#define MVNETA_TX_FIFO_EMPTY_TIMEOUT   10000
> +
> +#define MVNETA_TX_MTU_MAX              0x3ffff
> +
> +/* Max number of Rx descriptors */
> +#define MVNETA_MAX_RXD 128
> +
> +/* Max number of Tx descriptors */
> +#define MVNETA_MAX_TXD 532
> +
> +/* descriptor aligned size */
> +#define MVNETA_DESC_ALIGNED_SIZE       32
> +
> +#define MVNETA_RX_PKT_SIZE(mtu) \
> +       ALIGN((mtu) + 2 + 4 + ETH_HLEN + 4, MVNETA_CPU_D_CACHE_LINE_SIZE)
> +
> +#define MVNETA_RX_BUF_SIZE(pkt_size)   ((pkt_size) + NET_SKB_PAD)
> +
> +struct mvneta_stats {
> +       struct  u64_stats_sync syncp;
> +       u64     packets;
> +       u64     bytes;
> +};
> +
> +struct mvneta_port {
> +       int pkt_size;
> +       void __iomem *base;
> +       struct mvneta_rx_queue *rxqs;
> +       struct mvneta_tx_queue *txqs;
> +       struct timer_list tx_done_timer;
> +       struct net_device *dev;
> +
> +       u32 cause_rx_tx;
> +       struct napi_struct napi;
> +
> +       /* Flags */
> +       unsigned long flags;
> +#define MVNETA_F_TX_DONE_TIMER_BIT  0
> +
> +       /* Napi weight */
> +       int weight;
> +
> +       /* Core clock */
> +       unsigned int clk_rate_hz;
> +       u8 mcast_count[256];
> +       u16 tx_ring_size;
> +       u16 rx_ring_size;
> +       struct mvneta_stats tx_stats;
> +       struct mvneta_stats rx_stats;
> +
> +       struct mii_bus *mii_bus;
> +       struct phy_device *phy_dev;
> +       phy_interface_t phy_interface;
> +       unsigned int link;
> +       unsigned int duplex;
> +       unsigned int speed;
> +};
> +
> +/*
> + * The mvneta_tx_desc and mvneta_rx_desc structures describe the
> + * layout of the transmit and reception DMA descriptors, and their
> + * layout is therefore defined by the hardware design
> + */
> +struct mvneta_tx_desc {
> +       u32  command;           /* Options used by HW for packet transmitting.*/
> +#define MVNETA_TX_L3_OFF_SHIFT 0
> +#define MVNETA_TX_IP_HLEN_SHIFT        8
> +#define MVNETA_TX_L4_UDP       BIT(16)
> +#define MVNETA_TX_L3_IP6       BIT(17)
> +#define MVNETA_TXD_IP_CSUM     BIT(18)
> +#define MVNETA_TXD_Z_PAD       BIT(19)
> +#define MVNETA_TXD_L_DESC      BIT(20)
> +#define MVNETA_TXD_F_DESC      BIT(21)
> +#define MVNETA_TXD_FLZ_DESC    (MVNETA_TXD_Z_PAD  | \
> +                                MVNETA_TXD_L_DESC | \
> +                                MVNETA_TXD_F_DESC)
> +#define MVNETA_TX_L4_CSUM_FULL BIT(30)
> +#define MVNETA_TX_L4_CSUM_NOT  BIT(31)
> +
> +       u16  reserverd1;        /* csum_l4 (for future use)             */
> +       u16  data_size;         /* Data size of transmitted packet in bytes */
> +       u32  buf_phys_addr;     /* Physical addr of transmitted buffer  */
> +       u32  reserved2;         /* hw_cmd - (for future use, PMT)       */
> +       u32  reserved3[4];      /* Reserved - (for future use)          */
> +};
> +
> +struct mvneta_rx_desc {
> +       u32  status;            /* Info about received packet           */
> +#define MVNETA_RXD_ERR_CRC             0x0
> +#define MVNETA_RXD_ERR_SUMMARY         BIT(16)
> +#define MVNETA_RXD_ERR_OVERRUN         BIT(17)
> +#define MVNETA_RXD_ERR_LEN             BIT(18)
> +#define MVNETA_RXD_ERR_RESOURCE                (BIT(17) | BIT(18))
> +#define MVNETA_RXD_ERR_CODE_MASK       (BIT(17) | BIT(18))
> +#define MVNETA_RXD_L3_IP4              BIT(25)
> +#define MVNETA_RXD_FIRST_LAST_DESC     (BIT(26) | BIT(27))
> +#define MVNETA_RXD_L4_CSUM_OK          BIT(30)
> +
> +       u16  reserved1;         /* pnc_info - (for future use, PnC)     */
> +       u16  data_size;         /* Size of received packet in bytes     */
> +       u32  buf_phys_addr;     /* Physical address of the buffer       */
> +       u32  reserved2;         /* pnc_flow_id  (for future use, PnC)   */
> +       u32  buf_cookie;        /* cookie for access to RX buffer in rx path */
> +       u16  reserved3;         /* prefetch_cmd, for future use         */
> +       u16  reserved4;         /* csum_l4 - (for future use, PnC)      */
> +       u32  reserved5;         /* pnc_extra PnC (for future use, PnC)  */
> +       u32  reserved6;         /* hw_cmd (for future use, PnC and HWF) */
> +};
> +
> +struct mvneta_tx_queue {
> +       /* Number of this TX queue, in the range 0-7 */
> +       u8 id;
> +
> +       /* Number of TX DMA descriptors in the descriptor ring */
> +       int size;
> +
> +       /* Number of currently used TX DMA descriptor in the
> +        * descriptor ring */
> +       int count;
> +
> +       /* Array of transmitted skb */
> +       struct sk_buff **tx_skb;
> +
> +       /* Index of last TX DMA descriptor that was inserted */
> +       int txq_put_index;
> +
> +       /* Index of the TX DMA descriptor to be cleaned up */
> +       int txq_get_index;
> +
> +       u32 done_pkts_coal;
> +
> +       /* Virtual address of the TX DMA descriptors array */
> +       struct mvneta_tx_desc *descs;
> +
> +       /* DMA address of the TX DMA descriptors array */
> +       dma_addr_t descs_phys;
> +
> +       /* Index of the last TX DMA descriptor */
> +       int last_desc;
> +
> +       /* Index of the next TX DMA descriptor to process */
> +       int next_desc_to_proc;
> +};
> +
> +struct mvneta_rx_queue {
> +       /* rx queue number, in the range 0-7 */
> +       u8 id;
> +
> +       /* num of rx descriptors in the rx descriptor ring */
> +       int size;
> +
> +       /* counter of times when mvneta_refill() failed */
> +       int missed;
> +
> +       u32 pkts_coal;
> +       u32 time_coal;
> +
> +       /* Virtual address of the RX DMA descriptors array */
> +       struct mvneta_rx_desc *descs;
> +
> +       /* DMA address of the RX DMA descriptors array */
> +       dma_addr_t descs_phys;
> +
> +       /* Index of the last RX DMA descriptor */
> +       int last_desc;
> +
> +       /* Index of the next RX DMA descriptor to process */
> +       int next_desc_to_proc;
> +};
> +
> +static int rxq_number = 8;
> +static int txq_number = 8;
> +
> +static int rxq_def;
> +static int txq_def;
> +
> +#define MVNETA_DRIVER_NAME "mvneta"
> +#define MVNETA_DRIVER_VERSION "1.0"
> +
> +/* Utility/helper methods */
> +
> +/* Write helper method */
> +static void mvreg_write(struct mvneta_port *pp, u32 offset, u32 data)
> +{
> +       writel(data, pp->base + offset);
> +}
> +
> +/* Read helper method */
> +static u32 mvreg_read(struct mvneta_port *pp, u32 offset)
> +{
> +       return readl(pp->base + offset);
> +}
> +
> +/* Increment txq get counter */
> +static void mvneta_txq_inc_get(struct mvneta_tx_queue *txq)
> +{
> +       txq->txq_get_index++;
> +       if (txq->txq_get_index == txq->size)
> +               txq->txq_get_index = 0;
> +}
> +
> +/* Increment txq put counter */
> +static void mvneta_txq_inc_put(struct mvneta_tx_queue *txq)
> +{
> +       txq->txq_put_index++;
> +       if (txq->txq_put_index == txq->size)
> +               txq->txq_put_index = 0;
> +}
> +
> +
> +/* Clear all MIB counters */
> +static void mvneta_mib_counters_clear(struct mvneta_port *pp)
> +{
> +       int i;
> +       u32 dummy;
> +
> +       /* Perform dummy reads from MIB counters */
> +       for (i = 0; i < MVNETA_MIB_LATE_COLLISION; i += 4)
> +               dummy = mvreg_read(pp, (MVNETA_MIB_COUNTERS_BASE + i));
> +}
> +
> +/* Get System Network Statistics */
> +struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev,
> +                                            struct rtnl_link_stats64 *stats)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       unsigned int start;
> +
> +       memset(stats, 0, sizeof(struct rtnl_link_stats64));
> +
> +       do {
> +               start = u64_stats_fetch_begin_bh(&pp->rx_stats.syncp);
> +               stats->rx_packets = pp->rx_stats.packets;
> +               stats->rx_bytes = pp->rx_stats.bytes;
> +       } while (u64_stats_fetch_retry_bh(&pp->rx_stats.syncp, start));
> +
> +
> +       do {
> +               start = u64_stats_fetch_begin_bh(&pp->tx_stats.syncp);
> +               stats->tx_packets = pp->tx_stats.packets;
> +               stats->tx_bytes = pp->tx_stats.bytes;
> +       } while (u64_stats_fetch_retry_bh(&pp->tx_stats.syncp, start));
> +
> +       stats->rx_errors        = dev->stats.rx_errors;
> +       stats->rx_dropped       = dev->stats.rx_dropped;
> +
> +       stats->tx_dropped       = dev->stats.tx_dropped;
> +
> +       return stats;
> +}
> +
> +/* Rx descriptors helper methods */
> +
> +/* Add number of descriptors ready to receive new packets */
> +static void mvneta_rxq_non_occup_desc_add(struct mvneta_port *pp,
> +                                         struct mvneta_rx_queue *rxq,
> +                                         int ndescs)
> +{
> +       /* Only MVNETA_RXQ_ADD_NON_OCCUPIED_MAX (255) descriptors can
> +        * be added at once */
> +       while (ndescs > MVNETA_RXQ_ADD_NON_OCCUPIED_MAX) {
> +               mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id),
> +                           (MVNETA_RXQ_ADD_NON_OCCUPIED_MAX <<
> +                            MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT));
> +               ndescs -= MVNETA_RXQ_ADD_NON_OCCUPIED_MAX;
> +       }
> +
> +       mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id),
> +                   (ndescs << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT));
> +}
> +
> +/* Get number of RX descriptors occupied by received packets */
> +static int mvneta_rxq_busy_desc_num_get(struct mvneta_port *pp,
> +                                       struct mvneta_rx_queue *rxq)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_RXQ_STATUS_REG(rxq->id));
> +       return val & MVNETA_RXQ_OCCUPIED_ALL_MASK;
> +}
> +
> +/*
> + * Update num of rx desc called upon return from rx path or
> + * from mvneta_rxq_drop_pkts().
> + */
> +static void mvneta_rxq_desc_num_update(struct mvneta_port *pp,
> +                                      struct mvneta_rx_queue *rxq,
> +                                      int rx_done, int rx_filled)
> +{
> +       u32 val;
> +
> +       if ((rx_done <= 0xff) && (rx_filled <= 0xff)) {
> +               val = rx_done |
> +                 (rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT);
> +               mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
> +               return;
> +       }
> +
> +       /* Only 255 descriptors can be added at once */
> +       while ((rx_done > 0) || (rx_filled > 0)) {
> +               if (rx_done <= 0xff) {
> +                       val = rx_done;
> +                       rx_done = 0;
> +               } else {
> +                       val = 0xff;
> +                       rx_done -= 0xff;
> +               }
> +               if (rx_filled <= 0xff) {
> +                       val |= rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
> +                       rx_filled = 0;
> +               } else {
> +                       val |= 0xff << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
> +                       rx_filled -= 0xff;
> +               }
> +               mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
> +       }
> +}
> +
> +/* Get pointer to next RX descriptor to be processed by SW */
> +static struct mvneta_rx_desc *
> +mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq)
> +{
> +       int rx_desc = rxq->next_desc_to_proc;
> +       rxq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc);
> +       return rxq->descs + rx_desc;
> +}
> +
> +/* Change maximum receive size of the port. */
> +static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size)
> +{
> +       u32 val;
> +
> +       val =  mvreg_read(pp, MVNETA_GMAC_CTRL_0);
> +       val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK;
> +       val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) <<
> +               MVNETA_GMAC_MAX_RX_SIZE_SHIFT;
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
> +}
> +
> +
> +/* Set rx queue offset */
> +static void mvneta_rxq_offset_set(struct mvneta_port *pp,
> +                                 struct mvneta_rx_queue *rxq,
> +                                 int offset)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
> +       val &= ~MVNETA_RXQ_PKT_OFFSET_ALL_MASK;
> +
> +       /* Offset is in */
> +       val |= MVNETA_RXQ_PKT_OFFSET_MASK(offset >> 3);
> +       mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
> +}
> +
> +
> +/* Tx descriptors helper methods */
> +
> +/* Update HW with number of TX descriptors to be sent */
> +static void mvneta_txq_pend_desc_add(struct mvneta_port *pp,
> +                                    struct mvneta_tx_queue *txq,
> +                                    int pend_desc)
> +{
> +       u32 val;
> +
> +       /* Only 255 descriptors can be added at once ; Assume caller
> +          process TX desriptors in quanta less than 256 */
> +       val = pend_desc;
> +       mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
> +}
> +
> +/* Get pointer to next TX descriptor to be processed (send) by HW */
> +static struct mvneta_tx_desc *
> +mvneta_txq_next_desc_get(struct mvneta_tx_queue *txq)
> +{
> +       int tx_desc = txq->next_desc_to_proc;
> +       txq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(txq, tx_desc);
> +       return txq->descs + tx_desc;
> +}
> +
> +/* Release the last allocated TX descriptor. Useful to handle DMA
> + * mapping failures in the TX path. */
> +static void mvneta_txq_desc_put(struct mvneta_tx_queue *txq)
> +{
> +       if (txq->next_desc_to_proc == 0)
> +               txq->next_desc_to_proc = txq->last_desc - 1;
> +       else
> +               txq->next_desc_to_proc--;
> +}
> +
> +/* Set rxq buf size */
> +static void mvneta_rxq_buf_size_set(struct mvneta_port *pp,
> +                                   struct mvneta_rx_queue *rxq,
> +                                   int buf_size)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_RXQ_SIZE_REG(rxq->id));
> +
> +       val &= ~MVNETA_RXQ_BUF_SIZE_MASK;
> +       val |= ((buf_size >> 3) << MVNETA_RXQ_BUF_SIZE_SHIFT);
> +
> +       mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), val);
> +}
> +
> +/* Disable buffer management (BM) */
> +static void mvneta_rxq_bm_disable(struct mvneta_port *pp,
> +                                 struct mvneta_rx_queue *rxq)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
> +       val &= ~MVNETA_RXQ_HW_BUF_ALLOC;
> +       mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
> +}
> +
> +
> +
> +/* Sets the RGMII Enable bit (RGMIIEn) in port MAC control register */
> +static void __devinit mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable)
> +{
> +       u32  val;
> +
> +       val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
> +
> +       if (enable)
> +               val |= MVNETA_GMAC2_PORT_RGMII;
> +       else
> +               val &= ~MVNETA_GMAC2_PORT_RGMII;
> +
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
> +}
> +
> +/* Config SGMII port */
> +static void __devinit mvneta_port_sgmii_config(struct mvneta_port *pp)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
> +       val |= MVNETA_GMAC2_PSC_ENABLE;
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
> +}
> +
> +/* Start the Ethernet port RX and TX activity */
> +static void mvneta_port_up(struct mvneta_port *pp)
> +{
> +       int queue;
> +       u32 q_map;
> +
> +       /* Enable all initialized TXs. */
> +       mvneta_mib_counters_clear(pp);
> +       q_map = 0;
> +       for (queue = 0; queue < txq_number; queue++) {
> +               struct mvneta_tx_queue *txq = &pp->txqs[queue];
> +               if (txq->descs != NULL)
> +                       q_map |= (1 << queue);
> +       }
> +       mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
> +
> +       /* Enable all initialized RXQs. */
> +       q_map = 0;
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
> +               if (rxq->descs != NULL)
> +                       q_map |= (1 << queue);
> +       }
> +
> +       mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
> +}
> +
> +/* Stop the Ethernet port activity */
> +static void mvneta_port_down(struct mvneta_port *pp)
> +{
> +       u32 val;
> +       int count;
> +
> +       /* Stop Rx port activity. Check port Rx activity. */
> +       val = mvreg_read(pp, MVNETA_RXQ_CMD) & MVNETA_RXQ_ENABLE_MASK;
> +
> +       /* Issue stop command for active channels only */
> +       if (val != 0)
> +               mvreg_write(pp, MVNETA_RXQ_CMD,
> +                           val << MVNETA_RXQ_DISABLE_SHIFT);
> +
> +       /* Wait for all Rx activity to terminate. */
> +       count = 0;
> +       do {
> +               if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) {
> +                       netdev_warn(pp->dev,
> +                                   "TIMEOUT for RX stopped ! rx_queue_cmd: 0x08%x\n",
> +                                   val);
> +                       break;
> +               }
> +               mdelay(1);
> +
> +               val = mvreg_read(pp, MVNETA_RXQ_CMD);
> +       } while (val & 0xff);
> +
> +       /* Stop Tx port activity. Check port Tx activity. Issue stop
> +          command for active channels only  */
> +       val = (mvreg_read(pp, MVNETA_TXQ_CMD)) & MVNETA_TXQ_ENABLE_MASK;
> +
> +       if (val != 0)
> +               mvreg_write(pp, MVNETA_TXQ_CMD,
> +                           (val << MVNETA_TXQ_DISABLE_SHIFT));
> +
> +       /* Wait for all Tx activity to terminate. */
> +       count = 0;
> +       do {
> +               if (count++ >= MVNETA_TX_DISABLE_TIMEOUT_MSEC) {
> +                       netdev_warn(pp->dev,
> +                                   "TIMEOUT for TX stopped status=0x%08x\n",
> +                                   val);
> +                       break;
> +               }
> +               mdelay(1);
> +
> +               /* Check TX Command reg that all Txqs are stopped */
> +               val = mvreg_read(pp, MVNETA_TXQ_CMD);
> +
> +       } while (val & 0xff);
> +
> +       /* Double check to verify that TX FIFO is empty */
> +       count = 0;
> +       do {
> +               if (count++ >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) {
> +                       netdev_warn(pp->dev,
> +                                   "TX FIFO empty timeout status=0x08%x", val);
> +                       break;
> +               }
> +               mdelay(1);
> +
> +               val = mvreg_read(pp, MVNETA_PORT_STATUS);
> +       } while (!(val & MVNETA_TX_FIFO_EMPTY) &&
> +                (val & MVNETA_TX_IN_PRGRS));
> +
> +       udelay(200);
> +}
> +
> +/* Enable the port by setting the port enable bit of the MAC control register */
> +static void mvneta_port_enable(struct mvneta_port *pp)
> +{
> +       u32 val;
> +
> +       /* Enable port */
> +       val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
> +       val |= MVNETA_GMAC0_PORT_ENABLE;
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
> +}
> +
> +/* Disable the port and wait for about 200 usec before retuning */
> +static void mvneta_port_disable(struct mvneta_port *pp)
> +{
> +       u32 val;
> +
> +       /* Reset the Enable bit in the Serial Control Register */
> +       val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
> +       val &= ~MVNETA_GMAC0_PORT_ENABLE;
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
> +
> +       udelay(200);
> +}
> +
> +/* Multicast tables methods */
> +
> +/* Set all entries in Unicast MAC Table; queue==-1 means reject all */
> +static void mvneta_set_ucast_table(struct mvneta_port *pp, int queue)
> +{
> +       int offset;
> +       u32 val;
> +
> +       if (queue == -1) {
> +               val = 0;
> +       } else {
> +               val = 0x1 | (queue << 1);
> +               val |= (val << 24) | (val << 16) | (val << 8);
> +       }
> +
> +       for (offset = 0; offset <= 0xc; offset += 4)
> +               mvreg_write(pp, MVNETA_DA_FILT_UCAST_BASE + offset, val);
> +}
> +
> +/* Set all entries in Special Multicast MAC Table; queue==-1 means reject all */
> +static void mvneta_set_special_mcast_table(struct mvneta_port *pp, int queue)
> +{
> +       int offset;
> +       u32 val;
> +
> +       if (queue == -1) {
> +               val = 0;
> +       } else {
> +               val = 0x1 | (queue << 1);
> +               val |= (val << 24) | (val << 16) | (val << 8);
> +       }
> +
> +       for (offset = 0; offset <= 0xfc; offset += 4)
> +               mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + offset, val);
> +
> +}
> +
> +/* Set all entries in Other Multicast MAC Table. queue==-1 means reject all */
> +static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
> +{
> +       int offset;
> +       u32 val;
> +
> +       if (queue == -1) {
> +               memset(pp->mcast_count, 0, sizeof(pp->mcast_count));
> +               val = 0;
> +       } else {
> +               memset(pp->mcast_count, 1, sizeof(pp->mcast_count));
> +               val = 0x1 | (queue << 1);
> +               val |= (val << 24) | (val << 16) | (val << 8);
> +       }
> +
> +       for (offset = 0; offset <= 0xfc; offset += 4)
> +               mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
> +}
> +
> +/* This method sets defaults to the NETA port:
> + *     Clears interrupt Cause and Mask registers.
> + *     Clears all MAC tables.
> + *     Sets defaults to all registers.
> + *     Resets RX and TX descriptor rings.
> + *     Resets PHY.
> + * This method can be called after mvneta_port_down() to return the port
> + *     settings to defaults.
> + */
> +static void mvneta_defaults_set(struct mvneta_port *pp)
> +{
> +       int cpu;
> +       int queue;
> +       u32 val;
> +
> +       /* Clear all Cause registers */
> +       mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
> +       mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
> +       mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
> +
> +       /* Mask all interrupts */
> +       mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
> +       mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
> +       mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
> +       mvreg_write(pp, MVNETA_INTR_ENABLE, 0);
> +
> +       /* Enable MBUS Retry bit16 */
> +       mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
> +
> +       /* Set CPU queue access map - all CPUs have access to all RX
> +          queues and to all TX queues */
> +       for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++)
> +               mvreg_write(pp, MVNETA_CPU_MAP(cpu),
> +                           (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
> +                            MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
> +
> +       /* Reset RX and TX DMAs */
> +       mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
> +       mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET);
> +
> +       /* Disable Legacy WRR, Disable EJP, Release from reset */
> +       mvreg_write(pp, MVNETA_TXQ_CMD_1, 0);
> +       for (queue = 0; queue < txq_number; queue++) {
> +               mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(queue), 0);
> +               mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(queue), 0);
> +       }
> +
> +       mvreg_write(pp, MVNETA_PORT_TX_RESET, 0);
> +       mvreg_write(pp, MVNETA_PORT_RX_RESET, 0);
> +
> +       /* Set Port Acceleration Mode */
> +       val = MVNETA_ACC_MODE_EXT;
> +       mvreg_write(pp, MVNETA_ACC_MODE, val);
> +
> +       /* Update val of portCfg register accordingly with all RxQueue types */
> +       val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def);
> +       mvreg_write(pp, MVNETA_PORT_CONFIG, val);
> +
> +       val = 0;
> +       mvreg_write(pp, MVNETA_PORT_CONFIG_EXTEND, val);
> +       mvreg_write(pp, MVNETA_RX_MIN_FRAME_SIZE, 64);
> +
> +       /* Build PORT_SDMA_CONFIG_REG */
> +       val = 0;
> +
> +       /* Default burst size */
> +       val |= MVNETA_TX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16);
> +       val |= MVNETA_RX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16);
> +
> +       val |= (MVNETA_RX_NO_DATA_SWAP | MVNETA_TX_NO_DATA_SWAP |
> +               MVNETA_NO_DESC_SWAP);
> +
> +       /* Assign port SDMA configuration */
> +       mvreg_write(pp, MVNETA_SDMA_CONFIG, val);
> +
> +       mvneta_set_ucast_table(pp, -1);
> +       mvneta_set_special_mcast_table(pp, -1);
> +       mvneta_set_other_mcast_table(pp, -1);
> +
> +       /* Set port interrupt enable register - default enable all */
> +       mvreg_write(pp, MVNETA_INTR_ENABLE,
> +                   (MVNETA_RXQ_INTR_ENABLE_ALL_MASK
> +                    | MVNETA_TXQ_INTR_ENABLE_ALL_MASK));
> +}
> +
> +/* Set max sizes for tx queues */
> +static void mvneta_txq_max_tx_size_set(struct mvneta_port *pp, int max_tx_size)
> +
> +{
> +       u32 val, size, mtu;
> +       int queue;
> +
> +       mtu = max_tx_size * 8;
> +       if (mtu > MVNETA_TX_MTU_MAX)
> +               mtu = MVNETA_TX_MTU_MAX;
> +
> +       /* Set MTU */
> +       val = mvreg_read(pp, MVNETA_TX_MTU);
> +       val &= ~MVNETA_TX_MTU_MAX;
> +       val |= mtu;
> +       mvreg_write(pp, MVNETA_TX_MTU, val);
> +
> +       /* TX token size and all TXQs token size must be larger that MTU */
> +       val = mvreg_read(pp, MVNETA_TX_TOKEN_SIZE);
> +
> +       size = val & MVNETA_TX_TOKEN_SIZE_MAX;
> +       if (size < mtu) {
> +               size = mtu;
> +               val &= ~MVNETA_TX_TOKEN_SIZE_MAX;
> +               val |= size;
> +               mvreg_write(pp, MVNETA_TX_TOKEN_SIZE, val);
> +       }
> +       for (queue = 0; queue < txq_number; queue++) {
> +               val = mvreg_read(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue));
> +
> +               size = val & MVNETA_TXQ_TOKEN_SIZE_MAX;
> +               if (size < mtu) {
> +                       size = mtu;
> +                       val &= ~MVNETA_TXQ_TOKEN_SIZE_MAX;
> +                       val |= size;
> +                       mvreg_write(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue), val);
> +               }
> +       }
> +}
> +
> +/* Set unicast address */
> +static int mvneta_set_ucast_addr(struct mvneta_port *pp, u8 last_nibble,
> +                               int queue)
> +{
> +       unsigned int unicast_reg;
> +       unsigned int tbl_offset;
> +       unsigned int reg_offset;
> +
> +       /* Locate the Unicast table entry */
> +       last_nibble = (0xf & last_nibble);
> +
> +       /* offset from unicast tbl base */
> +       tbl_offset = (last_nibble / 4) * 4;
> +
> +       /* offset within the above reg  */
> +       reg_offset = last_nibble % 4;
> +
> +       unicast_reg = mvreg_read(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset));
> +
> +       if (queue == -1) {
> +               /* Clear accepts frame bit at specified unicast DA tbl entry */
> +               unicast_reg &= ~(0xff << (8 * reg_offset));
> +       } else {
> +               unicast_reg &= ~(0xff << (8 * reg_offset));
> +               unicast_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
> +       }
> +
> +       mvreg_write(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset), unicast_reg);
> +       return 1;
> +}
> +
> +/* Set mac address */
> +static int mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr,
> +                              int queue)
> +{
> +       unsigned int mac_h;
> +       unsigned int mac_l;
> +
> +       if (queue >= 1) {
> +               netdev_err(pp->dev, "RX queue #%d is out of range\n", queue);
> +               return -EINVAL;
> +       }
> +
> +       if (queue != -1) {
> +               mac_l = (addr[4] << 8) | (addr[5]);
> +               mac_h = (addr[0] << 24) | (addr[1] << 16) |
> +                       (addr[2] << 8) | (addr[3] << 0);
> +
> +               mvreg_write(pp, MVNETA_MAC_ADDR_LOW, mac_l);
> +               mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, mac_h);
> +       }
> +
> +       /* Accept frames of this address */
> +       mvneta_set_ucast_addr(pp, addr[5], queue);
> +
> +       return 0;
> +}
> +
> +/* Mask interrupts */
> +static void mvneta_interrupts_mask(void *priv)
> +{
> +       struct mvneta_port *pp = priv;
> +
> +       /* Mask all ethernet port interrupts */
> +       mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
> +       mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
> +       mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
> +}
> +
> +/* Unmask interrupts */
> +static void mvneta_interrupts_unmask(void *priv)
> +{
> +       struct mvneta_port *pp = priv;
> +
> +       mvreg_write(pp, MVNETA_INTR_NEW_MASK,
> +                   MVNETA_RX_INTR_MASK(rxq_number));
> +}
> +
> +/*
> + * Set the number of packets that will be received before
> + * RX interrupt will be generated by HW.
> + */
> +static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp,
> +                                   struct mvneta_rx_queue *rxq, u32 value)
> +{
> +       mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id),
> +                   value | MVNETA_RXQ_NON_OCCUPIED(0));
> +       rxq->pkts_coal = value;
> +}
> +
> +/*
> + * Set the time delay in usec before
> + * RX interrupt will be generated by HW.
> + */
> +static void mvneta_rx_time_coal_set(struct mvneta_port *pp,
> +                                   struct mvneta_rx_queue *rxq, u32 value)
> +{
> +       u32 val = (pp->clk_rate_hz / 1000000) * value;
> +
> +       mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val);
> +       rxq->time_coal = value;
> +}
> +
> +/* Set threshold for TX_DONE pkts coalescing */
> +static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp,
> +                                        struct mvneta_tx_queue *txq, u32 value)
> +{
> +       u32 val;
> +
> +       val = mvreg_read(pp, MVNETA_TXQ_SIZE_REG(txq->id));
> +
> +       val &= ~MVNETA_TXQ_SENT_THRESH_ALL_MASK;
> +       val |= MVNETA_TXQ_SENT_THRESH_MASK(value);
> +
> +       mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val);
> +
> +       txq->done_pkts_coal = value;
> +}
> +
> +/* Trigger tx done timer in MVNETA_TX_DONE_TIMER_PERIOD msecs */
> +static void mvneta_add_tx_done_timer(struct mvneta_port *pp)
> +{
> +       if (test_and_set_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags) == 0) {
> +               pp->tx_done_timer.expires = jiffies +
> +                       msecs_to_jiffies(MVNETA_TX_DONE_TIMER_PERIOD);
> +               add_timer(&pp->tx_done_timer);
> +       }
> +}
> +
> +
> +/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */
> +static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc,
> +                               u32 phys_addr, u32 cookie)
> +{
> +       rx_desc->buf_cookie = cookie;
> +       rx_desc->buf_phys_addr = phys_addr;
> +}
> +
> +/* Decrement sent descriptors counter */
> +static void mvneta_txq_sent_desc_dec(struct mvneta_port *pp,
> +                                    struct mvneta_tx_queue *txq,
> +                                    int sent_desc)
> +{
> +       u32 val;
> +
> +       /* Only 255 TX descriptors can be updated at once */
> +       while (sent_desc > 0xff) {
> +               val = 0xff << MVNETA_TXQ_DEC_SENT_SHIFT;
> +               mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
> +               sent_desc = sent_desc - 0xff;
> +       }
> +
> +       val = sent_desc << MVNETA_TXQ_DEC_SENT_SHIFT;
> +       mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
> +}
> +
> +/* Get number of TX descriptors already sent by HW */
> +static int mvneta_txq_sent_desc_num_get(struct mvneta_port *pp,
> +                                       struct mvneta_tx_queue *txq)
> +{
> +       u32 val;
> +       int sent_desc;
> +
> +       val = mvreg_read(pp, MVNETA_TXQ_STATUS_REG(txq->id));
> +       sent_desc = (val & MVNETA_TXQ_SENT_DESC_MASK) >>
> +               MVNETA_TXQ_SENT_DESC_SHIFT;
> +
> +       return sent_desc;
> +}
> +
> +/*
> + * Get number of sent descriptors and decrement counter.
> + *  The number of sent descriptors is returned.
> + */
> +static int mvneta_txq_sent_desc_proc(struct mvneta_port *pp,
> +                                    struct mvneta_tx_queue *txq)
> +{
> +       int sent_desc;
> +
> +       /* Get number of sent descriptors */
> +       sent_desc = mvneta_txq_sent_desc_num_get(pp, txq);
> +
> +       /* Decrement sent descriptors counter */
> +       if (sent_desc)
> +               mvneta_txq_sent_desc_dec(pp, txq, sent_desc);
> +
> +       return sent_desc;
> +}
> +
> +/* Set TXQ descriptors fields relevant for CSUM calculation */
> +static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto,
> +                               int ip_hdr_len, int l4_proto)
> +{
> +       u32 command;
> +
> +       /* Fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk,
> +          G_L4_chk, L4_type; required only for checksum
> +          calculation */
> +       command =  l3_offs    << MVNETA_TX_L3_OFF_SHIFT;
> +       command |= ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT;
> +
> +       if (l3_proto == swab16(ETH_P_IP))
> +               command |= MVNETA_TXD_IP_CSUM;
> +       else
> +               command |= MVNETA_TX_L3_IP6;
> +
> +       if (l4_proto == IPPROTO_TCP)
> +               command |=  MVNETA_TX_L4_CSUM_FULL;
> +       else if (l4_proto == IPPROTO_UDP)
> +               command |= MVNETA_TX_L4_UDP | MVNETA_TX_L4_CSUM_FULL;
> +       else
> +               command |= MVNETA_TX_L4_CSUM_NOT;
> +
> +       return command;
> +}
> +
> +
> +/* Display more error info */
> +static void mvneta_rx_error(struct mvneta_port *pp,
> +                           struct mvneta_rx_desc *rx_desc)
> +{
> +       u32 status = rx_desc->status;
> +
> +       if ((status & MVNETA_RXD_FIRST_LAST_DESC)
> +           != MVNETA_RXD_FIRST_LAST_DESC) {
> +               netdev_err(pp->dev,
> +                          "bad rx status %08x (buffer oversize), size=%d\n",
> +                          rx_desc->status, rx_desc->data_size);
> +               return;
> +       }
> +
> +       switch (status & MVNETA_RXD_ERR_CODE_MASK) {
> +       case MVNETA_RXD_ERR_CRC:
> +               netdev_err(pp->dev, "bad rx status %08x (crc error), size=%d\n",
> +                          status, rx_desc->data_size);
> +               break;
> +       case MVNETA_RXD_ERR_OVERRUN:
> +               netdev_err(pp->dev, "bad rx status %08x (overrun error), size=%d\n",
> +                          status, rx_desc->data_size);
> +               break;
> +       case MVNETA_RXD_ERR_LEN:
> +               netdev_err(pp->dev, "bad rx status %08x (max frame length error), size=%d\n",
> +                          status, rx_desc->data_size);
> +               break;
> +       case MVNETA_RXD_ERR_RESOURCE:
> +               netdev_err(pp->dev, "bad rx status %08x (resource error), size=%d\n",
> +                          status, rx_desc->data_size);
> +               break;
> +       }
> +}
> +
> +/* Handle RX checksum offload */
> +static void mvneta_rx_csum(struct mvneta_port *pp,
> +                          struct mvneta_rx_desc *rx_desc,
> +                          struct sk_buff *skb)
> +{
> +       if ((rx_desc->status & MVNETA_RXD_L3_IP4) &&
> +           (rx_desc->status & MVNETA_RXD_L4_CSUM_OK)) {
> +               skb->csum = 0;
> +               skb->ip_summed = CHECKSUM_UNNECESSARY;
> +               return;
> +       }
> +
> +       skb->ip_summed = CHECKSUM_NONE;
> +}
> +
> +/* Return tx queue pointer (find last set bit) according to causeTxDone reg */
> +static struct mvneta_tx_queue *mvneta_tx_done_policy(struct mvneta_port *pp,
> +                                                    u32 cause)
> +{
> +       int queue;
> +       queue = fls(cause) - 1;
> +       if (queue < 0 || queue >= txq_number)
> +               return NULL;
> +       return &pp->txqs[queue];
> +}
> +
> +/* Free tx queue skbuffs */
> +static void mvneta_txq_bufs_free(struct mvneta_port *pp,
> +                                struct mvneta_tx_queue *txq, int num)
> +{
> +       int i;
> +
> +       for (i = 0; i < num; i++) {
> +               struct mvneta_tx_desc *tx_desc = txq->descs +
> +                       txq->txq_get_index;
> +               struct sk_buff *skb = txq->tx_skb[txq->txq_get_index];
> +
> +               mvneta_txq_inc_get(txq);
> +
> +               if (!skb)
> +                       continue;
> +
> +               dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
> +                                tx_desc->data_size, DMA_TO_DEVICE);
> +               dev_kfree_skb_any(skb);
> +       }
> +}
> +
> +/* Handle end of transmission */
> +static int mvneta_txq_done(struct mvneta_port *pp,
> +                          struct mvneta_tx_queue *txq)
> +{
> +       struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id);
> +       int tx_done;
> +
> +       tx_done = mvneta_txq_sent_desc_proc(pp, txq);
> +       if (tx_done == 0)
> +               return tx_done;
> +       mvneta_txq_bufs_free(pp, txq, tx_done);
> +
> +       txq->count -= tx_done;
> +
> +       if (netif_tx_queue_stopped(nq)) {
> +               if (txq->size - txq->count >= MAX_SKB_FRAGS + 1)
> +                       netif_tx_wake_queue(nq);
> +       }
> +
> +       return tx_done;
> +}
> +
> +/* Refill processing */
> +static int mvneta_rx_refill(struct mvneta_port *pp,
> +                           struct mvneta_rx_desc *rx_desc)
> +
> +{
> +       dma_addr_t phys_addr;
> +       struct sk_buff *skb;
> +
> +       skb = netdev_alloc_skb(pp->dev, pp->pkt_size);
> +       if (!skb)
> +               return 1;
> +
> +       phys_addr = dma_map_single(pp->dev->dev.parent, skb->head,
> +                                  MVNETA_RX_BUF_SIZE(pp->pkt_size),
> +                                  DMA_FROM_DEVICE);
> +       if (unlikely(dma_mapping_error(pp->dev->dev.parent,
> +                                      phys_addr))) {
> +               dev_kfree_skb(skb);
> +               return 1;
> +       }
> +
> +       mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb);
> +
> +       return 0;
> +}
> +
> +/* Handle tx checksum */
> +static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb)
> +{
> +       if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +               int ip_hdr_len = 0;
> +               u8 l4_proto;
> +
> +               if (skb->protocol == htons(ETH_P_IP)) {
> +                       struct iphdr *ip4h = ip_hdr(skb);
> +
> +                       /* Calculate IPv4 checksum and L4 checksum */
> +                       ip_hdr_len = ip4h->ihl;
> +                       l4_proto = ip4h->protocol;
> +               } else if (skb->protocol == htons(ETH_P_IPV6)) {
> +                       struct ipv6hdr *ip6h = ipv6_hdr(skb);
> +
> +                       /* Read l4_protocol from one of IPv6 extra headers */
> +                       if (skb_network_header_len(skb) > 0)
> +                               ip_hdr_len = (skb_network_header_len(skb) >> 2);
> +                       l4_proto = ip6h->nexthdr;
> +               } else
> +                       return MVNETA_TX_L4_CSUM_NOT;
> +
> +               return mvneta_txq_desc_csum(skb_network_offset(skb),
> +                               skb->protocol, ip_hdr_len, l4_proto);
> +       }
> +
> +       return MVNETA_TX_L4_CSUM_NOT;
> +}
> +
> +/*
> + * Returns rx queue pointer (find last set bit) according to causeRxTx
> + * value
> + */
> +static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp,
> +                                               u32 cause)
> +{
> +       int queue = fls(cause >> 8) - 1;
> +       if (queue < 0 || queue >= rxq_number)
> +               return NULL;
> +       return &pp->rxqs[queue];
> +}
> +
> +/* Drop packets received by the RXQ and free buffers */
> +static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
> +                                struct mvneta_rx_queue *rxq)
> +{
> +       int rx_done, i;
> +
> +       rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
> +       for (i = 0; i < rxq->size; i++) {
> +               struct mvneta_rx_desc *rx_desc = rxq->descs + i;
> +               struct sk_buff *skb = (struct sk_buff *)rx_desc->buf_cookie;
> +               dev_kfree_skb_any(skb);
> +               dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
> +                                rx_desc->data_size, DMA_FROM_DEVICE);
> +       }
> +
> +       if (rx_done)
> +               mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
> +}
> +
> +/* Main rx processing */
> +static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
> +                    struct mvneta_rx_queue *rxq)
> +{
> +       struct net_device *dev = pp->dev;
> +       int rx_done, rx_filled;
> +
> +       /* Get number of received packets */
> +       rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
> +
> +       if (rx_todo > rx_done)
> +               rx_todo = rx_done;
> +
> +       rx_done = 0;
> +       rx_filled = 0;
> +
> +       /* Fairness NAPI loop */
> +       while (rx_done < rx_todo) {
> +               struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
> +               struct sk_buff *skb;
> +               u32 rx_status;
> +               int rx_bytes, err;
> +
> +               prefetch(rx_desc);
> +               rx_done++;
> +               rx_filled++;
> +               rx_status = rx_desc->status;
> +               skb = (struct sk_buff *)rx_desc->buf_cookie;
> +
> +               if (((rx_status & MVNETA_RXD_FIRST_LAST_DESC)
> +                    != MVNETA_RXD_FIRST_LAST_DESC)
> +                   || (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
> +                       dev->stats.rx_errors++;
> +                       mvneta_rx_error(pp, rx_desc);
> +                       mvneta_rx_desc_fill(rx_desc, rx_desc->buf_phys_addr,
> +                                           (u32)skb);
> +                       continue;
> +               }
> +
> +               dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
> +                                rx_desc->data_size, DMA_FROM_DEVICE);
> +
> +               rx_bytes = rx_desc->data_size -
> +                       (MVNETA_ETH_CRC_SIZE + MVNETA_MH_SIZE);
> +               u64_stats_update_begin(&pp->rx_stats.syncp);
> +               pp->rx_stats.packets++;
> +               pp->rx_stats.bytes += rx_bytes;
> +               u64_stats_update_end(&pp->rx_stats.syncp);
> +
> +               /* Linux processing */
> +               skb_reserve(skb, MVNETA_MH_SIZE);
> +               skb_put(skb, rx_bytes);
> +
> +               skb->protocol = eth_type_trans(skb, dev);
> +
> +               mvneta_rx_csum(pp, rx_desc, skb);
> +
> +               napi_gro_receive(&pp->napi, skb);
> +
> +               /* Refill processing */
> +               err = mvneta_rx_refill(pp, rx_desc);
> +               if (err) {
> +                       netdev_err(pp->dev, "Linux processing - Can't refill\n");
> +                       rxq->missed++;
> +                       rx_filled--;
> +               }
> +       }
> +
> +       /* Update rxq management counters */
> +       mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_filled);
> +
> +       return rx_done;
> +}
> +
> +/* Handle tx fragmentation processing */
> +static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
> +                                 struct mvneta_tx_queue *txq)
> +{
> +       struct mvneta_tx_desc *tx_desc;
> +       int i, j;
> +
> +       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> +               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +               void *addr = page_address(frag->page.p) + frag->page_offset;
> +
> +               tx_desc = mvneta_txq_next_desc_get(txq);
> +               tx_desc->data_size = frag->size;
> +
> +               tx_desc->buf_phys_addr =
> +                       dma_map_single(pp->dev->dev.parent, addr,
> +                                      tx_desc->data_size, DMA_TO_DEVICE);
> +
> +               if (dma_mapping_error(pp->dev->dev.parent,
> +                                     tx_desc->buf_phys_addr)) {
> +                       mvneta_txq_desc_put(txq);
> +                       goto error;
> +               }
> +
> +               if (i == (skb_shinfo(skb)->nr_frags - 1)) {
> +                       /* Last descriptor */
> +                       tx_desc->command = (MVNETA_TXD_L_DESC |
> +                                           MVNETA_TXD_Z_PAD);
> +
> +                       txq->tx_skb[txq->txq_put_index] = skb;
> +
> +                       mvneta_txq_inc_put(txq);
> +               } else {
> +                       /* Descriptor in the middle: Not First, Not Last */
> +                       tx_desc->command = 0;
> +
> +                       txq->tx_skb[txq->txq_put_index] = NULL;
> +                       mvneta_txq_inc_put(txq);
> +               }
> +       }
> +
> +       return 0;
> +
> +error:
> +       /* Release all descriptors that were used to map fragments of
> +        * this packet, as well as the corresponding DMA mappings */
> +       for (j = i - 1; j >= 0; j--) {
> +               tx_desc = txq->descs + j;
> +               dma_unmap_single(pp->dev->dev.parent,
> +                                tx_desc->buf_phys_addr,
> +                                tx_desc->data_size,
> +                                DMA_TO_DEVICE);
> +               mvneta_txq_desc_put(txq);
> +       }
> +
> +       return -ENOMEM;
> +}
> +
> +/* Main tx processing */
> +static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       struct mvneta_tx_queue *txq = &pp->txqs[txq_def];
> +       struct netdev_queue *nq;
> +       struct mvneta_tx_desc *tx_desc;
> +       int frags = 0;
> +       u32 tx_cmd;
> +
> +       if (!netif_running(dev))
> +               goto out;
> +
> +       frags = skb_shinfo(skb)->nr_frags + 1;
> +       nq    = netdev_get_tx_queue(dev, txq_def);
> +
> +       /* Get a descriptor for the first part of the packet */
> +       tx_desc = mvneta_txq_next_desc_get(txq);
> +
> +       tx_cmd = mvneta_skb_tx_csum(pp, skb);
> +
> +       tx_desc->data_size = skb_headlen(skb);
> +
> +       tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, skb->data,
> +                                               tx_desc->data_size,
> +                                               DMA_TO_DEVICE);
> +       if (unlikely(dma_mapping_error(dev->dev.parent,
> +                                      tx_desc->buf_phys_addr))) {
> +               mvneta_txq_desc_put(txq);
> +               frags = 0;
> +               goto out;
> +       }
> +
> +       if (frags == 1) {
> +               /* First and Last descriptor */
> +               tx_cmd |= MVNETA_TXD_FLZ_DESC;
> +               tx_desc->command = tx_cmd;
> +               txq->tx_skb[txq->txq_put_index] = skb;
> +               mvneta_txq_inc_put(txq);
> +       } else {
> +               /* First but not Last */
> +               tx_cmd |= MVNETA_TXD_F_DESC;
> +               txq->tx_skb[txq->txq_put_index] = NULL;
> +               mvneta_txq_inc_put(txq);
> +               tx_desc->command = tx_cmd;
> +               /* Continue with other skb fragments */
> +               if (mvneta_tx_frag_process(pp, skb, txq)) {
> +                       dma_unmap_single(dev->dev.parent,
> +                                        tx_desc->buf_phys_addr,
> +                                        tx_desc->data_size,
> +                                        DMA_TO_DEVICE);
> +                       mvneta_txq_desc_put(txq);
> +                       frags = 0;
> +                       goto out;
> +               }
> +       }
> +
> +       txq->count += frags;
> +       mvneta_txq_pend_desc_add(pp, txq, frags);
> +
> +       if (txq->size - txq->count < MAX_SKB_FRAGS + 1)
> +               netif_tx_stop_queue(nq);
> +
> +out:
> +       if (frags > 0) {
> +               u64_stats_update_begin(&pp->tx_stats.syncp);
> +               pp->tx_stats.packets++;
> +               pp->tx_stats.bytes += skb->len;
> +               u64_stats_update_end(&pp->tx_stats.syncp);
> +
> +       } else {
> +               dev->stats.tx_dropped++;
> +               dev_kfree_skb_any(skb);
> +       }
> +
> +       if (txq->count >= MVNETA_TXDONE_COAL_PKTS)
> +               mvneta_txq_done(pp, txq);
> +
> +       /* If after calling mvneta_txq_done, count equals
> +               frags, we need to set the timer */
> +       if (txq->count == frags && frags > 0)
> +               mvneta_add_tx_done_timer(pp);
> +
> +       return NETDEV_TX_OK;
> +}
> +
> +
> +/* Free tx resources, when resetting a port */
> +static void mvneta_txq_done_force(struct mvneta_port *pp,
> +                                 struct mvneta_tx_queue *txq)
> +
> +{
> +       int tx_done = txq->count;
> +       mvneta_txq_bufs_free(pp, txq, tx_done);
> +
> +       /* reset txq */
> +       txq->count = 0;
> +       txq->txq_put_index = 0;
> +       txq->txq_get_index = 0;
> +}
> +
> +/* handle tx done - called from tx done timer callback */
> +static u32 mvneta_tx_done_gbe(struct mvneta_port *pp, u32 cause_tx_done,
> +                             int *tx_todo)
> +{
> +       struct mvneta_tx_queue *txq;
> +       u32 tx_done = 0;
> +       struct netdev_queue *nq;
> +
> +       *tx_todo = 0;
> +       while (cause_tx_done != 0) {
> +               txq = mvneta_tx_done_policy(pp, cause_tx_done);
> +               if (!txq)
> +                       break;
> +
> +               nq = netdev_get_tx_queue(pp->dev, txq->id);
> +               __netif_tx_lock(nq, smp_processor_id());
> +
> +               if (txq->count) {
> +                       tx_done += mvneta_txq_done(pp, txq);
> +                       *tx_todo += txq->count;
> +               }
> +
> +               __netif_tx_unlock(nq);
> +               cause_tx_done &= ~((1 << txq->id));
> +       }
> +
> +       return tx_done;
> +}
> +
> +/*
> + * Compute crc8 of the specified address, using a unique algorithm ,
> + * according to hw spec, different than generic crc8 algorithm
> + */
> +static int mvneta_addr_crc(unsigned char *addr)
> +{
> +       int crc = 0;
> +       int i;
> +
> +       for (i = 0; i < ETH_ALEN; i++) {
> +               int j;
> +
> +               crc = (crc ^ addr[i]) << 8;
> +               for (j = 7; j >= 0; j--) {
> +                       if (crc & (0x100 << j))
> +                               crc ^= 0x107 << j;
> +               }
> +       }
> +
> +       return crc;
> +}
> +
> +/* This method controls the net device special MAC multicast support.
> + * The Special Multicast Table for MAC addresses supports MAC of the form
> + * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF).
> + * The MAC DA[7:0] bits are used as a pointer to the Special Multicast
> + * Table entries in the DA-Filter table. This method set the Special
> + * Multicast Table appropriate entry.
> + */
> +static void mvneta_set_special_mcast_addr(struct mvneta_port *pp,
> +                                         unsigned char last_byte,
> +                                         int queue)
> +{
> +       unsigned int smc_table_reg;
> +       unsigned int tbl_offset;
> +       unsigned int reg_offset;
> +
> +       /* Register offset from SMC table base    */
> +       tbl_offset = (last_byte / 4);
> +       /* Entry offset within the above reg */
> +       reg_offset = last_byte % 4;
> +
> +       smc_table_reg = mvreg_read(pp, (MVNETA_DA_FILT_SPEC_MCAST
> +                                       + tbl_offset * 4));
> +
> +       if (queue == -1)
> +               smc_table_reg &= ~(0xff << (8 * reg_offset));
> +       else {
> +               smc_table_reg &= ~(0xff << (8 * reg_offset));
> +               smc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
> +       }
> +
> +       mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + tbl_offset * 4,
> +                   smc_table_reg);
> +}
> +
> +/* This method controls the network device Other MAC multicast support.
> + * The Other Multicast Table is used for multicast of another type.
> + * A CRC-8 is used as an index to the Other Multicast Table entries
> + * in the DA-Filter table.
> + * The method gets the CRC-8 value from the calling routine and
> + * sets the Other Multicast Table appropriate entry according to the
> + * specified CRC-8 .
> + */
> +static void mvneta_set_other_mcast_addr(struct mvneta_port *pp,
> +                                       unsigned char crc8,
> +                                       int queue)
> +{
> +       unsigned int omc_table_reg;
> +       unsigned int tbl_offset;
> +       unsigned int reg_offset;
> +
> +       tbl_offset = (crc8 / 4) * 4; /* Register offset from OMC table base */
> +       reg_offset = crc8 % 4;       /* Entry offset within the above reg   */
> +
> +       omc_table_reg = mvreg_read(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset);
> +
> +       if (queue == -1) {
> +               /* Clear accepts frame bit at specified Other DA table entry */
> +               omc_table_reg &= ~(0xff << (8 * reg_offset));
> +       } else {
> +               omc_table_reg &= ~(0xff << (8 * reg_offset));
> +               omc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
> +       }
> +
> +       mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset, omc_table_reg);
> +}
> +
> +/* The network device supports multicast using two tables:
> + *    1) Special Multicast Table for MAC addresses of the form
> + *       0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF).
> + *       The MAC DA[7:0] bits are used as a pointer to the Special Multicast
> + *       Table entries in the DA-Filter table.
> + *    2) Other Multicast Table for multicast of another type. A CRC-8 value
> + *       is used as an index to the Other Multicast Table entries in the
> + *       DA-Filter table.
> + */
> +static int mvneta_mcast_addr_set(struct mvneta_port *pp, unsigned char *p_addr,
> +                                int queue)
> +{
> +       unsigned char crc_result = 0;
> +
> +       if (memcmp(p_addr, "\x01\x00\x5e\x00\x00", 5) == 0) {
> +               mvneta_set_special_mcast_addr(pp, p_addr[5], queue);
> +               return 0;
> +       }
> +
> +       crc_result = mvneta_addr_crc(p_addr);
> +       if (queue == -1) {
> +               if (pp->mcast_count[crc_result] == 0) {
> +                       netdev_info(pp->dev, "No valid Mcast for crc8=0x%02x\n",
> +                                   crc_result);
> +                       return -EINVAL;
> +               }
> +
> +               pp->mcast_count[crc_result]--;
> +               if (pp->mcast_count[crc_result] != 0) {
> +                       netdev_info(pp->dev,
> +                                   "After delete there are %d valid Mcast for crc8=0x%02x\n",
> +                                   pp->mcast_count[crc_result], crc_result);
> +                       return -EINVAL;
> +               }
> +       } else
> +               pp->mcast_count[crc_result]++;
> +
> +       mvneta_set_other_mcast_addr(pp, crc_result, queue);
> +
> +       return 0;
> +}
> +
> +/* Configure Fitering mode of Ethernet port */
> +static void mvneta_rx_unicast_promisc_set(struct mvneta_port *pp,
> +                                         int is_promisc)
> +{
> +       u32 port_cfg_reg, val;
> +
> +       port_cfg_reg = mvreg_read(pp, MVNETA_PORT_CONFIG);
> +
> +       val = mvreg_read(pp, MVNETA_TYPE_PRIO);
> +
> +       /* Set / Clear UPM bit in port configuration register */
> +       if (is_promisc) {
> +               /* Accept all Unicast addresses */
> +               port_cfg_reg |= MVNETA_UNI_PROMISC_MODE;
> +               val |= MVNETA_FORCE_UNI;
> +               mvreg_write(pp, MVNETA_MAC_ADDR_LOW, 0xffff);
> +               mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, 0xffffffff);
> +       } else {
> +               /* Reject all Unicast addresses */
> +               port_cfg_reg &= ~MVNETA_UNI_PROMISC_MODE;
> +               val &= ~MVNETA_FORCE_UNI;
> +       }
> +
> +       mvreg_write(pp, MVNETA_PORT_CONFIG, port_cfg_reg);
> +       mvreg_write(pp, MVNETA_TYPE_PRIO, val);
> +}
> +
> +/* register unicast and multicast addresses */
> +static void mvneta_set_rx_mode(struct net_device *dev)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       struct netdev_hw_addr *ha;
> +       int queue = 0;
> +
> +       if (dev->flags & IFF_PROMISC) {
> +               /* Accept all: Multicast + Unicast */
> +               mvneta_rx_unicast_promisc_set(pp, 1);
> +               mvneta_set_ucast_table(pp, queue);
> +               mvneta_set_special_mcast_table(pp, queue);
> +               mvneta_set_other_mcast_table(pp, queue);
> +       } else {
> +               /* Accept single Unicast */
> +               mvneta_rx_unicast_promisc_set(pp, 0);
> +               mvneta_set_ucast_table(pp, -1);
> +               if ((mvneta_mac_addr_set(pp, dev->dev_addr, queue)) != 0)
> +                       netdev_err(dev, "mvneta_mac_addr_set failed\n");
> +
> +               if (dev->flags & IFF_ALLMULTI) {
> +                       /* Accept all multicast */
> +                       mvneta_set_special_mcast_table(pp, queue);
> +                       mvneta_set_other_mcast_table(pp, queue);
> +               } else {
> +                       /* Accept only initialized multicast */
> +                       mvneta_set_special_mcast_table(pp, -1);
> +                       mvneta_set_other_mcast_table(pp, -1);
> +
> +                       if (!netdev_mc_empty(dev)) {
> +                               netdev_for_each_mc_addr(ha, dev) {
> +                                       mvneta_mcast_addr_set(pp, ha->addr,
> +                                                             queue);
> +                               }
> +                       }
> +               }
> +       }
> +}
> +
> +/* Interrupt handling - the callback for request_irq() */
> +static irqreturn_t mvneta_isr(int irq, void *dev_id)
> +{
> +       struct mvneta_port *pp = (struct mvneta_port *)dev_id;
> +
> +       /* Mask all interrupts */
> +       mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
> +
> +       /* Verify that the device not already on the polling list */
> +       if (napi_schedule_prep(&pp->napi))
> +               __napi_schedule(&pp->napi);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +/* NAPI handler
> + * Bits 0 - 7 of the causeRxTx register indicate that are transmitted
> + * packets on the corresponding TXQ (Bit 0 is for TX queue 1).
> + * Bits 8 -15 of the cause Rx Tx register indicate that are received
> + * packets on the corresponding RXQ (Bit 8 is for RX queue 0).
> + * Each CPU has its own causeRxTx register
> + */
> +static int mvneta_poll(struct napi_struct *napi, int budget)
> +{
> +       int rx_done = 0;
> +       u32 cause_rx_tx;
> +       unsigned long flags;
> +       struct mvneta_port *pp = netdev_priv(napi->dev);
> +
> +       if (!netif_running(pp->dev)) {
> +               napi_complete(napi);
> +               return rx_done;
> +       }
> +
> +       /* Read cause register */
> +       cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) &
> +               MVNETA_RX_INTR_MASK(rxq_number);
> +
> +       /*
> +        * For the case where the last mvneta_poll did not process all
> +        * RX packets
> +        */
> +       cause_rx_tx |= pp->cause_rx_tx;
> +       if (rxq_number > 1) {
> +               while ((cause_rx_tx != 0) && (budget > 0)) {
> +                       int count;
> +                       struct mvneta_rx_queue *rxq;
> +                       /* get rx queue number from cause_rx_tx */
> +                       rxq = mvneta_rx_policy(pp, cause_rx_tx);
> +                       if (!rxq)
> +                               break;
> +
> +                       /* process the packet in that rx queue */
> +                       count = mvneta_rx(pp, budget, rxq);
> +                       rx_done += count;
> +                       budget -= count;
> +                       if (budget > 0) {
> +                               /* set off the rx bit of the corresponding bit
> +                                 in the cause rx tx register, so that next
> +                                 iteration will find the next rx queue where
> +                                 packets are received on */
> +                               cause_rx_tx &= ~((1 << rxq->id) << 8);
> +                       }
> +               }
> +       } else {
> +               rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
> +               budget -= rx_done;
> +       }
> +
> +       if (budget > 0) {
> +               cause_rx_tx = 0;
> +               napi_complete(napi);
> +               local_irq_save(flags);
> +               mvreg_write(pp, MVNETA_INTR_NEW_MASK,
> +                           MVNETA_RX_INTR_MASK(rxq_number));
> +               local_irq_restore(flags);
> +       }
> +
> +       pp->cause_rx_tx = cause_rx_tx;
> +       return rx_done;
> +}
> +
> +/* tx done timer callback */
> +static void mvneta_tx_done_timer_callback(unsigned long data)
> +{
> +       struct net_device *dev = (struct net_device *)data;
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       int tx_done = 0, tx_todo = 0;
> +
> +       if (!netif_running(dev))
> +               return ;
> +
> +       clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
> +
> +       tx_done = mvneta_tx_done_gbe(pp,
> +                                    (((1 << txq_number) - 1) &
> +                                     MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK),
> +                                    &tx_todo);
> +       if (tx_todo > 0)
> +               mvneta_add_tx_done_timer(pp);
> +}
> +
> +/* Handle rxq fill: allocates rxq skbs; called when initializing a port */
> +static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq,
> +                          int num)
> +{
> +       int i;
> +       struct net_device *dev = pp->dev;
> +
> +       for (i = 0; i < num; i++) {
> +               struct sk_buff *skb;
> +               struct mvneta_rx_desc *rx_desc;
> +               unsigned long phys_addr;
> +
> +               skb = dev_alloc_skb(pp->pkt_size);
> +               if (!skb) {
> +                       netdev_err(dev, "%s:rxq %d, %d of %d buffs  filled\n",
> +                               __func__, rxq->id, i, num);
> +                       break;
> +               }
> +
> +               rx_desc = rxq->descs + i;
> +               memset(rx_desc, 0, sizeof(struct mvneta_rx_desc));
> +               phys_addr = dma_map_single(dev->dev.parent, skb->head,
> +                                          MVNETA_RX_BUF_SIZE(pp->pkt_size),
> +                                          DMA_FROM_DEVICE);
> +               if (unlikely(dma_mapping_error(dev->dev.parent, phys_addr))) {
> +                       dev_kfree_skb(skb);
> +                       break;
> +               }
> +
> +               mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb);
> +       }
> +
> +       /* Add this number of RX descriptors as non occupied (ready to
> +          get packets) */
> +       mvneta_rxq_non_occup_desc_add(pp, rxq, i);
> +
> +       return i;
> +}
> +
> +/* Free all packets pending transmit from all TXQs and reset TX port */
> +static void mvneta_tx_reset(struct mvneta_port *pp)
> +{
> +       int queue;
> +
> +       /* free the skb's in the hal tx ring */
> +       for (queue = 0; queue < txq_number; queue++)
> +               mvneta_txq_done_force(pp, &pp->txqs[queue]);
> +
> +       mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET);
> +       mvreg_write(pp, MVNETA_PORT_TX_RESET, 0);
> +}
> +
> +static void mvneta_rx_reset(struct mvneta_port *pp)
> +{
> +       mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
> +       mvreg_write(pp, MVNETA_PORT_RX_RESET, 0);
> +}
> +
> +/* Rx/Tx queue initialization/cleanup methods */
> +
> +/* Create a specified RX queue */
> +static int mvneta_rxq_init(struct mvneta_port *pp,
> +                          struct mvneta_rx_queue *rxq)
> +
> +{
> +       rxq->size = pp->rx_ring_size;
> +
> +       /* Allocate memory for RX descriptors */
> +       rxq->descs = dma_alloc_coherent(pp->dev->dev.parent,
> +                                       rxq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                                       &rxq->descs_phys,
> +                                       GFP_KERNEL);
> +       if (rxq->descs == NULL) {
> +               netdev_err(pp->dev,
> +                          "rxQ=%d: Can't allocate %d bytes for %d RX descr\n",
> +                          rxq->id, rxq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                          rxq->size);
> +               return -ENOMEM;
> +       }
> +
> +       BUG_ON(rxq->descs !=
> +              PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
> +
> +       rxq->last_desc = rxq->size - 1;
> +
> +       /* Set Rx descriptors queue starting address */
> +       mvreg_write(pp, MVNETA_RXQ_BASE_ADDR_REG(rxq->id), rxq->descs_phys);
> +       mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size);
> +
> +       /* Set Offset */
> +       mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD);
> +
> +       /* Set coalescing pkts and time */
> +       mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
> +       mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal);
> +
> +       /* Fill RXQ with buffers from RX pool */
> +       mvneta_rxq_buf_size_set(pp, rxq, MVNETA_RX_BUF_SIZE(pp->pkt_size));
> +       mvneta_rxq_bm_disable(pp, rxq);
> +       mvneta_rxq_fill(pp, rxq, rxq->size);
> +
> +       return 0;
> +}
> +
> +/* Cleanup Rx queue */
> +static void mvneta_rxq_deinit(struct mvneta_port *pp,
> +                             struct mvneta_rx_queue *rxq)
> +{
> +       mvneta_rxq_drop_pkts(pp, rxq);
> +
> +       if (rxq->descs)
> +               dma_free_coherent(pp->dev->dev.parent,
> +                                 rxq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                                 rxq->descs,
> +                                 rxq->descs_phys);
> +
> +       rxq->descs             = NULL;
> +       rxq->last_desc         = 0;
> +       rxq->next_desc_to_proc = 0;
> +       rxq->descs_phys        = 0;
> +}
> +
> +/* Create and initialize a tx queue */
> +static int mvneta_txq_init(struct mvneta_port *pp,
> +                          struct mvneta_tx_queue *txq)
> +{
> +       txq->size = pp->tx_ring_size;
> +
> +       /* Allocate memory for TX descriptors */
> +       txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
> +                                       txq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                                       &txq->descs_phys,
> +                                       DMA_BIDIRECTIONAL);
> +       if (txq->descs == NULL) {
> +               netdev_err(pp->dev,
> +                          "txQ=%d: Can't allocate %d bytes for %d TX descr\n",
> +                          txq->id, txq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                          txq->size);
> +               return -ENOMEM;
> +       }
> +
> +       /* Make sure descriptor address is cache line size aligned  */
> +       BUG_ON(txq->descs !=
> +              PTR_ALIGN(txq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
> +
> +       txq->last_desc = txq->size - 1;
> +
> +       /* Set maximum bandwidth for enabled TXQs */
> +       mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff);
> +       mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff);
> +
> +       /* Set Tx descriptors queue starting address */
> +       mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys);
> +       mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size);
> +
> +       txq->tx_skb = kmalloc(txq->size * sizeof(*txq->tx_skb),
> +                             GFP_KERNEL);
> +       if (txq->tx_skb == NULL) {
> +               dma_free_coherent(pp->dev->dev.parent,
> +                                 txq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                                 txq->descs, txq->descs_phys);
> +               return -ENOMEM;
> +       }
> +       mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
> +
> +       return 0;
> +}
> +
> +/* Free allocated resources when mvneta_txq_init() fails to allocate memory*/
> +static void mvneta_txq_deinit(struct mvneta_port *pp,
> +                             struct mvneta_tx_queue *txq)
> +{
> +       kfree(txq->tx_skb);
> +
> +       if (txq->descs)
> +               dma_free_coherent(pp->dev->dev.parent,
> +                                 txq->size * MVNETA_DESC_ALIGNED_SIZE,
> +                                 txq->descs,
> +                                 txq->descs_phys);
> +
> +       txq->descs             = NULL;
> +       txq->last_desc         = 0;
> +       txq->next_desc_to_proc = 0;
> +       txq->descs_phys        = 0;
> +
> +       /* Set minimum bandwidth for disabled TXQs */
> +       mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0);
> +       mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0);
> +
> +       /* Set Tx descriptors queue starting address and size */
> +       mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), 0);
> +       mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), 0);
> +}
> +
> +/* Cleanup all Tx queues */
> +static void mvneta_cleanup_txqs(struct mvneta_port *pp)
> +{
> +       int queue;
> +       for (queue = 0; queue < txq_number; queue++)
> +               mvneta_txq_deinit(pp, &pp->txqs[queue]);
> +}
> +
> +/* Cleanup all Rx queues */
> +static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
> +{
> +       int queue;
> +       for (queue = 0; queue < rxq_number; queue++)
> +               mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
> +}
> +
> +
> +/* Init all Rx queues */
> +static int mvneta_setup_rxqs(struct mvneta_port *pp)
> +{
> +       int queue;
> +
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
> +               if (err) {
> +                       netdev_err(pp->dev,
> +                                  "%s: can't create RxQ rxq=%d\n",
> +                                  __func__, queue);
> +                       mvneta_cleanup_rxqs(pp);
> +                       return -ENODEV;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/* Init all tx queues */
> +static int mvneta_setup_txqs(struct mvneta_port *pp)
> +{
> +       int queue;
> +
> +       for (queue = 0; queue < txq_number; queue++) {
> +               int err = mvneta_txq_init(pp, &pp->txqs[queue]);
> +               if (err) {
> +                       netdev_err(pp->dev,
> +                                  "%s: can't create TxQ txq=%d\n",
> +                                  __func__, queue);
> +                       mvneta_cleanup_txqs(pp);
> +                       return err;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void mvneta_start_dev(struct mvneta_port *pp)
> +{
> +       mvneta_max_rx_size_set(pp, pp->pkt_size);
> +       mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
> +
> +       /* start the Rx/Tx activity */
> +       mvneta_port_enable(pp);
> +
> +       /* Enable polling on the port */
> +       napi_enable(&pp->napi);
> +
> +       /* Unmask interrupts */
> +       mvneta_interrupts_unmask(pp);
> +       smp_call_function_many(cpu_online_mask,
> +                              mvneta_interrupts_unmask,
> +                              pp, 1);
> +
> +       phy_start(pp->phy_dev);
> +       netif_tx_start_all_queues(pp->dev);
> +}
> +
> +static void mvneta_stop_dev(struct mvneta_port *pp)
> +{
> +       phy_stop(pp->phy_dev);
> +
> +       napi_disable(&pp->napi);
> +
> +       /* Stop upper layer */
> +       netif_carrier_off(pp->dev);
> +
> +       mvneta_port_down(pp);
> +       netif_tx_stop_all_queues(pp->dev);
> +
> +       /* Stop the port activity */
> +       mvneta_port_disable(pp);
> +
> +       /* Clear all ethernet port interrupts */
> +       mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
> +       mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
> +
> +       /* Mask all interrupts */
> +       mvneta_interrupts_mask(pp);
> +       smp_call_function_many(cpu_online_mask, mvneta_interrupts_mask,
> +                              pp, 1);
> +
> +       /* Reset TX port here. */
> +       mvneta_tx_reset(pp);
> +       mvneta_rx_reset(pp);
> +}
> +
> +/* tx timeout callback - display a message and stop/start the network device */
> +static void mvneta_tx_timeout(struct net_device *dev)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       netdev_info(dev, "tx timeout\n");
> +       mvneta_stop_dev(pp);
> +       mvneta_start_dev(pp);
> +}
> +
> +/* Return positive if MTU is valid */
> +static int mvneta_check_mtu_valid(struct net_device *dev, int mtu)
> +{
> +       if (mtu < 68) {
> +               netdev_err(dev, "cannot change mtu to less than 68\n");
> +               return -EINVAL;
> +       }
> +
> +       if (mtu > 9676 /* 9700 - 20 and rounding to 8 */) {
> +               netdev_info(dev, "Illegal MTU value %d, round to 9676", mtu);
> +               mtu = 9676;
> +       }
> +
> +       if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) {
> +               netdev_info(dev, "Illegal MTU value %d, rounding to %d",
> +                       mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8));
> +               mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8);
> +       }
> +
> +       return mtu;
> +}
> +
> +/* Change the device mtu */
> +static int mvneta_change_mtu(struct net_device *dev, int mtu)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       int ret;
> +
> +       mtu = mvneta_check_mtu_valid(dev, mtu);
> +       if (mtu < 0)
> +               return -EINVAL;
> +
> +       dev->mtu = mtu;
> +
> +       if (!netif_running(dev))
> +               return 0;
> +
> +       /*
> +        * The interface is running, so we have to force a
> +        * reallocation of the RXQs
> +        */
> +       mvneta_stop_dev(pp);
> +
> +       mvneta_cleanup_txqs(pp);
> +       mvneta_cleanup_rxqs(pp);
> +
> +       pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
> +
> +       ret = mvneta_setup_rxqs(pp);
> +       if (ret) {
> +               netdev_err(pp->dev, "unable to setup rxqs after MTU change\n");
> +               return ret;
> +       }
> +
> +       mvneta_setup_txqs(pp);
> +
> +       mvneta_start_dev(pp);
> +       mvneta_port_up(pp);
> +
> +       return 0;
> +}
> +
> +/* Handle setting mac address */
> +static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       u8 *mac = addr + 2;
> +       int i, ret;
> +
> +       if (netif_running(dev))
> +               return -EBUSY;
> +
> +       /* Remove previous address table entry */
> +       ret = mvneta_mac_addr_set(pp, dev->dev_addr, -1);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Set new addr in hw */
> +       ret = mvneta_mac_addr_set(pp, mac, rxq_def);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Set addr in the device */
> +       for (i = 0; i < ETH_ALEN; i++)
> +               dev->dev_addr[i] = mac[i];
> +
> +       return 0;
> +}
> +
> +/* MDIO / phylib functions */
> +
> +static int mvneta_mdio_read(struct mii_bus *bus, int mii_id,
> +                           int regnum)
> +{
> +       struct mvneta_port *pp = bus->priv;
> +       int count;
> +       u32 val;
> +
> +       /* Wait for the SMI register to be ready for another
> +        * operation */
> +       count = 0;
> +       while (1) {
> +               val = mvreg_read(pp, MVNETA_SMI);
> +               if (!(val & MVNETA_SMI_BUSY))
> +                       break;
> +
> +               if (count > 100) {
> +                       netdev_err(pp->dev, "Timeout: SMI busy for too long\n");
> +                       return -ETIMEDOUT;
> +               }
> +
> +               udelay(10);
> +               count++;
> +       }
> +
> +       mvreg_write(pp, MVNETA_SMI,
> +                   ((mii_id << MVNETA_SMI_PHY_ADDR_SHIFT) |
> +                   (regnum << MVNETA_SMI_PHY_REG_SHIFT)  |
> +                    MVNETA_SMI_READ_OPERATION));
> +
> +       /* Wait for the value to become available */
> +       count = 0;
> +       while (1) {
> +               val = mvreg_read(pp, MVNETA_SMI);
> +               if (val & MVNETA_SMI_READ_VALID)
> +                       break;
> +
> +               if (count > 100) {
> +                       netdev_err(pp->dev, "Timeout when reading PHY\n");
> +                       return -ETIMEDOUT;
> +               }
> +
> +               udelay(10);
> +               count++;
> +       }
> +
> +       return val & 0xFFFF;
> +}
> +
> +static int mvneta_mdio_write(struct mii_bus *bus, int mii_id,
> +                            int regnum, u16 value)
> +{
> +       struct mvneta_port *pp = bus->priv;
> +       int count;
> +       u32 val;
> +
> +       /* Wait for the SMI register to be ready for another
> +        * operation */
> +       count = 0;
> +       while (1) {
> +               val = mvreg_read(pp, MVNETA_SMI);
> +               if (!(val & MVNETA_SMI_BUSY))
> +                       break;
> +
> +               if (count > 100) {
> +                       netdev_err(pp->dev, "Timeout: SMI busy for too long\n");
> +                       return -ETIMEDOUT;
> +               }
> +
> +               udelay(10);
> +               count++;
> +       }
> +
> +       mvreg_write(pp, MVNETA_SMI,
> +                   ((mii_id << MVNETA_SMI_PHY_ADDR_SHIFT) |
> +                    (regnum << MVNETA_SMI_PHY_REG_SHIFT)  |
> +                    MVNETA_SMI_WRITE_OPERATION            |
> +                    (value << MVNETA_SMI_DATA_SHIFT)));
> +
> +       return 0;
> +}
> +
> +static int mvneta_mdio_reset(struct mii_bus *bus)
> +{
> +       return 0;
> +}
> +
> +
> +static void mvneta_adjust_link(struct net_device *ndev)
> +{
> +       struct mvneta_port *pp = netdev_priv(ndev);
> +       struct phy_device *phydev = pp->phy_dev;
> +       int status_change = 0;
> +
> +       if (phydev->link) {
> +               if ((pp->speed != phydev->speed) ||
> +                   (pp->duplex != phydev->duplex)) {
> +                       u32 val;
> +
> +                       val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
> +                       val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
> +                                MVNETA_GMAC_CONFIG_GMII_SPEED |
> +                                MVNETA_GMAC_CONFIG_FULL_DUPLEX);
> +
> +                       if (phydev->duplex)
> +                               val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
> +
> +                       if (phydev->speed == SPEED_1000)
> +                               val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
> +                       else
> +                               val |= MVNETA_GMAC_CONFIG_MII_SPEED;
> +
> +                       mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
> +
> +                       pp->duplex = phydev->duplex;
> +                       pp->speed  = phydev->speed;
> +               }
> +       }
> +
> +       if (phydev->link != pp->link) {
> +               if (!phydev->link) {
> +                       pp->duplex = -1;
> +                       pp->speed = 0;
> +               }
> +
> +               pp->link = phydev->link;
> +               status_change = 1;
> +       }
> +
> +       if (status_change) {
> +               if (phydev->link) {
> +                       u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
> +                       val |= (MVNETA_GMAC_FORCE_LINK_PASS |
> +                               MVNETA_GMAC_FORCE_LINK_DOWN);
> +                       mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
> +                       mvneta_port_up(pp);
> +                       netdev_info(pp->dev, "link up\n");
> +               } else {
> +                       mvneta_port_down(pp);
> +                       netdev_info(pp->dev, "link down\n");
> +               }
> +       }
> +}
> +
> +static int mvneta_mdio_probe(struct mvneta_port *pp)
> +{
> +       int ret;
> +       struct phy_device *phy_dev;
> +
> +       phy_dev = phy_find_first(pp->mii_bus);
> +       if (!phy_dev) {
> +               netdev_err(pp->dev, "no PHY found\n");
> +               return -ENODEV;
> +       }
> +
> +       ret = phy_connect_direct(pp->dev, phy_dev, mvneta_adjust_link, 0,
> +                                    pp->phy_interface);
> +       if (ret) {
> +               netdev_err(pp->dev, "could not attach to PHY\n");
> +               return ret;
> +       }
> +
> +       phy_dev->supported &= PHY_GBIT_FEATURES;
> +       phy_dev->advertising = phy_dev->supported;
> +
> +       pp->phy_dev = phy_dev;
> +       pp->link    = 0;
> +       pp->duplex  = 0;
> +       pp->speed   = 0;
> +
> +       return 0;
> +}
> +
> +static void mvneta_mdio_remove(struct mvneta_port *pp)
> +{
> +       phy_disconnect(pp->phy_dev);
> +       pp->phy_dev = NULL;
> +}
> +
> +static int mvneta_open(struct net_device *dev)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +       int ret;
> +
> +       ret = mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
> +       if (ret < 0) {
> +               netdev_err(dev, "mvneta_mac_addr_set failed\n");
> +               goto mac_addr_set_failure;
> +       }
> +
> +       pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
> +
> +       ret = mvneta_setup_rxqs(pp);
> +       if (ret)
> +               goto rxqs_setup_failure;
> +
> +       ret = mvneta_setup_txqs(pp);
> +       if (ret)
> +               goto txqs_setup_failure;
> +
> +       /* Connect to port interrupt line */
> +       ret = request_irq(pp->dev->irq, mvneta_isr, IRQF_DISABLED,
> +                         MVNETA_DRIVER_NAME, pp);
> +       if (ret) {
> +               netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
> +               goto request_irq_failure;
> +       }
> +
> +       /* In default link is down */
> +       netif_carrier_off(pp->dev);
> +
> +       ret = mvneta_mdio_probe(pp);
> +       if (ret < 0) {
> +               netdev_err(dev, "cannot probe MDIO bus\n");
> +               goto mdio_probe_failure;
> +       }
> +
> +       mvneta_start_dev(pp);
> +
> +       return 0;
> +
> +mdio_probe_failure:
> +       free_irq(pp->dev->irq, pp);
> +request_irq_failure:
> +       mvneta_cleanup_txqs(pp);
> +txqs_setup_failure:
> +       mvneta_cleanup_rxqs(pp);
> +rxqs_setup_failure:
> +mac_addr_set_failure:
> +       return ret;
> +}
> +
> +/* Stop the port, free port interrupt line */
> +static int mvneta_stop(struct net_device *dev)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       mvneta_stop_dev(pp);
> +       mvneta_cleanup_rxqs(pp);
> +       mvneta_cleanup_txqs(pp);
> +       del_timer(&pp->tx_done_timer);
> +       clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
> +       free_irq(dev->irq, pp);
> +       mvneta_mdio_remove(pp);
> +
> +       return 0;
> +}
> +
> +/* Ethtool methods */
> +
> +/* Get settings (phy address, speed) for ethtools */
> +int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       if (!pp->phy_dev)
> +               return -ENODEV;
> +
> +       return phy_ethtool_gset(pp->phy_dev, cmd);
> +}
> +
> +/* Set settings (phy address, speed) for ethtools */
> +int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       if (!pp->phy_dev)
> +               return -ENODEV;
> +
> +       return phy_ethtool_sset(pp->phy_dev, cmd);
> +}
> +
> +/* Set interrupt coalescing for ethtools */
> +static int mvneta_ethtool_set_coalesce(struct net_device *dev,
> +                                      struct ethtool_coalesce *c)
> +{
> +       int queue;
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
> +               rxq->time_coal = c->rx_coalesce_usecs;
> +               rxq->pkts_coal = c->rx_max_coalesced_frames;
> +               mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
> +               mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal);
> +       }
> +
> +       for (queue = 0; queue < txq_number; queue++) {
> +               struct mvneta_tx_queue *txq = &pp->txqs[queue];
> +               txq->done_pkts_coal = c->tx_max_coalesced_frames;
> +               mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
> +       }
> +
> +       return 0;
> +}
> +
> +/* get coalescing for ethtools */
> +static int mvneta_ethtool_get_coalesce(struct net_device *dev,
> +                              struct ethtool_coalesce *c)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       c->rx_coalesce_usecs        = pp->rxqs[0].time_coal;
> +       c->rx_max_coalesced_frames  = pp->rxqs[0].pkts_coal;
> +
> +       c->tx_max_coalesced_frames =  pp->txqs[0].done_pkts_coal;
> +       return 0;
> +}
> +
> +
> +static void mvneta_ethtool_get_drvinfo(struct net_device *dev,
> +                                   struct ethtool_drvinfo *drvinfo)
> +{
> +       strlcpy(drvinfo->driver, MVNETA_DRIVER_NAME,
> +               sizeof(drvinfo->driver));
> +       strlcpy(drvinfo->version, MVNETA_DRIVER_VERSION,
> +               sizeof(drvinfo->version));
> +       strlcpy(drvinfo->bus_info, dev_name(&dev->dev),
> +               sizeof(drvinfo->bus_info));
> +}
> +
> +
> +static void mvneta_ethtool_get_ringparam(struct net_device *netdev,
> +                               struct ethtool_ringparam *ring)
> +{
> +       struct mvneta_port *pp = netdev_priv(netdev);
> +
> +       ring->rx_max_pending = MVNETA_MAX_RXD;
> +       ring->tx_max_pending = MVNETA_MAX_TXD;
> +       ring->rx_pending = pp->rx_ring_size;
> +       ring->tx_pending = pp->tx_ring_size;
> +}
> +
> +static int mvneta_ethtool_set_ringparam(struct net_device *dev,
> +                              struct ethtool_ringparam *ring)
> +{
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       if ((ring->rx_pending == 0) || (ring->tx_pending == 0))
> +               return -EINVAL;
> +       pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ?
> +               ring->rx_pending : MVNETA_MAX_RXD;
> +       pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ?
> +               ring->tx_pending : MVNETA_MAX_TXD;
> +
> +       if (netif_running(dev)) {
> +               mvneta_stop(dev);
> +               if (mvneta_open(dev)) {
> +                       netdev_err(dev,
> +                                  "error on opening device after ring param change\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct net_device_ops mvneta_netdev_ops = {
> +       .ndo_open = mvneta_open,
> +       .ndo_stop = mvneta_stop,
> +       .ndo_start_xmit = mvneta_tx,
> +       .ndo_set_rx_mode = mvneta_set_rx_mode,
> +       .ndo_set_mac_address = mvneta_set_mac_addr,
> +       .ndo_change_mtu = mvneta_change_mtu,
> +       .ndo_tx_timeout = mvneta_tx_timeout,
> +       .ndo_get_stats64 = mvneta_get_stats64,
> +};
> +
> +const struct ethtool_ops mvneta_eth_tool_ops = {
> +       .get_link = ethtool_op_get_link,
> +       .get_settings = mvneta_ethtool_get_settings,
> +       .set_settings = mvneta_ethtool_set_settings,
> +       .set_coalesce = mvneta_ethtool_set_coalesce,
> +       .get_coalesce = mvneta_ethtool_get_coalesce,
> +       .get_drvinfo  = mvneta_ethtool_get_drvinfo,
> +       .get_ringparam  = mvneta_ethtool_get_ringparam,
> +       .set_ringparam  = mvneta_ethtool_set_ringparam,
> +};
> +
> +/* Initialize hw */
> +static int __devinit mvneta_init(struct mvneta_port *pp, int phy_addr)
> +{
> +       int queue, i, ret = 0;
> +
> +       /* Disable port */
> +       mvneta_port_disable(pp);
> +
> +       /* Set port default values */
> +       mvneta_defaults_set(pp);
> +
> +       pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue),
> +                          GFP_KERNEL);
> +       if (!pp->txqs) {
> +               netdev_err(pp->dev, "out of memory in allocating tx queue\n");
> +               ret = -ENOMEM;
> +               goto txqs_alloc_failure;
> +       }
> +
> +       /* Initialize TX descriptor rings */
> +       for (queue = 0; queue < txq_number; queue++) {
> +               struct mvneta_tx_queue *txq = &pp->txqs[queue];
> +               txq->id = queue;
> +               txq->size = pp->tx_ring_size;
> +               txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS;
> +       }
> +
> +       pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue),
> +                          GFP_KERNEL);
> +       if (!pp->rxqs) {
> +               netdev_err(pp->dev, "out of memory in allocating rx queue\n");
> +               ret = -ENOMEM;
> +               goto rxqs_alloc_failure;
> +       }
> +
> +       /* Create Rx descriptor rings */
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
> +               rxq->id = queue;
> +               rxq->size = pp->rx_ring_size;
> +               rxq->pkts_coal = MVNETA_RX_COAL_PKTS;
> +               rxq->time_coal = MVNETA_RX_COAL_USEC;
> +       }
> +
> +       pp->mii_bus = mdiobus_alloc();
> +       if (!pp->mii_bus) {
> +               netdev_err(pp->dev, "Cannot allocate MDIO bus\n");
> +               ret = -ENOMEM;
> +               goto mdiobus_alloc_failure;
> +       }
> +
> +       pp->mii_bus->name = "mvneta_mii_bus";
> +       pp->mii_bus->read = mvneta_mdio_read;
> +       pp->mii_bus->write = mvneta_mdio_write;
> +       pp->mii_bus->reset = mvneta_mdio_reset;
> +       snprintf(pp->mii_bus->id, MII_BUS_ID_SIZE, "%s-mii",
> +                dev_name(pp->dev->dev.parent));
> +       pp->mii_bus->priv = pp;
> +       pp->mii_bus->parent = pp->dev->dev.parent;
> +       pp->mii_bus->phy_mask = ~(1 << phy_addr);
> +
> +       pp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
> +       if (!pp->mii_bus->irq) {
> +               netdev_err(pp->dev, "Cannot allocate PHY IRQ array\n");
> +               ret = -ENOMEM;
> +               goto mdiobus_irq_alloc_failure;
> +       }
> +
> +       for (i = 0; i < PHY_MAX_ADDR; i++)
> +               pp->mii_bus->irq[i] = PHY_POLL;
> +
> +       ret = mdiobus_register(pp->mii_bus);
> +       if (ret < 0) {
> +               netdev_err(pp->dev, "Cannot register MDIO bus (%d)\n", ret);
> +               goto mdiobus_register_failure;
> +       }
> +
> +       return 0;
> +
> +mdiobus_register_failure:
> +       kfree(pp->mii_bus->irq);
> +mdiobus_irq_alloc_failure:
> +       mdiobus_free(pp->mii_bus);
> +mdiobus_alloc_failure:
> +       kfree(pp->rxqs);
> +rxqs_alloc_failure:
> +       kfree(pp->txqs);
> +txqs_alloc_failure:
> +       return ret;
> +}
> +
> +static void __devexit mvneta_deinit(struct mvneta_port *pp)
> +{
> +       mdiobus_unregister(pp->mii_bus);
> +       kfree(pp->mii_bus->irq);
> +       mdiobus_free(pp->mii_bus);
> +       kfree(pp->txqs);
> +       kfree(pp->rxqs);
> +}
> +
> +/* platform glue : initialize decoding windows */
> +static void __devinit mvneta_conf_mbus_windows(struct mvneta_port *pp,
> +                               const struct mbus_dram_target_info *dram)
> +{
> +       u32 win_enable;
> +       u32 win_protect;
> +       int i;
> +
> +       for (i = 0; i < 6; i++) {
> +               mvreg_write(pp, MVNETA_WIN_BASE(i), 0);
> +               mvreg_write(pp, MVNETA_WIN_SIZE(i), 0);
> +
> +               if (i < 4)
> +                       mvreg_write(pp, MVNETA_WIN_REMAP(i), 0);
> +       }
> +
> +       win_enable = 0x3f;
> +       win_protect = 0;
> +
> +       for (i = 0; i < dram->num_cs; i++) {
> +               const struct mbus_dram_window *cs = dram->cs + i;
> +               mvreg_write(pp, MVNETA_WIN_BASE(i),
> +                           (cs->base & 0xffff0000) |
> +                           (cs->mbus_attr << 8) |
> +                           dram->mbus_dram_target_id);
> +
> +               mvreg_write(pp, MVNETA_WIN_SIZE(i),
> +                           (cs->size - 1) & 0xffff0000);
> +
> +               win_enable &= ~(1 << i);
> +               win_protect |= 3 << (2 * i);
> +       }
> +
> +       mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable);
> +}
> +
> +/* Power up the port */
> +static void __devinit mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
> +{
> +       u32 val;
> +
> +       /* MAC Cause register should be cleared */
> +       mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0);
> +
> +       if (phy_mode == PHY_INTERFACE_MODE_SGMII)
> +               mvneta_port_sgmii_config(pp);
> +
> +       mvneta_gmac_rgmii_set(pp, 1);
> +
> +       /* Cancel Port Reset */
> +       val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
> +       val &= ~MVNETA_GMAC2_PORT_RESET;
> +       mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
> +
> +       while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
> +               MVNETA_GMAC2_PORT_RESET) != 0)
> +               continue;
> +}
> +
> +/* Device initialization routine */
> +static int __devinit mvneta_probe(struct platform_device *pdev)
> +{
> +       int err = -EINVAL;
> +       struct mvneta_port *pp;
> +       struct net_device *dev;
> +       u32 phy_addr, clk_rate_hz;
> +       int phy_mode;
> +       const char *mac_addr;
> +       const struct mbus_dram_target_info *dram_target_info;
> +       struct device_node *dn = pdev->dev.of_node;
> +
> +       dev = alloc_etherdev_mq(sizeof(struct mvneta_port), 8);
> +       if (!dev)
> +               return -ENOMEM;
> +
> +       dev->irq = irq_of_parse_and_map(dn, 0);
> +       if (dev->irq == 0) {
> +               err = -EINVAL;
> +               goto err_irq;
> +       }
> +
> +       if (of_property_read_u32(dn, "phy-addr", &phy_addr) != 0) {
> +               dev_err(&pdev->dev, "could not read phy-addr\n");
> +               err = -ENODEV;
> +               goto err_node;
> +       }
> +
> +       phy_mode = of_get_phy_mode(dn);
> +       if (phy_mode < 0) {
> +               dev_err(&pdev->dev, "wrong phy-mode\n");
> +               err = -EINVAL;
> +               goto err_node;
> +       }
> +
> +       if (of_property_read_u32(dn, "clock-frequency", &clk_rate_hz) != 0) {
> +               dev_err(&pdev->dev, "could not read clock-frequency\n");
> +               err = -EINVAL;
> +               goto err_node;
> +       }
> +
> +       mac_addr = of_get_mac_address(dn);
> +
> +       if (!mac_addr || !is_valid_ether_addr(mac_addr))
> +               eth_hw_addr_random(dev);
> +       else
> +               memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
> +
> +       dev->tx_queue_len = MVNETA_MAX_TXD;
> +       dev->watchdog_timeo = 5 * HZ;
> +       dev->netdev_ops = &mvneta_netdev_ops;
> +
> +       SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops);
> +
> +       pp = netdev_priv(dev);
> +
> +       pp->tx_done_timer.function = mvneta_tx_done_timer_callback;
> +       init_timer(&pp->tx_done_timer);
> +       clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
> +
> +       pp->weight = MVNETA_RX_POLL_WEIGHT;
> +       pp->clk_rate_hz = clk_rate_hz;
> +       pp->phy_interface = phy_mode;
> +
> +       pp->base = of_iomap(dn, 0);
> +       if (pp->base == NULL) {
> +               err = -ENOMEM;
> +               goto err_node;
> +       }
> +
> +       pp->tx_done_timer.data = (unsigned long)dev;
> +
> +       pp->tx_ring_size = MVNETA_MAX_TXD;
> +       pp->rx_ring_size = MVNETA_MAX_RXD;
> +
> +       pp->dev = dev;
> +       SET_NETDEV_DEV(dev, &pdev->dev);
> +
> +       if (mvneta_init(pp, phy_addr)) {
> +               dev_err(&pdev->dev, "can't init eth hal\n");
> +               err = -ENODEV;
> +               goto err_base;
> +       }
> +       mvneta_port_power_up(pp, phy_mode);
> +
> +       dram_target_info = mv_mbus_dram_info();
> +       if (dram_target_info)
> +               mvneta_conf_mbus_windows(pp, dram_target_info);
> +
> +       netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight);
> +
> +       if (register_netdev(dev)) {
> +               dev_err(&pdev->dev, "failed to register\n");
> +               err = ENOMEM;
> +               goto err_base;
> +       }
> +
> +       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
> +       dev->hw_features =  NETIF_F_SG | NETIF_F_IP_CSUM;
> +       dev->priv_flags |= IFF_UNICAST_FLT;
> +
> +       dev_info(&pdev->dev, "%s, mac: %pM\n", dev->name,
> +                dev->dev_addr);
> +
> +       platform_set_drvdata(pdev, pp->dev);
> +
> +       return 0;
> +err_base:
> +       iounmap(pp->base);
> +err_node:
> +       irq_dispose_mapping(dev->irq);
> +err_irq:
> +       free_netdev(dev);
> +       return err;
> +}
> +
> +/* Device removal routine */
> +static int __devexit mvneta_remove(struct platform_device *pdev)
> +{
> +       struct net_device  *dev = platform_get_drvdata(pdev);
> +       struct mvneta_port *pp = netdev_priv(dev);
> +
> +       iounmap(pp->base);
> +
> +       unregister_netdev(dev);
> +       irq_dispose_mapping(dev->irq);
> +       free_netdev(dev);
> +       mvneta_deinit(pp);
> +
> +       platform_set_drvdata(pdev, NULL);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id mvneta_match[] = {
> +       { .compatible = "marvell,armada-370-neta" },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, mvneta_match);
> +
> +static struct platform_driver mvneta_driver = {
> +       .probe = mvneta_probe,
> +       .remove = __devexit_p(mvneta_remove),
> +       .driver = {
> +               .name = MVNETA_DRIVER_NAME,
> +               .of_match_table = mvneta_match,
> +       },
> +};
> +
> +module_platform_driver(mvneta_driver);
> +
> +MODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com");
> +MODULE_AUTHOR("Rami Rosen <rosenr@marvell.com>, Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
> +MODULE_LICENSE("GPL");
> +
> +module_param(rxq_number, int, S_IRUGO);
> +module_param(txq_number, int, S_IRUGO);
> +
> +module_param(rxq_def, int, S_IRUGO);
> +module_param(txq_def, int, S_IRUGO);
> --
> 1.7.9.5
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



-- 
Nobuhiro Iwamatsu
   iwamatsu at {nigauri.org / debian.org}
   GPG ID: 40AD1FA6

^ permalink raw reply

* [PATCH] Linus's tree: wireless: Spelling corrections
From: Ashwini Singh @ 2012-10-30 11:48 UTC (permalink / raw)
  To: johannes
  Cc: linville, davem, linux-wireless, netdev, linux-kernel,
	kernel-janitors, Ashwini Singh

Few spelling corrections in wireless directory.

Signed-off-by: Ashwini Singh <asingh.engi@gmail.com>
---
 net/wireless/radiotap.c    |    2 +-
 net/wireless/reg.c         |    6 +++---
 net/wireless/wext-compat.c |    2 +-
 net/wireless/wext-core.c   |    2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c
index 7d604c0..eaf4282 100644
--- a/net/wireless/radiotap.c
+++ b/net/wireless/radiotap.c
@@ -190,7 +190,7 @@ static void find_ns(struct ieee80211_radiotap_iterator *iterator,
  * present fields.  @this_arg can be changed by the caller (eg,
  * incremented to move inside a compound argument like
  * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
- * little-endian format whatever the endianess of your CPU.
+ * little-endian format whatever the endianness of your CPU.
  *
  * Alignment Gotcha:
  * You must take care when dereferencing iterator.this_arg
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 3b8cbbc..b98ff98 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -718,7 +718,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
 
 	/*
 	 * Follow the driver's regulatory domain, if present, unless a country
-	 * IE has been processed or a user wants to help complaince further
+	 * IE has been processed or a user wants to help compliance further
 	 */
 	if (!custom_regd &&
 	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
@@ -2133,7 +2133,7 @@ bool reg_supported_dfs_region(u8 dfs_region)
 	case NL80211_DFS_JP:
 		return true;
 	default:
-		REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
+		REG_DBG_PRINT("Ignoring unknown DFS master region: %d\n",
 			      dfs_region);
 		return false;
 	}
@@ -2155,7 +2155,7 @@ static void print_dfs_region(u8 dfs_region)
 		pr_info(" DFS Master region JP");
 		break;
 	default:
-		pr_info(" DFS Master region Uknown");
+		pr_info(" DFS Master region Unknown");
 		break;
 	}
 }
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 494379e..7642f60 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1144,7 +1144,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
 		switch (wrq->flags & IW_POWER_MODE) {
 		case IW_POWER_ON:       /* If not specified */
 		case IW_POWER_MODE:     /* If set all mask */
-		case IW_POWER_ALL_R:    /* If explicitely state all */
+		case IW_POWER_ALL_R:    /* If explicitly state all */
 			ps = true;
 			break;
 		default:                /* Otherwise we ignore */
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index c8717c1..c9514ce 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -849,7 +849,7 @@ out:
  *
  * Note : our current commit strategy is currently pretty dumb,
  * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
+ * The goal is to try to aggregate as many changes as possible
  * before doing the commit. Drivers that will define a commit handler
  * are usually those that need a reset after changing parameters, so
  * we want to minimise the number of reset.
-- 
1.7.0.1

^ permalink raw reply related

* [PATCH] Linus's tree: wireless: Spelling corrections
From: asingh.engi @ 2012-10-30 11:47 UTC (permalink / raw)
  To: johannes
  Cc: linville, davem, linux-wireless, netdev, linux-kernel,
	kernel-janitors, Ashwini Singh

From: Ashwini Singh <asingh.engi@gmail.com>

Few spelling corrections in wireless directory.

Signed-off-by: Ashwini Singh <asingh.engi@gmail.com>
---
 net/wireless/radiotap.c    |    2 +-
 net/wireless/reg.c         |    6 +++---
 net/wireless/wext-compat.c |    2 +-
 net/wireless/wext-core.c   |    2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c
index 7d604c0..eaf4282 100644
--- a/net/wireless/radiotap.c
+++ b/net/wireless/radiotap.c
@@ -190,7 +190,7 @@ static void find_ns(struct ieee80211_radiotap_iterator *iterator,
  * present fields.  @this_arg can be changed by the caller (eg,
  * incremented to move inside a compound argument like
  * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
- * little-endian format whatever the endianess of your CPU.
+ * little-endian format whatever the endianness of your CPU.
  *
  * Alignment Gotcha:
  * You must take care when dereferencing iterator.this_arg
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 3b8cbbc..b98ff98 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -718,7 +718,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
 
 	/*
 	 * Follow the driver's regulatory domain, if present, unless a country
-	 * IE has been processed or a user wants to help complaince further
+	 * IE has been processed or a user wants to help compliance further
 	 */
 	if (!custom_regd &&
 	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
@@ -2133,7 +2133,7 @@ bool reg_supported_dfs_region(u8 dfs_region)
 	case NL80211_DFS_JP:
 		return true;
 	default:
-		REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
+		REG_DBG_PRINT("Ignoring unknown DFS master region: %d\n",
 			      dfs_region);
 		return false;
 	}
@@ -2155,7 +2155,7 @@ static void print_dfs_region(u8 dfs_region)
 		pr_info(" DFS Master region JP");
 		break;
 	default:
-		pr_info(" DFS Master region Uknown");
+		pr_info(" DFS Master region Unknown");
 		break;
 	}
 }
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 494379e..7642f60 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1144,7 +1144,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
 		switch (wrq->flags & IW_POWER_MODE) {
 		case IW_POWER_ON:       /* If not specified */
 		case IW_POWER_MODE:     /* If set all mask */
-		case IW_POWER_ALL_R:    /* If explicitely state all */
+		case IW_POWER_ALL_R:    /* If explicitly state all */
 			ps = true;
 			break;
 		default:                /* Otherwise we ignore */
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index c8717c1..c9514ce 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -849,7 +849,7 @@ out:
  *
  * Note : our current commit strategy is currently pretty dumb,
  * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
+ * The goal is to try to aggregate as many changes as possible
  * before doing the commit. Drivers that will define a commit handler
  * are usually those that need a reset after changing parameters, so
  * we want to minimise the number of reset.
-- 
1.7.0.1

^ permalink raw reply related

* [PATCH] net/at91_ether: fix the use of macb structure
From: Nicolas Ferre @ 2012-10-30 11:30 UTC (permalink / raw)
  To: netdev, davem, havard
  Cc: bhutchings, linux-arm-kernel, plagnioj, patrice.vilchez,
	linux-kernel, manabian, Nicolas Ferre
In-Reply-To: <75eb5df193c504e68ebff7d6a6a88d9a1cc678cb.1351591858.git.nicolas.ferre@atmel.com>

Due to the use of common structure in at91_ether and macb drivers,
change the name of DMA descriptor structures in at91_ether as well:
dma_desc => macb_dma_desc

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
Hi,

This patch is a fix for the patch
[PATCH v3 06/10] net/macb: clean up ring buffer logic
that I have just sent.

I would prefer to merge it with the patch mentioned above
that introduces the issue. Tell me if you do it or if I
have to provide a v4 for this patch series.

 drivers/net/ethernet/cadence/at91_ether.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index b92815a..0d6392d 100644
--- a/drivers/net/ethernet/cadence/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -156,7 +156,7 @@ static int at91ether_start(struct net_device *dev)
 	int i;
 
 	lp->rx_ring = dma_alloc_coherent(&lp->pdev->dev,
-					MAX_RX_DESCR * sizeof(struct dma_desc),
+					MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 					&lp->rx_ring_dma, GFP_KERNEL);
 	if (!lp->rx_ring) {
 		netdev_err(lp->dev, "unable to alloc rx ring DMA buffer\n");
@@ -170,7 +170,7 @@ static int at91ether_start(struct net_device *dev)
 		netdev_err(lp->dev, "unable to alloc rx data DMA buffer\n");
 
 		dma_free_coherent(&lp->pdev->dev,
-					MAX_RX_DESCR * sizeof(struct dma_desc),
+					MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 					lp->rx_ring, lp->rx_ring_dma);
 		lp->rx_ring = NULL;
 		return -ENOMEM;
@@ -256,7 +256,7 @@ static int at91ether_close(struct net_device *dev)
 	netif_stop_queue(dev);
 
 	dma_free_coherent(&lp->pdev->dev,
-				MAX_RX_DESCR * sizeof(struct dma_desc),
+				MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 				lp->rx_ring, lp->rx_ring_dma);
 	lp->rx_ring = NULL;
 
-- 
1.8.0

^ permalink raw reply related


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