All of lore.kernel.org
 help / color / mirror / Atom feed
* Linux-2.6.9 - MPC8260 FCC/MDIO improvements (I hope !)
@ 2004-11-09 16:04 miguel.valero
  0 siblings, 0 replies; only message in thread
From: miguel.valero @ 2004-11-09 16:04 UTC (permalink / raw)
  To: trini; +Cc: linuxppc-embedded

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

I send it again, this time to the right list address, hopefuly.

Miguel

----- Forwarded by Miguel Valero/AxxessIT on 09.11.04 16:55 -----


Miguel Valero
28.10.04 14:11

 
        To:     paulus@samba.org, trini@kernel.crashing.org
        cc:     linuxppc-dev@lists.linuxppc.org, linuxppc-embedded@lists.linuxppc.org, 
Morten Banzon/AxxessIT@AXXESSIT
        Subject:        Linux-2.6.9 - MPC8260 FCC/MDIO improvements (I hope !)

Paul and Tom,
I would like to make my first contribution to the linux kernel. Find patch 
linux-2.6.9-fcc-mdio.patch enclosed.

It is not sure that I chose the best possible way to introduce (what I 
think are) my improvements, and I will below try to explain what I have 
done.

The headlines are:

1.- I have removed platform dependent code from fcc_enet.c and replaced it 
by CONFIG_ definitions that I have added to the arch/ppc/8260_io/Kconfig 
file

Basically, the configurable parameters that I have introduced are 
-> For each enabled FCCx_ENET, which internal clocks out of the possible 
ones are used for TX and RX

2.- I have added flexibility to place the MDIO and MDCK lines and the PHY 
interrupt line at will, also as CONFIG_ in the same Kconfig (in the 
current implementation, MDIO and MDCK could only be on port C)

-> For both, MDIO and MDCK, on which parallell IO port, and pin (may be 
allocated on different IO ports)
-> Whether PHY interrupt is used or not, and if so, on which interrupt 
source
-> Also, in the case of several FCC's being used, it is assumed that all 
PHYs can be managed over the same MDIO/MDCK, and that the PHY addresses 
increase with one for each FCC
-> The PHY interrupt is requested only once (today it would be requested 
once for each FCC in use), and linked to the first FCC device in use

3.- I have connected the FCC device to the generic MII driver, so that one 
can perform ioctl SIOCxMIIxxx

I have tried the mii-tool with success.
What I have not done here is to protect the PHY access against concurrent 
accesses from user space.

4.- I have added default handling of the Marvell 88E6060 low power 6 port 
switch. The management of this ethernet switch is done through MDIO.

5.- I have also added the possibility to configure the PHY address (would 
be for FCC1) manually and disable the autodiscovery.


I have tested that on a custom board with MPC8280.


I hope you guys find this interesting enough as to have it included in the 
kernel. 
However, I was very much in doubt about whether the necessary definitions 
to add the required flexibility actually belong in the platform specific 
header file. Of course, a lot of work is then required to add the 
definitions in many of these files. I guess you will decide what is best.

In addition, I have seen that there has been a major change in linux-2.6.9 
concerning the MPC8260 serial driver, that has been moved to 
drivers/serial/cpm_uart.
I was wondering whether the FCC driver will also be moved soon. If this is 
the case, I would appreciate that the improvements I implemented be 
introduced before the moving !

Best regards
Miguel A. Valero
R & D Engineer
------------------------------------------------------------------------------------------------------
AXXESSIT ASA
PB 219 Økern
Risløkkveien 2 N-0510 Oslo
------------------------------------------------------------------------------------------------------
Tel. +47 69707746
Fax. +47 69707702
Mob. +47 93033827
------------------------------------------------------------------------------------------------------





[-- Attachment #2: linux-2.6.9-fcc-mdio.patch --]
[-- Type: application/octet-stream, Size: 45111 bytes --]

--- linux-2.6.9/arch/ppc/8260_io/fcc_enet.c	2004-10-18 23:54:07.000000000 +0200
+++ linux-2.6.9.axxmetro/arch/ppc/8260_io/fcc_enet.c	2004-10-28 13:16:12.000000000 +0200
@@ -32,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/mii.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 
@@ -57,6 +58,7 @@ typedef struct {
 
 typedef struct {
 	uint id;
+	uint id_mask;
 	char *name;
 
 	const phy_cmd_t *config;
@@ -132,6 +134,15 @@ static struct net_device_stats *fcc_enet
 static void set_multicast_list(struct net_device *dev);
 static void fcc_restart(struct net_device *dev, int duplex);
 static int fcc_enet_set_mac_address(struct net_device *dev, void *addr);
+#ifdef	CONFIG_USE_MDIO
+static int fcc_enet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+#endif	/* CONFIG_USE_MDIO */
+
+#ifdef CONFIG_FCC_88E6060
+#ifndef CONFIG_PHY_ADDRESS
+#error "You need to configure the base PHY address of the 88E6060"
+#endif
+#endif
 
 /* These will be configurable for the FCC choice.
  * Multiple ports can be configured.  There is little choice among the
@@ -158,25 +169,165 @@ static int fcc_enet_set_mac_address(stru
 #define PA1_DIRA0	(PA1_RXDAT | PA1_CRS | PA1_COL | PA1_RXER | PA1_RXDV)
 #define PA1_DIRA1	(PA1_TXDAT | PA1_TXEN | PA1_TXER)
 
-#ifdef CONFIG_SBC82xx
-/* rx is clk9, tx is clk10 */
-#define PC_F1RXCLK     ((uint)0x00000100)
-#define PC_F1TXCLK     ((uint)0x00000200)
-#define CMX1_CLK_ROUTE ((uint)0x25000000)
-#define CMX1_CLK_MASK  ((uint)0xff000000)
-#elif defined(CONFIG_ADS8272)
-#define PC_F1RXCLK	((uint)0x00000400)
-#define PC_F1TXCLK	((uint)0x00000200)
-#define CMX1_CLK_ROUTE	((uint)0x36000000)
-#define CMX1_CLK_MASK	((uint)0xff000000)
-#else /* other boards */
-/* CLK12 is receive, CLK11 is transmit.  These are board specific. */
-#define PC_F1RXCLK	((uint)0x00000800)
-#define PC_F1TXCLK	((uint)0x00000400)
-#define CMX1_CLK_ROUTE	((uint)0x3e000000)
-#define CMX1_CLK_MASK	((uint)0xff000000)
+#if defined(CONFIG_FCC1_ENET)
+#if !defined(CONFIG_FCC1_MII_RX_CLOCK) || !defined(CONFIG_FCC1_MII_TX_CLOCK)
+#error "You have to configure the FCC1 MII clock numbers"
+#endif
+#if (CONFIG_FCC1_MII_RX_CLOCK == CONFIG_FCC1_MII_TX_CLOCK)
+#error "You cannot place the FCC1 MII RX and TX clocks on the same CLK"
+#else
+#if ((CONFIG_FCC1_MII_RX_CLOCK == 9)  || (CONFIG_FCC1_MII_RX_CLOCK == 10) || \
+     (CONFIG_FCC1_MII_RX_CLOCK == 11) || (CONFIG_FCC1_MII_RX_CLOCK == 12))
+#define PC_F1RXCLK     ((uint)(0x00000001 << (CONFIG_FCC1_MII_RX_CLOCK - 1)))
+#else
+#error "You can place the FCC1 MII RX clock only on CLK 9, 10, 11 or 12"
 #endif
 
+#if ((CONFIG_FCC1_MII_TX_CLOCK == 9)  || (CONFIG_FCC1_MII_TX_CLOCK == 10) || \
+     (CONFIG_FCC1_MII_TX_CLOCK == 11) || (CONFIG_FCC1_MII_TX_CLOCK == 12))
+#define PC_F1TXCLK     ((uint)(0x00000001 << (CONFIG_FCC1_MII_TX_CLOCK - 1)))
+#else
+#error "You can place the FCC1 MII TX clock only on CLK 9, 10, 11 or 12"
+#endif
+
+#if (CONFIG_FCC1_MII_RX_CLOCK == 9)
+#define CMX1_RXCLK_ROUTE        ((uint)0x20000000)
+#endif
+#if (CONFIG_FCC1_MII_RX_CLOCK == 10)
+#define CMX1_RXCLK_ROUTE        ((uint)0x28000000)
+#endif
+#if (CONFIG_FCC1_MII_RX_CLOCK == 11)
+#define CMX1_RXCLK_ROUTE        ((uint)0x30000000)
+#endif
+#if (CONFIG_FCC1_MII_RX_CLOCK == 12)
+#define CMX1_RXCLK_ROUTE        ((uint)0x380000000)
+#endif
+
+#if (CONFIG_FCC1_MII_TX_CLOCK == 9)
+#define CMX1_TXCLK_ROUTE        ((uint)0x04000000)
+#endif
+#if (CONFIG_FCC1_MII_TX_CLOCK == 10)
+#define CMX1_TXCLK_ROUTE        ((uint)0x05000000)
+#endif
+#if (CONFIG_FCC1_MII_TX_CLOCK == 11)
+#define CMX1_TXCLK_ROUTE        ((uint)0x06000000)
+#endif
+#if (CONFIG_FCC1_MII_TX_CLOCK == 12)
+#define CMX1_TXCLK_ROUTE        ((uint)0x070000000)
+#endif
+
+#define CMX1_CLK_ROUTE  ((uint)(CMX1_RXCLK_ROUTE | CMX1_TXCLK_ROUTE))
+#define CMX1_CLK_MASK   ((uint)0xff000000)
+
+#endif /* CONFIG_FCC1_MII_RX_CLOCK == CONFIG_FCC1_MII_TX_CLOCK */
+#endif /* CONFIG_FCC1_ENET */
+
+#if defined(CONFIG_FCC2_ENET)
+#if !defined(CONFIG_FCC2_MII_RX_CLOCK) || !defined(CONFIG_FCC2_MII_TX_CLOCK)
+#error "You have to configure the FCC2 MII clock numbers"
+#endif
+#if (CONFIG_FCC2_MII_RX_CLOCK == CONFIG_FCC2_MII_TX_CLOCK)
+#error "You cannot place the FCC2 MII RX and TX clocks on the same CLK"
+#else
+#if ((CONFIG_FCC2_MII_RX_CLOCK == 13) || (CONFIG_FCC2_MII_RX_CLOCK == 14) || \
+     (CONFIG_FCC2_MII_RX_CLOCK == 16) || (CONFIG_FCC2_MII_RX_CLOCK == 16))
+#define PC_F2RXCLK     ((uint)(0x00000001 << (CONFIG_FCC2_MII_RX_CLOCK - 1)))
+#else
+#error "You can place the FCC2 MII RX clock only on CLK 13, 14, 15 or 16"
+#endif
+
+#if ((CONFIG_FCC2_MII_TX_CLOCK == 13) || (CONFIG_FCC2_MII_TX_CLOCK == 14) || \
+     (CONFIG_FCC2_MII_TX_CLOCK == 15) || (CONFIG_FCC2_MII_TX_CLOCK == 16))
+#define PC_F2TXCLK     ((uint)(0x00000001 << (CONFIG_FCC2_MII_TX_CLOCK - 1)))
+#else
+#error "You can place the FCC2 MII TX clock only on CLK 13, 14, 15 or 16"
+#endif
+
+#if (CONFIG_FCC2_MII_RX_CLOCK == 13)
+#define CMX2_RXCLK_ROUTE        ((uint)0x00200000)
+#endif
+#if (CONFIG_FCC2_MII_RX_CLOCK == 14)
+#define CMX2_RXCLK_ROUTE        ((uint)0x00280000)
+#endif
+#if (CONFIG_FCC2_MII_RX_CLOCK == 15)
+#define CMX2_RXCLK_ROUTE        ((uint)0x00300000)
+#endif
+#if (CONFIG_FCC2_MII_RX_CLOCK == 16)
+#define CMX2_RXCLK_ROUTE        ((uint)0x003800000)
+#endif
+
+#if (CONFIG_FCC2_MII_TX_CLOCK == 13)
+#define CMX2_TXCLK_ROUTE        ((uint)0x00040000)
+#endif
+#if (CONFIG_FCC2_MII_TX_CLOCK == 14)
+#define CMX2_TXCLK_ROUTE        ((uint)0x00050000)
+#endif
+#if (CONFIG_FCC2_MII_TX_CLOCK == 15)
+#define CMX2_TXCLK_ROUTE        ((uint)0x00060000)
+#endif
+#if (CONFIG_FCC2_MII_TX_CLOCK == 16)
+#define CMX2_TXCLK_ROUTE        ((uint)0x000700000)
+#endif
+
+#define CMX2_CLK_ROUTE  ((uint)(CMX2_RXCLK_ROUTE | CMX2_TXCLK_ROUTE))
+#define CMX2_CLK_MASK   ((uint)0x00ff0000)
+
+#endif /* CONFIG_FCC2_MII_RX_CLOCK == CONFIG_FCC2_MII_TX_CLOCK */
+#endif /* CONFIG_FCC2_ENET */
+
+#if defined(CONFIG_FCC3_ENET)
+#if !defined(CONFIG_FCC3_MII_RX_CLOCK) || !defined(CONFIG_FCC3_MII_TX_CLOCK)
+#error "You have to configure the FCC3 MII clock numbers"
+#endif
+#if (CONFIG_FCC3_MII_RX_CLOCK == CONFIG_FCC3_MII_TX_CLOCK)
+#error "You cannot place the FCC3 MII RX and TX clocks on the same CLK"
+#else
+#if ((CONFIG_FCC3_MII_RX_CLOCK == 13) || (CONFIG_FCC3_MII_RX_CLOCK == 14) || \
+     (CONFIG_FCC3_MII_RX_CLOCK == 16) || (CONFIG_FCC3_MII_RX_CLOCK == 16))
+#define PC_F3RXCLK     ((uint)(0x00000001 << (CONFIG_FCC3_MII_RX_CLOCK - 1)))
+#else
+#error "You can place the FCC3 MII RX clock only on CLK 13, 14, 15 or 16"
+#endif
+
+#if ((CONFIG_FCC3_MII_TX_CLOCK == 13) || (CONFIG_FCC3_MII_TX_CLOCK == 14) || \
+     (CONFIG_FCC3_MII_TX_CLOCK == 15) || (CONFIG_FCC3_MII_TX_CLOCK == 16))
+#define PC_F3TXCLK     ((uint)(0x00000001 << (CONFIG_FCC3_MII_TX_CLOCK - 1)))
+#else
+#error "You can place the FCC3 MII TX clock only on CLK 13, 14, 15 or 16"
+#endif
+
+#if (CONFIG_FCC3_MII_RX_CLOCK == 13)
+#define CMX3_RXCLK_ROUTE        ((uint)0x00002000)
+#endif
+#if (CONFIG_FCC3_MII_RX_CLOCK == 14)
+#define CMX3_RXCLK_ROUTE        ((uint)0x00002800)
+#endif
+#if (CONFIG_FCC3_MII_RX_CLOCK == 15)
+#define CMX3_RXCLK_ROUTE        ((uint)0x00003000)
+#endif
+#if (CONFIG_FCC3_MII_RX_CLOCK == 16)
+#define CMX3_RXCLK_ROUTE        ((uint)0x000038000)
+#endif
+
+#if (CONFIG_FCC3_MII_TX_CLOCK == 13)
+#define CMX3_TXCLK_ROUTE        ((uint)0x00000400)
+#endif
+#if (CONFIG_FCC3_MII_TX_CLOCK == 14)
+#define CMX3_TXCLK_ROUTE        ((uint)0x00000500)
+#endif
+#if (CONFIG_FCC3_MII_TX_CLOCK == 15)
+#define CMX3_TXCLK_ROUTE        ((uint)0x00000600)
+#endif
+#if (CONFIG_FCC3_MII_TX_CLOCK == 16)
+#define CMX3_TXCLK_ROUTE        ((uint)0x000007000)
+#endif
+
+#define CMX3_CLK_ROUTE  ((uint)(CMX3_RXCLK_ROUTE | CMX3_TXCLK_ROUTE))
+#define CMX3_CLK_MASK   ((uint)0x0000ff00)
+
+#endif /* CONFIG_FCC3_MII_RX_CLOCK == CONFIG_FCC3_MII_TX_CLOCK */
+#endif /* CONFIG_FCC3_ENET */
+
 /* I/O Pin assignment for FCC2.  I don't yet know the best way to do this,
  * but there is little variation among the choices.
  */
@@ -194,20 +345,6 @@ static int fcc_enet_set_mac_address(stru
 #define PB2_DIRB0	(PB2_RXDAT | PB2_CRS | PB2_COL | PB2_RXER | PB2_RXDV)
 #define PB2_DIRB1	(PB2_TXDAT | PB2_TXEN | PB2_TXER)
 
-/* CLK13 is receive, CLK14 is transmit.  These are board dependent.
-*/
-#ifdef CONFIG_ADS8272
-#define PC_F2RXCLK	((uint)0x00004000)
-#define PC_F2TXCLK	((uint)0x00008000)
-#define CMX2_CLK_ROUTE	((uint)0x00370000)
-#define CMX2_CLK_MASK	((uint)0x00ff0000)
-#else
-#define PC_F2RXCLK	((uint)0x00001000)
-#define PC_F2TXCLK	((uint)0x00002000)
-#define CMX2_CLK_ROUTE	((uint)0x00250000)
-#define CMX2_CLK_MASK	((uint)0x00ff0000)
-#endif
-
 /* I/O Pin assignment for FCC3.  I don't yet know the best way to do this,
  * but there is little variation among the choices.
  */
@@ -225,26 +362,21 @@ static int fcc_enet_set_mac_address(stru
 #define PB3_DIRB0	(PB3_RXDAT | PB3_CRS | PB3_COL | PB3_RXER | PB3_RXDV)
 #define PB3_DIRB1	(PB3_TXDAT | PB3_TXEN | PB3_TXER)
 
-/* CLK15 is receive, CLK16 is transmit.  These are board dependent.
-*/
-#define PC_F3RXCLK	((uint)0x00004000)
-#define PC_F3TXCLK	((uint)0x00008000)
-#define CMX3_CLK_ROUTE	((uint)0x00003700)
-#define CMX3_CLK_MASK	((uint)0x0000ff00)
-
 /* MII status/control serial interface.
 */
-#ifdef	CONFIG_TQM8260
-/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */
-#define PC_MDIO		((uint)0x00000002)
-#define PC_MDCK		((uint)0x00000001)
-#elif defined(CONFIG_ADS8272)
-#define PC_MDIO		((uint)0x00002000)
-#define PC_MDCK		((uint)0x00001000)
+#ifdef  CONFIG_USE_MDIO
+#if ((CONFIG_MDIO_PARALLELL_IO_PORT == CONFIG_MDC_PARALLELL_IO_PORT) && \
+     (CONFIG_MDIO_PORT_PIN == CONFIG_MDC_PORT_PIN))
+#error "You cannot place the MDIO and MDC lines on the same pin"
 #else
-#define PC_MDIO		((uint)0x00000004)
-#define PC_MDCK		((uint)0x00000020)
-#endif
+#define PIOP_MDIO               ((uint)(0x80000000 >> CONFIG_MDIO_PORT_PIN))
+#define PIOP_MDCK               ((uint)(0x80000000 >> CONFIG_MDC_PORT_PIN))
+#endif /* (CONFIG_MDIO_PORT_PIN == CONFIG_MDC_PORT_PIN) */
+#else
+/* Just set MDIO and MDCK on PC30 and PC31 respectively (from TQM8260) */
+#define PIOP_MDIO               ((uint)0x00000002)
+#define PIOP_MDCK               ((uint)0x00000001)
+#endif /* CONFIG_USE_MDIO */
 
 /* A table of information for supporting FCCs.  This does two things.
  * First, we know how many FCCs we have and they are always externally
@@ -268,31 +400,17 @@ static fcc_info_t fcc_ports[] = {
 #ifdef CONFIG_FCC1_ENET
 	{ 0, CPM_CR_FCC1_SBLOCK, CPM_CR_FCC1_PAGE, PROFF_FCC1, SIU_INT_FCC1,
 		(PC_F1RXCLK | PC_F1TXCLK), CMX1_CLK_ROUTE, CMX1_CLK_MASK,
-# if defined(CONFIG_TQM8260) || defined(CONFIG_ADS8272)
-		PC_MDIO, PC_MDCK },
-# else
-		0x00000004, 0x00000100 },
-# endif
+                PIOP_MDIO, PIOP_MDCK },
 #endif
 #ifdef CONFIG_FCC2_ENET
 	{ 1, CPM_CR_FCC2_SBLOCK, CPM_CR_FCC2_PAGE, PROFF_FCC2, SIU_INT_FCC2,
 		(PC_F2RXCLK | PC_F2TXCLK), CMX2_CLK_ROUTE, CMX2_CLK_MASK,
-# if defined(CONFIG_TQM8260) || defined(CONFIG_ADS8272)
-		PC_MDIO, PC_MDCK },
-# elif defined(CONFIG_EST8260) || defined(CONFIG_ADS8260)
-		0x00400000, 0x00200000 },
-# else
-		0x00000002, 0x00000080 },
-# endif
+                PIOP_MDIO, PIOP_MDCK },
 #endif
 #ifdef CONFIG_FCC3_ENET
 	{ 2, CPM_CR_FCC3_SBLOCK, CPM_CR_FCC3_PAGE, PROFF_FCC3, SIU_INT_FCC3,
 		(PC_F3RXCLK | PC_F3TXCLK), CMX3_CLK_ROUTE, CMX3_CLK_MASK,
-# if defined(CONFIG_TQM8260) || defined(CONFIG_ADS8272)
-		PC_MDIO, PC_MDCK },
-# else
-		0x00000001, 0x00000040 },
-# endif
+                PIOP_MDIO, PIOP_MDCK },
 #endif
 };
 
@@ -329,11 +447,11 @@ struct fcc_enet_private {
 	uint	phy_id_done;
 	uint	phy_status;
 	phy_info_t	*phy;
-	struct tq_struct phy_task;
-
+	struct tasklet_struct phy_relink_task;
+	struct tasklet_struct phy_display_conf_task;
 	uint	sequence_done;
-
 	uint	phy_addr;
+	struct mii_if_info mii;
 #endif	/* CONFIG_USE_MDIO */
 
 	int	link;
@@ -363,6 +481,10 @@ static void	fcc_stop(struct net_device *
 #define mk_mii_write(REG, VAL)	(0x50020000 | ((REG & 0x1f) << 18) | \
 						(VAL & 0xffff))
 #define mk_mii_end	0
+
+/* Used in connection with the plug to the generic MII device */
+static uint mii_mdio_read_value;
+
 #endif	/* CONFIG_USE_MDIO */
 
 
@@ -496,7 +618,7 @@ static irqreturn_t
 fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs)
 {
 	struct	net_device *dev = dev_id;
-	volatile struct	fcc_enet_private *cep;
+	struct	fcc_enet_private *cep;
 	volatile cbd_t	*bdp;
 	ushort	int_events;
 	int	must_restart;
@@ -777,9 +899,62 @@ static void mii_do_cmd(struct net_device
 		mii_queue(dev, (c+k)->mii_data, (c+k)->funct);
 }
 
+/* The callback functions given to the generic MII device.
+   This is a bit tricky for reading, due to how the MII read function is
+   implemented here, using a callback.
+   It is done by using a static variable - mii_mdio_read_value
+   The generic MII driver calls mii_mdio_read, who in turn sends the
+   result of the read command to mii_mdio_read_return. This ones puts
+   the value in mii_mdio_read_value.
+   See below for info about the Marvell 88E6060 PHY/Switch. */
+   
+static void mii_mdio_read_return(uint mii_reg, struct net_device *dev)
+{
+	mii_mdio_read_value = mii_reg;
+}
+
+static int mii_mdio_read(struct net_device *dev, int addr, int reg)
+{
+#ifdef CONFIG_FCC_88E6060
+	struct fcc_enet_private *fep = dev->priv;
+	uint save_phy_addr = fep->phy_addr;
+
+	fep->phy_addr = addr;
+#endif
+
+	mii_mdio_read_value = 0;
+	mii_queue(dev, mk_mii_read(reg), mii_mdio_read_return);
+#ifdef CONFIG_FCC_88E6060
+	fep->phy_addr = save_phy_addr;
+#endif
+#if (0)
+	printk("mii_mdio_read: addr=%d, reg=%d, value=0x%04x\n", addr, reg, mii_mdio_read_value);
+#endif
+
+	return mii_mdio_read_value;
+}
+
+static void mii_mdio_write(struct net_device *dev, int addr, int reg, int data)
+{
+#ifdef CONFIG_FCC_88E6060
+	struct fcc_enet_private *fep = dev->priv;
+	uint save_phy_addr = fep->phy_addr;
+
+	fep->phy_addr = addr;
+#endif
+
+	mii_queue(dev, mk_mii_write(reg, data), NULL);
+#ifdef CONFIG_FCC_88E6060
+	fep->phy_addr = save_phy_addr;
+#endif
+}
+
+/* We could use the generic MII device to do things as done by the
+   functions below, but I did not want to change things that work !
+*/
 static void mii_parse_sr(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
@@ -797,7 +972,7 @@ static void mii_parse_sr(uint mii_reg, s
 
 static void mii_parse_cr(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP);
@@ -812,7 +987,7 @@ static void mii_parse_cr(uint mii_reg, s
 
 static void mii_parse_anar(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_CONF_SPMASK);
@@ -828,6 +1003,7 @@ static void mii_parse_anar(uint mii_reg,
 
 	fep->phy_status = s;
 }
+
 /* ------------------------------------------------------------------------- */
 /* The Level one LXT970 is used by many boards				     */
 
@@ -841,7 +1017,7 @@ static void mii_parse_anar(uint mii_reg,
 
 static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_STAT_SPMASK);
@@ -862,7 +1038,8 @@ static void mii_parse_lxt970_csr(uint mi
 }
 
 static phy_info_t phy_info_lxt970 = {
-	0x07810000,
+	0x78100000,
+	0x7ff00000,
 	"LXT970",
 
 	(const phy_cmd_t []) {  /* config */
@@ -924,7 +1101,7 @@ static phy_info_t phy_info_lxt970 = {
 
 static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_STAT_SPMASK);
@@ -947,7 +1124,8 @@ static void mii_parse_lxt971_sr2(uint mi
 }
 
 static phy_info_t phy_info_lxt971 = {
-	0x0001378e,
+	0x001378e0,
+	0x001fffe0,
 	"LXT971",
 
 	(const phy_cmd_t []) {  /* config */
@@ -1003,7 +1181,7 @@ static phy_info_t phy_info_lxt971 = {
 
 static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	s &= ~(PHY_STAT_SPMASK);
@@ -1019,7 +1197,8 @@ static void mii_parse_qs6612_pcr(uint mi
 }
 
 static phy_info_t phy_info_qs6612 = {
-	0x00181440,
+	0x01814400,
+	0x01fffc00,
 	"QS6612",
 
 	(const phy_cmd_t []) {  /* config */
@@ -1061,9 +1240,269 @@ static phy_info_t phy_info_qs6612 = {
 	},
 };
 
-
 #endif /* CONFIG_FEC_QS6612 */
 
+#ifdef CONFIG_FCC_88E6060
+
+/* ------------------------------------------------------------------------- */
+/* The Marvell Low Power 6-Port 10/100 Ethernet Switch
+   This device has 6 ports (here named from 0 to 5).
+   Typically, port 5 is connected to the CPU.
+   The other ports, from 0 to 4, are usually connected to external interfaces.
+   The management of this device takes place over MDIO.
+   It appears to have 12 PHYs, and the address of the first PHY can be
+   configured (in HW) to be either 0 or 16.
+   Now, if we assume that the device is configured so that the address of
+   the first PHY is 0:
+   - PHY addresses from 0 to 4 are the real PHY's of ports from 0 to 4.
+     Port 5 does not have a PHY.
+   - PHY addresses from 8 to 13 provide access to switch port specific
+     registers, as f.e. port control, port status, port based VLAN map ...
+     for each port from 0 to 5.
+   - PHY address 15 provides access to a few switch global as f.e. global
+     control, global status and ATU related registers.
+   In the implementation below, port 5 is connected to the CPU's FCC, while
+   ports 1 and 3 are connected to external interfaces. Ports 0 and 4 are
+   not used.
+*/
+
+/* register definitions */
+/* Global switch registers */
+#define MII_88E6060_GSR      0   /* Global Status Register  */
+#define MII_88E6060_GCR      4   /* Global Control Register  */
+/* Port registers */
+#define MII_88E6060_PSR      0   /* Port Status Register  */
+#define MII_88E6060_SWID     3   /* Switch Identifier Register  */
+#define MII_88E6060_PCR      4   /* Port Control Register  */
+#define MII_88E6060_PBVM     6   /* Port Based VLAN Map Register */
+#define MII_88E6060_PAV      11  /* Port Association Vector Register */
+#define MII_88E6060_RXFC     16  /* RX Frame Counter Register */
+#define MII_88E6060_TXFC     17  /* TX Frame Counter Register */
+/* PHY registers */
+#define MII_88E6060_SSR      17  /* Specific Status Register      */
+#define MII_88E6060_IER      18  /* Interrupt Status Register  */
+#define MII_88E6060_ISR      19  /* Interrupt Status Register  */
+
+#if (0)
+static void mii_88e6060_print_gsr(uint mii_reg, struct net_device *dev)
+{
+	printk("gsr=%04x\n", mii_reg);
+}
+
+static void mii_88e6060_print_gcr(uint mii_reg, struct net_device *dev)
+{
+	printk("gcr=%04x\n", mii_reg);
+}
+
+static void mii_88e6060_print_psr(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+  
+	printk("port %d - psr=%04x\n", fep->phy_addr - 8 - CONFIG_PHY_ADDRESS, mii_reg);
+}
+
+static void mii_88e6060_print_swid(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+
+	printk("port %d - swid=%04x\n", fep->phy_addr - 8 - CONFIG_PHY_ADDRESS, mii_reg);
+}
+
+static void mii_88e6060_print_pcr(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+
+	printk("port %d - pcr=%04x\n", fep->phy_addr - 8 - CONFIG_PHY_ADDRESS, mii_reg);
+}
+
+static void mii_88e6060_print_pbvm(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+
+	printk("port %d - pbvm=%04x\n", fep->phy_addr - 8 - CONFIG_PHY_ADDRESS, mii_reg);
+}
+
+static void mii_88e6060_print_pav(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+
+	printk("port %d - pav=%04x\n", fep->phy_addr - 8 - CONFIG_PHY_ADDRESS, mii_reg);
+}
+
+static void mii_88e6060_dump_all(struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint i, save_phy_addr = fep->phy_addr;
+
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 15;
+	mii_queue(dev, mk_mii_read(MII_88E6060_GSR), mii_88e6060_print_gsr);
+	mii_queue(dev, mk_mii_read(MII_88E6060_GCR), mii_88e6060_print_gcr);
+
+	for (i=8;i<14;i++) {
+		fep->phy_addr = CONFIG_PHY_ADDRESS + i;
+		mii_queue(dev, mk_mii_read(MII_88E6060_PSR), mii_88e6060_print_psr);
+		mii_queue(dev, mk_mii_read(MII_88E6060_SWID), mii_88e6060_print_swid);
+		mii_queue(dev, mk_mii_read(MII_88E6060_PCR), mii_88e6060_print_pcr);
+		mii_queue(dev, mk_mii_read(MII_88E6060_PBVM), mii_88e6060_print_pbvm);
+		mii_queue(dev, mk_mii_read(MII_88E6060_PAV), mii_88e6060_print_pav);
+	}
+
+	fep->phy_addr = save_phy_addr;
+}
+#endif
+
+static void mii_88e6060_config(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint save_phy_addr = fep->phy_addr;
+
+	/* Read CR and ANAR on port 1 PHY to setup the device PHY status */
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 1;
+	mii_queue(dev, mk_mii_read(MII_REG_CR), mii_parse_cr);
+	mii_queue(dev, mk_mii_read(MII_REG_ANAR), mii_parse_anar);
+	/* Restore the original PHY address */
+	fep->phy_addr = save_phy_addr;
+}
+
+static void mii_88e6060_startup(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint i, save_phy_addr = fep->phy_addr;
+	ushort ier[5] = {0x0000, 0x0c00, 0x0000, 0x0c00, 0x0000};
+
+	/* Enable link and AN completed interrupt and start AN process
+	   on ports 1 and 3 */
+	fep->phy_addr = CONFIG_PHY_ADDRESS;
+	for (i=0;i<5;i++) {
+		mii_queue(dev, mk_mii_read(MII_88E6060_ISR), NULL);
+		mii_queue(dev, mk_mii_write(MII_88E6060_IER, ier[i]), NULL);
+		mii_queue(dev, mk_mii_write(MII_REG_CR, 0x1200), NULL);
+		fep->phy_addr++;
+	}
+	/* Enable ports 1, 3 and 5 (the CPU port) and set VLAN's so that:
+	   - port 1 can only send to port 5
+	   - port 3 can only send to port 5
+	   - port 5 can send to both 1 and 5
+	*/
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 9;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PBVM, 0x0020), NULL);
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x8003), NULL);
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 11;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PBVM, 0x0020), NULL);
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x8003), NULL);
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 13;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PBVM, 0x000A), NULL);
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x8003), NULL);
+#if (0)
+	mii_88e6060_dump_all(dev);
+#endif
+	/* Point to the last 88E6060 PHY, that gives access to the GCR
+	   and enable PHY interrupts */
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 15;
+	mii_queue(dev, mk_mii_write(MII_88E6060_GCR, 0x0002), NULL);
+  
+	/* Restore the original PHY address */
+	fep->phy_addr = save_phy_addr;
+}
+
+static void mii_88e6060_ack_int(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint i, save_phy_addr = fep->phy_addr;
+
+	/* Read ISR, SR and ANER on all ports to acknowledge the interrupt */
+	fep->phy_addr = CONFIG_PHY_ADDRESS;
+	for (i=0;i<5;i++) {
+		mii_queue(dev, mk_mii_read(MII_88E6060_ISR), NULL);
+		mii_queue(dev, mk_mii_read(MII_REG_SR), NULL);
+		mii_queue(dev, mk_mii_read(MII_REG_ANER), NULL);
+		fep->phy_addr++;
+	}
+	/* Point to the last 88E6060 PHY, that gives access to the GSR
+	   and read it to acknowledge there as well */
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 15;
+	mii_queue(dev, mk_mii_read(MII_88E6060_GSR), NULL);
+  
+	/* Restore the original PHY address */
+	fep->phy_addr = save_phy_addr;
+}
+
+static void mii_88e6060_shutdown(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint i, save_phy_addr = fep->phy_addr;
+
+	/* Disable all interrupts in the switch GCR */
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 15;
+	mii_queue(dev, mk_mii_write(MII_88E6060_GCR, 0x0000), NULL);
+
+	/* Disable all interrupts in all the ports PHY IER */
+	fep->phy_addr = CONFIG_PHY_ADDRESS;
+	for (i=0;i<5;i++) {
+		mii_queue(dev, mk_mii_write(MII_88E6060_IER, 0x0000), NULL);
+		fep->phy_addr++;
+	}
+	/* Disable ports 1, 3 and 5 (the CPU port) */
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 9;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x0000), NULL);
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 11;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x0000), NULL);
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 13;
+	mii_queue(dev, mk_mii_write(MII_88E6060_PCR, 0x0000), NULL);
+
+	/* Restore the original PHY address */
+	fep->phy_addr = save_phy_addr;
+}
+
+static void mii_parse_88e6060_ssr(uint mii_reg, struct net_device *dev)
+{
+	struct fcc_enet_private *fep = dev->priv;
+	uint s = fep->phy_status;
+
+	s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK);
+
+	if (mii_reg & 0x0800) { /* Resolved is set: AN completed or disabled */
+		switch((mii_reg >> 13) & 3) {
+		case 0: s |= PHY_STAT_10HDX;  break;
+		case 1: s |= PHY_STAT_100HDX; break;
+		case 2: s |= PHY_STAT_10FDX;  break;
+		case 3: s |= PHY_STAT_100FDX; break;
+		}
+	}
+	else {
+		s |= PHY_STAT_10HDX;
+	}
+	if (mii_reg & 0x0400) { /* Real time link status */
+		s |= PHY_STAT_LINK;
+	}
+
+	fep->phy_status = s;
+}
+
+static phy_info_t phy_info_88e6060 = {
+	0x01410c00,
+	0xfffffc00,
+	"88E6060",
+
+	(const phy_cmd_t []) {  /* config */
+		/* parse cr and anar to get some info */
+		{ mk_mii_read(MII_REG_CR), mii_88e6060_config },
+		{ mk_mii_end, }
+	},
+	(const phy_cmd_t []) {  /* startup - enable interrupts */
+		{ mk_mii_read(MII_REG_CR), mii_88e6060_startup },
+		{ mk_mii_end, }
+	},
+	(const phy_cmd_t []) { /* ack_int */
+		{ mk_mii_read(MII_REG_CR), mii_88e6060_ack_int },
+		{ mk_mii_end, }
+	},
+	(const phy_cmd_t []) {  /* shutdown - disable interrupts */
+		{ mk_mii_read(MII_REG_CR), mii_88e6060_shutdown },
+		{ mk_mii_end, }
+	},
+};
+#endif /* CONFIG_FCC_88E6060 */
 
 /* ------------------------------------------------------------------------- */
 /* The Davicom DM9131 is used on the HYMOD board			     */
@@ -1152,12 +1591,16 @@ static phy_info_t *phy_info[] = {
 	&phy_info_dm9131,
 #endif /* CONFIG_FEC_DM9131 */
 
+#ifdef CONFIG_FCC_88E6060
+	&phy_info_88e6060,
+#endif /* CONFIG_FCC_88E6060 */
+
 	NULL
 };
 
 static void mii_display_status(struct net_device *dev)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	if (!fep->link && !fep->old_link) {
@@ -1191,9 +1634,13 @@ static void mii_display_status(struct ne
 	printk(".\n");
 }
 
+/*
 static void mii_display_config(struct net_device *dev)
+*/
+static void mii_display_config(unsigned long data)
 {
-	volatile struct fcc_enet_private *fep = dev->priv;
+  struct net_device *dev = (struct net_device *)data;
+	struct fcc_enet_private *fep = dev->priv;
 	uint s = fep->phy_status;
 
 	printk("%s: config: auto-negotiation ", dev->name);
@@ -1222,14 +1669,58 @@ static void mii_display_config(struct ne
 	fep->sequence_done = 1;
 }
 
-static void mii_relink(struct net_device *dev)
+#ifdef CONFIG_FCC_88E6060
+/* The logic below is rather platform dependent.
+   Here, we check if we have link on ports 1 or 3, and then set the
+   PHY address of the enet device to the corresponding PHY address
+   within the 88E6060 */
+static void mii_relink(unsigned long data)
 {
+	struct net_device *dev = (struct net_device *)data;
 	struct fcc_enet_private *fep = dev->priv;
 	int duplex;
 
+	fep->old_link = fep->link;
+	fep->phy_addr = CONFIG_PHY_ADDRESS + 1;
+	mii_queue(dev, mk_mii_read(MII_REG_SR), mii_parse_sr);
+	mii_queue(dev, mk_mii_read(MII_88E6060_SSR), mii_parse_88e6060_ssr);
 	fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
+
+	if (fep->link) {
+		duplex = 0;
+		if (fep->phy_status
+		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
+			duplex = 1;
+		fcc_restart(dev, duplex);
+	} else {
+		fep->phy_addr = CONFIG_PHY_ADDRESS + 3;
+		mii_queue(dev, mk_mii_read(MII_REG_SR), mii_parse_sr);
+		mii_queue(dev, mk_mii_read(MII_88E6060_SSR), mii_parse_88e6060_ssr);
+		fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
+
+		if (fep->link) {
+			duplex = 0;
+			if (fep->phy_status
+			    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
+				duplex = 1;
+			fcc_restart(dev, duplex);
+		} else {
+			fcc_stop(dev);
+		}
+	}
 	mii_display_status(dev);
+	fep->mii.phy_id = fep->phy_addr;
+}
+#else
+static void mii_relink(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct fcc_enet_private *fep = dev->priv;
+	int duplex;
+
 	fep->old_link = fep->link;
+	fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
+	mii_display_status(dev);
 
 	if (fep->link) {
 		duplex = 0;
@@ -1241,27 +1732,22 @@ static void mii_relink(struct net_device
 		fcc_stop(dev);
 	}
 }
+#endif /* CONFIG_FCC_88E6060 */
 
 static void mii_queue_relink(uint mii_reg, struct net_device *dev)
 {
 	struct fcc_enet_private *fep = dev->priv;
 
-	fep->phy_task.routine = (void *)mii_relink;
-	fep->phy_task.data = dev;
-	schedule_task(&fep->phy_task);
+	tasklet_schedule(&fep->phy_relink_task);
 }
 
 static void mii_queue_config(uint mii_reg, struct net_device *dev)
 {
 	struct fcc_enet_private *fep = dev->priv;
 
-	fep->phy_task.routine = (void *)mii_display_config;
-	fep->phy_task.data = dev;
-	schedule_task(&fep->phy_task);
+	tasklet_schedule(&fep->phy_display_conf_task);
 }
 
-
-
 phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink },
 			       { mk_mii_end, } };
 phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config },
@@ -1280,7 +1766,7 @@ mii_discover_phy3(uint mii_reg, struct n
 	fep->phy_id |= (mii_reg & 0xffff);
 
 	for(i = 0; phy_info[i]; i++)
-		if(phy_info[i]->id == (fep->phy_id >> 4))
+		if((fep->phy_id & phy_info[i]->id_mask) == phy_info[i]->id)
 			break;
 
 	if(!phy_info[i])
@@ -1304,19 +1790,22 @@ mii_discover_phy(uint mii_reg, struct ne
 
 	fep = dev->priv;
 
-	if ((phytype = (mii_reg & 0xfff)) != 0xfff) {
+	if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
 
 		/* Got first part of ID, now get remainder. */
 		fep->phy_id = phytype << 16;
 		mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3);
 	} else {
+#ifdef CONFIG_PHY_NO_ADDRESS_AUTODISCOVERY
+		printk("fec: No PHY device with address %d found.\n", CONFIG_PHY_ADDRESS);
+#else    
 		fep->phy_addr++;
 		if (fep->phy_addr < 32) {
-			mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
-							mii_discover_phy);
+			mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
 		} else {
 			printk("fec: No PHY device found.\n");
 		}
+#endif /* CONFIG_PHY_NO_ADDRESS_AUTODISCOVERY */
 	}
 }
 
@@ -1446,7 +1935,7 @@ static int __init fec_enet_init(void)
 	struct net_device *dev;
 	struct fcc_enet_private *cep;
 	fcc_info_t	*fip;
-	int	i, np, err;
+	int	i, np, err, irq_requested = 0;
 	volatile	cpm2_map_t		*immap;
 	volatile	iop_cpm2_t	*io;
 
@@ -1484,7 +1973,10 @@ static int __init fec_enet_init(void)
 		dev->get_stats = fcc_enet_get_stats;
 		dev->set_multicast_list = set_multicast_list;
 		dev->set_mac_address = fcc_enet_set_mac_address;
-
+#ifdef	CONFIG_USE_MDIO
+		dev->do_ioctl = fcc_enet_ioctl;
+#endif /* CONFIG_USE_MDIO */
+    
 		init_fcc_startup(fip, dev);
 
 		err = register_netdev(dev);
@@ -1500,10 +1992,41 @@ static int __init fec_enet_init(void)
 
 #ifdef	CONFIG_USE_MDIO
 		/* Queue up command to detect the PHY and initialize the
-	 	* remainder of the interface.
-	 	*/
+		* remainder of the interface, including the plugin to the
+		* generic MII handler.
+		*/
+		tasklet_init(&cep->phy_relink_task, mii_relink, (unsigned long)dev);
+		tasklet_init(&cep->phy_display_conf_task, mii_display_config, (unsigned long)dev);
+
+#ifdef CONFIG_PHY_NO_ADDRESS_AUTODISCOVERY
+#if (CONFIG_PHY_ADDRESS > 31)
+#error "You cannot specify a PHY address higher than 31"
+#else
+		cep->phy_addr = CONFIG_PHY_ADDRESS + fip->fc_fccnum;
+#endif
+#else
 		cep->phy_addr = 0;
+#endif  /* CONFIG_PHY_NO_ADDRESS_AUTODISCOVERY */
 		mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
+#ifdef CONFIG_FCC_88E6060
+		mii_queue(dev, mk_mii_read(MII_REG_CR), mii_88e6060_shutdown);
+#endif
+		/* MII setup */
+		cep->mii.phy_id = cep->phy_addr;
+		cep->mii.phy_id_mask = 0x1F;
+		cep->mii.reg_num_mask = 0x1F;
+		cep->mii.dev = dev;
+		cep->mii.mdio_read = mii_mdio_read;
+		cep->mii.mdio_write = mii_mdio_write;
+#ifdef CONFIG_PHY_INTERRUPT
+		if (irq_requested == 0) {
+			irq_requested = 1;
+			/* Request the PHY interrupt just once, related to the first used FCC */
+			if (request_irq(CONFIG_PHY_INTERRUPT_NUMBER, mii_link_interrupt, 0,
+					"mii", dev) < 0)
+				printk("Can't get MII IRQ %d\n", CONFIG_PHY_INTERRUPT_NUMBER);
+		}
+#endif	/* CONFIG_PHY_INTERRUPT */
 #endif	/* CONFIG_USE_MDIO */
 
 		fip++;
@@ -1581,12 +2104,74 @@ init_fcc_ioports(fcc_info_t *fip, volati
 	io->iop_pparc |= fip->fc_trxclocks;
 
 #ifdef	CONFIG_USE_MDIO
-	/* ....and the MII serial clock/data.
+	/* ....and the MII serial clock/data can be almost anywhere...
 	*/
-	io->iop_pdatc |= (fip->fc_mdio | fip->fc_mdck);
-	io->iop_podrc &= ~(fip->fc_mdio | fip->fc_mdck);
-	io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck);
-	io->iop_pparc &= ~(fip->fc_mdio | fip->fc_mdck);
+#if (CONFIG_MDIO_PARALLELL_IO_PORT == 1)
+#define IOP_PDAT_MDIO iop_pdata
+#define IOP_PODR_MDIO iop_podra
+#define IOP_PDIR_MDIO iop_pdira
+#define IOP_PPAR_MDIO iop_ppara
+#endif
+
+#if (CONFIG_MDIO_PARALLELL_IO_PORT == 2)
+#define IOP_PDAT_MDIO iop_pdatb
+#define IOP_PODR_MDIO iop_podrb
+#define IOP_PDIR_MDIO iop_pdirb
+#define IOP_PPAR_MDIO iop_pparb
+#endif
+
+#if (CONFIG_MDIO_PARALLELL_IO_PORT == 3)
+#define IOP_PDAT_MDIO iop_pdatc
+#define IOP_PODR_MDIO iop_podrc
+#define IOP_PDIR_MDIO iop_pdirc
+#define IOP_PPAR_MDIO iop_pparc
+#endif
+
+#if (CONFIG_MDIO_PARALLELL_IO_PORT == 4)
+#define IOP_PDAT_MDIO iop_pdatd
+#define IOP_PODR_MDIO iop_podrd
+#define IOP_PDIR_MDIO iop_pdird
+#define IOP_PPAR_MDIO iop_ppard
+#endif
+
+#if (CONFIG_MDC_PARALLELL_IO_PORT == 1)
+#define IOP_PDAT_MDC iop_pdata
+#define IOP_PODR_MDC iop_podra
+#define IOP_PDIR_MDC iop_pdira
+#define IOP_PPAR_MDC iop_ppara
+#endif
+
+#if (CONFIG_MDC_PARALLELL_IO_PORT == 2)
+#define IOP_PDAT_MDC iop_pdatb
+#define IOP_PODR_MDC iop_podrb
+#define IOP_PDIR_MDC iop_pdirb
+#define IOP_PPAR_MDC iop_pparb
+#endif
+
+#if (CONFIG_MDC_PARALLELL_IO_PORT == 3)
+#define IOP_PDAT_MDC iop_pdatc
+#define IOP_PODR_MDC iop_podrc
+#define IOP_PDIR_MDC iop_pdirc
+#define IOP_PPAR_MDC iop_pparc
+#endif
+
+#if (CONFIG_MDC_PARALLELL_IO_PORT == 4)
+#define IOP_PDAT_MDC iop_pdatd
+#define IOP_PODR_MDC iop_podrd
+#define IOP_PDIR_MDC iop_pdird
+#define IOP_PPAR_MDC iop_ppard
+#endif
+
+	io->IOP_PDAT_MDIO |= fip->fc_mdio;
+	io->IOP_PODR_MDIO &= ~fip->fc_mdio;
+	io->IOP_PDIR_MDIO |= fip->fc_mdio;
+	io->IOP_PPAR_MDIO &= ~fip->fc_mdio;
+  
+	io->IOP_PDAT_MDC |= fip->fc_mdck;
+	io->IOP_PODR_MDC &= ~fip->fc_mdck;
+	io->IOP_PDIR_MDC |= fip->fc_mdck;
+	io->IOP_PPAR_MDC &= ~fip->fc_mdck;
+
 #endif	/* CONFIG_USE_MDIO */
 
 	/* Configure Serial Interface clock routing.
@@ -1826,12 +2411,6 @@ init_fcc_startup(fcc_info_t *fip, struct
 							"fenet", dev) < 0)
 		printk("Can't get FCC IRQ %d\n", fip->fc_interrupt);
 
-#ifdef	CONFIG_USE_MDIO
-	if (request_irq(PHY_INTERRUPT, mii_link_interrupt, 0,
-							"mii", dev) < 0)
-		printk("Can't get MII IRQ %d\n", fip->fc_interrupt);
-#endif	/* CONFIG_USE_MDIO */
-
 	/* Set GFMR to enable Ethernet operating mode.
 	 */
 	fccp->fcc_gfmr = (FCC_GFMR_TCI | FCC_GFMR_MODE_ENET);
@@ -1873,17 +2452,17 @@ init_fcc_startup(fcc_info_t *fip, struct
  * the I2C in the CPM but I have to toggle these bits......
  */
 
-#define FCC_PDATC_MDIO(bit)					\
+#define FCC_PDAT_MDIO(bit)					\
 	if (bit)						\
-		io->iop_pdatc |= fip->fc_mdio;			\
+		io->IOP_PDAT_MDIO |= fip->fc_mdio;			\
 	else							\
-		io->iop_pdatc &= ~fip->fc_mdio;
+		io->IOP_PDAT_MDIO &= ~fip->fc_mdio;
 
-#define FCC_PDATC_MDC(bit)					\
+#define FCC_PDAT_MDC(bit)					\
 	if (bit)						\
-		io->iop_pdatc |= fip->fc_mdck;			\
+		io->IOP_PDAT_MDC |= fip->fc_mdck;			\
 	else							\
-		io->iop_pdatc &= ~fip->fc_mdck;
+		io->IOP_PDAT_MDC &= ~fip->fc_mdck;
 
 static uint
 mii_send_receive(fcc_info_t *fip, uint cmd)
@@ -1896,7 +2475,8 @@ mii_send_receive(fcc_info_t *fip, uint c
 	immap = (cpm2_map_t *)CPM_MAP_ADDR;
 	io = &immap->im_ioport;
 
-	io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck);
+	io->IOP_PDIR_MDIO |= fip->fc_mdio;
+  io->IOP_PDIR_MDC |= fip->fc_mdck;
 
 	read_op = ((cmd & 0xf0000000) == 0x60000000);
 
@@ -1904,10 +2484,10 @@ mii_send_receive(fcc_info_t *fip, uint c
 	 */
 	for (i = 0; i < 32; i++)
 	{
-		FCC_PDATC_MDC(0);
-		FCC_PDATC_MDIO(1);
+		FCC_PDAT_MDC(0);
+		FCC_PDAT_MDIO(1);
 		udelay(1);
-		FCC_PDATC_MDC(1);
+		FCC_PDAT_MDC(1);
 		udelay(1);
 	}
 
@@ -1915,10 +2495,10 @@ mii_send_receive(fcc_info_t *fip, uint c
 	 */
 	for (i = 0, off = 31; i < (read_op ? 14 : 32); i++, --off)
 	{
-		FCC_PDATC_MDC(0);
-		FCC_PDATC_MDIO((cmd >> off) & 0x00000001);
+		FCC_PDAT_MDC(0);
+		FCC_PDAT_MDIO((cmd >> off) & 0x00000001);
 		udelay(1);
-		FCC_PDATC_MDC(1);
+		FCC_PDAT_MDC(1);
 		udelay(1);
 	}
 
@@ -1928,34 +2508,35 @@ mii_send_receive(fcc_info_t *fip, uint c
 	{
 		retval >>= 16;
 
-		FCC_PDATC_MDC(0);
-		io->iop_pdirc &= ~fip->fc_mdio;
+		FCC_PDAT_MDC(0);
+		io->IOP_PDIR_MDIO &= ~fip->fc_mdio;
 		udelay(1);
-		FCC_PDATC_MDC(1);
+		FCC_PDAT_MDC(1);
 		udelay(1);
-		FCC_PDATC_MDC(0);
+		FCC_PDAT_MDC(0);
 		udelay(1);
 
 		for (i = 0, off = 15; i < 16; i++, off--)
 		{
-			FCC_PDATC_MDC(1);
+			FCC_PDAT_MDC(1);
 			retval <<= 1;
-			if (io->iop_pdatc & fip->fc_mdio)
+			if (io->IOP_PDAT_MDIO & fip->fc_mdio)
 				retval++;
 			udelay(1);
-			FCC_PDATC_MDC(0);
+			FCC_PDAT_MDC(0);
 			udelay(1);
 		}
 	}
 
-	io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck);
+	io->IOP_PDIR_MDIO |= fip->fc_mdio;
+  io->IOP_PDIR_MDC |= fip->fc_mdck;
 
 	for (i = 0; i < 32; i++)
 	{
-		FCC_PDATC_MDC(0);
-		FCC_PDATC_MDIO(1);
+		FCC_PDAT_MDC(0);
+		FCC_PDAT_MDIO(1);
 		udelay(1);
-		FCC_PDATC_MDC(1);
+		FCC_PDAT_MDC(1);
 		udelay(1);
 	}
 
@@ -2011,6 +2592,7 @@ fcc_enet_open(struct net_device *dev)
 			schedule();
 
 		mii_do_cmd(dev, fep->phy->startup);
+
 		netif_start_queue(dev);
 		return 0;		/* Success */
 	}
@@ -2022,3 +2604,15 @@ fcc_enet_open(struct net_device *dev)
 #endif	/* CONFIG_USE_MDIO */
 }
 
+#ifdef	CONFIG_USE_MDIO
+static int fcc_enet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct fcc_enet_private	*fcp = (struct fcc_enet_private *)dev->priv;
+	struct mii_ioctl_data *mii_data = if_mii(ifr);
+#if (0)
+	printk("fcc_enet_ioctl: CMD is %d - phy @ %d, reg %d\n",
+			cmd, mii_data->phy_id, mii_data->reg_num);
+#endif         
+	return generic_mii_ioctl(&fcp->mii, mii_data, cmd, NULL);
+}
+#endif	/* CONFIG_USE_MDIO */
--- linux-2.6.9/arch/ppc/8260_io/Kconfig	2004-10-18 23:55:24.000000000 +0200
+++ linux-2.6.9.axxmetro/arch/ppc/8260_io/Kconfig	2004-10-27 21:04:38.000000000 +0200
@@ -23,26 +23,135 @@ config FCC1_ENET
 	help
 	  Use CPM2 fast Ethernet controller 1 to drive Ethernet (default).
 
+config FCC1_MII_RX_CLOCK
+        int "FCC1 MII RX CLK number (9..12)"
+        depends on FCC1_ENET
+        default "9"
+        help
+          Set here the CLK number used as FCC1 MII RX clock.
+
+config FCC1_MII_TX_CLOCK
+        int "FCC1 MII TX CLK number (9..12)"
+        depends on FCC1_ENET
+        default "10"
+        help
+          Set here the CLK number used as FCC1 MII TX clock.
+
 config FCC2_ENET
 	bool "Ethernet on FCC2"
 	depends on FEC_ENET
 	help
 	  Use CPM2 fast Ethernet controller 2 to drive Ethernet.
 
+config FCC2_MII_RX_CLOCK
+        int "FCC2 MII RX CLK number (13..16)"
+        depends on FCC2_ENET
+        default "13"
+        help
+          Set here the CLK number used as FCC2 MII RX clock.
+
+config FCC2_MII_TX_CLOCK
+        int "FCC2 MII TX CLK number (13..16)"
+        depends on FCC2_ENET
+        default "14"
+        help
+          Set here the CLK number used as FCC2 MII TX clock.
+
 config FCC3_ENET
 	bool "Ethernet on FCC3"
 	depends on FEC_ENET
 	help
 	  Use CPM2 fast Ethernet controller 3 to drive Ethernet.
 
+config FCC3_MII_RX_CLOCK
+        int "FCC3 MII RX CLK number (13..16)"
+        depends on FCC3_ENET
+        default "15"
+        help
+          Set here the CLK number used as FCC3 MII RX clock.
+
+config FCC3_MII_TX_CLOCK
+        int "FCC3 MII TX CLK number (13..16)"
+        depends on FCC3_ENET
+        default "16"
+        help
+          Set here the CLK number used as FCC3 MII TX clock.
+
 config USE_MDIO
 	bool "Use MDIO for PHY configuration"
 	depends on FEC_ENET
 
+config MDIO_PARALLELL_IO_PORT
+        int "MDIO is on parallell IO Port (1..4)"
+        depends on USE_MDIO
+        default "1"
+        help
+          Set here the parallell IO Port where the MDIO line is placed.
+          1 means Port A, 2 means Port B, 3 means Port C and 4 means Port D.
+
+config MDIO_PORT_PIN
+        int "MDIO is on parallell IO Port pin (0..31)"
+        depends on USE_MDIO
+        default "24"
+        help
+          Set here the parallell IO Port pin number where the MDIO line
+          is placed.
+
+config MDC_PARALLELL_IO_PORT
+        int "MDC is on parallell IO Port (1..4)"
+        depends on USE_MDIO
+        default "1"
+        help
+          Set here the parallell IO Port where the MDC line is placed.
+          1 means Port A, 2 means Port B, 3 means Port C and 4 means Port D.
+
+config MDC_PORT_PIN
+        int "MDC is on parallell IO Port pin (0..31)"
+        depends on USE_MDIO
+        default "25"
+        help
+          Set here the parallell IO Port pin number where the MDC line
+          is placed.
+
+config PHY_NO_ADDRESS_AUTODISCOVERY
+        bool "Disable autodiscovery of PHY address"
+        depends on USE_MDIO
+        help
+          Hit this option if you want to specify the PHY address yourself.
+          You want to do so if you are using more than one FCC and a multi-PHY
+          device. Otherwise, all FCC's will get the same associated PHY address.
+          Do not hit if you are not sure.
+
+config PHY_ADDRESS
+        int "PHY address"
+        depends on PHY_NO_ADDRESS_AUTODISCOVERY
+        default "0"
+        help
+          You have disabled the PHY address autodiscovery. Enter the PHY address
+          here. If you are using more than one FCC, the FCC1 will get this
+          address, FCC2 will get this address + 1 and FCC3 will get this address +2.
+
+config PHY_INTERRUPT
+        bool "Enable PHY interrupt"
+        depends on USE_MDIO
+
+config PHY_INTERRUPT_NUMBER
+        int "PHY interrupt number"
+        depends on USE_MDIO && PHY_INTERRUPT
+        default "59"
+        help
+          See MPC8260 User's Manual Table 4-3. Encoding the interrupt vector.
+          You have to write the vector value here (in decimal), not the
+          interrupt number.
+
 choice
 	prompt "Type of PHY"
 	depends on 8260 && USE_MDIO
 	default FCC_LXT971
+        help
+          Choose the kind of PHY among the supported types.
+          Notice that in case you have enabled ethernet on several FCC, all
+          the PHYs are supposed to be of the same type.
 
 config FCC_LXT970
 	bool "LXT970"
@@ -53,6 +162,11 @@ config FCC_LXT971
 config FCC_QS6612
 	bool "QS6612"
 
+config FCC_88E6060
+	bool "88E6060"
+	help
+	  Marvell Low Power 6-Port 10/100 Ethernet Switch.
+
 endchoice
 endmenu
 

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-11-09 16:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-11-09 16:04 Linux-2.6.9 - MPC8260 FCC/MDIO improvements (I hope !) miguel.valero

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.