Netdev List
 help / color / mirror / Atom feed
* Re: [Patch] fix packet loss and massive ping spikes with PPP multi-link
From: Alan Cox @ 2010-03-26 16:02 UTC (permalink / raw)
  To: Richard Hartmann; +Cc: linux-kernel, netdev, linux-ppp
In-Reply-To: <2d460de71003260850x7f90d04cy79ac853464108182@mail.gmail.com>

> We are fully aware that this code is not nearly up for inclusion into
> the kernel. What we hope to achieve is that someone with the skills to
> do this properly will introduce an option to turn off fragmentation on
> PPP multi-link or to just do away with it completely.

You should be able to manage that I'm sure:

The main thing will be to take this chunk, strip out the // stuff and
then make it a function of its own so you've got clean code in the main
path of the form

#ifdef CONFIG_PPP_MULTILINK
	if (ml_explode) {
		if (!ppp_mp_explode(...)
	..
	} else {
		ppp_mp_roundrobin(...)
	}

and your bits as a new routine. You can then do this

static int ml_explode = 1;
module_param(ml_explode, int, 0600);
MODULE_PARM_DESC(ml_expode, "Set this to zero to disabling multilink \
	fragmentation when talking to cisco devices");

which will let you load the module with the option ml_explode = 0 if you
want that property.

Making it runtime per link selectable would be nicer but thats a bit more
work.

Alan


^ permalink raw reply

* [Patch] fix packet loss and massive ping spikes with PPP multi-link
From: Richard Hartmann @ 2010-03-26 15:50 UTC (permalink / raw)
  To: linux-kernel, netdev, linux-ppp

Hi all,

as you may be aware, it is recommended to switch of fragmentation when
doing PPP multi-link. Problems which will be seen if you don't do that
involve packet loss and massive spikes in the round-trip times.
An increase of 1.5 seconds(!) is what we usually see.

Every Cisco CPE offers to switch off fragmentation for multi-link, other
manufacturers are likely to offer it, as well.
We implemented a really ugly hack which allows us to do the same with
the Linux kernel. I can confirm that it gets rid of the problem 100%
of the time.

We are fully aware that this code is not nearly up for inclusion into
the kernel. What we hope to achieve is that someone with the skills to
do this properly will introduce an option to turn off fragmentation on
PPP multi-link or to just do away with it completely.

Some stats for a run of 4 hours each:

No patch:
    129 lost fragments, 575803 reordered
    127/3960 discarded fragments/bytes, 0 lost received

Patch:
    0 lost fragments, 0 reordered
    0/0 discarded fragments/bytes, 0 lost received

Unfortunately, I don't have paste-able stats for ping times available,
at the moment.


Any and all feedback on this is appreciated,
Richard

PS: Our image which is deployed in the field is using 2.6.32 which is
why we were forced to develop for and test with 2.6.32 instead of
linux-next; sorry for that.


--- /usr/src/linux-2.6.32.3/drivers/net/ppp_generic.c.orig	2010-03-25
16:56:05.000000000 +0100
+++ /usr/src/linux-2.6.32.3/drivers/net/ppp_generic.c	2010-03-26
14:42:42.000000000 +0100
@@ -123,6 +123,7 @@
 	struct net_device *dev;		/* network interface device a4 */
 	int		closing;	/* is device closing down? a8 */
 #ifdef CONFIG_PPP_MULTILINK
+	int		rrsched;	/* round robin scheduler for packet distribution */
 	int		nxchan;		/* next channel to send something on */
 	u32		nxseq;		/* next sequence number to send */
 	int		mrru;		/* MP: max reconst. receive unit */
@@ -1261,6 +1262,7 @@
 	struct list_head *list;
 	struct channel *pch;
 	struct sk_buff *skb = ppp->xmit_pending;
+	int i;

 	if (!skb)
 		return;
@@ -1292,10 +1294,40 @@
 	}

 #ifdef CONFIG_PPP_MULTILINK
-	/* Multilink: fragment the packet over as many links
-	   as can take the packet at the moment. */
-	if (!ppp_mp_explode(ppp, skb))
-		return;
+	ppp->rrsched++;
+//    printk(KERN_ERR "ppp: multi new packet, rrsched = %d\n", ppp->rrsched);
+
+	i = 0;
+	list_for_each_entry(pch, &ppp->channels, clist)      {
+//    	printk(KERN_ERR "ppp: channel %d ... \n", i);
+		if(pch->chan == NULL) continue;
+
+		if (ppp->rrsched % ppp->n_channels == i) {
+//    		printk(KERN_ERR "use channel %d\n", i);
+			spin_lock_bh(&pch->downl);
+			if (pch->chan) {
+//				++ppp->nxseq;
+				if (pch->chan->ops->start_xmit(pch->chan, skb)) {
+					ppp->xmit_pending = NULL;
+				}
+			} else {
+				/* channel got unregistered */
+				kfree_skb(skb);
+				ppp->xmit_pending = NULL;
+			}
+			spin_unlock_bh(&pch->downl);
+			return;
+		}
+		i++;
+	}
+//	printk(KERN_ERR "keep in queue\n");
+	return;
+
+
+//	/* Multilink: fragment the packet over as many links
+//	   as can take the packet at the moment. */
+//	if (!ppp_mp_explode(ppp, skb))
+//		return;
 #endif /* CONFIG_PPP_MULTILINK */

 	ppp->xmit_pending = NULL;

^ permalink raw reply

* Re: [alsa-devel] [PATCH 13/27] drivers/net/of: don't use deprecated field in of_platform_driver
From: Timur Tabi @ 2010-03-26 15:23 UTC (permalink / raw)
  To: Grant Likely; +Cc: devicetree-discuss, netdev
In-Reply-To: <20100311182259.13103.87535.stgit@angua>

I'm seeing this error with this patch applied, when building for an mpc8641_hpcn

  CC      drivers/net/gianfar.o
drivers/net/gianfar.c: In function 'gfar_of_init':
drivers/net/gianfar.c:606: error: 'struct platform_device' has no
member named 'node'
drivers/net/gianfar.c:644: error: 'struct platform_device' has no
member named 'node'
drivers/net/gianfar.c: In function 'gfar_probe':
drivers/net/gianfar.c:891: error: 'struct platform_device' has no
member named 'node'
make[2]: *** [drivers/net/gianfar.o] Error 1
make[1]: *** [drivers/net] Error 2
make: *** [drivers] Error 2


On Thu, Mar 11, 2010 at 1:23 PM, Grant Likely <grant.likely@secretlab.ca> wrote:
> .name, .match_table and .owner are duplicated in both of_platform_driver
> and device_driver, so the of_platform_driver copies will be removed soon.
>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
>
>  drivers/net/can/mscan/mpc5xxx_can.c           |    8 +++++---
>  drivers/net/can/sja1000/sja1000_of_platform.c |    8 +++++---
>  drivers/net/ehea/ehea_main.c                  |    7 +++++--
>  drivers/net/fs_enet/fs_enet-main.c            |    7 +++++--
>  drivers/net/fs_enet/mii-bitbang.c             |    7 +++++--
>  drivers/net/fs_enet/mii-fec.c                 |    7 +++++--
>  drivers/net/fsl_pq_mdio.c                     |    7 +++++--
>  drivers/net/gianfar.c                         |   10 ++++++----
>  drivers/net/ibm_newemac/core.c                |    8 +++++---
>  drivers/net/ibm_newemac/mal.c                 |    8 +++++---
>  drivers/net/ibm_newemac/rgmii.c               |    8 +++++---
>  drivers/net/ibm_newemac/tah.c                 |    8 +++++---
>  drivers/net/ibm_newemac/zmii.c                |    8 +++++---
>  drivers/net/ll_temac_main.c                   |    2 +-
>  drivers/net/myri_sbus.c                       |    7 +++++--
>  drivers/net/niu.c                             |    7 +++++--
>  drivers/net/phy/mdio-gpio.c                   |    7 +++++--
>  drivers/net/sunbmac.c                         |    7 +++++--
>  drivers/net/sunhme.c                          |    7 +++++--
>  drivers/net/sunlance.c                        |    7 +++++--
>  drivers/net/sunqe.c                           |    7 +++++--
>  drivers/net/ucc_geth.c                        |    7 +++++--
>  drivers/net/xilinx_emaclite.c                 |    7 +++++--
>  23 files changed, 112 insertions(+), 54 deletions(-)

-- 
Timur Tabi
Linux kernel developer at Freescale

^ permalink raw reply

* Re: [PATCH] netdev/fec.c: add phylib supporting to enable carrier detection
From: Wolfram Sang @ 2010-03-26 14:47 UTC (permalink / raw)
  To: Bryan Wu
  Cc: s.hauer, gerg, amit.kucheria, netdev, kernel-team, linux-kernel,
	linux-arm-kernel
In-Reply-To: <1269597052-10104-1-git-send-email-bryan.wu@canonical.com>

[-- Attachment #1: Type: text/plain, Size: 689 bytes --]

On Fri, Mar 26, 2010 at 05:50:52PM +0800, Bryan Wu wrote:
> BugLink: http://bugs.launchpad.net/bugs/457878
> 
>  - removed old MII phy control code
>  - add phylib supporting
>  - add ethtool interface to make user space NetworkManager works
> 
> Tested on Freescale i.MX51 Babbage board.
> 
> This patch is based on a patch from Frederic Rodo <fred.rodo@gmail.com>
> 
> Cc: Frederic Rodo <fred.rodo@gmail.com>
> Signed-off-by: Bryan Wu <bryan.wu@canonical.com>

Great, will test it soon.

Regards,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: IPv6 neighbor advertisements
From: Haile Seifu @ 2010-03-26 14:40 UTC (permalink / raw)
  To: netdev; +Cc: David Miller
In-Reply-To: <20100325.230733.214002385.davem@davemloft.net>

Hi,


Yes, but the neighbor entry can be checked for the protocol type and
then the confirmed/updated field can be reset if the else condition is
taken.


On Fri, Mar 26, 2010 at 2:07 AM, David Miller <davem@davemloft.net> wrote:
>
> Keep in mind that net/core/neighbour.c is for IPV4 ARP too.
>
> So if there is a conflict between what is allowed by ipv4
> and what is allowed by ipv6 in this scenerio, that would
> explain it.
>

^ permalink raw reply

* Re: WARNING: at net/sched/sch_generic.c:261 dev_watchdog+0x249/0x260()
From: Eric Dumazet @ 2010-03-26 14:34 UTC (permalink / raw)
  To: Lee; +Cc: netdev
In-Reply-To: <4BACC1FD.3000609@web.de>

Le vendredi 26 mars 2010 à 15:17 +0100, Lee a écrit :
> Hi again,
> 
> As this mail went unanswered, did I forget anything? Was it too verbose? 
> Should I file a bug report on bugzilla.kernel.org instead?
> 
> Regards,
> Lee
> 

Well, many problems with rtl8169, unfortunatly.

You could try Francois patch :

http://www.spinics.net/lists/netdev/msg124131.html



> On 22/03/10 21:52, Lee wrote:
> > Hi everyone,
> >
> > I get a kernel oops on 2.6.32.10 when I use an rt73usb WLAN stick with
> > hostapd 0.6.10. It's bridged with one of my ethernet (r8169) interfaces.
> > When I have the stick plugged in without hostapd, the kernel oops
> > doesn't happen. It's not easily triggered, it usually happens within a
> > few hours of use with hostapd. I have about a dozen of those from while
> > tracking this bug down, if needed.
> >
> > Everything seems to work fine (before and after the oops), but at least
> > on one occasion I had a large delay on my ssh line when the oops happened.
> >
> > I have no idea what is causing this, but will happily test any patches
> > that are handed in my direction :)
> >
> > Kernel oops, .config and hostapd.conf are attached. Please tell me if
> > anything else is needed to hunt down this bug.
> >
> > Regards,
> > Lee
> 



^ permalink raw reply

* Re: behavior of recvmmsg() on blocking sockets
From: Eric Dumazet @ 2010-03-26 14:20 UTC (permalink / raw)
  To: Ulrich Drepper; +Cc: Brandon Black, netdev, linux-kernel
In-Reply-To: <a36005b51003260500v6d8b7a5av1bbba848cc8998a4@mail.gmail.com>

Le vendredi 26 mars 2010 à 05:00 -0700, Ulrich Drepper a écrit :
> On Wed, Mar 24, 2010 at 16:35, Brandon Black <blblack@gmail.com> wrote:
> > +                /* MSG_WAITFORONE turns on MSG_DONTWAIT after one packet */
> > +               if (!datagrams++ && flags & MSG_WAITFORONE)
> > +                       flags |= MSG_DONTWAIT;
> 
> There should be an extra pair of parenthesis around the & operands but
> aside from that I could imagine this to work.
> 
> Would be nice if the netdev folks would respond.


It seems fine, Brandon please submit a formal patch.

Thanks

^ permalink raw reply

* Re: WARNING: at net/sched/sch_generic.c:261 dev_watchdog+0x249/0x260()
From: Lee @ 2010-03-26 14:17 UTC (permalink / raw)
  To: netdev
In-Reply-To: <4BA7D897.9050407@web.de>

Hi again,

As this mail went unanswered, did I forget anything? Was it too verbose? 
Should I file a bug report on bugzilla.kernel.org instead?

Regards,
Lee

On 22/03/10 21:52, Lee wrote:
> Hi everyone,
>
> I get a kernel oops on 2.6.32.10 when I use an rt73usb WLAN stick with
> hostapd 0.6.10. It's bridged with one of my ethernet (r8169) interfaces.
> When I have the stick plugged in without hostapd, the kernel oops
> doesn't happen. It's not easily triggered, it usually happens within a
> few hours of use with hostapd. I have about a dozen of those from while
> tracking this bug down, if needed.
>
> Everything seems to work fine (before and after the oops), but at least
> on one occasion I had a large delay on my ssh line when the oops happened.
>
> I have no idea what is causing this, but will happily test any patches
> that are handed in my direction :)
>
> Kernel oops, .config and hostapd.conf are attached. Please tell me if
> anything else is needed to hunt down this bug.
>
> Regards,
> Lee



^ permalink raw reply

* [iproute2] a 'ip rule ...' bug?
From: thomas yang @ 2010-03-26 12:15 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev

in  /etc/iproute2/rt_dsfield
...
# Newer RFC2597 values
0x28	AF11
0x30	AF12
0x38	AF13
...

---in   ip  man page ---
ip rule add - insert a new rule

tos TOS
dsfield TOS
    select the TOS value to match.
---

[root@localhost ~]# ip rule add dsfield 0x28 table 200
Error: argument "dsfield" is wrong: Failed to parse rule type

why???
how to match 'dsfield' ?

[root@localhost ~]# ip rule add tos 0x28 table 200
RTNETLINK answers: Invalid argument

(My OS is Fedora 11 :  kernel-2.6.30.10-105.2.23.fc11.i586 ,
iproute-2.6.29-2.fc11.i586 ;
on my another linux box ( Fedora 7)   'ip rule'  also has the same
problem  to match  tos and dsfield. )


--
Tom

^ permalink raw reply

* Re: behavior of recvmmsg() on blocking sockets
From: Ulrich Drepper @ 2010-03-26 12:00 UTC (permalink / raw)
  To: Brandon Black; +Cc: netdev, linux-kernel
In-Reply-To: <84621a61003241635h65687366t6b28148281d529c9@mail.gmail.com>

On Wed, Mar 24, 2010 at 16:35, Brandon Black <blblack@gmail.com> wrote:
> +                /* MSG_WAITFORONE turns on MSG_DONTWAIT after one packet */
> +               if (!datagrams++ && flags & MSG_WAITFORONE)
> +                       flags |= MSG_DONTWAIT;

There should be an extra pair of parenthesis around the & operands but
aside from that I could imagine this to work.

Would be nice if the netdev folks would respond.

^ permalink raw reply

* RE: [PATCHv1 3/4] netxen: added sanity check for pci map
From: Amit Salecha @ 2010-03-26 10:40 UTC (permalink / raw)
  To: David Miller; +Cc: netdev@vger.kernel.org, Ameen Rahman
In-Reply-To: <20100325.120446.98057142.davem@davemloft.net>

David, 
	Sorry for inconvenience caused. I'll take care in future work.
	I have submitted reworked patches.

-Amit 

-----Original Message-----
From: David Miller [mailto:davem@davemloft.net] 
Sent: Friday, March 26, 2010 12:35 AM
To: Amit Salecha
Cc: netdev@vger.kernel.org; Ameen Rahman
Subject: Re: [PATCHv1 3/4] netxen: added sanity check for pci map

From: Amit Kumar Salecha <amit.salecha@qlogic.com>
Date: Thu, 25 Mar 2010 00:06:45 -0700

> Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
> 
> Return value of ioremap is not checked, NULL check added.

You're not even thinking when you make these changes.

Let's try again ok?

These ioremap() calls store the pointers into local variables.
So it doesn't work at all to just jump to err_out to clean
them up and iounmap them.

The unamp code you end up calling in netxen_pci_map() only does an
iounmap() on pointers stored in the netxen_adapter struct.  At this
point they haven't been stored there yet.

So they still leak.

Can you tell my patience on this patch set is very much running
out?  If you put half of the effort writing these patches as I
am putting into reviewing them, we wouldn't have to go back
and forth on this so many times.

Double check your work, and resubmit this whole patch set once you've
fixed this one, I think the other ones look fine.

Thanks.

^ permalink raw reply

* [PATCHv2 1/4] netxen: fix bios version calculation
From: Amit Kumar Salecha @ 2010-03-26 10:30 UTC (permalink / raw)
  To: davem; +Cc: netdev, ameen.rahman
In-Reply-To: <1269599410-17809-1-git-send-email-amit.salecha@qlogic.com>

Bios sub version from unified fw image is calculated incorrect.

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
---
 drivers/net/netxen/netxen_nic_init.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 1c63610..7eb925a 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -761,7 +761,7 @@ nx_get_bios_version(struct netxen_adapter *adapter)
 	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) {
 		bios_ver = cpu_to_le32(*((u32 *) (&fw->data[prd_off])
 						+ NX_UNI_BIOS_VERSION_OFF));
-		return (bios_ver << 24) + ((bios_ver >> 8) & 0xff00) +
+		return (bios_ver << 16) + ((bios_ver >> 8) & 0xff00) +
 							(bios_ver >> 24);
 	} else
 		return cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]);
-- 
1.6.0.2


^ permalink raw reply related

* [PATCHv2 2/4] netxen: fix warning in ioaddr for NX3031 chip
From: Amit Kumar Salecha @ 2010-03-26 10:30 UTC (permalink / raw)
  To: davem; +Cc: netdev, ameen.rahman
In-Reply-To: <1269599410-17809-1-git-send-email-amit.salecha@qlogic.com>

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>

crb_intr_mask/crb_sts_consumer is predefined for NX2031 not for
NX3031. For NX3031, these values get defined in rx context creation.
---
 drivers/net/netxen/netxen_nic_ctx.c |   14 ++++++++------
 1 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/netxen/netxen_nic_ctx.c
index 2a8ef5f..f26e547 100644
--- a/drivers/net/netxen/netxen_nic_ctx.c
+++ b/drivers/net/netxen/netxen_nic_ctx.c
@@ -669,13 +669,15 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
 		}
 		sds_ring->desc_head = (struct status_desc *)addr;
 
-		sds_ring->crb_sts_consumer =
-			netxen_get_ioaddr(adapter,
-			recv_crb_registers[port].crb_sts_consumer[ring]);
+		if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
+			sds_ring->crb_sts_consumer =
+				netxen_get_ioaddr(adapter,
+				recv_crb_registers[port].crb_sts_consumer[ring]);
 
-		sds_ring->crb_intr_mask =
-			netxen_get_ioaddr(adapter,
-			recv_crb_registers[port].sw_int_mask[ring]);
+			sds_ring->crb_intr_mask =
+				netxen_get_ioaddr(adapter,
+				recv_crb_registers[port].sw_int_mask[ring]);
+		}
 	}
 
 
-- 
1.6.0.2


^ permalink raw reply related

* [PATCHv2 4/4] netxen: update version to 4.0.73
From: Amit Kumar Salecha @ 2010-03-26 10:30 UTC (permalink / raw)
  To: davem; +Cc: netdev, ameen.rahman
In-Reply-To: <1269599410-17809-1-git-send-email-amit.salecha@qlogic.com>

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
---
 drivers/net/netxen/netxen_nic.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index 144d2e8..0f70383 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -53,8 +53,8 @@
 
 #define _NETXEN_NIC_LINUX_MAJOR 4
 #define _NETXEN_NIC_LINUX_MINOR 0
-#define _NETXEN_NIC_LINUX_SUBVERSION 72
-#define NETXEN_NIC_LINUX_VERSIONID  "4.0.72"
+#define _NETXEN_NIC_LINUX_SUBVERSION 73
+#define NETXEN_NIC_LINUX_VERSIONID  "4.0.73"
 
 #define NETXEN_VERSION_CODE(a, b, c)	(((a) << 24) + ((b) << 16) + (c))
 #define _major(v)	(((v) >> 24) & 0xff)
-- 
1.6.0.2


^ permalink raw reply related

* [PATCHv2 0/4]netxen: bug fixes
From: Amit Kumar Salecha @ 2010-03-26 10:30 UTC (permalink / raw)
  To: davem; +Cc: netdev, ameen.rahman

David,
   Series of 4 patches to fix bugs.
   These are v2 patches, fixing ioremap issue reported by you.
   Apply them on net-2.6 branch.

-Amit

^ permalink raw reply

* [PATCHv2 3/4] netxen: added sanity check for pci map
From: Amit Kumar Salecha @ 2010-03-26 10:30 UTC (permalink / raw)
  To: davem; +Cc: netdev, ameen.rahman
In-Reply-To: <1269599410-17809-1-git-send-email-amit.salecha@qlogic.com>

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>

Return value of ioremap is not checked, NULL check added.

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
---
 drivers/net/netxen/netxen_nic_main.c |   45 ++++++++++++++++++++-------------
 1 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 9a7a0f3..01808b2 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -604,16 +604,14 @@ netxen_cleanup_pci_map(struct netxen_adapter *adapter)
 static int
 netxen_setup_pci_map(struct netxen_adapter *adapter)
 {
-	void __iomem *mem_ptr0 = NULL;
-	void __iomem *mem_ptr1 = NULL;
-	void __iomem *mem_ptr2 = NULL;
 	void __iomem *db_ptr = NULL;
 
 	resource_size_t mem_base, db_base;
-	unsigned long mem_len, db_len = 0, pci_len0 = 0;
+	unsigned long mem_len, db_len = 0;
 
 	struct pci_dev *pdev = adapter->pdev;
 	int pci_func = adapter->ahw.pci_func;
+	struct netxen_hardware_context *ahw = &adapter->ahw;
 
 	int err = 0;
 
@@ -630,24 +628,40 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
 
 	/* 128 Meg of memory */
 	if (mem_len == NETXEN_PCI_128MB_SIZE) {
-		mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE);
-		mem_ptr1 = ioremap(mem_base + SECOND_PAGE_GROUP_START,
+
+		ahw->pci_base0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE);
+		ahw->pci_base1 = ioremap(mem_base + SECOND_PAGE_GROUP_START,
 				SECOND_PAGE_GROUP_SIZE);
-		mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
+		ahw->pci_base2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
 				THIRD_PAGE_GROUP_SIZE);
-		pci_len0 = FIRST_PAGE_GROUP_SIZE;
+		if (ahw->pci_base0 == NULL || ahw->pci_base1 == NULL ||
+						ahw->pci_base2 == NULL) {
+			dev_err(&pdev->dev, "failed to map PCI bar 0\n");
+			err = -EIO;
+			goto err_out;
+		}
+
+		ahw->pci_len0 = FIRST_PAGE_GROUP_SIZE;
+
 	} else if (mem_len == NETXEN_PCI_32MB_SIZE) {
-		mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
-		mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
+
+		ahw->pci_base1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
+		ahw->pci_base2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
 			SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE);
+		if (ahw->pci_base1 == NULL || ahw->pci_base2 == NULL) {
+			dev_err(&pdev->dev, "failed to map PCI bar 0\n");
+			err = -EIO;
+			goto err_out;
+		}
+
 	} else if (mem_len == NETXEN_PCI_2MB_SIZE) {
 
-		mem_ptr0 = pci_ioremap_bar(pdev, 0);
-		if (mem_ptr0 == NULL) {
+		ahw->pci_base0 = pci_ioremap_bar(pdev, 0);
+		if (ahw->pci_base0 == NULL) {
 			dev_err(&pdev->dev, "failed to map PCI bar 0\n");
 			return -EIO;
 		}
-		pci_len0 = mem_len;
+		ahw->pci_len0 = mem_len;
 	} else {
 		return -EIO;
 	}
@@ -656,11 +670,6 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
 
 	dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20));
 
-	adapter->ahw.pci_base0 = mem_ptr0;
-	adapter->ahw.pci_len0 = pci_len0;
-	adapter->ahw.pci_base1 = mem_ptr1;
-	adapter->ahw.pci_base2 = mem_ptr2;
-
 	if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) {
 		adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter,
 			NETXEN_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func)));
-- 
1.6.0.2


^ permalink raw reply related

* Re: [PATCH] netdev/fec.c: add phylib supporting to enable carrier detection
From: Amit Kucheria @ 2010-03-26 10:29 UTC (permalink / raw)
  To: Bryan Wu
  Cc: netdev, s.hauer, linux-kernel, w.sang, kernel-team, gerg,
	linux-arm-kernel
In-Reply-To: <1269597052-10104-1-git-send-email-bryan.wu@canonical.com>

On 10 Mar 26, Bryan Wu wrote:
> BugLink: http://bugs.launchpad.net/bugs/457878
> 
>  - removed old MII phy control code
>  - add phylib supporting
>  - add ethtool interface to make user space NetworkManager works
> 
> Tested on Freescale i.MX51 Babbage board.
> 
> This patch is based on a patch from Frederic Rodo <fred.rodo@gmail.com>
> 
> Cc: Frederic Rodo <fred.rodo@gmail.com>
> Signed-off-by: Bryan Wu <bryan.wu@canonical.com>

While I ack this patch, I wonder if we should add to the various board
Kconfig options, a dependency select'ing the right phylib for that board.

This would prevent breakage of ethernet on those boards because they forgot
to select the right phylib after this change.

e.g.

diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig
index 1576d51..b67ba00 100644
--- a/arch/arm/mach-mx5/Kconfig
+++ b/arch/arm/mach-mx5/Kconfig
@@ -10,6 +10,7 @@ comment "MX5 platforms:"
 
 config MACH_MX51_BABBAGE
 	bool "Support MX51 BABBAGE platforms"
+	select SMSC_PHY
 	help
 	  Include support for MX51 Babbage platform, also known as MX51EVK in
 	  u-boot. This includes specific configurations for the board and its

> ---
>  drivers/net/Kconfig |    1 +
>  drivers/net/fec.c   | 1125 ++++++++++++---------------------------------------
>  2 files changed, 253 insertions(+), 873 deletions(-)
> 
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 0ba5b8e..41f6a70 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -1916,6 +1916,7 @@ config FEC
>  	bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
>  	depends on M523x || M527x || M5272 || M528x || M520x || M532x || \
>  		MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5
> +	select PHYLIB
>  	help
>  	  Say Y here if you want to use the built-in 10/100 Fast ethernet
>  	  controller on some Motorola ColdFire and Freescale i.MX processors.
> diff --git a/drivers/net/fec.c b/drivers/net/fec.c
> index 9f98c1c..fca1f66 100644
> --- a/drivers/net/fec.c
> +++ b/drivers/net/fec.c
> @@ -40,6 +40,7 @@
>  #include <linux/irq.h>
>  #include <linux/clk.h>
>  #include <linux/platform_device.h>
> +#include <linux/phy.h>
>  
>  #include <asm/cacheflush.h>
>  
> @@ -61,7 +62,6 @@
>   * Define the fixed address of the FEC hardware.
>   */
>  #if defined(CONFIG_M5272)
> -#define HAVE_mii_link_interrupt
>  
>  static unsigned char	fec_mac_default[] = {
>  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> @@ -86,23 +86,6 @@ static unsigned char	fec_mac_default[] = {
>  #endif
>  #endif /* CONFIG_M5272 */
>  
> -/* Forward declarations of some structures to support different PHYs */
> -
> -typedef struct {
> -	uint mii_data;
> -	void (*funct)(uint mii_reg, struct net_device *dev);
> -} phy_cmd_t;
> -
> -typedef struct {
> -	uint id;
> -	char *name;
> -
> -	const phy_cmd_t *config;
> -	const phy_cmd_t *startup;
> -	const phy_cmd_t *ack_int;
> -	const phy_cmd_t *shutdown;
> -} phy_info_t;
> -
>  /* The number of Tx and Rx buffers.  These are allocated from the page
>   * pool.  The code may assume these are power of two, so it it best
>   * to keep them that size.
> @@ -189,29 +172,21 @@ struct fec_enet_private {
>  	uint	tx_full;
>  	/* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
>  	spinlock_t hw_lock;
> -	/* hold while accessing the mii_list_t() elements */
> -	spinlock_t mii_lock;
> -
> -	uint	phy_id;
> -	uint	phy_id_done;
> -	uint	phy_status;
> -	uint	phy_speed;
> -	phy_info_t const	*phy;
> -	struct work_struct phy_task;
>  
> -	uint	sequence_done;
> -	uint	mii_phy_task_queued;
> +	struct  platform_device *pdev;
>  
> -	uint	phy_addr;
> +	int	opened;
>  
> +	/* Phylib and MDIO interface */
> +	struct  mii_bus *mii_bus;
> +	struct  phy_device *phy_dev;
> +	int     mii_timeout;
> +	uint    phy_speed;
>  	int	index;
> -	int	opened;
>  	int	link;
> -	int	old_link;
>  	int	full_duplex;
>  };
>  
> -static void fec_enet_mii(struct net_device *dev);
>  static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
>  static void fec_enet_tx(struct net_device *dev);
>  static void fec_enet_rx(struct net_device *dev);
> @@ -219,67 +194,20 @@ static int fec_enet_close(struct net_device *dev);
>  static void fec_restart(struct net_device *dev, int duplex);
>  static void fec_stop(struct net_device *dev);
>  
> +/* FEC MII MMFR bits definition */
> +#define FEC_MMFR_ST		(1 << 30)
> +#define FEC_MMFR_OP_READ	(2 << 28)
> +#define FEC_MMFR_OP_WRITE	(1 << 28)
> +#define FEC_MMFR_PA(v)		((v & 0x1f) << 23)
> +#define FEC_MMFR_RA(v)		((v & 0x1f) << 18)
> +#define FEC_MMFR_TA		(2 << 16)
> +#define FEC_MMFR_DATA(v)	(v & 0xffff)
>  
> -/* MII processing.  We keep this as simple as possible.  Requests are
> - * placed on the list (if there is room).  When the request is finished
> - * by the MII, an optional function may be called.
> - */
> -typedef struct mii_list {
> -	uint	mii_regval;
> -	void	(*mii_func)(uint val, struct net_device *dev);
> -	struct	mii_list *mii_next;
> -} mii_list_t;
> -
> -#define		NMII	20
> -static mii_list_t	mii_cmds[NMII];
> -static mii_list_t	*mii_free;
> -static mii_list_t	*mii_head;
> -static mii_list_t	*mii_tail;
> -
> -static int	mii_queue(struct net_device *dev, int request,
> -				void (*func)(uint, struct net_device *));
> -
> -/* Make MII read/write commands for the FEC */
> -#define mk_mii_read(REG)	(0x60020000 | ((REG & 0x1f) << 18))
> -#define mk_mii_write(REG, VAL)	(0x50020000 | ((REG & 0x1f) << 18) | \
> -						(VAL & 0xffff))
> -#define mk_mii_end	0
> +#define FEC_MII_TIMEOUT		10000
>  
>  /* Transmitter timeout */
>  #define TX_TIMEOUT (2 * HZ)
>  
> -/* Register definitions for the PHY */
> -
> -#define MII_REG_CR          0  /* Control Register                         */
> -#define MII_REG_SR          1  /* Status Register                          */
> -#define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
> -#define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
> -#define MII_REG_ANAR        4  /* A-N Advertisement Register               */
> -#define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */
> -#define MII_REG_ANER        6  /* A-N Expansion Register                   */
> -#define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */
> -#define MII_REG_ANLPRNPR    8  /* A-N Link Partner Received Next Page Reg. */
> -
> -/* values for phy_status */
> -
> -#define PHY_CONF_ANE	0x0001  /* 1 auto-negotiation enabled */
> -#define PHY_CONF_LOOP	0x0002  /* 1 loopback mode enabled */
> -#define PHY_CONF_SPMASK	0x00f0  /* mask for speed */
> -#define PHY_CONF_10HDX	0x0010  /* 10 Mbit half duplex supported */
> -#define PHY_CONF_10FDX	0x0020  /* 10 Mbit full duplex supported */
> -#define PHY_CONF_100HDX	0x0040  /* 100 Mbit half duplex supported */
> -#define PHY_CONF_100FDX	0x0080  /* 100 Mbit full duplex supported */
> -
> -#define PHY_STAT_LINK	0x0100  /* 1 up - 0 down */
> -#define PHY_STAT_FAULT	0x0200  /* 1 remote fault */
> -#define PHY_STAT_ANC	0x0400  /* 1 auto-negotiation complete	*/
> -#define PHY_STAT_SPMASK	0xf000  /* mask for speed */
> -#define PHY_STAT_10HDX	0x1000  /* 10 Mbit half duplex selected	*/
> -#define PHY_STAT_10FDX	0x2000  /* 10 Mbit full duplex selected	*/
> -#define PHY_STAT_100HDX	0x4000  /* 100 Mbit half duplex selected */
> -#define PHY_STAT_100FDX	0x8000  /* 100 Mbit full duplex selected */
> -
> -
>  static int
>  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  {
> @@ -406,12 +334,6 @@ fec_enet_interrupt(int irq, void * dev_id)
>  			ret = IRQ_HANDLED;
>  			fec_enet_tx(dev);
>  		}
> -
> -		if (int_events & FEC_ENET_MII) {
> -			ret = IRQ_HANDLED;
> -			fec_enet_mii(dev);
> -		}
> -
>  	} while (int_events);
>  
>  	return ret;
> @@ -607,827 +529,312 @@ rx_processing_done:
>  	spin_unlock(&fep->hw_lock);
>  }
>  
> -/* called from interrupt context */
> -static void
> -fec_enet_mii(struct net_device *dev)
> -{
> -	struct	fec_enet_private *fep;
> -	mii_list_t	*mip;
> -
> -	fep = netdev_priv(dev);
> -	spin_lock(&fep->mii_lock);
> -
> -	if ((mip = mii_head) == NULL) {
> -		printk("MII and no head!\n");
> -		goto unlock;
> -	}
> -
> -	if (mip->mii_func != NULL)
> -		(*(mip->mii_func))(readl(fep->hwp + FEC_MII_DATA), dev);
> -
> -	mii_head = mip->mii_next;
> -	mip->mii_next = mii_free;
> -	mii_free = mip;
> -
> -	if ((mip = mii_head) != NULL)
> -		writel(mip->mii_regval, fep->hwp + FEC_MII_DATA);
> -
> -unlock:
> -	spin_unlock(&fep->mii_lock);
> -}
> -
> -static int
> -mii_queue_unlocked(struct net_device *dev, int regval,
> -		void (*func)(uint, struct net_device *))
> +/* ------------------------------------------------------------------------- */
> +#ifdef CONFIG_M5272
> +static void __inline__ fec_get_mac(struct net_device *dev)
>  {
> -	struct fec_enet_private *fep;
> -	mii_list_t	*mip;
> -	int		retval;
> -
> -	/* Add PHY address to register command */
> -	fep = netdev_priv(dev);
> +	struct fec_enet_private *fep = netdev_priv(dev);
> +	unsigned char *iap, tmpaddr[ETH_ALEN];
>  
> -	regval |= fep->phy_addr << 23;
> -	retval = 0;
> -
> -	if ((mip = mii_free) != NULL) {
> -		mii_free = mip->mii_next;
> -		mip->mii_regval = regval;
> -		mip->mii_func = func;
> -		mip->mii_next = NULL;
> -		if (mii_head) {
> -			mii_tail->mii_next = mip;
> -			mii_tail = mip;
> -		} else {
> -			mii_head = mii_tail = mip;
> -			writel(regval, fep->hwp + FEC_MII_DATA);
> -		}
> +	if (FEC_FLASHMAC) {
> +		/*
> +		 * Get MAC address from FLASH.
> +		 * If it is all 1's or 0's, use the default.
> +		 */
> +		iap = (unsigned char *)FEC_FLASHMAC;
> +		if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
> +		    (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
> +			iap = fec_mac_default;
> +		if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
> +		    (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
> +			iap = fec_mac_default;
>  	} else {
> -		retval = 1;
> +		*((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
> +		*((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
> +		iap = &tmpaddr[0];
>  	}
>  
> -	return retval;
> -}
> -
> -static int
> -mii_queue(struct net_device *dev, int regval,
> -		void (*func)(uint, struct net_device *))
> -{
> -	struct fec_enet_private *fep;
> -	unsigned long   flags;
> -	int             retval;
> -	fep = netdev_priv(dev);
> -	spin_lock_irqsave(&fep->mii_lock, flags);
> -	retval = mii_queue_unlocked(dev, regval, func);
> -	spin_unlock_irqrestore(&fep->mii_lock, flags);
> -	return retval;
> -}
> -
> -static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
> -{
> -	if(!c)
> -		return;
> +	memcpy(dev->dev_addr, iap, ETH_ALEN);
>  
> -	for (; c->mii_data != mk_mii_end; c++)
> -		mii_queue(dev, c->mii_data, c->funct);
> +	/* Adjust MAC if using default MAC address */
> +	if (iap == fec_mac_default)
> +		 dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
>  }
> +#endif
>  
> -static void mii_parse_sr(uint mii_reg, struct net_device *dev)
> -{
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> -
> -	status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
> -
> -	if (mii_reg & 0x0004)
> -		status |= PHY_STAT_LINK;
> -	if (mii_reg & 0x0010)
> -		status |= PHY_STAT_FAULT;
> -	if (mii_reg & 0x0020)
> -		status |= PHY_STAT_ANC;
> -	*s = status;
> -}
> +/* ------------------------------------------------------------------------- */
>  
> -static void mii_parse_cr(uint mii_reg, struct net_device *dev)
> +/*
> + * Phy section
> + */
> +static void fec_enet_adjust_link(struct net_device *dev)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> -
> -	status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
> -
> -	if (mii_reg & 0x1000)
> -		status |= PHY_CONF_ANE;
> -	if (mii_reg & 0x4000)
> -		status |= PHY_CONF_LOOP;
> -	*s = status;
> -}
> +	struct phy_device *phy_dev = fep->phy_dev;
> +	unsigned long flags;
>  
> -static void mii_parse_anar(uint mii_reg, struct net_device *dev)
> -{
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> -
> -	status = *s & ~(PHY_CONF_SPMASK);
> -
> -	if (mii_reg & 0x0020)
> -		status |= PHY_CONF_10HDX;
> -	if (mii_reg & 0x0040)
> -		status |= PHY_CONF_10FDX;
> -	if (mii_reg & 0x0080)
> -		status |= PHY_CONF_100HDX;
> -	if (mii_reg & 0x00100)
> -		status |= PHY_CONF_100FDX;
> -	*s = status;
> -}
> +	int status_change = 0;
>  
> -/* ------------------------------------------------------------------------- */
> -/* The Level one LXT970 is used by many boards				     */
> +	spin_lock_irqsave(&fep->hw_lock, flags);
>  
> -#define MII_LXT970_MIRROR    16  /* Mirror register           */
> -#define MII_LXT970_IER       17  /* Interrupt Enable Register */
> -#define MII_LXT970_ISR       18  /* Interrupt Status Register */
> -#define MII_LXT970_CONFIG    19  /* Configuration Register    */
> -#define MII_LXT970_CSR       20  /* Chip Status Register      */
> +	/* Prevent a state halted on mii error */
> +	if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
> +		phy_dev->state = PHY_RESUMING;
> +		goto spin_unlock;
> +	}
>  
> -static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
> -{
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> +	/* Duplex link change */
> +	if (phy_dev->link) {
> +		if (fep->full_duplex != phy_dev->duplex) {
> +			fec_restart(dev, phy_dev->duplex);
> +			status_change = 1;
> +		}
> +	}
>  
> -	status = *s & ~(PHY_STAT_SPMASK);
> -	if (mii_reg & 0x0800) {
> -		if (mii_reg & 0x1000)
> -			status |= PHY_STAT_100FDX;
> +	/* Link on or off change */
> +	if (phy_dev->link != fep->link) {
> +		fep->link = phy_dev->link;
> +		if (phy_dev->link)
> +			fec_restart(dev, phy_dev->duplex);
>  		else
> -			status |= PHY_STAT_100HDX;
> -	} else {
> -		if (mii_reg & 0x1000)
> -			status |= PHY_STAT_10FDX;
> -		else
> -			status |= PHY_STAT_10HDX;
> +			fec_stop(dev);
> +		status_change = 1;
>  	}
> -	*s = status;
> -}
> -
> -static phy_cmd_t const phy_cmd_lxt970_config[] = {
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
> -		{ mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
> -		/* read SR and ISR to acknowledge */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_read(MII_LXT970_ISR), NULL },
> -
> -		/* find out the current status */
> -		{ mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
> -		{ mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_info_t const phy_info_lxt970 = {
> -	.id = 0x07810000,
> -	.name = "LXT970",
> -	.config = phy_cmd_lxt970_config,
> -	.startup = phy_cmd_lxt970_startup,
> -	.ack_int = phy_cmd_lxt970_ack_int,
> -	.shutdown = phy_cmd_lxt970_shutdown
> -};
>  
> -/* ------------------------------------------------------------------------- */
> -/* The Level one LXT971 is used on some of my custom boards                  */
> -
> -/* register definitions for the 971 */
> +spin_unlock:
> +	spin_unlock_irqrestore(&fep->hw_lock, flags);
>  
> -#define MII_LXT971_PCR       16  /* Port Control Register     */
> -#define MII_LXT971_SR2       17  /* Status Register 2         */
> -#define MII_LXT971_IER       18  /* Interrupt Enable Register */
> -#define MII_LXT971_ISR       19  /* Interrupt Status Register */
> -#define MII_LXT971_LCR       20  /* LED Control Register      */
> -#define MII_LXT971_TCR       30  /* Transmit Control Register */
> +	if (status_change)
> +		phy_print_status(phy_dev);
> +}
>  
>  /*
> - * I had some nice ideas of running the MDIO faster...
> - * The 971 should support 8MHz and I tried it, but things acted really
> - * weird, so 2.5 MHz ought to be enough for anyone...
> + * NOTE: a MII transaction is during around 25 us, so polling it...
>   */
> -
> -static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
> +static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
>  {
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> +	struct fec_enet_private *fep = bus->priv;
> +	int timeout = FEC_MII_TIMEOUT;
>  
> -	status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
> +	fep->mii_timeout = 0;
>  
> -	if (mii_reg & 0x0400) {
> -		fep->link = 1;
> -		status |= PHY_STAT_LINK;
> -	} else {
> -		fep->link = 0;
> -	}
> -	if (mii_reg & 0x0080)
> -		status |= PHY_STAT_ANC;
> -	if (mii_reg & 0x4000) {
> -		if (mii_reg & 0x0200)
> -			status |= PHY_STAT_100FDX;
> -		else
> -			status |= PHY_STAT_100HDX;
> -	} else {
> -		if (mii_reg & 0x0200)
> -			status |= PHY_STAT_10FDX;
> -		else
> -			status |= PHY_STAT_10HDX;
> +	/* clear MII end of transfer bit*/
> +	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
> +
> +	/* start a read op */
> +	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
> +		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> +		FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
> +
> +	/* wait for end of transfer */
> +	while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
> +		cpu_relax();
> +		if (timeout-- < 0) {
> +			fep->mii_timeout = 1;
> +			printk(KERN_ERR "FEC: MDIO read timeout\n");
> +			return -ETIMEDOUT;
> +		}
>  	}
> -	if (mii_reg & 0x0008)
> -		status |= PHY_STAT_FAULT;
>  
> -	*s = status;
> +	/* return value */
> +	return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
>  }
>  
> -static phy_cmd_t const phy_cmd_lxt971_config[] = {
> -		/* limit to 10MBit because my prototype board
> -		 * doesn't work with 100. */
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt971_startup[] = {  /* enable interrupts */
> -		{ mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
> -		/* Somehow does the 971 tell me that the link is down
> -		 * the first read after power-up.
> -		 * read here to get a valid value in ack_int */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
> -		/* acknowledge the int before reading status ! */
> -		{ mk_mii_read(MII_LXT971_ISR), NULL },
> -		/* find out the current status */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
> -		{ mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_info_t const phy_info_lxt971 = {
> -	.id = 0x0001378e,
> -	.name = "LXT971",
> -	.config = phy_cmd_lxt971_config,
> -	.startup = phy_cmd_lxt971_startup,
> -	.ack_int = phy_cmd_lxt971_ack_int,
> -	.shutdown = phy_cmd_lxt971_shutdown
> -};
> -
> -/* ------------------------------------------------------------------------- */
> -/* The Quality Semiconductor QS6612 is used on the RPX CLLF                  */
> -
> -/* register definitions */
> -
> -#define MII_QS6612_MCR       17  /* Mode Control Register      */
> -#define MII_QS6612_FTR       27  /* Factory Test Register      */
> -#define MII_QS6612_MCO       28  /* Misc. Control Register     */
> -#define MII_QS6612_ISR       29  /* Interrupt Source Register  */
> -#define MII_QS6612_IMR       30  /* Interrupt Mask Register    */
> -#define MII_QS6612_PCR       31  /* 100BaseTx PHY Control Reg. */
> -
> -static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
> +static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
> +			   u16 value)
>  {
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> +	struct fec_enet_private *fep = bus->priv;
> +	int timeout = FEC_MII_TIMEOUT;
>  
> -	status = *s & ~(PHY_STAT_SPMASK);
> +	fep->mii_timeout = 0;
>  
> -	switch((mii_reg >> 2) & 7) {
> -	case 1: status |= PHY_STAT_10HDX; break;
> -	case 2: status |= PHY_STAT_100HDX; break;
> -	case 5: status |= PHY_STAT_10FDX; break;
> -	case 6: status |= PHY_STAT_100FDX; break;
> -}
> -
> -	*s = status;
> -}
> -
> -static phy_cmd_t const phy_cmd_qs6612_config[] = {
> -		/* The PHY powers up isolated on the RPX,
> -		 * so send a command to allow operation.
> -		 */
> -		{ mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
> -
> -		/* parse cr and anar to get some info */
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_qs6612_startup[] = {  /* enable interrupts */
> -		{ mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
> -		/* we need to read ISR, SR and ANER to acknowledge */
> -		{ mk_mii_read(MII_QS6612_ISR), NULL },
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_read(MII_REG_ANER), NULL },
> -
> -		/* read pcr to get info */
> -		{ mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
> -		{ mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_info_t const phy_info_qs6612 = {
> -	.id = 0x00181440,
> -	.name = "QS6612",
> -	.config = phy_cmd_qs6612_config,
> -	.startup = phy_cmd_qs6612_startup,
> -	.ack_int = phy_cmd_qs6612_ack_int,
> -	.shutdown = phy_cmd_qs6612_shutdown
> -};
> -
> -/* ------------------------------------------------------------------------- */
> -/* AMD AM79C874 phy                                                          */
> +	/* clear MII end of transfer bit*/
> +	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
>  
> -/* register definitions for the 874 */
> +	/* start a read op */
> +	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
> +		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
> +		FEC_MMFR_TA | FEC_MMFR_DATA(value),
> +		fep->hwp + FEC_MII_DATA);
> +
> +	/* wait for end of transfer */
> +	while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
> +		cpu_relax();
> +		if (timeout-- < 0) {
> +			fep->mii_timeout = 1;
> +			printk(KERN_ERR "FEC: MDIO write timeout\n");
> +			return -ETIMEDOUT;
> +		}
> +	}
>  
> -#define MII_AM79C874_MFR       16  /* Miscellaneous Feature Register */
> -#define MII_AM79C874_ICSR      17  /* Interrupt/Status Register      */
> -#define MII_AM79C874_DR        18  /* Diagnostic Register            */
> -#define MII_AM79C874_PMLR      19  /* Power and Loopback Register    */
> -#define MII_AM79C874_MCR       21  /* ModeControl Register           */
> -#define MII_AM79C874_DC        23  /* Disconnect Counter             */
> -#define MII_AM79C874_REC       24  /* Recieve Error Counter          */
> +	return 0;
> +}
>  
> -static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
> +static int fec_enet_mdio_reset(struct mii_bus *bus)
>  {
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -	uint status;
> -
> -	status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
> -
> -	if (mii_reg & 0x0080)
> -		status |= PHY_STAT_ANC;
> -	if (mii_reg & 0x0400)
> -		status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
> -	else
> -		status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
> -
> -	*s = status;
> +	return 0;
>  }
>  
> -static phy_cmd_t const phy_cmd_am79c874_config[] = {
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_am79c874_startup[] = {  /* enable interrupts */
> -		{ mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
> -		/* find out the current status */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
> -		/* we only need to read ISR to acknowledge */
> -		{ mk_mii_read(MII_AM79C874_ICSR), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
> -		{ mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_info_t const phy_info_am79c874 = {
> -	.id = 0x00022561,
> -	.name = "AM79C874",
> -	.config = phy_cmd_am79c874_config,
> -	.startup = phy_cmd_am79c874_startup,
> -	.ack_int = phy_cmd_am79c874_ack_int,
> -	.shutdown = phy_cmd_am79c874_shutdown
> -};
> -
> -
> -/* ------------------------------------------------------------------------- */
> -/* Kendin KS8721BL phy                                                       */
> -
> -/* register definitions for the 8721 */
> -
> -#define MII_KS8721BL_RXERCR	21
> -#define MII_KS8721BL_ICSR	27
> -#define	MII_KS8721BL_PHYCR	31
> -
> -static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_ks8721bl_startup[] = {  /* enable interrupts */
> -		{ mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
> -		/* find out the current status */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		/* we only need to read ISR to acknowledge */
> -		{ mk_mii_read(MII_KS8721BL_ICSR), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
> -		{ mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
> -		{ mk_mii_end, }
> -	};
> -static phy_info_t const phy_info_ks8721bl = {
> -	.id = 0x00022161,
> -	.name = "KS8721BL",
> -	.config = phy_cmd_ks8721bl_config,
> -	.startup = phy_cmd_ks8721bl_startup,
> -	.ack_int = phy_cmd_ks8721bl_ack_int,
> -	.shutdown = phy_cmd_ks8721bl_shutdown
> -};
> -
> -/* ------------------------------------------------------------------------- */
> -/* register definitions for the DP83848 */
> -
> -#define MII_DP8384X_PHYSTST    16  /* PHY Status Register */
> -
> -static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
> +static int fec_enet_mii_probe(struct net_device *dev)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> -
> -	*s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
> -
> -	/* Link up */
> -	if (mii_reg & 0x0001) {
> -		fep->link = 1;
> -		*s |= PHY_STAT_LINK;
> -	} else
> -		fep->link = 0;
> -	/* Status of link */
> -	if (mii_reg & 0x0010)   /* Autonegotioation complete */
> -		*s |= PHY_STAT_ANC;
> -	if (mii_reg & 0x0002) {   /* 10MBps? */
> -		if (mii_reg & 0x0004)   /* Full Duplex? */
> -			*s |= PHY_STAT_10FDX;
> -		else
> -			*s |= PHY_STAT_10HDX;
> -	} else {                  /* 100 Mbps? */
> -		if (mii_reg & 0x0004)   /* Full Duplex? */
> -			*s |= PHY_STAT_100FDX;
> -		else
> -			*s |= PHY_STAT_100HDX;
> -	}
> -	if (mii_reg & 0x0008)
> -		*s |= PHY_STAT_FAULT;
> -}
> -
> -static phy_info_t phy_info_dp83848= {
> -	0x020005c9,
> -	"DP83848",
> +	struct phy_device *phy_dev = NULL;
> +	int phy_addr;
>  
> -	(const phy_cmd_t []) {  /* config */
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) {  /* startup - enable interrupts */
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) {  /* shutdown */
> -		{ mk_mii_end, }
> -	},
> -};
> +	/* find the first phy */
> +	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
> +		if (fep->mii_bus->phy_map[phy_addr]) {
> +			phy_dev = fep->mii_bus->phy_map[phy_addr];
> +			break;
> +		}
> +	}
>  
> -static phy_info_t phy_info_lan8700 = {
> -	0x0007C0C,
> -	"LAN8700",
> -	(const phy_cmd_t []) { /* config */
> -		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
> -		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) { /* startup */
> -		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
> -		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) { /* act_int */
> -		{ mk_mii_end, }
> -	},
> -	(const phy_cmd_t []) { /* shutdown */
> -		{ mk_mii_end, }
> -	},
> -};
> -/* ------------------------------------------------------------------------- */
> +	if (!phy_dev) {
> +		printk(KERN_ERR "%s: no PHY found\n", dev->name);
> +		return -ENODEV;
> +	}
>  
> -static phy_info_t const * const phy_info[] = {
> -	&phy_info_lxt970,
> -	&phy_info_lxt971,
> -	&phy_info_qs6612,
> -	&phy_info_am79c874,
> -	&phy_info_ks8721bl,
> -	&phy_info_dp83848,
> -	&phy_info_lan8700,
> -	NULL
> -};
> +	/* attach the mac to the phy */
> +	phy_dev = phy_connect(dev, dev_name(&phy_dev->dev),
> +			     &fec_enet_adjust_link, 0,
> +			     PHY_INTERFACE_MODE_MII);
> +	if (IS_ERR(phy_dev)) {
> +		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
> +		return PTR_ERR(phy_dev);
> +	}
>  
> -/* ------------------------------------------------------------------------- */
> -#ifdef HAVE_mii_link_interrupt
> -static irqreturn_t
> -mii_link_interrupt(int irq, void * dev_id);
> +	/* mask with MAC supported features */
> +	phy_dev->supported &= PHY_BASIC_FEATURES;
> +	phy_dev->advertising = phy_dev->supported;
>  
> -/*
> - *	This is specific to the MII interrupt setup of the M5272EVB.
> - */
> -static void __inline__ fec_request_mii_intr(struct net_device *dev)
> -{
> -	if (request_irq(66, mii_link_interrupt, IRQF_DISABLED, "fec(MII)", dev) != 0)
> -		printk("FEC: Could not allocate fec(MII) IRQ(66)!\n");
> -}
> +	fep->phy_dev = phy_dev;
> +	fep->link = 0;
> +	fep->full_duplex = 0;
>  
> -static void __inline__ fec_disable_phy_intr(struct net_device *dev)
> -{
> -	free_irq(66, dev);
> +	return 0;
>  }
> -#endif
>  
> -#ifdef CONFIG_M5272
> -static void __inline__ fec_get_mac(struct net_device *dev)
> +static int fec_enet_mii_init(struct platform_device *pdev)
>  {
> +	struct net_device *dev = platform_get_drvdata(pdev);
>  	struct fec_enet_private *fep = netdev_priv(dev);
> -	unsigned char *iap, tmpaddr[ETH_ALEN];
> +	int err = -ENXIO, i;
>  
> -	if (FEC_FLASHMAC) {
> -		/*
> -		 * Get MAC address from FLASH.
> -		 * If it is all 1's or 0's, use the default.
> -		 */
> -		iap = (unsigned char *)FEC_FLASHMAC;
> -		if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
> -		    (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
> -			iap = fec_mac_default;
> -		if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
> -		    (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
> -			iap = fec_mac_default;
> -	} else {
> -		*((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
> -		*((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
> -		iap = &tmpaddr[0];
> -	}
> -
> -	memcpy(dev->dev_addr, iap, ETH_ALEN);
> -
> -	/* Adjust MAC if using default MAC address */
> -	if (iap == fec_mac_default)
> -		 dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
> -}
> -#endif
> +	fep->mii_timeout = 0;
>  
> -/* ------------------------------------------------------------------------- */
> -
> -static void mii_display_status(struct net_device *dev)
> -{
> -	struct fec_enet_private *fep = netdev_priv(dev);
> -	volatile uint *s = &(fep->phy_status);
> +	/*
> +	 * Set MII speed to 2.5 MHz
> +	 */
> +	fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
> +					/ 2500000) / 2) & 0x3F) << 1;
> +	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  
> -	if (!fep->link && !fep->old_link) {
> -		/* Link is still down - don't print anything */
> -		return;
> +	fep->mii_bus = mdiobus_alloc();
> +	if (fep->mii_bus == NULL) {
> +		err = -ENOMEM;
> +		goto err_out;
>  	}
>  
> -	printk("%s: status: ", dev->name);
> -
> -	if (!fep->link) {
> -		printk("link down");
> -	} else {
> -		printk("link up");
> -
> -		switch(*s & PHY_STAT_SPMASK) {
> -		case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
> -		case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
> -		case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
> -		case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
> -		default:
> -			printk(", Unknown speed/duplex");
> -		}
> -
> -		if (*s & PHY_STAT_ANC)
> -			printk(", auto-negotiation complete");
> +	fep->mii_bus->name = "fec_enet_mii_bus";
> +	fep->mii_bus->read = fec_enet_mdio_read;
> +	fep->mii_bus->write = fec_enet_mdio_write;
> +	fep->mii_bus->reset = fec_enet_mdio_reset;
> +	snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
> +	fep->mii_bus->priv = fep;
> +	fep->mii_bus->parent = &pdev->dev;
> +
> +	fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
> +	if (!fep->mii_bus->irq) {
> +		err = -ENOMEM;
> +		goto err_out_free_mdiobus;
>  	}
>  
> -	if (*s & PHY_STAT_FAULT)
> -		printk(", remote fault");
> -
> -	printk(".\n");
> -}
> -
> -static void mii_display_config(struct work_struct *work)
> -{
> -	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
> -	struct net_device *dev = fep->netdev;
> -	uint status = fep->phy_status;
> +	for (i = 0; i < PHY_MAX_ADDR; i++)
> +		fep->mii_bus->irq[i] = PHY_POLL;
>  
> -	/*
> -	** When we get here, phy_task is already removed from
> -	** the workqueue.  It is thus safe to allow to reuse it.
> -	*/
> -	fep->mii_phy_task_queued = 0;
> -	printk("%s: config: auto-negotiation ", dev->name);
> -
> -	if (status & PHY_CONF_ANE)
> -		printk("on");
> -	else
> -		printk("off");
> +	platform_set_drvdata(dev, fep->mii_bus);
>  
> -	if (status & PHY_CONF_100FDX)
> -		printk(", 100FDX");
> -	if (status & PHY_CONF_100HDX)
> -		printk(", 100HDX");
> -	if (status & PHY_CONF_10FDX)
> -		printk(", 10FDX");
> -	if (status & PHY_CONF_10HDX)
> -		printk(", 10HDX");
> -	if (!(status & PHY_CONF_SPMASK))
> -		printk(", No speed/duplex selected?");
> +	if (mdiobus_register(fep->mii_bus))
> +		goto err_out_free_mdio_irq;
>  
> -	if (status & PHY_CONF_LOOP)
> -		printk(", loopback enabled");
> +	if (fec_enet_mii_probe(dev) != 0)
> +		goto err_out_unregister_bus;
>  
> -	printk(".\n");
> +	return 0;
>  
> -	fep->sequence_done = 1;
> +err_out_unregister_bus:
> +	mdiobus_unregister(fep->mii_bus);
> +err_out_free_mdio_irq:
> +	kfree(fep->mii_bus->irq);
> +err_out_free_mdiobus:
> +	mdiobus_free(fep->mii_bus);
> +err_out:
> +	return err;
>  }
>  
> -static void mii_relink(struct work_struct *work)
> +static void fec_enet_mii_remove(struct fec_enet_private *fep)
>  {
> -	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
> -	struct net_device *dev = fep->netdev;
> -	int duplex;
> -
> -	/*
> -	** When we get here, phy_task is already removed from
> -	** the workqueue.  It is thus safe to allow to reuse it.
> -	*/
> -	fep->mii_phy_task_queued = 0;
> -	fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
> -	mii_display_status(dev);
> -	fep->old_link = fep->link;
> -
> -	if (fep->link) {
> -		duplex = 0;
> -		if (fep->phy_status
> -		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
> -			duplex = 1;
> -		fec_restart(dev, duplex);
> -	} else
> -		fec_stop(dev);
> +	if (fep->phy_dev)
> +		phy_disconnect(fep->phy_dev);
> +	mdiobus_unregister(fep->mii_bus);
> +	kfree(fep->mii_bus->irq);
> +	mdiobus_free(fep->mii_bus);
>  }
>  
> -/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
> -static void mii_queue_relink(uint mii_reg, struct net_device *dev)
> +static int fec_enet_get_settings(struct net_device *dev,
> +				  struct ethtool_cmd *cmd)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	struct phy_device *phydev = fep->phy_dev;
>  
> -	/*
> -	 * We cannot queue phy_task twice in the workqueue.  It
> -	 * would cause an endless loop in the workqueue.
> -	 * Fortunately, if the last mii_relink entry has not yet been
> -	 * executed now, it will do the job for the current interrupt,
> -	 * which is just what we want.
> -	 */
> -	if (fep->mii_phy_task_queued)
> -		return;
> +	if (!phydev)
> +		return -ENODEV;
>  
> -	fep->mii_phy_task_queued = 1;
> -	INIT_WORK(&fep->phy_task, mii_relink);
> -	schedule_work(&fep->phy_task);
> +	return phy_ethtool_gset(phydev, cmd);
>  }
>  
> -/* mii_queue_config is called in interrupt context from fec_enet_mii */
> -static void mii_queue_config(uint mii_reg, struct net_device *dev)
> +static int fec_enet_set_settings(struct net_device *dev,
> +				 struct ethtool_cmd *cmd)
>  {
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	struct phy_device *phydev = fep->phy_dev;
>  
> -	if (fep->mii_phy_task_queued)
> -		return;
> +	if (!phydev)
> +		return -ENODEV;
>  
> -	fep->mii_phy_task_queued = 1;
> -	INIT_WORK(&fep->phy_task, mii_display_config);
> -	schedule_work(&fep->phy_task);
> +	return phy_ethtool_sset(phydev, cmd);
>  }
>  
> -phy_cmd_t const phy_cmd_relink[] = {
> -	{ mk_mii_read(MII_REG_CR), mii_queue_relink },
> -	{ mk_mii_end, }
> -	};
> -phy_cmd_t const phy_cmd_config[] = {
> -	{ mk_mii_read(MII_REG_CR), mii_queue_config },
> -	{ mk_mii_end, }
> -	};
> -
> -/* Read remainder of PHY ID. */
> -static void
> -mii_discover_phy3(uint mii_reg, struct net_device *dev)
> +static void fec_enet_get_drvinfo(struct net_device *dev,
> +				 struct ethtool_drvinfo *info)
>  {
> -	struct fec_enet_private *fep;
> -	int i;
> -
> -	fep = netdev_priv(dev);
> -	fep->phy_id |= (mii_reg & 0xffff);
> -	printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
> -
> -	for(i = 0; phy_info[i]; i++) {
> -		if(phy_info[i]->id == (fep->phy_id >> 4))
> -			break;
> -	}
> -
> -	if (phy_info[i])
> -		printk(" -- %s\n", phy_info[i]->name);
> -	else
> -		printk(" -- unknown PHY!\n");
> +	struct fec_enet_private *fep = netdev_priv(dev);
>  
> -	fep->phy = phy_info[i];
> -	fep->phy_id_done = 1;
> +	strcpy(info->driver, fep->pdev->dev.driver->name);
> +	strcpy(info->version, "Revision: 1.0");
> +	strcpy(info->bus_info, dev_name(&dev->dev));
>  }
>  
> -/* Scan all of the MII PHY addresses looking for someone to respond
> - * with a valid ID.  This usually happens quickly.
> - */
> -static void
> -mii_discover_phy(uint mii_reg, struct net_device *dev)
> -{
> -	struct fec_enet_private *fep;
> -	uint phytype;
> -
> -	fep = netdev_priv(dev);
> -
> -	if (fep->phy_addr < 32) {
> -		if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
> -
> -			/* Got first part of ID, now get remainder */
> -			fep->phy_id = phytype << 16;
> -			mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR2),
> -							mii_discover_phy3);
> -		} else {
> -			fep->phy_addr++;
> -			mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR1),
> -							mii_discover_phy);
> -		}
> -	} else {
> -		printk("FEC: No PHY device found.\n");
> -		/* Disable external MII interface */
> -		writel(0, fep->hwp + FEC_MII_SPEED);
> -		fep->phy_speed = 0;
> -#ifdef HAVE_mii_link_interrupt
> -		fec_disable_phy_intr(dev);
> -#endif
> -	}
> -}
> +static struct ethtool_ops fec_enet_ethtool_ops = {
> +	.get_settings		= fec_enet_get_settings,
> +	.set_settings		= fec_enet_set_settings,
> +	.get_drvinfo		= fec_enet_get_drvinfo,
> +	.get_link		= ethtool_op_get_link,
> +};
>  
> -/* This interrupt occurs when the PHY detects a link change */
> -#ifdef HAVE_mii_link_interrupt
> -static irqreturn_t
> -mii_link_interrupt(int irq, void * dev_id)
> +static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
>  {
> -	struct	net_device *dev = dev_id;
>  	struct fec_enet_private *fep = netdev_priv(dev);
> +	struct phy_device *phydev = fep->phy_dev;
>  
> -	mii_do_cmd(dev, fep->phy->ack_int);
> -	mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
> +	if (!netif_running(dev))
> +		return -EINVAL;
>  
> -	return IRQ_HANDLED;
> +	if (!phydev)
> +		return -ENODEV;
> +
> +	return phy_mii_ioctl(phydev, if_mii(rq), cmd);
>  }
> -#endif
>  
>  static void fec_enet_free_buffers(struct net_device *dev)
>  {
> @@ -1509,35 +916,8 @@ fec_enet_open(struct net_device *dev)
>  	if (ret)
>  		return ret;
>  
> -	fep->sequence_done = 0;
> -	fep->link = 0;
> -
> -	fec_restart(dev, 1);
> -
> -	if (fep->phy) {
> -		mii_do_cmd(dev, fep->phy->ack_int);
> -		mii_do_cmd(dev, fep->phy->config);
> -		mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
> -
> -		/* Poll until the PHY tells us its configuration
> -		 * (not link state).
> -		 * Request is initiated by mii_do_cmd above, but answer
> -		 * comes by interrupt.
> -		 * This should take about 25 usec per register at 2.5 MHz,
> -		 * and we read approximately 5 registers.
> -		 */
> -		while(!fep->sequence_done)
> -			schedule();
> -
> -		mii_do_cmd(dev, fep->phy->startup);
> -	}
> -
> -	/* Set the initial link state to true. A lot of hardware
> -	 * based on this device does not implement a PHY interrupt,
> -	 * so we are never notified of link change.
> -	 */
> -	fep->link = 1;
> -
> +	/* schedule a link state check */
> +	phy_start(fep->phy_dev);
>  	netif_start_queue(dev);
>  	fep->opened = 1;
>  	return 0;
> @@ -1550,6 +930,7 @@ fec_enet_close(struct net_device *dev)
>  
>  	/* Don't know what to do yet. */
>  	fep->opened = 0;
> +	phy_stop(fep->phy_dev);
>  	netif_stop_queue(dev);
>  	fec_stop(dev);
>  
> @@ -1666,6 +1047,7 @@ static const struct net_device_ops fec_netdev_ops = {
>  	.ndo_validate_addr	= eth_validate_addr,
>  	.ndo_tx_timeout		= fec_timeout,
>  	.ndo_set_mac_address	= fec_set_mac_address,
> +	.ndo_do_ioctl           = fec_enet_ioctl,
>  };
>  
>   /*
> @@ -1689,7 +1071,6 @@ static int fec_enet_init(struct net_device *dev, int index)
>  	}
>  
>  	spin_lock_init(&fep->hw_lock);
> -	spin_lock_init(&fep->mii_lock);
>  
>  	fep->index = index;
>  	fep->hwp = (void __iomem *)dev->base_addr;
> @@ -1716,16 +1097,10 @@ static int fec_enet_init(struct net_device *dev, int index)
>  	fep->rx_bd_base = cbd_base;
>  	fep->tx_bd_base = cbd_base + RX_RING_SIZE;
>  
> -#ifdef HAVE_mii_link_interrupt
> -	fec_request_mii_intr(dev);
> -#endif
>  	/* The FEC Ethernet specific entries in the device structure */
>  	dev->watchdog_timeo = TX_TIMEOUT;
>  	dev->netdev_ops = &fec_netdev_ops;
> -
> -	for (i=0; i<NMII-1; i++)
> -		mii_cmds[i].mii_next = &mii_cmds[i+1];
> -	mii_free = mii_cmds;
> +	dev->ethtool_ops = &fec_enet_ethtool_ops;
>  
>  	/* Set MII speed to 2.5 MHz */
>  	fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
> @@ -1760,13 +1135,6 @@ static int fec_enet_init(struct net_device *dev, int index)
>  
>  	fec_restart(dev, 0);
>  
> -	/* Queue up command to detect the PHY and initialize the
> -	 * remainder of the interface.
> -	 */
> -	fep->phy_id_done = 0;
> -	fep->phy_addr = 0;
> -	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
> -
>  	return 0;
>  }
>  
> @@ -1835,8 +1203,7 @@ fec_restart(struct net_device *dev, int duplex)
>  	writel(0, fep->hwp + FEC_R_DES_ACTIVE);
>  
>  	/* Enable interrupts we wish to service */
> -	writel(FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII,
> -			fep->hwp + FEC_IMASK);
> +	writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
>  }
>  
>  static void
> @@ -1859,7 +1226,6 @@ fec_stop(struct net_device *dev)
>  	/* Clear outstanding MII command interrupts. */
>  	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
>  
> -	writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
>  	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  }
>  
> @@ -1891,6 +1257,7 @@ fec_probe(struct platform_device *pdev)
>  	memset(fep, 0, sizeof(*fep));
>  
>  	ndev->base_addr = (unsigned long)ioremap(r->start, resource_size(r));
> +	fep->pdev = pdev;
>  
>  	if (!ndev->base_addr) {
>  		ret = -ENOMEM;
> @@ -1926,13 +1293,24 @@ fec_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto failed_init;
>  
> +	ret = fec_enet_mii_init(pdev);
> +	if (ret)
> +		goto failed_mii_init;
> +
>  	ret = register_netdev(ndev);
>  	if (ret)
>  		goto failed_register;
>  
> +	printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
> +		"(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
> +		fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
> +		fep->phy_dev->irq);
> +
>  	return 0;
>  
>  failed_register:
> +	fec_enet_mii_remove(fep);
> +failed_mii_init:
>  failed_init:
>  	clk_disable(fep->clk);
>  	clk_put(fep->clk);
> @@ -1959,6 +1337,7 @@ fec_drv_remove(struct platform_device *pdev)
>  	platform_set_drvdata(pdev, NULL);
>  
>  	fec_stop(ndev);
> +	fec_enet_mii_remove(fep);
>  	clk_disable(fep->clk);
>  	clk_put(fep->clk);
>  	iounmap((void __iomem *)ndev->base_addr);
> -- 
> 1.7.0.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
-------------------------------------------------------------------------
Amit Kucheria,				 Kernel Developer, Verdurent
-------------------------------------------------------------------------

^ permalink raw reply related

* [PATCH] netdev/fec.c: add phylib supporting to enable carrier detection
From: Bryan Wu @ 2010-03-26  9:50 UTC (permalink / raw)
  To: s.hauer, gerg, amit.kucheria
  Cc: netdev, kernel-team, linux-kernel, linux-arm-kernel, w.sang

BugLink: http://bugs.launchpad.net/bugs/457878

 - removed old MII phy control code
 - add phylib supporting
 - add ethtool interface to make user space NetworkManager works

Tested on Freescale i.MX51 Babbage board.

This patch is based on a patch from Frederic Rodo <fred.rodo@gmail.com>

Cc: Frederic Rodo <fred.rodo@gmail.com>
Signed-off-by: Bryan Wu <bryan.wu@canonical.com>
---
 drivers/net/Kconfig |    1 +
 drivers/net/fec.c   | 1125 ++++++++++++---------------------------------------
 2 files changed, 253 insertions(+), 873 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 0ba5b8e..41f6a70 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1916,6 +1916,7 @@ config FEC
 	bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
 	depends on M523x || M527x || M5272 || M528x || M520x || M532x || \
 		MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5
+	select PHYLIB
 	help
 	  Say Y here if you want to use the built-in 10/100 Fast ethernet
 	  controller on some Motorola ColdFire and Freescale i.MX processors.
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 9f98c1c..fca1f66 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -40,6 +40,7 @@
 #include <linux/irq.h>
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/phy.h>
 
 #include <asm/cacheflush.h>
 
@@ -61,7 +62,6 @@
  * Define the fixed address of the FEC hardware.
  */
 #if defined(CONFIG_M5272)
-#define HAVE_mii_link_interrupt
 
 static unsigned char	fec_mac_default[] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -86,23 +86,6 @@ static unsigned char	fec_mac_default[] = {
 #endif
 #endif /* CONFIG_M5272 */
 
-/* Forward declarations of some structures to support different PHYs */
-
-typedef struct {
-	uint mii_data;
-	void (*funct)(uint mii_reg, struct net_device *dev);
-} phy_cmd_t;
-
-typedef struct {
-	uint id;
-	char *name;
-
-	const phy_cmd_t *config;
-	const phy_cmd_t *startup;
-	const phy_cmd_t *ack_int;
-	const phy_cmd_t *shutdown;
-} phy_info_t;
-
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
  * to keep them that size.
@@ -189,29 +172,21 @@ struct fec_enet_private {
 	uint	tx_full;
 	/* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
 	spinlock_t hw_lock;
-	/* hold while accessing the mii_list_t() elements */
-	spinlock_t mii_lock;
-
-	uint	phy_id;
-	uint	phy_id_done;
-	uint	phy_status;
-	uint	phy_speed;
-	phy_info_t const	*phy;
-	struct work_struct phy_task;
 
-	uint	sequence_done;
-	uint	mii_phy_task_queued;
+	struct  platform_device *pdev;
 
-	uint	phy_addr;
+	int	opened;
 
+	/* Phylib and MDIO interface */
+	struct  mii_bus *mii_bus;
+	struct  phy_device *phy_dev;
+	int     mii_timeout;
+	uint    phy_speed;
 	int	index;
-	int	opened;
 	int	link;
-	int	old_link;
 	int	full_duplex;
 };
 
-static void fec_enet_mii(struct net_device *dev);
 static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
 static void fec_enet_tx(struct net_device *dev);
 static void fec_enet_rx(struct net_device *dev);
@@ -219,67 +194,20 @@ static int fec_enet_close(struct net_device *dev);
 static void fec_restart(struct net_device *dev, int duplex);
 static void fec_stop(struct net_device *dev);
 
+/* FEC MII MMFR bits definition */
+#define FEC_MMFR_ST		(1 << 30)
+#define FEC_MMFR_OP_READ	(2 << 28)
+#define FEC_MMFR_OP_WRITE	(1 << 28)
+#define FEC_MMFR_PA(v)		((v & 0x1f) << 23)
+#define FEC_MMFR_RA(v)		((v & 0x1f) << 18)
+#define FEC_MMFR_TA		(2 << 16)
+#define FEC_MMFR_DATA(v)	(v & 0xffff)
 
-/* MII processing.  We keep this as simple as possible.  Requests are
- * placed on the list (if there is room).  When the request is finished
- * by the MII, an optional function may be called.
- */
-typedef struct mii_list {
-	uint	mii_regval;
-	void	(*mii_func)(uint val, struct net_device *dev);
-	struct	mii_list *mii_next;
-} mii_list_t;
-
-#define		NMII	20
-static mii_list_t	mii_cmds[NMII];
-static mii_list_t	*mii_free;
-static mii_list_t	*mii_head;
-static mii_list_t	*mii_tail;
-
-static int	mii_queue(struct net_device *dev, int request,
-				void (*func)(uint, struct net_device *));
-
-/* Make MII read/write commands for the FEC */
-#define mk_mii_read(REG)	(0x60020000 | ((REG & 0x1f) << 18))
-#define mk_mii_write(REG, VAL)	(0x50020000 | ((REG & 0x1f) << 18) | \
-						(VAL & 0xffff))
-#define mk_mii_end	0
+#define FEC_MII_TIMEOUT		10000
 
 /* Transmitter timeout */
 #define TX_TIMEOUT (2 * HZ)
 
-/* Register definitions for the PHY */
-
-#define MII_REG_CR          0  /* Control Register                         */
-#define MII_REG_SR          1  /* Status Register                          */
-#define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
-#define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
-#define MII_REG_ANAR        4  /* A-N Advertisement Register               */
-#define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */
-#define MII_REG_ANER        6  /* A-N Expansion Register                   */
-#define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */
-#define MII_REG_ANLPRNPR    8  /* A-N Link Partner Received Next Page Reg. */
-
-/* values for phy_status */
-
-#define PHY_CONF_ANE	0x0001  /* 1 auto-negotiation enabled */
-#define PHY_CONF_LOOP	0x0002  /* 1 loopback mode enabled */
-#define PHY_CONF_SPMASK	0x00f0  /* mask for speed */
-#define PHY_CONF_10HDX	0x0010  /* 10 Mbit half duplex supported */
-#define PHY_CONF_10FDX	0x0020  /* 10 Mbit full duplex supported */
-#define PHY_CONF_100HDX	0x0040  /* 100 Mbit half duplex supported */
-#define PHY_CONF_100FDX	0x0080  /* 100 Mbit full duplex supported */
-
-#define PHY_STAT_LINK	0x0100  /* 1 up - 0 down */
-#define PHY_STAT_FAULT	0x0200  /* 1 remote fault */
-#define PHY_STAT_ANC	0x0400  /* 1 auto-negotiation complete	*/
-#define PHY_STAT_SPMASK	0xf000  /* mask for speed */
-#define PHY_STAT_10HDX	0x1000  /* 10 Mbit half duplex selected	*/
-#define PHY_STAT_10FDX	0x2000  /* 10 Mbit full duplex selected	*/
-#define PHY_STAT_100HDX	0x4000  /* 100 Mbit half duplex selected */
-#define PHY_STAT_100FDX	0x8000  /* 100 Mbit full duplex selected */
-
-
 static int
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
@@ -406,12 +334,6 @@ fec_enet_interrupt(int irq, void * dev_id)
 			ret = IRQ_HANDLED;
 			fec_enet_tx(dev);
 		}
-
-		if (int_events & FEC_ENET_MII) {
-			ret = IRQ_HANDLED;
-			fec_enet_mii(dev);
-		}
-
 	} while (int_events);
 
 	return ret;
@@ -607,827 +529,312 @@ rx_processing_done:
 	spin_unlock(&fep->hw_lock);
 }
 
-/* called from interrupt context */
-static void
-fec_enet_mii(struct net_device *dev)
-{
-	struct	fec_enet_private *fep;
-	mii_list_t	*mip;
-
-	fep = netdev_priv(dev);
-	spin_lock(&fep->mii_lock);
-
-	if ((mip = mii_head) == NULL) {
-		printk("MII and no head!\n");
-		goto unlock;
-	}
-
-	if (mip->mii_func != NULL)
-		(*(mip->mii_func))(readl(fep->hwp + FEC_MII_DATA), dev);
-
-	mii_head = mip->mii_next;
-	mip->mii_next = mii_free;
-	mii_free = mip;
-
-	if ((mip = mii_head) != NULL)
-		writel(mip->mii_regval, fep->hwp + FEC_MII_DATA);
-
-unlock:
-	spin_unlock(&fep->mii_lock);
-}
-
-static int
-mii_queue_unlocked(struct net_device *dev, int regval,
-		void (*func)(uint, struct net_device *))
+/* ------------------------------------------------------------------------- */
+#ifdef CONFIG_M5272
+static void __inline__ fec_get_mac(struct net_device *dev)
 {
-	struct fec_enet_private *fep;
-	mii_list_t	*mip;
-	int		retval;
-
-	/* Add PHY address to register command */
-	fep = netdev_priv(dev);
+	struct fec_enet_private *fep = netdev_priv(dev);
+	unsigned char *iap, tmpaddr[ETH_ALEN];
 
-	regval |= fep->phy_addr << 23;
-	retval = 0;
-
-	if ((mip = mii_free) != NULL) {
-		mii_free = mip->mii_next;
-		mip->mii_regval = regval;
-		mip->mii_func = func;
-		mip->mii_next = NULL;
-		if (mii_head) {
-			mii_tail->mii_next = mip;
-			mii_tail = mip;
-		} else {
-			mii_head = mii_tail = mip;
-			writel(regval, fep->hwp + FEC_MII_DATA);
-		}
+	if (FEC_FLASHMAC) {
+		/*
+		 * Get MAC address from FLASH.
+		 * If it is all 1's or 0's, use the default.
+		 */
+		iap = (unsigned char *)FEC_FLASHMAC;
+		if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+		    (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+			iap = fec_mac_default;
+		if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+		    (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+			iap = fec_mac_default;
 	} else {
-		retval = 1;
+		*((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
+		*((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
+		iap = &tmpaddr[0];
 	}
 
-	return retval;
-}
-
-static int
-mii_queue(struct net_device *dev, int regval,
-		void (*func)(uint, struct net_device *))
-{
-	struct fec_enet_private *fep;
-	unsigned long   flags;
-	int             retval;
-	fep = netdev_priv(dev);
-	spin_lock_irqsave(&fep->mii_lock, flags);
-	retval = mii_queue_unlocked(dev, regval, func);
-	spin_unlock_irqrestore(&fep->mii_lock, flags);
-	return retval;
-}
-
-static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
-{
-	if(!c)
-		return;
+	memcpy(dev->dev_addr, iap, ETH_ALEN);
 
-	for (; c->mii_data != mk_mii_end; c++)
-		mii_queue(dev, c->mii_data, c->funct);
+	/* Adjust MAC if using default MAC address */
+	if (iap == fec_mac_default)
+		 dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
 }
+#endif
 
-static void mii_parse_sr(uint mii_reg, struct net_device *dev)
-{
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
-
-	status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
-
-	if (mii_reg & 0x0004)
-		status |= PHY_STAT_LINK;
-	if (mii_reg & 0x0010)
-		status |= PHY_STAT_FAULT;
-	if (mii_reg & 0x0020)
-		status |= PHY_STAT_ANC;
-	*s = status;
-}
+/* ------------------------------------------------------------------------- */
 
-static void mii_parse_cr(uint mii_reg, struct net_device *dev)
+/*
+ * Phy section
+ */
+static void fec_enet_adjust_link(struct net_device *dev)
 {
 	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
-
-	status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
-
-	if (mii_reg & 0x1000)
-		status |= PHY_CONF_ANE;
-	if (mii_reg & 0x4000)
-		status |= PHY_CONF_LOOP;
-	*s = status;
-}
+	struct phy_device *phy_dev = fep->phy_dev;
+	unsigned long flags;
 
-static void mii_parse_anar(uint mii_reg, struct net_device *dev)
-{
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
-
-	status = *s & ~(PHY_CONF_SPMASK);
-
-	if (mii_reg & 0x0020)
-		status |= PHY_CONF_10HDX;
-	if (mii_reg & 0x0040)
-		status |= PHY_CONF_10FDX;
-	if (mii_reg & 0x0080)
-		status |= PHY_CONF_100HDX;
-	if (mii_reg & 0x00100)
-		status |= PHY_CONF_100FDX;
-	*s = status;
-}
+	int status_change = 0;
 
-/* ------------------------------------------------------------------------- */
-/* The Level one LXT970 is used by many boards				     */
+	spin_lock_irqsave(&fep->hw_lock, flags);
 
-#define MII_LXT970_MIRROR    16  /* Mirror register           */
-#define MII_LXT970_IER       17  /* Interrupt Enable Register */
-#define MII_LXT970_ISR       18  /* Interrupt Status Register */
-#define MII_LXT970_CONFIG    19  /* Configuration Register    */
-#define MII_LXT970_CSR       20  /* Chip Status Register      */
+	/* Prevent a state halted on mii error */
+	if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
+		phy_dev->state = PHY_RESUMING;
+		goto spin_unlock;
+	}
 
-static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
-{
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
+	/* Duplex link change */
+	if (phy_dev->link) {
+		if (fep->full_duplex != phy_dev->duplex) {
+			fec_restart(dev, phy_dev->duplex);
+			status_change = 1;
+		}
+	}
 
-	status = *s & ~(PHY_STAT_SPMASK);
-	if (mii_reg & 0x0800) {
-		if (mii_reg & 0x1000)
-			status |= PHY_STAT_100FDX;
+	/* Link on or off change */
+	if (phy_dev->link != fep->link) {
+		fep->link = phy_dev->link;
+		if (phy_dev->link)
+			fec_restart(dev, phy_dev->duplex);
 		else
-			status |= PHY_STAT_100HDX;
-	} else {
-		if (mii_reg & 0x1000)
-			status |= PHY_STAT_10FDX;
-		else
-			status |= PHY_STAT_10HDX;
+			fec_stop(dev);
+		status_change = 1;
 	}
-	*s = status;
-}
-
-static phy_cmd_t const phy_cmd_lxt970_config[] = {
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
-		{ mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
-		/* read SR and ISR to acknowledge */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_read(MII_LXT970_ISR), NULL },
-
-		/* find out the current status */
-		{ mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
-		{ mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
-		{ mk_mii_end, }
-	};
-static phy_info_t const phy_info_lxt970 = {
-	.id = 0x07810000,
-	.name = "LXT970",
-	.config = phy_cmd_lxt970_config,
-	.startup = phy_cmd_lxt970_startup,
-	.ack_int = phy_cmd_lxt970_ack_int,
-	.shutdown = phy_cmd_lxt970_shutdown
-};
 
-/* ------------------------------------------------------------------------- */
-/* The Level one LXT971 is used on some of my custom boards                  */
-
-/* register definitions for the 971 */
+spin_unlock:
+	spin_unlock_irqrestore(&fep->hw_lock, flags);
 
-#define MII_LXT971_PCR       16  /* Port Control Register     */
-#define MII_LXT971_SR2       17  /* Status Register 2         */
-#define MII_LXT971_IER       18  /* Interrupt Enable Register */
-#define MII_LXT971_ISR       19  /* Interrupt Status Register */
-#define MII_LXT971_LCR       20  /* LED Control Register      */
-#define MII_LXT971_TCR       30  /* Transmit Control Register */
+	if (status_change)
+		phy_print_status(phy_dev);
+}
 
 /*
- * I had some nice ideas of running the MDIO faster...
- * The 971 should support 8MHz and I tried it, but things acted really
- * weird, so 2.5 MHz ought to be enough for anyone...
+ * NOTE: a MII transaction is during around 25 us, so polling it...
  */
-
-static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
+static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
+	struct fec_enet_private *fep = bus->priv;
+	int timeout = FEC_MII_TIMEOUT;
 
-	status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
+	fep->mii_timeout = 0;
 
-	if (mii_reg & 0x0400) {
-		fep->link = 1;
-		status |= PHY_STAT_LINK;
-	} else {
-		fep->link = 0;
-	}
-	if (mii_reg & 0x0080)
-		status |= PHY_STAT_ANC;
-	if (mii_reg & 0x4000) {
-		if (mii_reg & 0x0200)
-			status |= PHY_STAT_100FDX;
-		else
-			status |= PHY_STAT_100HDX;
-	} else {
-		if (mii_reg & 0x0200)
-			status |= PHY_STAT_10FDX;
-		else
-			status |= PHY_STAT_10HDX;
+	/* clear MII end of transfer bit*/
+	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+
+	/* start a read op */
+	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+		FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
+
+	/* wait for end of transfer */
+	while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
+		cpu_relax();
+		if (timeout-- < 0) {
+			fep->mii_timeout = 1;
+			printk(KERN_ERR "FEC: MDIO read timeout\n");
+			return -ETIMEDOUT;
+		}
 	}
-	if (mii_reg & 0x0008)
-		status |= PHY_STAT_FAULT;
 
-	*s = status;
+	/* return value */
+	return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
 }
 
-static phy_cmd_t const phy_cmd_lxt971_config[] = {
-		/* limit to 10MBit because my prototype board
-		 * doesn't work with 100. */
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt971_startup[] = {  /* enable interrupts */
-		{ mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
-		/* Somehow does the 971 tell me that the link is down
-		 * the first read after power-up.
-		 * read here to get a valid value in ack_int */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
-		/* acknowledge the int before reading status ! */
-		{ mk_mii_read(MII_LXT971_ISR), NULL },
-		/* find out the current status */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
-		{ mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
-		{ mk_mii_end, }
-	};
-static phy_info_t const phy_info_lxt971 = {
-	.id = 0x0001378e,
-	.name = "LXT971",
-	.config = phy_cmd_lxt971_config,
-	.startup = phy_cmd_lxt971_startup,
-	.ack_int = phy_cmd_lxt971_ack_int,
-	.shutdown = phy_cmd_lxt971_shutdown
-};
-
-/* ------------------------------------------------------------------------- */
-/* The Quality Semiconductor QS6612 is used on the RPX CLLF                  */
-
-/* register definitions */
-
-#define MII_QS6612_MCR       17  /* Mode Control Register      */
-#define MII_QS6612_FTR       27  /* Factory Test Register      */
-#define MII_QS6612_MCO       28  /* Misc. Control Register     */
-#define MII_QS6612_ISR       29  /* Interrupt Source Register  */
-#define MII_QS6612_IMR       30  /* Interrupt Mask Register    */
-#define MII_QS6612_PCR       31  /* 100BaseTx PHY Control Reg. */
-
-static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
+static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+			   u16 value)
 {
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
+	struct fec_enet_private *fep = bus->priv;
+	int timeout = FEC_MII_TIMEOUT;
 
-	status = *s & ~(PHY_STAT_SPMASK);
+	fep->mii_timeout = 0;
 
-	switch((mii_reg >> 2) & 7) {
-	case 1: status |= PHY_STAT_10HDX; break;
-	case 2: status |= PHY_STAT_100HDX; break;
-	case 5: status |= PHY_STAT_10FDX; break;
-	case 6: status |= PHY_STAT_100FDX; break;
-}
-
-	*s = status;
-}
-
-static phy_cmd_t const phy_cmd_qs6612_config[] = {
-		/* The PHY powers up isolated on the RPX,
-		 * so send a command to allow operation.
-		 */
-		{ mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
-
-		/* parse cr and anar to get some info */
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_qs6612_startup[] = {  /* enable interrupts */
-		{ mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
-		/* we need to read ISR, SR and ANER to acknowledge */
-		{ mk_mii_read(MII_QS6612_ISR), NULL },
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_read(MII_REG_ANER), NULL },
-
-		/* read pcr to get info */
-		{ mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
-		{ mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
-		{ mk_mii_end, }
-	};
-static phy_info_t const phy_info_qs6612 = {
-	.id = 0x00181440,
-	.name = "QS6612",
-	.config = phy_cmd_qs6612_config,
-	.startup = phy_cmd_qs6612_startup,
-	.ack_int = phy_cmd_qs6612_ack_int,
-	.shutdown = phy_cmd_qs6612_shutdown
-};
-
-/* ------------------------------------------------------------------------- */
-/* AMD AM79C874 phy                                                          */
+	/* clear MII end of transfer bit*/
+	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
 
-/* register definitions for the 874 */
+	/* start a read op */
+	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+		FEC_MMFR_TA | FEC_MMFR_DATA(value),
+		fep->hwp + FEC_MII_DATA);
+
+	/* wait for end of transfer */
+	while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
+		cpu_relax();
+		if (timeout-- < 0) {
+			fep->mii_timeout = 1;
+			printk(KERN_ERR "FEC: MDIO write timeout\n");
+			return -ETIMEDOUT;
+		}
+	}
 
-#define MII_AM79C874_MFR       16  /* Miscellaneous Feature Register */
-#define MII_AM79C874_ICSR      17  /* Interrupt/Status Register      */
-#define MII_AM79C874_DR        18  /* Diagnostic Register            */
-#define MII_AM79C874_PMLR      19  /* Power and Loopback Register    */
-#define MII_AM79C874_MCR       21  /* ModeControl Register           */
-#define MII_AM79C874_DC        23  /* Disconnect Counter             */
-#define MII_AM79C874_REC       24  /* Recieve Error Counter          */
+	return 0;
+}
 
-static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
+static int fec_enet_mdio_reset(struct mii_bus *bus)
 {
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-	uint status;
-
-	status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
-
-	if (mii_reg & 0x0080)
-		status |= PHY_STAT_ANC;
-	if (mii_reg & 0x0400)
-		status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
-	else
-		status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
-
-	*s = status;
+	return 0;
 }
 
-static phy_cmd_t const phy_cmd_am79c874_config[] = {
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_am79c874_startup[] = {  /* enable interrupts */
-		{ mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
-		/* find out the current status */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
-		/* we only need to read ISR to acknowledge */
-		{ mk_mii_read(MII_AM79C874_ICSR), NULL },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
-		{ mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
-		{ mk_mii_end, }
-	};
-static phy_info_t const phy_info_am79c874 = {
-	.id = 0x00022561,
-	.name = "AM79C874",
-	.config = phy_cmd_am79c874_config,
-	.startup = phy_cmd_am79c874_startup,
-	.ack_int = phy_cmd_am79c874_ack_int,
-	.shutdown = phy_cmd_am79c874_shutdown
-};
-
-
-/* ------------------------------------------------------------------------- */
-/* Kendin KS8721BL phy                                                       */
-
-/* register definitions for the 8721 */
-
-#define MII_KS8721BL_RXERCR	21
-#define MII_KS8721BL_ICSR	27
-#define	MII_KS8721BL_PHYCR	31
-
-static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_ks8721bl_startup[] = {  /* enable interrupts */
-		{ mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
-		/* find out the current status */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		/* we only need to read ISR to acknowledge */
-		{ mk_mii_read(MII_KS8721BL_ICSR), NULL },
-		{ mk_mii_end, }
-	};
-static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
-		{ mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
-		{ mk_mii_end, }
-	};
-static phy_info_t const phy_info_ks8721bl = {
-	.id = 0x00022161,
-	.name = "KS8721BL",
-	.config = phy_cmd_ks8721bl_config,
-	.startup = phy_cmd_ks8721bl_startup,
-	.ack_int = phy_cmd_ks8721bl_ack_int,
-	.shutdown = phy_cmd_ks8721bl_shutdown
-};
-
-/* ------------------------------------------------------------------------- */
-/* register definitions for the DP83848 */
-
-#define MII_DP8384X_PHYSTST    16  /* PHY Status Register */
-
-static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
+static int fec_enet_mii_probe(struct net_device *dev)
 {
 	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
-
-	*s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
-
-	/* Link up */
-	if (mii_reg & 0x0001) {
-		fep->link = 1;
-		*s |= PHY_STAT_LINK;
-	} else
-		fep->link = 0;
-	/* Status of link */
-	if (mii_reg & 0x0010)   /* Autonegotioation complete */
-		*s |= PHY_STAT_ANC;
-	if (mii_reg & 0x0002) {   /* 10MBps? */
-		if (mii_reg & 0x0004)   /* Full Duplex? */
-			*s |= PHY_STAT_10FDX;
-		else
-			*s |= PHY_STAT_10HDX;
-	} else {                  /* 100 Mbps? */
-		if (mii_reg & 0x0004)   /* Full Duplex? */
-			*s |= PHY_STAT_100FDX;
-		else
-			*s |= PHY_STAT_100HDX;
-	}
-	if (mii_reg & 0x0008)
-		*s |= PHY_STAT_FAULT;
-}
-
-static phy_info_t phy_info_dp83848= {
-	0x020005c9,
-	"DP83848",
+	struct phy_device *phy_dev = NULL;
+	int phy_addr;
 
-	(const phy_cmd_t []) {  /* config */
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) {  /* startup - enable interrupts */
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) {  /* shutdown */
-		{ mk_mii_end, }
-	},
-};
+	/* find the first phy */
+	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+		if (fep->mii_bus->phy_map[phy_addr]) {
+			phy_dev = fep->mii_bus->phy_map[phy_addr];
+			break;
+		}
+	}
 
-static phy_info_t phy_info_lan8700 = {
-	0x0007C0C,
-	"LAN8700",
-	(const phy_cmd_t []) { /* config */
-		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
-		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) { /* startup */
-		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) { /* act_int */
-		{ mk_mii_end, }
-	},
-	(const phy_cmd_t []) { /* shutdown */
-		{ mk_mii_end, }
-	},
-};
-/* ------------------------------------------------------------------------- */
+	if (!phy_dev) {
+		printk(KERN_ERR "%s: no PHY found\n", dev->name);
+		return -ENODEV;
+	}
 
-static phy_info_t const * const phy_info[] = {
-	&phy_info_lxt970,
-	&phy_info_lxt971,
-	&phy_info_qs6612,
-	&phy_info_am79c874,
-	&phy_info_ks8721bl,
-	&phy_info_dp83848,
-	&phy_info_lan8700,
-	NULL
-};
+	/* attach the mac to the phy */
+	phy_dev = phy_connect(dev, dev_name(&phy_dev->dev),
+			     &fec_enet_adjust_link, 0,
+			     PHY_INTERFACE_MODE_MII);
+	if (IS_ERR(phy_dev)) {
+		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+		return PTR_ERR(phy_dev);
+	}
 
-/* ------------------------------------------------------------------------- */
-#ifdef HAVE_mii_link_interrupt
-static irqreturn_t
-mii_link_interrupt(int irq, void * dev_id);
+	/* mask with MAC supported features */
+	phy_dev->supported &= PHY_BASIC_FEATURES;
+	phy_dev->advertising = phy_dev->supported;
 
-/*
- *	This is specific to the MII interrupt setup of the M5272EVB.
- */
-static void __inline__ fec_request_mii_intr(struct net_device *dev)
-{
-	if (request_irq(66, mii_link_interrupt, IRQF_DISABLED, "fec(MII)", dev) != 0)
-		printk("FEC: Could not allocate fec(MII) IRQ(66)!\n");
-}
+	fep->phy_dev = phy_dev;
+	fep->link = 0;
+	fep->full_duplex = 0;
 
-static void __inline__ fec_disable_phy_intr(struct net_device *dev)
-{
-	free_irq(66, dev);
+	return 0;
 }
-#endif
 
-#ifdef CONFIG_M5272
-static void __inline__ fec_get_mac(struct net_device *dev)
+static int fec_enet_mii_init(struct platform_device *pdev)
 {
+	struct net_device *dev = platform_get_drvdata(pdev);
 	struct fec_enet_private *fep = netdev_priv(dev);
-	unsigned char *iap, tmpaddr[ETH_ALEN];
+	int err = -ENXIO, i;
 
-	if (FEC_FLASHMAC) {
-		/*
-		 * Get MAC address from FLASH.
-		 * If it is all 1's or 0's, use the default.
-		 */
-		iap = (unsigned char *)FEC_FLASHMAC;
-		if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
-		    (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
-			iap = fec_mac_default;
-		if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
-		    (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
-			iap = fec_mac_default;
-	} else {
-		*((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
-		*((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
-		iap = &tmpaddr[0];
-	}
-
-	memcpy(dev->dev_addr, iap, ETH_ALEN);
-
-	/* Adjust MAC if using default MAC address */
-	if (iap == fec_mac_default)
-		 dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
-}
-#endif
+	fep->mii_timeout = 0;
 
-/* ------------------------------------------------------------------------- */
-
-static void mii_display_status(struct net_device *dev)
-{
-	struct fec_enet_private *fep = netdev_priv(dev);
-	volatile uint *s = &(fep->phy_status);
+	/*
+	 * Set MII speed to 2.5 MHz
+	 */
+	fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
+					/ 2500000) / 2) & 0x3F) << 1;
+	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
-	if (!fep->link && !fep->old_link) {
-		/* Link is still down - don't print anything */
-		return;
+	fep->mii_bus = mdiobus_alloc();
+	if (fep->mii_bus == NULL) {
+		err = -ENOMEM;
+		goto err_out;
 	}
 
-	printk("%s: status: ", dev->name);
-
-	if (!fep->link) {
-		printk("link down");
-	} else {
-		printk("link up");
-
-		switch(*s & PHY_STAT_SPMASK) {
-		case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
-		case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
-		case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
-		case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
-		default:
-			printk(", Unknown speed/duplex");
-		}
-
-		if (*s & PHY_STAT_ANC)
-			printk(", auto-negotiation complete");
+	fep->mii_bus->name = "fec_enet_mii_bus";
+	fep->mii_bus->read = fec_enet_mdio_read;
+	fep->mii_bus->write = fec_enet_mdio_write;
+	fep->mii_bus->reset = fec_enet_mdio_reset;
+	snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+	fep->mii_bus->priv = fep;
+	fep->mii_bus->parent = &pdev->dev;
+
+	fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+	if (!fep->mii_bus->irq) {
+		err = -ENOMEM;
+		goto err_out_free_mdiobus;
 	}
 
-	if (*s & PHY_STAT_FAULT)
-		printk(", remote fault");
-
-	printk(".\n");
-}
-
-static void mii_display_config(struct work_struct *work)
-{
-	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
-	struct net_device *dev = fep->netdev;
-	uint status = fep->phy_status;
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		fep->mii_bus->irq[i] = PHY_POLL;
 
-	/*
-	** When we get here, phy_task is already removed from
-	** the workqueue.  It is thus safe to allow to reuse it.
-	*/
-	fep->mii_phy_task_queued = 0;
-	printk("%s: config: auto-negotiation ", dev->name);
-
-	if (status & PHY_CONF_ANE)
-		printk("on");
-	else
-		printk("off");
+	platform_set_drvdata(dev, fep->mii_bus);
 
-	if (status & PHY_CONF_100FDX)
-		printk(", 100FDX");
-	if (status & PHY_CONF_100HDX)
-		printk(", 100HDX");
-	if (status & PHY_CONF_10FDX)
-		printk(", 10FDX");
-	if (status & PHY_CONF_10HDX)
-		printk(", 10HDX");
-	if (!(status & PHY_CONF_SPMASK))
-		printk(", No speed/duplex selected?");
+	if (mdiobus_register(fep->mii_bus))
+		goto err_out_free_mdio_irq;
 
-	if (status & PHY_CONF_LOOP)
-		printk(", loopback enabled");
+	if (fec_enet_mii_probe(dev) != 0)
+		goto err_out_unregister_bus;
 
-	printk(".\n");
+	return 0;
 
-	fep->sequence_done = 1;
+err_out_unregister_bus:
+	mdiobus_unregister(fep->mii_bus);
+err_out_free_mdio_irq:
+	kfree(fep->mii_bus->irq);
+err_out_free_mdiobus:
+	mdiobus_free(fep->mii_bus);
+err_out:
+	return err;
 }
 
-static void mii_relink(struct work_struct *work)
+static void fec_enet_mii_remove(struct fec_enet_private *fep)
 {
-	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
-	struct net_device *dev = fep->netdev;
-	int duplex;
-
-	/*
-	** When we get here, phy_task is already removed from
-	** the workqueue.  It is thus safe to allow to reuse it.
-	*/
-	fep->mii_phy_task_queued = 0;
-	fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
-	mii_display_status(dev);
-	fep->old_link = fep->link;
-
-	if (fep->link) {
-		duplex = 0;
-		if (fep->phy_status
-		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
-			duplex = 1;
-		fec_restart(dev, duplex);
-	} else
-		fec_stop(dev);
+	if (fep->phy_dev)
+		phy_disconnect(fep->phy_dev);
+	mdiobus_unregister(fep->mii_bus);
+	kfree(fep->mii_bus->irq);
+	mdiobus_free(fep->mii_bus);
 }
 
-/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
-static void mii_queue_relink(uint mii_reg, struct net_device *dev)
+static int fec_enet_get_settings(struct net_device *dev,
+				  struct ethtool_cmd *cmd)
 {
 	struct fec_enet_private *fep = netdev_priv(dev);
+	struct phy_device *phydev = fep->phy_dev;
 
-	/*
-	 * We cannot queue phy_task twice in the workqueue.  It
-	 * would cause an endless loop in the workqueue.
-	 * Fortunately, if the last mii_relink entry has not yet been
-	 * executed now, it will do the job for the current interrupt,
-	 * which is just what we want.
-	 */
-	if (fep->mii_phy_task_queued)
-		return;
+	if (!phydev)
+		return -ENODEV;
 
-	fep->mii_phy_task_queued = 1;
-	INIT_WORK(&fep->phy_task, mii_relink);
-	schedule_work(&fep->phy_task);
+	return phy_ethtool_gset(phydev, cmd);
 }
 
-/* mii_queue_config is called in interrupt context from fec_enet_mii */
-static void mii_queue_config(uint mii_reg, struct net_device *dev)
+static int fec_enet_set_settings(struct net_device *dev,
+				 struct ethtool_cmd *cmd)
 {
 	struct fec_enet_private *fep = netdev_priv(dev);
+	struct phy_device *phydev = fep->phy_dev;
 
-	if (fep->mii_phy_task_queued)
-		return;
+	if (!phydev)
+		return -ENODEV;
 
-	fep->mii_phy_task_queued = 1;
-	INIT_WORK(&fep->phy_task, mii_display_config);
-	schedule_work(&fep->phy_task);
+	return phy_ethtool_sset(phydev, cmd);
 }
 
-phy_cmd_t const phy_cmd_relink[] = {
-	{ mk_mii_read(MII_REG_CR), mii_queue_relink },
-	{ mk_mii_end, }
-	};
-phy_cmd_t const phy_cmd_config[] = {
-	{ mk_mii_read(MII_REG_CR), mii_queue_config },
-	{ mk_mii_end, }
-	};
-
-/* Read remainder of PHY ID. */
-static void
-mii_discover_phy3(uint mii_reg, struct net_device *dev)
+static void fec_enet_get_drvinfo(struct net_device *dev,
+				 struct ethtool_drvinfo *info)
 {
-	struct fec_enet_private *fep;
-	int i;
-
-	fep = netdev_priv(dev);
-	fep->phy_id |= (mii_reg & 0xffff);
-	printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
-
-	for(i = 0; phy_info[i]; i++) {
-		if(phy_info[i]->id == (fep->phy_id >> 4))
-			break;
-	}
-
-	if (phy_info[i])
-		printk(" -- %s\n", phy_info[i]->name);
-	else
-		printk(" -- unknown PHY!\n");
+	struct fec_enet_private *fep = netdev_priv(dev);
 
-	fep->phy = phy_info[i];
-	fep->phy_id_done = 1;
+	strcpy(info->driver, fep->pdev->dev.driver->name);
+	strcpy(info->version, "Revision: 1.0");
+	strcpy(info->bus_info, dev_name(&dev->dev));
 }
 
-/* Scan all of the MII PHY addresses looking for someone to respond
- * with a valid ID.  This usually happens quickly.
- */
-static void
-mii_discover_phy(uint mii_reg, struct net_device *dev)
-{
-	struct fec_enet_private *fep;
-	uint phytype;
-
-	fep = netdev_priv(dev);
-
-	if (fep->phy_addr < 32) {
-		if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
-
-			/* Got first part of ID, now get remainder */
-			fep->phy_id = phytype << 16;
-			mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR2),
-							mii_discover_phy3);
-		} else {
-			fep->phy_addr++;
-			mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR1),
-							mii_discover_phy);
-		}
-	} else {
-		printk("FEC: No PHY device found.\n");
-		/* Disable external MII interface */
-		writel(0, fep->hwp + FEC_MII_SPEED);
-		fep->phy_speed = 0;
-#ifdef HAVE_mii_link_interrupt
-		fec_disable_phy_intr(dev);
-#endif
-	}
-}
+static struct ethtool_ops fec_enet_ethtool_ops = {
+	.get_settings		= fec_enet_get_settings,
+	.set_settings		= fec_enet_set_settings,
+	.get_drvinfo		= fec_enet_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+};
 
-/* This interrupt occurs when the PHY detects a link change */
-#ifdef HAVE_mii_link_interrupt
-static irqreturn_t
-mii_link_interrupt(int irq, void * dev_id)
+static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct	net_device *dev = dev_id;
 	struct fec_enet_private *fep = netdev_priv(dev);
+	struct phy_device *phydev = fep->phy_dev;
 
-	mii_do_cmd(dev, fep->phy->ack_int);
-	mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
+	if (!netif_running(dev))
+		return -EINVAL;
 
-	return IRQ_HANDLED;
+	if (!phydev)
+		return -ENODEV;
+
+	return phy_mii_ioctl(phydev, if_mii(rq), cmd);
 }
-#endif
 
 static void fec_enet_free_buffers(struct net_device *dev)
 {
@@ -1509,35 +916,8 @@ fec_enet_open(struct net_device *dev)
 	if (ret)
 		return ret;
 
-	fep->sequence_done = 0;
-	fep->link = 0;
-
-	fec_restart(dev, 1);
-
-	if (fep->phy) {
-		mii_do_cmd(dev, fep->phy->ack_int);
-		mii_do_cmd(dev, fep->phy->config);
-		mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
-
-		/* Poll until the PHY tells us its configuration
-		 * (not link state).
-		 * Request is initiated by mii_do_cmd above, but answer
-		 * comes by interrupt.
-		 * This should take about 25 usec per register at 2.5 MHz,
-		 * and we read approximately 5 registers.
-		 */
-		while(!fep->sequence_done)
-			schedule();
-
-		mii_do_cmd(dev, fep->phy->startup);
-	}
-
-	/* Set the initial link state to true. A lot of hardware
-	 * based on this device does not implement a PHY interrupt,
-	 * so we are never notified of link change.
-	 */
-	fep->link = 1;
-
+	/* schedule a link state check */
+	phy_start(fep->phy_dev);
 	netif_start_queue(dev);
 	fep->opened = 1;
 	return 0;
@@ -1550,6 +930,7 @@ fec_enet_close(struct net_device *dev)
 
 	/* Don't know what to do yet. */
 	fep->opened = 0;
+	phy_stop(fep->phy_dev);
 	netif_stop_queue(dev);
 	fec_stop(dev);
 
@@ -1666,6 +1047,7 @@ static const struct net_device_ops fec_netdev_ops = {
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_tx_timeout		= fec_timeout,
 	.ndo_set_mac_address	= fec_set_mac_address,
+	.ndo_do_ioctl           = fec_enet_ioctl,
 };
 
  /*
@@ -1689,7 +1071,6 @@ static int fec_enet_init(struct net_device *dev, int index)
 	}
 
 	spin_lock_init(&fep->hw_lock);
-	spin_lock_init(&fep->mii_lock);
 
 	fep->index = index;
 	fep->hwp = (void __iomem *)dev->base_addr;
@@ -1716,16 +1097,10 @@ static int fec_enet_init(struct net_device *dev, int index)
 	fep->rx_bd_base = cbd_base;
 	fep->tx_bd_base = cbd_base + RX_RING_SIZE;
 
-#ifdef HAVE_mii_link_interrupt
-	fec_request_mii_intr(dev);
-#endif
 	/* The FEC Ethernet specific entries in the device structure */
 	dev->watchdog_timeo = TX_TIMEOUT;
 	dev->netdev_ops = &fec_netdev_ops;
-
-	for (i=0; i<NMII-1; i++)
-		mii_cmds[i].mii_next = &mii_cmds[i+1];
-	mii_free = mii_cmds;
+	dev->ethtool_ops = &fec_enet_ethtool_ops;
 
 	/* Set MII speed to 2.5 MHz */
 	fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
@@ -1760,13 +1135,6 @@ static int fec_enet_init(struct net_device *dev, int index)
 
 	fec_restart(dev, 0);
 
-	/* Queue up command to detect the PHY and initialize the
-	 * remainder of the interface.
-	 */
-	fep->phy_id_done = 0;
-	fep->phy_addr = 0;
-	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
-
 	return 0;
 }
 
@@ -1835,8 +1203,7 @@ fec_restart(struct net_device *dev, int duplex)
 	writel(0, fep->hwp + FEC_R_DES_ACTIVE);
 
 	/* Enable interrupts we wish to service */
-	writel(FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII,
-			fep->hwp + FEC_IMASK);
+	writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
 }
 
 static void
@@ -1859,7 +1226,6 @@ fec_stop(struct net_device *dev)
 	/* Clear outstanding MII command interrupts. */
 	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
 
-	writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 }
 
@@ -1891,6 +1257,7 @@ fec_probe(struct platform_device *pdev)
 	memset(fep, 0, sizeof(*fep));
 
 	ndev->base_addr = (unsigned long)ioremap(r->start, resource_size(r));
+	fep->pdev = pdev;
 
 	if (!ndev->base_addr) {
 		ret = -ENOMEM;
@@ -1926,13 +1293,24 @@ fec_probe(struct platform_device *pdev)
 	if (ret)
 		goto failed_init;
 
+	ret = fec_enet_mii_init(pdev);
+	if (ret)
+		goto failed_mii_init;
+
 	ret = register_netdev(ndev);
 	if (ret)
 		goto failed_register;
 
+	printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
+		"(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
+		fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
+		fep->phy_dev->irq);
+
 	return 0;
 
 failed_register:
+	fec_enet_mii_remove(fep);
+failed_mii_init:
 failed_init:
 	clk_disable(fep->clk);
 	clk_put(fep->clk);
@@ -1959,6 +1337,7 @@ fec_drv_remove(struct platform_device *pdev)
 	platform_set_drvdata(pdev, NULL);
 
 	fec_stop(ndev);
+	fec_enet_mii_remove(fep);
 	clk_disable(fep->clk);
 	clk_put(fep->clk);
 	iounmap((void __iomem *)ndev->base_addr);
-- 
1.7.0.1

^ permalink raw reply related

* [PATCH] net: ipmr/ip6mr: prevent out-of-bounds vif_table access
From: Nicolas Dichtel @ 2010-03-26  9:45 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 90 bytes --]

Hi,

please consider the attached patch about IPv4 and IPv6 multicast.


Regards,
Nicolas

[-- Attachment #2: 0001-net-ipmr-ip6mr-prevent-out-of-bounds-vif_table-acc.patch --]
[-- Type: text/x-diff, Size: 2922 bytes --]

>From fb0b874d55bfe2b81499d4c17598b94ac6c58594 Mon Sep 17 00:00:00 2001
From: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Date: Fri, 26 Mar 2010 10:12:34 +0100
Subject: [PATCH] net: ipmr/ip6mr: prevent out-of-bounds vif_table access

When cache is unresolved, c->mf[6]c_parent is set to 65535 and
minvif, maxvif are not initialized, hence we must avoid to
parse IIF and OIF.
A second problem can happen when the user dumps a cache entry
where a VIF, that was referenced at creation time, has been
removed.

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
 net/ipv4/ipmr.c  |   11 +++++++----
 net/ipv6/ip6mr.c |   11 +++++++----
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 0b9d03c..d0a6092 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1616,17 +1616,20 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
 	int ct;
 	struct rtnexthop *nhp;
 	struct net *net = mfc_net(c);
-	struct net_device *dev = net->ipv4.vif_table[c->mfc_parent].dev;
 	u8 *b = skb_tail_pointer(skb);
 	struct rtattr *mp_head;
 
-	if (dev)
-		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
+	/* If cache is unresolved, don't try to parse IIF and OIF */
+	if (c->mfc_parent > MAXVIFS)
+		return -ENOENT;
+
+	if (VIF_EXISTS(net, c->mfc_parent))
+		RTA_PUT(skb, RTA_IIF, 4, &net->ipv4.vif_table[c->mfc_parent].dev->ifindex);
 
 	mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
 
 	for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
-		if (c->mfc_un.res.ttls[ct] < 255) {
+		if (VIF_EXISTS(net, ct) && c->mfc_un.res.ttls[ct] < 255) {
 			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
 				goto rtattr_failure;
 			nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 23e4ac0..27acfb5 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1695,17 +1695,20 @@ ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm)
 	int ct;
 	struct rtnexthop *nhp;
 	struct net *net = mfc6_net(c);
-	struct net_device *dev = net->ipv6.vif6_table[c->mf6c_parent].dev;
 	u8 *b = skb_tail_pointer(skb);
 	struct rtattr *mp_head;
 
-	if (dev)
-		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
+	/* If cache is unresolved, don't try to parse IIF and OIF */
+	if (c->mf6c_parent > MAXMIFS)
+		return -ENOENT;
+
+	if (MIF_EXISTS(net, c->mf6c_parent))
+		RTA_PUT(skb, RTA_IIF, 4, &net->ipv6.vif6_table[c->mf6c_parent].dev->ifindex);
 
 	mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
 
 	for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
-		if (c->mfc_un.res.ttls[ct] < 255) {
+		if (MIF_EXISTS(net, ct) && c->mfc_un.res.ttls[ct] < 255) {
 			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
 				goto rtattr_failure;
 			nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
-- 
1.5.4.5


^ permalink raw reply related

* Re: [Regression] r8169: enable 64-bit DMA by default for PCI Express devices (v2)
From: J�rn Engel @ 2010-03-26  9:12 UTC (permalink / raw)
  To: Robert Hancock; +Cc: David Miller, torvalds, linux-kernel, netdev, romieu
In-Reply-To: <51f3faa71003251756h17374375yd3a5d2acee2ffab9@mail.gmail.com>

On Thu, 25 March 2010 18:56:03 -0600, Robert Hancock wrote:
> 
> Francois, ping? Is there anyone else that has access to this kind of
> information about these chips?
> 
> It's kind of interesting that there's only been one report of this
> though. Either the affected chips are rare among people testing
> 2.6.34-rc or there's something more to this. Maybe something
> wierd/unusual about Jörn's system?
> 
> Jörn, are any other devices on your system working with 64-bit
> addressing? Try doing this:
> 
> find /sys -name "*dma_mask_bits*" | xargs cat
> 
> Does anything show more than 32?

I've slightly changed the command:
# for i in `find /sys -name "*dma_mask_bits*"`; do echo -n "$i: "; cat $i; done
/sys/devices/pci0000:00/0000:00:00.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:00.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:02.0/dma_mask_bits: 36
/sys/devices/pci0000:00/0000:00:02.0/consistent_dma_mask_bits: 36
/sys/devices/pci0000:00/0000:00:1c.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1c.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1c.1/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1c.1/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1c.1/0000:01:00.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1c.1/0000:01:00.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.1/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.1/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.2/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.2/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.3/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.3/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.7/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1d.7/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1e.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1e.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.0/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.0/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.1/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.1/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.2/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.2/consistent_dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.3/dma_mask_bits: 32
/sys/devices/pci0000:00/0000:00:1f.3/consistent_dma_mask_bits: 32

One device, which should be this one:
00:02.0 VGA compatible controller: Intel Corporation 82G33/G31 Express Integrated Graphics Controller (rev 10)

J�rn

^ permalink raw reply

* Re: [Bugme-new] [Bug 15571] New: TCP madness - some packets are shunned.
From: Arnd Hannemann @ 2010-03-26  8:41 UTC (permalink / raw)
  To: Jasen Betts; +Cc: netdev, bugzilla-daemon, bugme-daemon, Andrew Morton
In-Reply-To: <20100326053151.GA23938@treshna.com>

[re-adding CCs] please reply to all

Am 26.03.2010 06:31, schrieb Jasen Betts:
> On Thu, Mar 25, 2010 at 04:34:25PM +0100, Arnd Hannemann wrote:
>> Am 22.03.2010 22:37, schrieb Andrew Morton:
>>>
>>> (switched to email.  Please respond via emailed reply-to-all, not via the
>>> bugzilla web interface).
>>>
>>> On Thu, 18 Mar 2010 02:46:29 GMT
>>> bugzilla-daemon@bugzilla.kernel.org wrote:
>>>
>>>> http://bugzilla.kernel.org/show_bug.cgi?id=15571
>>>>
>>>>                URL: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=53646
>>>>                     2
>>>>            Summary: TCP madness - some packets are shunned.
>>>>            Product: Networking
>>>>            Version: 2.5
>>>>     Kernel Version: 2.6.30
>>>>           Platform: All
>>>>         OS/Version: Linux
>>>>               Tree: Mainline
>>>>             Status: NEW
>>>>           Severity: normal
>>>>           Priority: P1
>>>>          Component: Other
>>>>         AssignedTo: acme@ghostprotocols.net
>>>>         ReportedBy: jasen@treshna.com
>>>>         Regression: No
>>>>
>>>>
>>>> The host http://www.cv-it.com is virtually unreachable with kernel 2.6.26 
>>>> (and later) slow with kernel 2.6.18 and just fine with windows XP.
>>>>
>>>> I used telnet to port 80 for testing.
>>>>
>>>> it seems to be a TCP issue, as the having the XP machine behind a linux based
>>>> iptables firewall pc causes no problems, but telnet from the firewall pc itself
>>>> to port 80 on www.cv-it.com does not work
>>
>> For me it seems to be the host is messing up with the window scale option.
>> Although it claims to support window scaling:
>> 16:23:17.466592 IP x.x.x.x.51151 > 121.199.32.220.80: Flags [S], seq 2159265664, win 5840, options [mss 1460,sackOK,TS val 8382141 ecr 0,nop,wscale 7], length 0
>> 16:23:17.761697 IP 121.199.32.220.80 > x.x.x.x.51151: Flags [S.], seq 3910885479, ack 2159265665, win 65535, options [mss 1448,sackOK,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,wscale 8], length 0
>>
>> My host (linux 2.6.32) is offering a window of 5888 (46<<7):
>> 16:23:17.761740 IP x.x.x.x.51151 > 121.199.32.220.80: Flags [.], ack 1, win 46, length 0
>>
>> And cv-it.com seems to think there is only a window of 46 ignoring the previously negotiated window scaling:
>> 16:23:23.066318 IP 121.199.32.220.80 > x.x.x.x.51151: Flags [.], seq 1:47, ack 112, win 65160, length 46
>>
>> You can disable window scaling with:
>> sysctl -w "net.ipv4.tcp_window_scaling=0"
>>
> 
> yeah, that works for me.
> 
> I don't know a lot about this stuff. wikipedia says windows XP does window
> scaling also, yet it's not a problem with XP only with linux.

This may be pure coincidence that XP "works".
For instance if XP is only using a window scale of 1 or 2, the effect of
ignoring the window scale may not be so drastic. However, the problem is:
 once you negotiated the window scale for a connection you must not change
it and you may actually need a big window for performance reasons.
So you have to pick a window scale value, so you can express the
largest window you are going to use.
In Linux the maximum tcp receive window can be manipulated with the
"net.ipv4.tcp_rmem" sysctl. (The max is the third value)
Recent linux kernels use the amount of ram your machine has to calculate
the default value for this.

> 
>> My host (linux 2.6.32) is offering a window of 5888 (46<<7):
>> 16:23:17.761740 IP x.x.x.x.51151 > 121.199.32.220.80: Flags [.], ack 1, win 46, length 0
> 
> so you mean it seems to see '46' as '46' instead of 46<<7 == 5888 

Exactly.

Best regards,
Arnd

^ permalink raw reply

* [PATCH net-next-2.6] ipv6: Use __fls() instead of fls() in __ipv6_addr_diff().
From: YOSHIFUJI Hideaki @ 2010-03-26  8:01 UTC (permalink / raw)
  To: davem; +Cc: yoshfuji, netdev

Because we have ensured that the argument is non-zero,
it is better to use __fls() and generate better code.

Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
 include/net/ipv6.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e72fb10..619ab34 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -422,7 +422,7 @@ static inline int __ipv6_addr_diff(const void *token1, const void *token2, int a
 	for (i = 0; i < addrlen; i++) {
 		__be32 xb = a1[i] ^ a2[i];
 		if (xb)
-			return i * 32 + 32 - fls(ntohl(xb));
+			return i * 32 + 32 - __fls(ntohl(xb));
 	}
 
 	/*
-- 
1.5.6.5


^ permalink raw reply related

* Re: mmotm 2010-03-23 - IPv6 warnings...
From: David Miller @ 2010-03-26  6:23 UTC (permalink / raw)
  To: Valdis.Kletnieks; +Cc: akpm, netdev, linux-kernel, shemminger
In-Reply-To: <4161.1269584302@localhost>

From: Valdis.Kletnieks@vt.edu
Date: Fri, 26 Mar 2010 02:18:22 -0400

> Confirming looks like a good fix - no cruft in dmesg, and I have ipv6 addresses:

Great, thanks for testing.

Andrew your box should be good now too.

^ permalink raw reply

* Re: mmotm 2010-03-23 - IPv6 warnings...
From: Valdis.Kletnieks @ 2010-03-26  6:18 UTC (permalink / raw)
  To: David Miller; +Cc: akpm, netdev, linux-kernel, shemminger
In-Reply-To: <20100325.214050.101816448.davem@davemloft.net>

[-- Attachment #1: Type: text/plain, Size: 1777 bytes --]

On Thu, 25 Mar 2010 21:40:50 PDT, David Miller said:
> From: Valdis.Kletnieks@vt.edu
> Date: Thu, 25 Mar 2010 12:45:30 -0400
> 
> > On Wed, 24 Mar 2010 18:42:26 EDT, Andrew Morton said:
> >> On Wed, 24 Mar 2010 21:36:41 -0400 Valdis.Kletnieks@vt.edu wrote:
> >> 
> >> > On Tue, 23 Mar 2010 15:34:59 PDT, akpm@linux-foundation.org said:
> >> > > The mm-of-the-moment snapshot 2010-03-23-15-34 has been uploaded to
> >> > > 
> >> > >    http://userweb.kernel.org/~akpm/mmotm/
> >> > 
> >> > Seen in my dmesg.  It may be relevant that I'm at home, and my IPv6
> >> > prefix arrives via a PPP VPN connection. This happened about 20-25 secon
ds
> >> > after I launched pppd.
> >> 
> >> Yes, thanks, I get the same - it doesn't seem to break anything.  It
> >> also happens some time after boot has completed.
> > 
> > Just doing an 'ifup eth0' on a network with IPv6 on it is sufficient.
> 
> I just checked the following into net-next-2.6 which should at least
> kill that dmesg log.
> 
> If you still are missing your ipv6 addresses, let me know how
> they normally get added, maybe that's enough to let me figure
> it out.

Confirming looks like a good fix - no cruft in dmesg, and I have ipv6 addresses:

%  ifconfig ppp0
ppp0      Link encap:Point-to-Point Protocol  
          inet addr:128.173.34.103  P-t-P:128.173.32.21  Mask:255.255.255.255
          inet6 addr: 2001:468:c80:3b04:105d:a65c:28c4:75d1/64 Scope:Global
          inet6 addr: fe80::105d:a65c:28c4:75d1/10 Scope:Link
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1460  Metric:1
          RX packets:79 errors:0 dropped:0 overruns:0 frame:0
          TX packets:85 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:30943 (30.2 KiB)  TX bytes:6746 (6.5 KiB)

[-- Attachment #2: Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply

* Re: [PATCH RFC 1/2] flow: virtualize get and entry deletion methods
From: Timo Teräs @ 2010-03-26  6:17 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, herbert
In-Reply-To: <20100325.122611.267401605.davem@davemloft.net>

David Miller wrote:
> From: Timo Teras <timo.teras@iki.fi>
> Date: Thu, 25 Mar 2010 11:24:50 +0200
> 
>> This allows to validate the cached object before returning it.
>> It also allows to destruct object properly, if the last reference
>> was held in flow cache. This is also a prepartion for caching
>> bundles in the flow cache.
>>
>> In return for virtualizing the methods, we save on:
>> - not having to regenerate the whole flow cache on policy removal:
>>   each flow matching a killed policy gets refreshed as the getter
>>   function notices it smartly.
>> - we do not have to call flow_cache_flush from policy gc, since the
>>   flow cache now properly deletes the object if it had any references
>>
>> This also means the flow cache entry deletion does more work. If
>> it's too slow now, may have to implement delayed deletion of flow
>> cache entries. But this is a save because this enables immediate
>> deletion of policies and bundles.
>>
>> Signed-off-by: Timo Teras <timo.teras@iki.fi>
> 
> I'm concerned about the new costs being added here.
> 
> We have to now take the policy lock as a reader every time the flow
> cache wants to grab a reference.  So we now have this plus the
> indirect function call new overhead.

If we want to have the flow cache generic, we pretty much need
indirect calls. But considering that it might make sense to cache
bundles, or "xfrm cache entries" on all flow directions (so we can
track both the main and sub policies) we could make it specialized.

> Maybe we can make the dead state check safe to do asynchronously
> somehow?  I wonder if the policy layer is overdue for an RCU
> conversion or similar.

I looked at the code and the policy lock is not needed much anymore.
I think it was most heavily used to protected ->bundles which is
now removed. But yes, I also previously said that ->walk.dead should
probably be converted to atomic_t. It is only written once when
the policy is killed. So we can make it accessible without the lock.

Considering that the whole cache was broken previously, and we
needed to take write lock on policy for each forwarded packet,
it does not sound that bad. Apparently locally originating traffic
directly to xfrm destination (not via gre) would get cached on the
socket dst cache and avoids the xfrm_lookup on fast path entirely(?).

We can get away from the per-cpu design with RCU hash. But I think
we still need to track the hash entries similar to this. Though,
there's probably some other tricks doable with RCU which I'm not
all familiar with. I will take a quick look on the rcu thingy
Herbert mentioned earlier.

> Anyways, something to think about.  Otherwise I don't mind these
> changes.

Ok, I'll add "convert walk.dead to atomic_t" so we can access it
without a lock.

I did also notice that the policy locking is not right exactly.
E.g. migration can touch templates, and we read templates currently
without locks. So I think bundle creation should be protected
with policy read lock. But even this can probably be avoided by
RCU type bundle creation. We just take bundle genid before starting
to create it, create bundle, and check if genid was changed while
doing this we retry.

We might even get away from policy->lock all together. In most
places it's only used to protect walk.dead. And bundle creation
can be synchronizes as above. The only remaining place seems to
be the timer function. I think it's safe to remove locking there
too, and synchronize using timer deletion. All this is because
any changes to policy will result in xfrm_policy replacement:
the old is killed and new one inserted atomically.

Do you think this would work?

- Timo

^ permalink raw reply


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