netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] MII bus API for PHY devices
       [not found] <069B6F33-341C-11D9-9652-000393DBC2E8@freescale.com>
@ 2004-11-18 17:52 ` Andy Fleming
  2004-11-18 19:34   ` Jason McMullan
  2004-11-18 23:26   ` Benjamin Herrenschmidt
  0 siblings, 2 replies; 13+ messages in thread
From: Andy Fleming @ 2004-11-18 17:52 UTC (permalink / raw)
  To: jason.mcmullan; +Cc: netdev, linux-kernel

Replying this to the netdev list for their perusal.

First, I'm flattered that you've based this on the gianfar phy code, 
which I stole shamelessly from Benh's code, but Benh changed his code, 
and so I stole once more (the sungem_phy code he mentioned).  The 
current 2.6.9 gianfar driver has a completely different PHY 
infrastructure.  One which is a better model, I think, for abstraction 
than my previous code.

Which brings me to item #2: I have taken the code from the previous 
patch, and combined it with my newer PHY code, which should ease the 
way for someone to add some of the WOL features, and such.  I have 
compiled, and tested this code, and it works with the gianfar driver.

However, I've got a few questions for the network device community, on 
how best to finish this off.

The primary issue is how to classify the MII bus.  Should it be a 
proper bus, fitting within the new driver model?  This seems like the 
proper method to me, since the MII bus is, in fact, a bus.  But it's a 
bit of a strange bus.  The bus is attached at two ends.  One end has 
some number of PHY devices, and the corresponding drivers for them.  
The other end has some number of ethernet controllers, and their 
drivers.

So we have 3 implementation decisions which are affected by this:

1) How should we pass initialization information from the system to the 
bus.  Information like which irq to use for each PHY, and what the 
address space for the bus's controls is.  I would like to enforce 
encapsulation so that the ethernet drivers don't need to know this 
information, or pass it to the bus.

2) How should we reflect the dependency of the ethernet driver on the 
mii bus driver?

3) How should we bind ethernet drivers to PHY drivers?

Oh, and a 4th side-issue:
Should each PHY have its own file?  Or should we dump all the PHY 
drivers in one file?  And if so, should THAT file be separate from the 
mii bus implementation file?

Andy Fleming
Open Source Team
Freescale Semiconductor, Inc

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 17:52 ` [PATCH] MII bus API for PHY devices Andy Fleming
@ 2004-11-18 19:34   ` Jason McMullan
  2004-11-18 19:50     ` Andy Fleming
  2004-11-18 23:26   ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 13+ messages in thread
From: Jason McMullan @ 2004-11-18 19:34 UTC (permalink / raw)
  To: Andy Fleming; +Cc: netdev, linux-kernel

On Thu, 2004-11-18 at 11:52 -0600, Andy Fleming wrote:
> 1) How should we pass initialization information from the system to the 
> bus.  Information like which irq to use for each PHY, and what the 
> address space for the bus's controls is.  I would like to enforce 
> encapsulation so that the ethernet drivers don't need to know this 
> information, or pass it to the bus.

(Just an off-the-cuff answer here)

In line with the OCP->platform work I've been doing, I would think
that creating 'phy' devices on the platform bus would be appropriate,
with 'platform_data' that describes (a) the platform device ethernet
it's bus is on and (b) it's PHY ID on that bus. The PHY's IRQ would
be in it's platform resources.

> 2) How should we reflect the dependency of the ethernet driver on the 
> mii bus driver?

Hmm. Don't really know from a sysfs perspective...


> 3) How should we bind ethernet drivers to PHY drivers?

A PHY 'platform_data' struct like:

struct phy_device_data {
	struct {
		const char *name;
		int id;
	} ethernet_platform_device_parent;
	int	phy_id;
}
	
> Oh, and a 4th side-issue:
> Should each PHY have its own file? 

Actually, each PHY should have it's own device directory, like every
other device. Eventually, PHYs should have /dev/phy* entries, where
user-space can read/write PHY registers.

-- 
Jason McMullan <jason.mcmullan@timesys.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 19:34   ` Jason McMullan
@ 2004-11-18 19:50     ` Andy Fleming
  2004-11-18 21:00       ` Jason McMullan
  0 siblings, 1 reply; 13+ messages in thread
From: Andy Fleming @ 2004-11-18 19:50 UTC (permalink / raw)
  To: Jason McMullan
  Cc: <netdev@oss.sgi.com>, <linux-kernel@vger.kernel.org>


On Nov 18, 2004, at 13:34, Jason McMullan wrote:
>> 3) How should we bind ethernet drivers to PHY drivers?
>
> A PHY 'platform_data' struct like:
>
> struct phy_device_data {
> 	struct {
> 		const char *name;
> 		int id;
> 	} ethernet_platform_device_parent;
> 	int	phy_id;
> }

So you would have each PHY know the controller to which it's attached?  
I would have thought the other way around... Hm.  I will definitely 
have to read up on my driver model stuff

> 	
>> Oh, and a 4th side-issue:
>> Should each PHY have its own file?
>
> Actually, each PHY should have it's own device directory, like every
> other device. Eventually, PHYs should have /dev/phy* entries, where
> user-space can read/write PHY registers.

I think you misunderstood.  Are you talking about sysfs?  I was talking 
about actual source files.  i.e. should there be dm9161.c, m88e1101.c, 
cis8201.c, etc.

Also, do we need user-space to read/write PHY registers.  ethtool has 
this capability, I believe, and the interfaces there are settled.

Andy Fleming

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 19:50     ` Andy Fleming
@ 2004-11-18 21:00       ` Jason McMullan
  0 siblings, 0 replies; 13+ messages in thread
From: Jason McMullan @ 2004-11-18 21:00 UTC (permalink / raw)
  To: Andy Fleming
  Cc: <netdev@oss.sgi.com>, <linux-kernel@vger.kernel.org>

On Thu, 2004-11-18 at 13:50 -0600, Andy Fleming wrote:
> Jason McMullan said:
> >
> > Actually, each PHY should have it's own device directory, like every
> > other device. Eventually, PHYs should have /dev/phy* entries, where
> > user-space can read/write PHY registers.
> 
> I think you misunderstood.  Are you talking about sysfs?  I was talking 
> about actual source files.  i.e. should there be dm9161.c, m88e1101.c, 
> cis8201.c, etc.

	Yes, I am talking about sysfs. And yes, I think every PHY should have
it's own .c file. (although most people could get away with
using a non-IRQ 'drivers/net/phy/phy-generic.c'

> Also, do we need user-space to read/write PHY registers.  ethtool has 
> this capability, I believe, and the interfaces there are settled.

Doh! I forgot.

-- 
Jason McMullan <jason.mcmullan@timesys.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 17:52 ` [PATCH] MII bus API for PHY devices Andy Fleming
  2004-11-18 19:34   ` Jason McMullan
@ 2004-11-18 23:26   ` Benjamin Herrenschmidt
  2004-11-19 16:41     ` Jason McMullan
  2004-11-19 21:18     ` Andy Fleming
  1 sibling, 2 replies; 13+ messages in thread
From: Benjamin Herrenschmidt @ 2004-11-18 23:26 UTC (permalink / raw)
  To: Andy Fleming; +Cc: jason.mcmullan, netdev, Linux Kernel list

On Thu, 2004-11-18 at 11:52 -0600, Andy Fleming wrote:

> 1) How should we pass initialization information from the system to the 
> bus.  Information like which irq to use for each PHY, and what the 
> address space for the bus's controls is.  I would like to enforce 
> encapsulation so that the ethernet drivers don't need to know this 
> information, or pass it to the bus.

Unfortunately, this is all quite platform specific and the ethernet
driver may be the only one to know what to do here... add to that
various special cases of the way the PHY is wired or controlled, I think
we can't completely avoid special casing...

> 2) How should we reflect the dependency of the ethernet driver on the 
> mii bus driver?

The ethernet driver can instanciate the PHYs at it's childs, though the
case of several MACs sharing PHYs will be difficult to represent...

> 3) How should we bind ethernet drivers to PHY drivers?

I would have them instanciated by the ethernet driver. Besides, the PHY
driver will need to be able to identify it's "parent" driver in some
ways to deal with special cases. It would be nice to have a library of
utility code to independently deal with link tracking (basically what
drivers like sungem do independently), with a callback to the ethernet
driver to inform it of actual changes (notifier ?). MACs often have
autopoll features and PHY often have interrupts, but from experience,
that's not very useful and a good old timer based polling tend to do a
better job most of the time.

> Oh, and a 4th side-issue:
> Should each PHY have its own file?  Or should we dump all the PHY 
> drivers in one file?  And if so, should THAT file be separate from the 
> mii bus implementation file?

I'd put all bcm5xxx in the same file ... they can be put together by
families...

Also, 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 23:26   ` Benjamin Herrenschmidt
@ 2004-11-19 16:41     ` Jason McMullan
  2004-11-19 21:18     ` Andy Fleming
  1 sibling, 0 replies; 13+ messages in thread
From: Jason McMullan @ 2004-11-19 16:41 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Andy Fleming, netdev, Linux Kernel list

On Fri, 2004-11-19 at 10:26 +1100, Benjamin Herrenschmidt wrote:
> The ethernet driver can instanciate the PHYs at it's childs, though the
> case of several MACs sharing PHYs will be difficult to represent...

I think *requiring* a binding to ethernet devices is a bad idea. 

For example, on many MPC8260 embedded systems, the MII bus is controlled
by GPIO pins, not an ethernet MDIO system.

Some applications may use the MII bus to control non-PHY devices, such
as Broadcom ethernet switches. I'm working for a general MII bus that
ethernet PHYs and other devices can all use.

-- 
Jason McMullan <jason.mcmullan@timesys.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
@ 2004-11-19 20:18 Manfred Spraul
  2004-11-19 21:01 ` Andy Fleming
  0 siblings, 1 reply; 13+ messages in thread
From: Manfred Spraul @ 2004-11-19 20:18 UTC (permalink / raw)
  To: Andy Fleming, Jason McMullan; +Cc: Linux Kernel Mailing List, Netdev

Hi,

I don't like the polling/interrupt setup part:
- for a nic driver, there is no irq line that could be requested by 
mii_phy_irq_enable().
- if the mii bus driver uses it's own timers, then locking within the 
nics will be more difficult.

Could you make that part optional? For a nic driver, I would prefer if I 
could just call the ->startup part without the request_irq. If the nic 
irq handler notices that the nic got an event, then it would call an 
appropriate mii_bus function.

This also applies for something like /dev/phy/xy: With natsemi, it would 
be very tricky to add proper locking. The nic as an internal phy and an 
external mii bus. The internal phy is partially visible on the external 
bus and any accesses to the phy id of the internal phy on the external 
bus cause lockups. No big deal, I just move the internal phy around [the 
phy id doesn't matter], but I would prefer if I have to do that just for 
ethtool, not for multiple interfaces.

--
    Manfred

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-19 20:18 [PATCH] MII bus API for PHY devices Manfred Spraul
@ 2004-11-19 21:01 ` Andy Fleming
  0 siblings, 0 replies; 13+ messages in thread
From: Andy Fleming @ 2004-11-19 21:01 UTC (permalink / raw)
  To: Manfred Spraul; +Cc: Jason McMullan, Linux Kernel Mailing List, Netdev


On Nov 19, 2004, at 14:18, Manfred Spraul wrote:

> Hi,
>
> I don't like the polling/interrupt setup part:
> - for a nic driver, there is no irq line that could be requested by 
> mii_phy_irq_enable().
> - if the mii bus driver uses it's own timers, then locking within the 
> nics will be more difficult.

I'm not sure I accept the argument that locking will be more difficult. 
  Jason's patch requires that a callback be registered for the interrupt 
or the polling (My update has a similar scheme).  The function you 
register is essentially like an extra interrupt, except it is never 
invoked at interrupt time.  All the function has to do is react 
properly to link state.  If, previously, you checked link state in your 
interrupt handler, you could still do it there, I suspect.

>
> Could you make that part optional? For a nic driver, I would prefer if 
> I could just call the ->startup part without the request_irq. If the 
> nic irq handler notices that the nic got an event, then it would call 
> an appropriate mii_bus function.

I think it would be doable to arrange the interface such that drivers 
could adopt only the PHY configuration infrastructure, and not any of 
the polling/interrupt infrastructure.  Of course, as it is, it is at 
least WHOLLY optional, so no driver has to use it at all.

>
> This also applies for something like /dev/phy/xy: With natsemi, it 
> would be very tricky to add proper locking. The nic as an internal phy 
> and an external mii bus. The internal phy is partially visible on the 
> external bus and any accesses to the phy id of the internal phy on the 
> external bus cause lockups. No big deal, I just move the internal phy 
> around [the phy id doesn't matter], but I would prefer if I have to do 
> that just for ethtool, not for multiple interfaces.

I agree with this point -- Accessing the PHY through /dev registers is 
a recipe for some mess.  Though I could be convinced that it is 
manageable.  I do think, however, that the ethtool interface is 
sufficient to the task.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-18 23:26   ` Benjamin Herrenschmidt
  2004-11-19 16:41     ` Jason McMullan
@ 2004-11-19 21:18     ` Andy Fleming
  2004-11-19 22:43       ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 13+ messages in thread
From: Andy Fleming @ 2004-11-19 21:18 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: netdev, Linux Kernel list, jason.mcmullan, Andy Fleming


On Nov 18, 2004, at 17:26, Benjamin Herrenschmidt wrote:

> On Thu, 2004-11-18 at 11:52 -0600, Andy Fleming wrote:
>
>> 1) How should we pass initialization information from the system to 
>> the
>> bus.  Information like which irq to use for each PHY, and what the
>> address space for the bus's controls is.  I would like to enforce
>> encapsulation so that the ethernet drivers don't need to know this
>> information, or pass it to the bus.
>
> Unfortunately, this is all quite platform specific and the ethernet
> driver may be the only one to know what to do here... add to that
> various special cases of the way the PHY is wired or controlled, I 
> think
> we can't completely avoid special casing...

Well, under the system I'm currently envisioning, the driver would be 
able to provide the data needed by the mii bus, but the hope would be 
to enable board files (for when the PHY is soldered on the motherboard, 
and the enet is not -- like on an MPC85xx) to provide this information 
instead, and leave out the enet as middleman.

>
>> 2) How should we reflect the dependency of the ethernet driver on the
>> mii bus driver?
>
> The ethernet driver can instanciate the PHYs at it's childs, though the
> case of several MACs sharing PHYs will be difficult to represent...

I really don't want the driver to intantiate PHYs directly.  The PHY is 
its own device, and the less net drivers have to understand their inner 
workings, the better.  However, I hadn't considered the possibility of 
multiple MACs sharing the same PHY.  It does, as you say, support my 
argument, though.  With some careful design, the mii bus should be able 
to handle this type of setup easily.

One of my goals, personally, is to allow multiple net drivers to share 
the same mii bus, as in the case of the FCC enet controllers' PHYs on 
an 8560 ADS, which can be accessed through TSEC1's MII Management bus.


>
>> 3) How should we bind ethernet drivers to PHY drivers?
>
> I would have them instanciated by the ethernet driver. Besides, the PHY
> driver will need to be able to identify it's "parent" driver in some
> ways to deal with special cases. It would be nice to have a library of
> utility code to independently deal with link tracking (basically what
> drivers like sungem do independently), with a callback to the ethernet
> driver to inform it of actual changes (notifier ?). MACs often have
> autopoll features and PHY often have interrupts, but from experience,
> that's not very useful and a good old timer based polling tend to do a
> better job most of the time.

So when you say instantiated, would you consider calling an "attach" 
function with the phy_id and bus_id of the desired PHY instantiation?  
I'm fine with that.  The PHY would need to be able to send 
notifications to the enet controller (currently done through a 
callback).  I'm interested in ideas on how the notifier could be used 
(I have a distaste for callbacks).

Autopoll features sound pretty neat.  I think the system should support 
that.  PHY interrupts are supported (they work quite well on my 85xx 
system), as is timer-based polling.  Do you really think that there are 
special cases which can't be handled using a library similar to the 
sungem_phy one?
>
>> Oh, and a 4th side-issue:
>> Should each PHY have its own file?  Or should we dump all the PHY
>> drivers in one file?  And if so, should THAT file be separate from the
>> mii bus implementation file?
>
> I'd put all bcm5xxx in the same file ... they can be put together by
> families...

Yeah, that sounds good.


Andy Fleming
Open Source Team
Freescale Semiconductor, Inc

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-19 21:18     ` Andy Fleming
@ 2004-11-19 22:43       ` Benjamin Herrenschmidt
  2004-11-20  0:04         ` Andy Fleming
  0 siblings, 1 reply; 13+ messages in thread
From: Benjamin Herrenschmidt @ 2004-11-19 22:43 UTC (permalink / raw)
  To: Andy Fleming; +Cc: netdev, Linux Kernel list, jason.mcmullan, Andy Fleming

On Fri, 2004-11-19 at 15:18 -0600, Andy Fleming wrote:

> So when you say instantiated, would you consider calling an "attach" 
> function with the phy_id and bus_id of the desired PHY instantiation?  
> I'm fine with that.  The PHY would need to be able to send 
> notifications to the enet controller (currently done through a 
> callback).  I'm interested in ideas on how the notifier could be used 
> (I have a distaste for callbacks).

Look at the notifier lists in include/linux/notifier.h

> Autopoll features sound pretty neat.  I think the system should support 
> that.

But that becomes MAC-dependant again... That means you'd need 1) a way
for the MAC driver to ask the PHY driver what register it wants
autopolled, and a function in the PHY driver for the MAC to call when it
detects a change. Also, autopoll is broken in some MACs...

>   PHY interrupts are supported (they work quite well on my 85xx 
> system), as is timer-based polling.  Do you really think that there are 
> special cases which can't be handled using a library similar to the 
> sungem_phy one?

Nope. I think timer based polling with a sungem-like fallback mecanism
to forced speeds would be nice.

Ben.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-19 22:43       ` Benjamin Herrenschmidt
@ 2004-11-20  0:04         ` Andy Fleming
  2004-11-23 18:18           ` Jason McMullan
  2004-12-02 18:29           ` [PATCH] MII bus API for PHY devices Rev 2.0 Jason McMullan
  0 siblings, 2 replies; 13+ messages in thread
From: Andy Fleming @ 2004-11-20  0:04 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Jason McMullan, Linux Kernel list, Netdev


On Nov 19, 2004, at 16:43, Benjamin Herrenschmidt wrote:

> On Fri, 2004-11-19 at 15:18 -0600, Andy Fleming wrote:
>
>> So when you say instantiated, would you consider calling an "attach"
>> function with the phy_id and bus_id of the desired PHY instantiation?
>> I'm fine with that.  The PHY would need to be able to send
>> notifications to the enet controller (currently done through a
>> callback).  I'm interested in ideas on how the notifier could be used
>> (I have a distaste for callbacks).
>
> Look at the notifier lists in include/linux/notifier.h

Ok, will do.

>
>> Autopoll features sound pretty neat.  I think the system should 
>> support
>> that.
>
> But that becomes MAC-dependant again... That means you'd need 1) a way
> for the MAC driver to ask the PHY driver what register it wants
> autopolled, and a function in the PHY driver for the MAC to call when 
> it
> detects a change. Also, autopoll is broken in some MACs...

What I'm envisioning here is that the driver would be able to tell the 
PHY infrastructure that it's going to do its own thing, and then make 
use of the reading/configuring part of the infrastructure, similar to 
how sungem and gianfar are currently set up.  But they would have the 
option of letting the infrastructure also handle the status updates.  
And, of course, the driver would not go through the effort to use 
autopoll if it were broken.

>
>>   PHY interrupts are supported (they work quite well on my 85xx
>> system), as is timer-based polling.  Do you really think that there 
>> are
>> special cases which can't be handled using a library similar to the
>> sungem_phy one?
>
> Nope. I think timer based polling with a sungem-like fallback mecanism
> to forced speeds would be nice.

Yes, I agree.  The system I currently have does fallback to forced, 
though it doesn't yet support the "magic aneg" feature you mentioned.  
But that should be easy to add, and so it shall be.


Andy Fleming

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] MII bus API for PHY devices
  2004-11-20  0:04         ` Andy Fleming
@ 2004-11-23 18:18           ` Jason McMullan
  2004-12-02 18:29           ` [PATCH] MII bus API for PHY devices Rev 2.0 Jason McMullan
  1 sibling, 0 replies; 13+ messages in thread
From: Jason McMullan @ 2004-11-23 18:18 UTC (permalink / raw)
  To: Andy Fleming; +Cc: Benjamin Herrenschmidt, Netdev

I've added pluggable PHY support modules and a bitbanger MII bus
framework. 

New patch at:
	http://www.evillabs.net/~gus/patches/driver-mii-bus.patch

RPX/EP8260 MII bus example for bit-banger:
	http://www.evillabs.net/~gus/patches/rpx8260_mii.c

-- 
Jason McMullan <jason.mcmullan@timesys.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH] MII bus API for PHY devices Rev 2.0
  2004-11-20  0:04         ` Andy Fleming
  2004-11-23 18:18           ` Jason McMullan
@ 2004-12-02 18:29           ` Jason McMullan
  1 sibling, 0 replies; 13+ messages in thread
From: Jason McMullan @ 2004-12-02 18:29 UTC (permalink / raw)
  To: Andy Fleming; +Cc: Benjamin Herrenschmidt, Netdev

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

Ok, given previous input, I now release mii-bus 2.0

	* CIS8201 interrupt support
	* The PHY device api is now similar to 'sungem' 
	* Set up your PHY as autoneg or forced speeds.

-- 
Jason McMullan <jason.mcmullan@timesys.com>

[-- Attachment #2: driver-mii-bus.patch --]
[-- Type: text/x-patch, Size: 42946 bytes --]

#### Auto-generated patch ####
Signed-off-by: Jason McMullan <jmcmullan@timesys.com>
Date:          Thu, 02 Dec 2004 13:20:49 -0500
Description:   MII Bus interface
Depends:

###############################

Index of changes:

 drivers/net/Makefile            |    4 
 linux/drivers/net/mii_bitbang.c |  134 ++++++++
 linux/drivers/net/mii_bitbang.h |   40 ++
 linux/drivers/net/mii_bus.c     |  639 ++++++++++++++++++++++++++++++++++++++++
 linux/drivers/net/phy_cicada.c  |  177 +++++++++++
 linux/drivers/net/phy_davicom.c |  140 ++++++++
 linux/drivers/net/phy_lxt97x.c  |  210 +++++++++++++
 linux/drivers/net/phy_marvell.c |  125 +++++++
 linux/include/linux/mii_bus.h   |  191 +++++++++++
 9 files changed, 1659 insertions(+), 1 deletion(-)


--- linux-orig/drivers/net/Makefile
+++ linux/drivers/net/Makefile
@@ -62,7 +62,9 @@
 # end link order section
 #
 
-obj-$(CONFIG_MII) += mii.o
+obj-$(CONFIG_MII) += mii.o mii_bus.o mii_bitbang.o \
+		     phy_davicom.o phy_marvell.o phy_cicada.o \
+		     phy_lxt97x.o
 
 obj-$(CONFIG_SUNDANCE) += sundance.o
 obj-$(CONFIG_HAMACHI) += hamachi.o
--- /dev/null
+++ linux/drivers/net/mii_bitbang.c
@@ -0,0 +1,134 @@
+/* 
+ * drivers/net/mii_bitbang.c
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <asm/string.h>
+
+#include "mii_bitbang.h"
+
+#undef PHY_READ
+#undef PHY_WRITE
+#define PHY_READ	0
+#define PHY_WRITE	1
+
+/* 
+ * 1st byte: 0101PPPP on writes, where PPPP is the MSB of the phy-id
+ *           0110PPPP on reads, where  PPPP is the MSB of the phy-id
+ * 2nd byte: PRRRRR10, P is the LSB of the phy-id, R is the register
+ * 3rd,4th bytes: control on writes, values on reads
+ */
+
+static inline void mii_bitbang_mark(void *priv)
+{
+	struct mii_bitbang *info = priv;
+	int i;
+
+	/* Write preamble */
+	for (i = 0; i < 32; i++)
+		info->send(info->priv, 1);
+}
+
+static inline void mii_bitbang_phy_id(void *priv, int phy_id, int reg, int is_write)
+{
+	struct mii_bitbang *info = priv;
+	int i;
+
+	/* Preamble */
+	info->send(info->priv,0);
+	info->send(info->priv,1);
+	if (is_write) {
+		info->send(info->priv,0);
+		info->send(info->priv,1);
+	} else {
+		info->send(info->priv,1);
+		info->send(info->priv,0);
+	}
+
+	/* Write PHY addr */
+	for (i = 0; i < 5; i++)
+		info->send(info->priv, (phy_id >> (4-i)) & 1);
+
+	/* Write the register */
+	for (i = 0; i < 5; i++)
+		info->send(info->priv, (reg >> (4-i)) & 1);
+
+	info->send(info->priv,1);
+	info->send(info->priv,0);
+}
+
+static int mii_bitbang_read(void *priv, int phy_id, int reg)
+{
+	struct mii_bitbang *info = priv;
+	int i;
+	int retval=0;
+
+	mii_bitbang_mark(priv);
+	mii_bitbang_phy_id(priv, phy_id, reg, PHY_READ);
+
+	for (i = 0; i < 16; i++)
+		retval = (retval << 1) | (info->recv(info->priv) & 1);
+
+	mii_bitbang_mark(priv);
+
+	return retval;
+}
+
+static int mii_bitbang_write(void *priv, int phy_id, int reg, uint16_t val)
+{
+	struct mii_bitbang *info = priv;
+	int i;
+
+	mii_bitbang_mark(priv);
+	mii_bitbang_phy_id(priv, phy_id, reg, PHY_WRITE);
+
+	for (i=0; i < 16; i++)
+		info->send(info->priv, (val >> (15-i)) & 1);
+
+	mii_bitbang_mark(priv);
+
+	return 0;
+}
+
+static void mii_bitbang_reset(void *priv)
+{
+	struct mii_bitbang *info = priv;
+
+	info->reset(info->priv);
+}
+
+/* Creates a bitbang MII bus
+ * Returns < 0 on error, otherwise a bus ID
+ */
+int mii_bitbang_register(struct mii_bitbang *info)
+{
+	memset(&info->bus, 0, sizeof(info->bus));
+
+	info->bus.name = info->name;
+	info->bus.priv = info;
+	info->bus.read = mii_bitbang_read;
+	info->bus.write = mii_bitbang_write;
+	info->bus.reset = mii_bitbang_reset;
+
+	return mii_bus_register(&info->bus);
+}
+
+
+/* Unregisters a bitbang MII bus
+ */
+void mii_bitbang_unregister(struct mii_bitbang *info)
+{
+	mii_bus_unregister(&info->bus);
+}
+
+

--- /dev/null
+++ linux/drivers/net/mii_bitbang.h
@@ -0,0 +1,40 @@
+/* 
+ * drivers/net/mii_bitbang.h
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef _NET_MII_BITBANG_H
+#define _NET_MII_BITBANG_H
+
+#include <linux/mii_bus.h>
+
+struct mii_bitbang {
+	const char *name;	/* Name of device */
+	void *priv;		/* Private data */
+
+	void (*send)(void *priv, int bit);	/* Send one bit */
+	int (*recv)(void *priv);		/* Recv one bit */
+	void (*reset)(void *priv);
+
+	/* Auto-filled-in information */
+	struct mii_bus bus;
+};
+
+/* Creates a bitbang MII bus
+ * Returns < 0 on error, otherwise a bus ID
+ */
+extern int mii_bitbang_register(struct mii_bitbang *info);
+
+/* Unregisters a bitbang MII bus
+ */
+extern void mii_bitbang_unregister(struct mii_bitbang *info);
+
+#endif /* _NET_MII_BITBANG_H */

--- /dev/null
+++ linux/drivers/net/mii_bus.c
@@ -0,0 +1,639 @@
+/* 
+ * drivers/net/mii_bus.c
+ *
+ * Adapeted from drivers/net/gianfar_mii.c, by Andy Fleming
+ *
+ * Author: Jason McMullan (jason.mcmullan@timesys.com) to 
+ * 	   be a generic mii interface
+ *
+ * Copyright (c) 2004 Timesys Inc
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/mii_bus.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+
+#undef DEBUG
+
+static struct mii_bus *mii_bus[8];
+
+#define MII_BUS_MAX	(sizeof(mii_bus)/sizeof(struct mii_bus *))
+
+static inline struct phy_info *mii_phy_of(struct mii_if_info *mii)
+{
+	if (mii != NULL) {
+		int bus,id;
+		bus = MII_BUS(mii->phy_id);
+		id  = MII_ID(mii->phy_id);
+		return mii_bus[bus]->phy[id];
+	}
+
+	return NULL;
+}
+
+/* Write value to the PHY for this device to the register at regnum,
+ * waiting until the write is done before it returns.  All PHY 
+ * configuration has to be done through the TSEC1 MIIM regs */
+EXPORT_SYMBOL(mii_bus_write);
+int mii_bus_write(int bus, int id, int regnum, uint16_t value)
+{
+	if (mii_bus[bus] == NULL)
+		return -EINVAL;
+
+#ifdef DEBUG
+	printk(KERN_INFO "phy%d.%d: Write 0x%.2x <- 0x%.4x\n", bus, id, regnum, value);
+#endif
+	mii_bus[bus]->write(mii_bus[bus]->priv, id, regnum, value);
+	return 0;
+}
+
+/* Reads from register regnum in the PHY for device dev,
+ * returning the value.  Clears miimcom first.  All PHY
+ * configuration has to be done through the TSEC1 MIIM regs */
+EXPORT_SYMBOL(mii_bus_read);
+int mii_bus_read(int bus, int id, int regnum)
+{
+	if (mii_bus[bus] == NULL)
+		return -EINVAL;
+
+#ifdef DEBUG
+	{
+		int rv;
+		rv = mii_bus[bus]->read(mii_bus[bus]->priv, id, regnum);
+		printk(KERN_INFO "phy%d.%d: Read  0x%.2x -> 0x%.4x\n", bus, id, regnum, rv);
+		return rv;
+	}
+#else
+	return mii_bus[bus]->read(mii_bus[bus]->priv, id, regnum);
+#endif
+}
+
+/* Helper function */
+static int phy_set_autoneg(struct phy_info *phy, uint32_t advertise)
+{
+	int err;
+
+	err = phy->ops->set_autoneg(phy, advertise);
+	if (err < 0)
+		return err;
+
+	phy->negotiate.advertise = advertise;
+	phy->negotiate.autoneg = AUTONEG_ENABLE;
+	phy->negotiate.timeout = jiffies + MII_TIMEOUT;
+	phy->state.autoneg = 1;
+
+	return 0;
+}
+
+#define BRIEF_MII_ERRORS
+EXPORT_SYMBOL(phy_gen_poll);
+/* Wait for auto-negotiation to complete */
+int phy_gen_poll(struct phy_info *phy)
+{
+	struct phy_state *pstate;
+	uint16_t val;
+
+	pstate = &phy->state;
+
+	phy_read(phy, MII_BMSR);	/* Dummy read */
+	val = phy_read(phy, MII_BMSR);
+
+	/* If the link just came up, restart the auto-neg procedure
+	 */
+	if (val & BMSR_LSTATUS) {
+		if (pstate->link == 0 && 
+		    pstate->autoneg == 0 && phy->negotiate.autoneg) {
+			phy_set_autoneg(phy, phy->negotiate.advertise);
+			return -EAGAIN;
+		}
+		pstate->link = 1;
+	} else {
+		pstate->link = 0;
+	}
+
+	/* Only auto-negotiate if the link has just gone up */
+	if (pstate->link && pstate->autoneg && 
+	    (time_after(jiffies,phy->negotiate.timeout) || 
+	     (val & BMSR_ANEGCOMPLETE))) {
+#ifdef BRIEF_MII_ERRORS
+		if (val & BMSR_ANEGCOMPLETE) {
+			printk(KERN_INFO "%s: Auto-negotiation done\n",
+			       phy->name);
+		} else {
+			printk(KERN_INFO
+			       "%s: Auto-negotiation timed out\n",
+			       phy->name);
+		}
+#endif
+
+		pstate->autoneg = 0;
+
+		if (val & BMSR_ANEGCOMPLETE) {
+			val = phy_read(phy, MII_LPA);
+			val &= phy_read(phy, MII_ADVERTISE);
+
+			/* According to IEEE 802.3, LPA decisions
+			 * must be done in this order
+			 */
+			if (val & LPA_100FULL) {
+				pstate->speed = SPEED_100;
+				pstate->duplex = DUPLEX_FULL;
+			} else if (val & LPA_100HALF) {
+				pstate->speed = SPEED_100;
+				pstate->duplex = DUPLEX_HALF;
+			} else if (val & LPA_10FULL) {
+				pstate->speed = SPEED_10;
+				pstate->duplex = DUPLEX_FULL;
+			} else if (val & LPA_10HALF) {
+				pstate->speed = SPEED_10;
+				pstate->duplex = DUPLEX_HALF;
+			} else {
+				pstate->speed = SPEED_10;
+				pstate->duplex = DUPLEX_HALF;
+			}
+		}
+	}
+
+	return (pstate->autoneg ? -EAGAIN : 0);
+}
+
+static struct phy_ops gen_ops = {
+	.set_autoneg = phy_gen_set_autoneg,
+	.poll = phy_gen_poll
+};
+
+static struct phy_info phy_info_generic = {
+	.id = 0x0,
+	.name = "Generic PHY",
+	.shift = 32,
+	.ops = &gen_ops
+};
+
+static LIST_HEAD(phy_list);
+
+/* Use the PHY ID registers to determine what type of PHY is attached
+ * to device dev.  return a struct phy_info structure describing that PHY
+ */
+struct phy_info *mii_phy_get_info(int bus, int id)
+{
+	struct list_head *lp;
+	uint16_t phy_reg;
+	uint32_t phy_id;
+	struct phy_info *info = NULL;
+
+	if (mii_bus[bus] == NULL)
+		return NULL;
+
+	/* Grab the bits from PHYIR1, and put them in the upper half */
+	phy_reg = mii_bus_read(bus, id, MII_PHYSID1);
+	phy_id = (phy_reg & 0xffff) << 16;
+
+	/* Grab the bits from PHYIR2, and put them in the lower half */
+	phy_reg = mii_bus_read(bus, id, MII_PHYSID2);
+	phy_id |= (phy_reg & 0xffff);
+
+	/* loop through all the known PHY types, and find one that
+	 * matches the ID we read from the PHY. */
+	list_for_each(lp, &phy_list) {
+		struct phy_info *phy = list_entry(lp, struct phy_info, list);
+		if ((phy->id >> phy->shift) == (phy_id >> phy->shift)) {
+			info = phy;
+			break;
+		}
+	}
+
+	if (info == NULL) {
+		printk(KERN_WARNING
+		       "phy%d.%d: PHY id 0x%x is not supported!\n", bus, id,
+		       phy_id);
+	} else {
+		printk(KERN_INFO "phy%d.%d: PHY is %s (%x)\n", bus, id,
+		       info->name, phy_id);
+	}
+
+	return info;
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+	return mii_bus_read(MII_BUS(phy_id), MII_ID(phy_id), reg);
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int reg, int val)
+{
+	mii_bus_write(MII_BUS(phy_id), MII_ID(phy_id), reg, val & 0xffff);
+}
+
+static inline void mii_phy_irq_ack(struct mii_if_info *mii)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+
+	phy->ops->int_ack(phy);
+}
+
+static irqreturn_t mii_phy_irq(int irq, void *data, struct pt_regs *regs)
+{
+	struct mii_if_info *mii = (void *)data;
+	struct phy_info *phy = mii_phy_of(mii);
+
+	mii_phy_irq_ack(mii);
+
+	/* Schedule the bottom half */
+	schedule_work(&phy->delta.tq);
+
+	return IRQ_HANDLED;
+}
+
+EXPORT_SYMBOL(mii_phy_irq_enable);
+int mii_phy_irq_enable(struct mii_if_info *mii, int irq, void (*func) (void *),
+		       void *data)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+	int err;
+
+	if (phy == NULL)
+		return -EINVAL;
+
+	if (phy->delta.data != NULL)
+		return -EBUSY;
+
+	if (phy->ops->int_ack == NULL ||
+	    phy->ops->int_enable == NULL ||
+	    phy->ops->int_disable == NULL)
+		return -EINVAL;
+
+	phy->delta.irq = irq;
+	phy->delta.func = func;
+	phy->delta.data = data;
+
+	err = request_irq(irq, mii_phy_irq, SA_SHIRQ, phy->name, mii);
+	if (err < 0) {
+		phy->delta.irq = -1;
+		phy->delta.func = NULL;
+		phy->delta.data = NULL;
+		return err;
+	}
+
+	phy->ops->int_enable(phy);
+	return 0;
+}
+
+EXPORT_SYMBOL(mii_phy_irq_disable);
+void mii_phy_irq_disable(struct mii_if_info *mii, void *data)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+
+	if (phy == NULL || phy->delta.data != data)
+		return;
+
+	phy->ops->int_disable(phy);
+
+	free_irq(phy->delta.irq, mii);
+	phy->delta.irq = -1;
+	phy->delta.func = NULL;
+	phy->delta.data = NULL;
+}
+
+/* Scheduled by the task queue */
+static void mii_phy_delta(void *data)
+{
+	struct mii_if_info *mii = (void *)data;
+	struct phy_info *phy = mii_phy_of(mii);
+	struct phy_state old;
+
+	old=phy->state;
+
+	phy->ops->poll(phy);
+
+	if (memcmp(&old,&phy->state,sizeof(old)) != 0 && phy->delta.func)
+		phy->delta.func(phy->delta.data);
+}
+
+static void mii_phy_poll(unsigned long data)
+{
+	struct mii_if_info *mii = (void *)data;
+	struct phy_info *phy = mii_phy_of(mii);
+
+	schedule_work(&phy->delta.tq);
+
+	mod_timer(&phy->delta.timer, jiffies + HZ * phy->delta.msecs / 1000);
+}
+
+EXPORT_SYMBOL(mii_phy_poll_enable);
+int mii_phy_poll_enable(struct mii_if_info *mii, unsigned long msecs,
+			void (*func) (void *), void *data)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+
+	if (phy == NULL)
+		return -EINVAL;
+
+	if (HZ * msecs / 1000 <= 0 || func == NULL)
+		return -EINVAL;
+
+	if (phy->delta.data != NULL)
+		return -EINVAL;
+
+	init_timer(&phy->delta.timer);
+	phy->delta.timer.function = mii_phy_poll;
+	phy->delta.timer.data = (unsigned long)mii;
+	phy->delta.data = data;
+	phy->delta.func = func;
+	phy->delta.msecs = msecs;
+	mod_timer(&phy->delta.timer, jiffies + HZ * msecs / 1000);
+	schedule_work(&phy->delta.tq);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mii_phy_poll_disable);
+void mii_phy_poll_disable(struct mii_if_info *mii, void *data)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+
+	if (phy == NULL || phy->delta.data == NULL)
+		return;
+
+	del_timer_sync(&phy->delta.timer);
+	phy->delta.func = NULL;
+	phy->delta.data = NULL;
+}
+
+EXPORT_SYMBOL(phy_gen_set_autoneg);
+int phy_gen_set_autoneg(struct phy_info *phy, uint32_t advertise)
+{
+	uint16_t adv, ctl;
+
+	adv = phy_read(phy, MII_ADVERTISE);
+	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+	if (advertise & ADVERTISED_10baseT_Half)
+		adv |= ADVERTISE_10HALF;
+	if (advertise & ADVERTISED_10baseT_Full)
+		adv |= ADVERTISE_10FULL;
+	if (advertise & ADVERTISED_100baseT_Half)
+		adv |= ADVERTISE_100HALF;
+	if (advertise & ADVERTISED_100baseT_Full)
+		adv |= ADVERTISE_100FULL;
+
+	/* Configure some basic stuff */
+	phy_write(phy, MII_ADVERTISE, adv);
+
+	/* Start auto negotiation */
+	ctl = phy_read(phy, MII_BMCR);
+	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+	phy_write(phy, MII_BMCR, ctl);
+
+	return 0;
+}
+
+
+EXPORT_SYMBOL(mii_phy_attach);
+int mii_phy_attach(struct mii_if_info *mii, struct net_device *dev, int bus,
+		   int id)
+{
+	struct phy_info *phy, *info;
+
+	if (mii_bus[bus] == NULL) {
+		printk(KERN_ERR
+		       "mii_phy_attach: Can't attach %s, no MII bus %d present\n",
+		       dev->name, bus);
+		return -ENODEV;
+	}
+
+	if (mii_bus[bus]->phy[id] != NULL) {
+		printk(KERN_ERR
+		       "mii_phy_attach: phy%d.%d is already attached to %s\n",
+		       bus, id, dev->name);
+		return -EBUSY;
+	}
+
+	info = mii_phy_get_info(bus, id);
+	if (info == NULL)
+		return -ENODEV;
+
+	phy = kmalloc(sizeof(*phy), GFP_KERNEL);
+	if (phy == NULL)
+		return -ENOMEM;
+
+	memcpy(phy, info, sizeof(*phy));
+
+	INIT_WORK(&phy->delta.tq, mii_phy_delta, mii);
+	snprintf(&phy->name[0],sizeof(phy->name),"phy%d.%d",bus,id);
+	phy->phy_id = MII_PHY_ID(bus, id);
+	phy->delta.func = NULL;
+	phy->delta.data = NULL;
+	phy->delta.irq = -1;
+	phy->state.link = 0;
+	phy->state.duplex = DUPLEX_HALF;
+	phy->state.speed = SPEED_10;
+	phy->negotiate.autoneg = 0;
+	phy->negotiate.advertise = 0;
+
+	memset(mii, 0, sizeof(*mii));
+	mii->phy_id = (bus << 5) | id;
+	mii->phy_id_mask = 0xff;
+	mii->reg_num_mask = 0x1f;
+	mii->dev = dev;
+	mii->mdio_read = mdio_read;
+	mii->mdio_write = mdio_write;
+
+	mii_bus[bus]->phy[id] = phy;
+
+	if (phy->ops->init != NULL)
+		phy->ops->init(phy);
+	return 0;
+}
+
+EXPORT_SYMBOL(mii_phy_detach);
+void mii_phy_detach(struct mii_if_info *mii)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+	struct mii_bus *pbus;
+
+	if (phy == NULL)
+		return;
+
+	pbus = mii_bus[MII_BUS(phy->phy_id)];
+
+	if (phy->delta.data != NULL) {
+		if (phy->delta.irq < 0)
+			mii_phy_poll_disable(mii, phy->delta.data);
+		else
+			mii_phy_irq_disable(mii, phy->delta.data);
+	}
+
+	pbus->phy[MII_ID(phy->phy_id)] = NULL;
+	kfree(phy);
+}
+
+EXPORT_SYMBOL(mii_phy_state);
+int mii_phy_state(struct mii_if_info *mii, struct phy_state *state)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+	int err = 0;
+
+	if (phy == NULL)
+		return -EINVAL;
+
+	if (phy->delta.func == NULL)
+		err = phy->ops->poll(phy);
+
+	memcpy(state, &phy->state, sizeof(*state));
+
+	return err;
+}
+
+EXPORT_SYMBOL(mii_phy_set_autoneg);
+int mii_phy_set_autoneg(struct mii_if_info *mii, uint32_t advertise)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+
+	if (phy == NULL || phy->ops->set_autoneg == NULL)
+		return -EINVAL;
+
+	return phy_set_autoneg(phy, advertise);
+}
+
+EXPORT_SYMBOL(mii_phy_set_forced);
+int mii_phy_set_forced(struct mii_if_info *mii, int speed, int duplex)
+{
+	struct phy_info *phy = mii_phy_of(mii);
+	int err = 0;
+
+	if (phy == NULL)
+		return -EINVAL;
+
+	if (phy->ops->set_forced)
+		err = phy->ops->set_forced(phy, speed, duplex);
+
+	if (err < 0)
+		return err;
+
+	phy->negotiate.autoneg = AUTONEG_DISABLE;
+	phy->state.speed = speed;
+	phy->state.duplex = duplex;
+	phy->state.autoneg = 0;
+
+	return 0;
+}
+
+static DECLARE_MUTEX(mii_bus_lock);
+
+EXPORT_SYMBOL(mii_bus_register);
+int mii_bus_register(struct mii_bus *bus)
+{
+	int bus_id;
+
+	if (bus == NULL || bus->name == NULL || bus->read == NULL ||
+	    bus->write == NULL)
+		return -EINVAL;
+
+	down(&mii_bus_lock);
+
+	for (bus_id = 0; bus_id < MII_BUS_MAX; bus_id++) {
+		if (mii_bus[bus_id] == NULL)
+			break;
+	}
+
+	if (bus_id >= MII_BUS_MAX) {
+		bus_id = -ENOMEM;
+		goto end;
+	}
+
+	mii_bus[bus_id] = bus;
+
+	if (bus->reset)
+		bus->reset(bus->priv);
+
+	printk(KERN_INFO "%s: registered as PHY bus %d\n", bus->name, bus_id);
+
+      end:
+	up(&mii_bus_lock);
+
+	return bus_id;
+}
+
+EXPORT_SYMBOL(mii_bus_unregister);
+void mii_bus_unregister(struct mii_bus *bus)
+{
+	int i;
+
+	down(&mii_bus_lock);
+
+	for (i = 0; i < MII_BUS_MAX; i++) {
+		if (mii_bus[i] == bus) {
+			mii_bus[i] = NULL;
+			break;
+		}
+	}
+
+	up(&mii_bus_lock);
+}
+
+/* Insert into 'phy_list' sorted by
+ * shift (smallest to largest)
+ */
+EXPORT_SYMBOL(phy_register);
+int phy_register(struct phy_info *info)
+{
+	struct list_head *lp;
+
+	if (info==NULL || info->ops == NULL || info->ops->poll == NULL)
+		return -EINVAL;
+
+	list_for_each(lp, &phy_list) {
+		struct phy_info *phy = list_entry(lp, struct phy_info, list);
+		if (phy->shift > info->shift)
+			break;
+
+		/* Check for duplicates */
+		if ((phy->shift==info->shift) && (info->id == phy->id))
+			return -EBUSY;
+	}
+
+	/* This does the 'right thing' even if lp == &phy_list
+	 */
+	list_add_tail(&info->list, lp);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(phy_unregister);
+void phy_unregister(struct phy_info *info)
+{
+	list_del_init(&info->list);
+}
+
+static int mii_bus_init(void)
+{
+	return phy_register(&phy_info_generic);
+}
+
+static void mii_bus_exit(void)
+{
+	phy_unregister(&phy_info_generic);
+}
+
+module_init(mii_bus_init);
+module_exit(mii_bus_exit);

--- /dev/null
+++ linux/drivers/net/phy_cicada.c
@@ -0,0 +1,177 @@
+/* 
+ * drivers/net/phy_cicada.c
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii_bus.h>
+
+/* Cicada Auxiliary Control/Status Register */
+#define MIIM_CIS8201_AUX_CONSTAT        0x1c
+#define MIIM_CIS8201_AUXCONSTAT_INIT    0x0004
+#define MIIM_CIS8201_AUXCONSTAT_DUPLEX  0x0020
+#define MIIM_CIS8201_AUXCONSTAT_SPEED   0x0018
+#define MIIM_CIS8201_AUXCONSTAT_GBIT    0x0010
+#define MIIM_CIS8201_AUXCONSTAT_100     0x0008
+                                                                                
+/* Cicada Extended Control Register 1 */
+#define MIIM_CIS8201_EXT_CON1           0x17
+#define MIIM_CIS8201_EXTCON1_INIT       0x0000
+
+/* CIS8201 */
+#define MII_CIS8201_EPCR        0x17
+#define EPCR_MODE_MASK          0x3000
+#define EPCR_GMII_MODE          0x0000
+#define EPCR_RGMII_MODE         0x1000
+#define EPCR_TBI_MODE           0x2000
+#define EPCR_RTBI_MODE          0x3000
+
+static int cis8201_init(struct phy_info *phy)
+{
+	uint16_t epcr;
+	const char *mode = "Unknown";
+
+	epcr = phy_read(phy, MII_CIS8201_EPCR);
+
+	switch (epcr & EPCR_MODE_MASK) {
+		case EPCR_GMII_MODE: mode = "GMII"; break;
+		case EPCR_RGMII_MODE: mode = "RGMII"; break;
+		case EPCR_TBI_MODE: mode = "TBI"; break;
+		case EPCR_RTBI_MODE: mode = "RTBI"; break;
+	}
+
+	printk(KERN_INFO "%s: %s mode\n",phy->name, mode);
+
+	return 0;
+}
+
+#define MII_CIS8201_INTR_CTRL	0x19
+#define MII_CIS8201_INTR_STAT	0x1a
+
+#define MII_CIS8201_INTR_ENABLE	0x8000
+#define MII_CIS8201_INTR_SPEED	0x4000
+#define MII_CIS8201_INTR_LINK	0x2000
+#define MII_CIS8201_INTR_DUPLEX	0x1000
+#define MII_CIS8201_INTR_AN_ERR	0x0800
+#define MII_CIS8201_INTR_AN_DON	0x0400
+#define MII_CIS8201_INTR_ALL	0x7c00
+
+static int cis8201_int_enable(struct phy_info *phy)
+{
+	phy_write(phy, MII_CIS8201_INTR_CTRL, MII_CIS8201_INTR_ENABLE | MII_CIS8201_INTR_ALL);
+
+	return 0;
+}
+
+static int cis8201_int_disable(struct phy_info *phy)
+{
+	phy_write(phy, MII_CIS8201_INTR_CTRL, 0);
+
+	return 0;
+}
+
+static int cis8201_int_ack(struct phy_info *phy)
+{
+	phy_read(phy, MII_CIS8201_INTR_STAT);
+
+	return 0;
+}
+
+#define	MII_CIS8201_ACSR	0x1c
+#define  ACSR_ENABLE_1000BASET	0x0004
+#define  ACSR_DUPLEX_STATUS	0x0020
+#define  ACSR_SPEED_1000BASET	0x0010
+#define  ACSR_SPEED_100BASET	0x0008
+
+static int cis8201_poll(struct phy_info *phy)
+{
+	uint16_t acsr;
+	struct phy_state *pstate = &phy->state;
+	int autoneg = phy->state.autoneg;
+	int err;
+
+	err = phy_gen_poll(phy);
+	if (err < 0)
+		return err;
+
+	if (pstate->link == 0)
+		return 0;
+
+	/* We use the old copy of 'phy->state.autoneg'
+	 * as phy_gen_poll will have set it to 0
+	 */
+	if (autoneg) {
+		acsr = phy_read(phy, MII_CIS8201_ACSR);
+
+		if (acsr & ACSR_DUPLEX_STATUS)
+			pstate->duplex = DUPLEX_FULL;
+		else
+			pstate->duplex = DUPLEX_HALF;
+		if (acsr & ACSR_SPEED_1000BASET) {
+			pstate->speed = SPEED_1000;
+		} else if (acsr & ACSR_SPEED_100BASET)
+			pstate->speed = SPEED_100;
+		else
+			pstate->speed = SPEED_10;
+	}
+
+	/* On non-aneg, we assume what we put in BMCR is the speed,
+	 * though magic-aneg shouldn't prevent this case from occurring
+	 */
+
+	return 0;
+}
+
+static int cis8201_set_autoneg(struct phy_info *phy, uint32_t advertise)
+{
+	uint16_t val;
+
+	/* Do the 1000BT setup here. */
+	val = phy_read(phy, MII_CIS8201_ACSR);
+	if (advertise & ADVERTISED_1000baseT_Full)
+		phy_write(phy, MII_CIS8201_ACSR, val | ACSR_ENABLE_1000BASET);
+	else
+		phy_write(phy, MII_CIS8201_ACSR, val & ~ACSR_ENABLE_1000BASET);
+
+	return phy_gen_set_autoneg(phy, advertise);
+}
+
+
+struct phy_ops phy_ops_cis8201 = {
+	.init 		= cis8201_init,
+	.set_autoneg 	= cis8201_set_autoneg,
+	.poll		= cis8201_poll,
+	.int_enable	= cis8201_int_enable,
+	.int_disable	= cis8201_int_disable,
+	.int_ack	= cis8201_int_ack
+};
+
+/* Cicada 8201 */
+static struct phy_info phy_info_cis8201 = {
+	.id = 0x000fc440,
+	.name = "CIS8201",
+	.shift = 4,
+	.ops = &phy_ops_cis8201
+};
+
+static int phy_cicada_init(void)
+{
+	return phy_register(&phy_info_cis8201);
+}
+
+static void phy_cicada_exit(void)
+{
+	phy_unregister(&phy_info_cis8201);
+}
+
+module_init(phy_cicada_init);
+module_exit(phy_cicada_exit);

--- /dev/null
+++ linux/drivers/net/phy_davicom.c
@@ -0,0 +1,140 @@
+/* 
+ * drivers/net/phy_davicom.c
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mii_bus.h>
+
+/* DM9161 Control register values */
+#define MIIM_DM9161_CR_STOP	0x0400
+#define MIIM_DM9161_CR_RSTAN	0x1200
+
+#define MIIM_DM9161_SCR		0x10
+#define MIIM_DM9161_SCR_INIT	0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MIIM_DM9161_SCSR	0x11
+#define MIIM_DM9161_SCSR_100F	0x8000
+#define MIIM_DM9161_SCSR_100H	0x4000
+#define MIIM_DM9161_SCSR_10F	0x2000
+#define MIIM_DM9161_SCSR_10H	0x1000
+
+/* DM9161 Interrupt Register */
+#define MIIM_DM9161_INTR	0x15
+#define MIIM_DM9161_INTR_PEND		0x8000
+#define MIIM_DM9161_INTR_DPLX_MASK	0x0800
+#define MIIM_DM9161_INTR_SPD_MASK	0x0400
+#define MIIM_DM9161_INTR_LINK_MASK	0x0200
+#define MIIM_DM9161_INTR_MASK		0x0100
+#define MIIM_DM9161_INTR_DPLX_CHANGE	0x0010
+#define MIIM_DM9161_INTR_SPD_CHANGE	0x0008
+#define MIIM_DM9161_INTR_LINK_CHANGE	0x0004
+#define MIIM_DM9161_INTR_INIT 		0x0000
+#define MIIM_DM9161_INTR_STOP	\
+(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
+ | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MIIM_DM9161_10BTCSR	0x12
+#define MIIM_DM9161_10BTCSR_INIT	0x7800
+
+static int dm9161_init(struct phy_info *phy)
+{
+	mdelay(2000);
+
+	phy_write(phy, MII_BMCR, MIIM_DM9161_CR_STOP);
+	phy_write(phy, MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT);
+	phy_write(phy, MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT);
+
+	return 0;
+}
+
+static int dm9161_int_enable(struct phy_info *phy)
+{
+	/* Clear any pending interrupts */
+	phy_read(phy, MIIM_DM9161_INTR);
+
+	return 0;
+}
+
+static int dm9161_int_ack(struct phy_info *phy)
+{
+	/* Clear any pending interrupts */
+	phy_read(phy, MIIM_DM9161_INTR);
+
+	return 0;
+}
+
+static int dm9161_int_disable(struct phy_info *phy)
+{
+	/* Clear any pending interrupts */
+	phy_read(phy, MIIM_DM9161_INTR);
+
+	return 0;
+}
+
+static int dm9161_poll(struct phy_info *phy)
+{
+	int autoneg = phy->state.autoneg;
+	int err;
+	uint16_t val;
+
+	err = phy_gen_poll(phy);
+	if (err < 0)
+		return err;
+
+	if (phy->state.link && autoneg) {
+		val = phy_read(phy, MIIM_DM9161_SCSR);
+
+		if (val & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
+			phy->state.speed = 100;
+		else
+			phy->state.speed = 10;
+
+		if (val & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
+			phy->state.duplex = 1;
+		else
+			phy->state.duplex = 0;
+	}
+
+	return 0;
+}
+
+static struct phy_ops phy_ops_dm9161 = {
+	.init = dm9161_init,
+	.poll = dm9161_poll,
+	.int_enable = dm9161_int_enable,
+	.int_ack = dm9161_int_ack,
+	.int_disable = dm9161_int_disable,
+};
+
+static struct phy_info phy_info_dm9161 = {
+	.id = 0x0181b880,
+	.name = "Davicom DM9161E",
+	.shift = 4,
+	.ops = &phy_ops_dm9161,
+};
+
+static int phy_davicom_init(void)
+{
+	return phy_register(&phy_info_dm9161);
+}
+
+static void phy_davicom_exit(void)
+{
+	phy_unregister(&phy_info_dm9161);
+}
+
+module_init(phy_davicom_init);
+module_exit(phy_davicom_exit);

--- /dev/null
+++ linux/drivers/net/phy_lxt97x.c
@@ -0,0 +1,210 @@
+/* 
+ * drivers/net/phy_lxt97x.c
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii_bus.h>
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT970 is used by many boards				     */
+
+#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      */
+
+static int lxt970_int_enable(struct phy_info *phy)
+{
+	phy_write(phy, MII_LXT970_IER, 0x0002);
+
+	return 0;
+};
+
+static int lxt970_int_ack(struct phy_info *phy)
+{
+	phy_read(phy, MII_LXT970_ISR);
+
+	return 0;
+}
+
+static int lxt970_int_disable(struct phy_info *phy)
+{
+	phy_write(phy, MII_LXT970_IER, 0x0000);
+
+	return 0;
+};
+
+static int lxt970_poll(struct phy_info *phy)
+{
+	int autoneg = phy->state.autoneg;
+	int err;
+
+	err = phy_gen_poll(phy);
+	if (err < 0)
+		return err;
+
+	if (phy->state.link && autoneg) {
+		/* find out the current state */
+		uint16_t val;
+
+		val = phy_read(phy, MII_LXT970_CSR);
+		if (val & 0x1000)
+			phy->state.duplex = 1;
+		else
+			phy->state.duplex = 0;
+
+		if (val & 0x0800) {
+			phy->state.speed = 100;
+		} else {
+			phy->state.speed = 10;
+		}
+	}
+
+	return 0;
+}
+
+
+static struct phy_ops phy_ops_lxt970 = {
+	.poll 		= lxt970_poll,
+	.int_enable	= lxt970_int_enable,
+	.int_ack	= lxt970_int_ack,
+	.int_disable	= lxt970_int_disable
+};
+
+static struct phy_info phy_info_lxt970 = {
+	.id = 0x07810000,
+	.shift = 4,
+	.name = "LXT970",
+	.ops = &phy_ops_lxt970,
+};
+
+/* Same as the LXT970, but different ID
+ */
+static struct phy_info phy_info_lxt970a = {
+	.id = 0x00810000,
+	.shift = 4,
+	.name = "LXT970A",
+	.ops = &phy_ops_lxt970,
+};
+
+/* register definitions for the 971 */
+
+#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 */
+
+static int lxt971_int_enable(struct phy_info *phy)
+{
+	phy_write(phy, MII_LXT971_IER, 0x00f2);
+
+	return 0;
+}
+
+static int lxt971_poll(struct phy_info *phy)
+{
+	int autoneg = phy->state.autoneg;
+	int err;
+
+	err = phy_gen_poll(phy);
+	if (err < 0)
+		return err;
+
+	if (phy->state.link && autoneg) {
+		/* find out the current state */
+		uint16_t val;
+
+		val = phy_read(phy, MII_LXT971_SR2);
+
+		if (val & 0x4000) {
+			phy->state.speed = 100;
+		} else {
+			phy->state.speed = 10;
+		}
+
+		if (val & 0x0200) {
+			phy->state.duplex = 1;
+		} else {
+			phy->state.duplex = 0;
+		}
+
+		if (val & 0x0008)
+			printk(KERN_DEBUG "%s: Fault detected\n",phy->name);
+	}
+
+	return 0;
+}
+
+static int lxt971_int_ack(struct phy_info *phy)
+{
+	phy_read(phy, MII_LXT971_ISR);
+
+	return 0;
+}
+
+static int lxt971_int_disable(struct phy_info *phy)
+{
+	phy_write(phy, MII_LXT971_IER, 0x0000);
+
+	return 0;
+};
+
+static struct phy_ops phy_ops_lxt971 = {
+	.poll 		= lxt971_poll,
+	.int_enable	= lxt971_int_enable,
+	.int_ack	= lxt971_int_ack,
+	.int_disable	= lxt971_int_disable,
+};
+
+static struct phy_info phy_info_lxt971 = {
+	.id = 0x001378e0,
+	.shift = 4,
+	.name = "LXT971",
+	.ops = &phy_ops_lxt971,
+};
+
+static int phy_lxt97x_init(void)
+{
+	int err;
+
+	err=phy_register(&phy_info_lxt970);
+	if (err)
+		return err;
+
+	err=phy_register(&phy_info_lxt970a);
+	if (err) {
+		phy_unregister(&phy_info_lxt970);
+		return err;
+	}
+
+	err=phy_register(&phy_info_lxt971);
+	if (err) {
+		phy_unregister(&phy_info_lxt970);
+		phy_unregister(&phy_info_lxt970a);
+	}
+
+	return err;
+}
+
+static void phy_lxt97x_exit(void)
+{
+	phy_unregister(&phy_info_lxt971);
+	phy_unregister(&phy_info_lxt970a);
+	phy_unregister(&phy_info_lxt970);
+}
+
+module_init(phy_lxt97x_init);
+module_exit(phy_lxt97x_exit);

--- /dev/null
+++ linux/drivers/net/phy_marvell.c
@@ -0,0 +1,125 @@
+/* 
+ * drivers/net/phy_marvell.c
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii_bus.h>
+
+/* 88E1011 PHY Status Register */
+#define MIIM_88E1011_PHY_STATUS         0x11
+#define MIIM_88E1011_PHYSTAT_SPEED      0xc000
+#define MIIM_88E1011_PHYSTAT_GBIT       0x8000
+#define MIIM_88E1011_PHYSTAT_100        0x4000
+#define MIIM_88E1011_PHYSTAT_DUPLEX     0x2000
+#define MIIM_88E1011_PHYSTAT_LINK	0x0400
+
+#define MIIM_88E1011_IEVENT		0x13
+#define MIIM_88E1011_IEVENT_CLEAR	0x0000
+
+#define MIIM_88E1011_IMASK		0x12
+#define MIIM_88E1011_IMASK_INIT		0x6400
+#define MIIM_88E1011_IMASK_CLEAR	0x0000
+
+static int marvell_int_enable(struct phy_info *phy)
+{
+	/* Clear the IEVENT register */
+	phy_read(phy, MIIM_88E1011_IEVENT);
+
+	/* Set up the mask */
+	phy_write(phy, MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT);
+
+	return 0;
+}
+
+static int marvell_int_ack(struct phy_info *phy)
+{
+	/* Clear the interrupt */
+	phy_read(phy, MIIM_88E1011_IEVENT);
+
+	return 0;
+}
+
+static int marvell_int_disable(struct phy_info *phy)
+{
+	/* Clear the interrupt */
+	phy_read(phy, MIIM_88E1011_IEVENT);
+	/* Disable Interrupts */
+	phy_write(phy, MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR);
+
+	return 0;
+}
+
+static int marvell_poll(struct phy_info *phy)
+{
+	int autoneg = phy->state.autoneg;
+	int err;
+
+	err = phy_gen_poll(phy);
+	if (err < 0)
+		return err;
+
+	if (phy->state.link && autoneg) {
+		uint16_t val;
+		unsigned int speed;
+
+		val = phy_read(phy, MIIM_88E1011_PHY_STATUS);
+
+		if (val & MIIM_88E1011_PHYSTAT_DUPLEX)
+			phy->state.duplex = 1;
+		else
+			phy->state.duplex = 0;
+
+		speed = (val & MIIM_88E1011_PHYSTAT_SPEED);
+
+		switch (speed) {
+			case MIIM_88E1011_PHYSTAT_GBIT:
+				phy->state.speed = 1000;
+				break;
+			case MIIM_88E1011_PHYSTAT_100:
+				phy->state.speed = 100;
+				break;
+			default:
+				phy->state.speed = 10;
+				break;
+		}
+	}
+
+	return 0;
+}
+
+static struct phy_ops phy_ops_marvell = {
+	.poll		= marvell_poll,
+	.int_enable	= marvell_int_enable,
+	.int_ack	= marvell_int_ack,
+	.int_disable	= marvell_int_disable,
+};
+
+static struct phy_info phy_info_M88E1011S = {
+	.id = 0x01410c60,
+	.name = "Marvell 88E1011S",
+	.shift = 4,
+	.ops = &phy_ops_marvell,
+};
+
+static int phy_marvell_init(void)
+{
+	return phy_register(&phy_info_M88E1011S);
+}
+
+static void phy_marvell_exit(void)
+{
+	phy_unregister(&phy_info_M88E1011S);
+}
+
+module_init(phy_marvell_init);
+module_exit(phy_marvell_exit);

--- /dev/null
+++ linux/include/linux/mii_bus.h
@@ -0,0 +1,191 @@
+/* 
+ * include/linux/mii_bus.h
+ *
+ * Author: Jason McMullan
+ *
+ * Copyright (c) 2004 Timesys Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef __MII_BUS_H
+#define __MII_BUS_H
+
+#ifdef __KERNEL__
+
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+
+#define MII_TIMEOUT 	(2*HZ)
+
+#define miim_end (-2)
+#define miim_read (-1)
+
+/* Macros for 'phy_id's used elsewhere.
+ * A PHY ID is 3 bits of bus, followed by 5 bits of id
+ */
+#define MII_BUS(phy_id)		(((phy_id) >> 5) & 0x7)
+#define MII_ID(phy_id)		((phy_id) & 0x1f)
+#define MII_PHY_ID(bus, id)	((((bus) & 0x7) << 5) | ((id) & 0x1f))
+
+/*
+ * Current PHY state
+ */
+struct phy_state {
+	unsigned int link:1;
+	unsigned int duplex:1;
+	unsigned int autoneg:1;
+	unsigned int loopback:1;
+	unsigned int speed:28;
+};
+
+/* PHY operations - borrowed from sungem_phy.h
+ *
+ * wol_options:	See WAKE_* in include/linux/ethtool.h
+ * advertise  : See ADVERTISED_* in include/linux/ethtool.h
+ */
+struct phy_info;
+
+struct phy_ops {
+	int	(*init)(struct phy_info *phy);
+	int	(*suspend)(struct phy_info *phy, uint32_t wol_options);
+	int	(*set_autoneg)(struct phy_info *phy, uint32_t advertise);
+	int	(*set_forced)(struct phy_info *phy, int speed, int duplex);
+
+	/* Polling */
+	int	(*poll)(struct phy_info *phy);
+
+	/* Interrupt-based */
+	int	(*int_enable)(struct phy_info *phy);
+	int	(*int_ack)(struct phy_info *phy);
+	int	(*int_disable)(struct phy_info *phy);
+};
+
+/* struct phy_info: a structure which defines attributes for a PHY
+ *
+ * id will contain a number which represents the PHY.  During
+ * startup, the driver will poll the PHY to find out what its
+ * UID--as defined by registers 2 and 3--is.  The 32-bit result
+ * gotten from the PHY will be shifted right by "shift" bits to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ * The struct phy_cmd entries represent pointers to an arrays of
+ * commands which tell the driver what to do to the PHY.
+ */
+struct phy_info {
+	struct list_head list;
+
+	uint32_t id;
+	char name[32];
+	unsigned int shift;
+
+	struct phy_ops *ops;
+
+	/* Per-PHY driver data goes here */
+	void *priv;
+
+	/* Your poll() routine should modify this.
+	 */
+	struct phy_state state;
+
+	/* Everything from here on down will
+	 * be filled in during registration
+	 */
+	int phy_id;
+
+	struct {
+		int irq;
+		unsigned long msecs;
+		void (*func) (void *data);
+		void *data;
+		struct work_struct tq;
+		struct timer_list timer;
+	} delta;
+
+	struct {
+		int autoneg;		/* 1=auto, 0=forced */
+		uint32_t advertise;	/* mask to allow */
+		unsigned long timeout;	/* jiffie stamp */
+	} negotiate;
+
+};
+
+struct mii_bus {
+	const char *name;
+	void *priv;
+	int (*read) (void *priv, int phy_id, int location);
+	int (*write) (void *priv, int phy_id, int location, uint16_t val);
+	void (*reset) (void *priv);
+
+	/* Auto-filled in values */
+	struct phy_info *phy[32];
+};
+
+/* MII bus registration
+ */
+extern int mii_bus_register(struct mii_bus *bus);
+extern void mii_bus_unregister(struct mii_bus *bus);
+
+/* Raw read/write routines
+ * Returns a 16-bit register value, or < 0 error code
+ */
+extern int mii_bus_read(int bus_id, int phy_id, int reg);
+extern int mii_bus_write(int bus_id, int phy_id, int reg, uint16_t val);
+
+/* Routines used by network devices that use the MII bus
+ */
+extern int mii_phy_attach(struct mii_if_info *mii, struct net_device *dev,
+			  int phy_bus, int phy_id);
+extern void mii_phy_detach(struct mii_if_info *mii);
+
+/* Read current phy state
+ */
+extern int mii_phy_state(struct mii_if_info *mii, struct phy_state *state);
+
+/* Reset MII, renegotiate link
+ */
+extern int mii_phy_set_autoneg(struct mii_if_info *mii, uint32_t advertise);
+extern int mii_phy_set_forced(struct mii_if_info *mii, int speed, int duplex);
+extern int mii_phy_suspend(struct mii_if_info *mii, uint32_t wol_options);
+
+/* Use an IRQ to determine when the PHY changes
+ */
+extern int mii_phy_irq_enable(struct mii_if_info *mii, int irq,
+			      void (*func) (void *), void *data);
+extern void mii_phy_irq_disable(struct mii_if_info *mii, void *data);
+
+/* Poll the PHY
+ */
+extern int mii_phy_poll_enable(struct mii_if_info *mii, unsigned long msecs,
+			       void (*func) (void *), void *data);
+extern void mii_phy_poll_disable(struct mii_if_info *mii, void *data);
+
+/*
+ * PHY device registration
+ */
+extern int phy_register(struct phy_info *phy);
+extern void phy_unregister(struct phy_info *phy);
+
+static inline int phy_read(struct phy_info *phy, int regnum)
+{
+	return mii_bus_read(MII_BUS(phy->phy_id), MII_ID(phy->phy_id), regnum);
+}
+
+static inline int phy_write(struct phy_info *phy, int reg, uint16_t val)
+{
+	return mii_bus_write(MII_BUS(phy->phy_id), MII_ID(phy->phy_id), reg, val);	
+}
+
+/* Generic 'struct phy_ops' device routines */
+extern int phy_gen_set_autoneg(struct phy_info *phy, u32 advertise);
+extern int phy_gen_poll(struct phy_info *phy);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MII_BUS_H */


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2004-12-02 18:29 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <069B6F33-341C-11D9-9652-000393DBC2E8@freescale.com>
2004-11-18 17:52 ` [PATCH] MII bus API for PHY devices Andy Fleming
2004-11-18 19:34   ` Jason McMullan
2004-11-18 19:50     ` Andy Fleming
2004-11-18 21:00       ` Jason McMullan
2004-11-18 23:26   ` Benjamin Herrenschmidt
2004-11-19 16:41     ` Jason McMullan
2004-11-19 21:18     ` Andy Fleming
2004-11-19 22:43       ` Benjamin Herrenschmidt
2004-11-20  0:04         ` Andy Fleming
2004-11-23 18:18           ` Jason McMullan
2004-12-02 18:29           ` [PATCH] MII bus API for PHY devices Rev 2.0 Jason McMullan
2004-11-19 20:18 [PATCH] MII bus API for PHY devices Manfred Spraul
2004-11-19 21:01 ` Andy Fleming

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).