From 56bd8193188213000186fb9e4cd49a356be7e290 Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Tue, 29 Jul 2008 18:22:38 +0000
Subject: [ar71xx] ethernet driver updates * new mii bus code, mac0 and mac1
 can use the mac0's miii bus from now on * swap eth0 & eth1 on RB433/450

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11995 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 .../ar71xx/files/arch/mips/ar71xx/mach-generic.c   |   5 +-
 .../ar71xx/files/arch/mips/ar71xx/mach-rb-4xx.c    |  11 +-
 .../ar71xx/files/arch/mips/ar71xx/mach-wp543.c     |   1 +
 .../linux/ar71xx/files/arch/mips/ar71xx/platform.c |  51 ++-
 .../linux/ar71xx/files/drivers/net/ag71xx/Makefile |   2 +-
 .../linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h | 160 ++++++--
 .../ar71xx/files/drivers/net/ag71xx/ag71xx_main.c  |  84 ++--
 .../ar71xx/files/drivers/net/ag71xx/ag71xx_mdio.c  | 226 +++++++++++
 .../ar71xx/files/drivers/net/ag71xx/ag71xx_mii.c   | 436 ---------------------
 .../ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c   | 290 ++++++++++++++
 .../files/include/asm-mips/mach-ar71xx/platform.h  |   6 +
 .../patches-2.6.26/801-ag71xx_mii_bus_id_fix.patch |  11 -
 12 files changed, 753 insertions(+), 530 deletions(-)
 create mode 100644 target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mdio.c
 delete mode 100644 target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mii.c
 create mode 100644 target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
 delete mode 100644 target/linux/ar71xx/patches-2.6.26/801-ag71xx_mii_bus_id_fix.patch

diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-generic.c b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-generic.c
index 4a1200d524..42b5e02678 100644
--- a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-generic.c
+++ b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-generic.c
@@ -49,8 +49,9 @@ static void __init ar71xx_generic_init(void)
 	ar71xx_add_device_spi(NULL, ar71xx_generic_spi_info,
 				ARRAY_SIZE(ar71xx_generic_spi_info));
 
-	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x001f0000);
-	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
+	ar71xx_add_device_mdio(0xffe0ffff);
+	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x000f0000);
+	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0x00100000);
 
 	ar71xx_add_device_usb();
 
diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb-4xx.c b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb-4xx.c
index 7504c349a6..a07f645d1c 100644
--- a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb-4xx.c
+++ b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb-4xx.c
@@ -153,6 +153,7 @@ static void __init rb411_setup(void)
 {
 	rb4xx_add_device_spi();
 
+	ar71xx_add_device_mdio(0xfffffffe);
 	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
 
 	platform_device_register(&rb4xx_leds_gpio_device);
@@ -167,8 +168,9 @@ static void __init rb433_setup(void)
 {
 	rb433_add_device_spi();
 
-	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
-	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
+	ar71xx_add_device_mdio(0xffffffec);
+	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0x00000010);
+	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000003);
 
 	platform_device_register(&rb4xx_leds_gpio_device);
 	platform_device_register(&rb4xx_nand_device);
@@ -182,8 +184,9 @@ static void __init rb450_setup(void)
 {
 	rb4xx_add_device_spi();
 
-	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
-	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0xffffffff);
+	ar71xx_add_device_mdio(0xffffffe0);
+	ar71xx_add_device_eth(1, PHY_INTERFACE_MODE_RMII, 0x00000010);
+	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x0000000f);
 
 	platform_device_register(&rb4xx_leds_gpio_device);
 	platform_device_register(&rb4xx_nand_device);
diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-wp543.c b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-wp543.c
index 6f1f39b2ab..21d9eb0b57 100644
--- a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-wp543.c
+++ b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-wp543.c
@@ -48,6 +48,7 @@ static void __init wp543_setup(void)
 {
 	ar71xx_add_device_spi(NULL, wp543_spi_info, ARRAY_SIZE(wp543_spi_info));
 
+	ar71xx_add_device_mdio(0xfffffffe);
 	ar71xx_add_device_eth(0, PHY_INTERFACE_MODE_MII, 0x00000001);
 
 	ar71xx_pci_init(ARRAY_SIZE(wp543_pci_irqs), wp543_pci_irqs);
diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/platform.c b/target/linux/ar71xx/files/arch/mips/ar71xx/platform.c
index 07bec246ba..208af8c8fd 100644
--- a/target/linux/ar71xx/files/arch/mips/ar71xx/platform.c
+++ b/target/linux/ar71xx/files/arch/mips/ar71xx/platform.c
@@ -143,12 +143,46 @@ static void __init ar71xx_add_device_uart(void)
 }
 #endif /* CONFIG_AR71XX_EARLY_SERIAL */
 
+static struct resource ar71xx_mdio_resources[] = {
+	{
+		.name	= "mdio_base",
+		.flags	= IORESOURCE_MEM,
+		.start	= AR71XX_GE0_BASE + 0x20,
+		.end	= AR71XX_GE0_BASE + 0x38 - 1,
+	}
+};
+
+static struct ag71xx_mdio_platform_data ar71xx_mdio_data = {
+	.phy_mask	= 0xffffffff,
+};
+
+static struct platform_device ar71xx_mdio_device = {
+	.name		= "ag71xx-mdio",
+	.id		= -1,
+	.resource	= ar71xx_mdio_resources,
+	.num_resources	= ARRAY_SIZE(ar71xx_mdio_resources),
+	.dev = {
+		.platform_data = &ar71xx_mdio_data,
+	},
+};
+
+void __init ar71xx_add_device_mdio(u32 phy_mask)
+{
+	ar71xx_mdio_data.phy_mask = phy_mask;
+	platform_device_register(&ar71xx_mdio_device);
+}
+
 static struct resource ar71xx_eth0_resources[] = {
 	{
 		.name	= "mac_base",
 		.flags	= IORESOURCE_MEM,
 		.start	= AR71XX_GE0_BASE,
-		.end	= AR71XX_GE0_BASE + AR71XX_GE0_SIZE - 1,
+		.end	= AR71XX_GE0_BASE + 0x20 - 1,
+	}, {
+		.name	= "mac_base2",
+		.flags	= IORESOURCE_MEM,
+		.start	= AR71XX_GE0_BASE + 0x38,
+		.end	= AR71XX_GE0_BASE + 0x200 - 1,
 	}, {
 		.name	= "mii_ctrl",
 		.flags	= IORESOURCE_MEM,
@@ -182,7 +216,12 @@ static struct resource ar71xx_eth1_resources[] = {
 		.name	= "mac_base",
 		.flags	= IORESOURCE_MEM,
 		.start	= AR71XX_GE1_BASE,
-		.end	= AR71XX_GE1_BASE + AR71XX_GE1_SIZE - 1,
+		.end	= AR71XX_GE1_BASE + 0x20 - 1,
+	}, {
+		.name	= "mac_base2",
+		.flags	= IORESOURCE_MEM,
+		.start	= AR71XX_GE1_BASE + 0x38,
+		.end	= AR71XX_GE1_BASE + 0x200 - 1,
 	}, {
 		.name	= "mii_ctrl",
 		.flags	= IORESOURCE_MEM,
@@ -211,6 +250,7 @@ static struct platform_device ar71xx_eth1_device = {
 	},
 };
 
+static int ar71xx_eth_instance __initdata;
 void __init ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
 				u32 phy_mask)
 {
@@ -235,6 +275,7 @@ void __init ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
 			BUG();
 		}
 		memcpy(ar71xx_eth0_data.mac_addr, ar71xx_mac_base, ETH_ALEN);
+		ar71xx_eth0_data.mac_addr[5] += ar71xx_eth_instance;
 		ar71xx_eth0_data.phy_if_mode = phy_if_mode;
 		ar71xx_eth0_data.phy_mask = phy_mask;
 		pdev = &ar71xx_eth0_device;
@@ -251,7 +292,7 @@ void __init ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
 			BUG();
 		}
 		memcpy(ar71xx_eth1_data.mac_addr, ar71xx_mac_base, ETH_ALEN);
-		ar71xx_eth1_data.mac_addr[5] += id;
+		ar71xx_eth1_data.mac_addr[5] += ar71xx_eth_instance;
 		ar71xx_eth1_data.phy_if_mode = phy_if_mode;
 		ar71xx_eth1_data.phy_mask = phy_mask;
 		pdev = &ar71xx_eth1_device;
@@ -261,8 +302,10 @@ void __init ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
 		break;
 	}
 
-	if (pdev)
+	if (pdev) {
 		platform_device_register(pdev);
+		ar71xx_eth_instance++;
+	}
 }
 
 static struct resource ar71xx_spi_resources[] = {
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/Makefile b/target/linux/ar71xx/files/drivers/net/ag71xx/Makefile
index 452ee36cbd..37c98d22a3 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/Makefile
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/Makefile
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_AG71XX)	+= ag71xx.o
 
-ag71xx-objs	:= ag71xx_main.o ag71xx_mii.o ag71xx_ethtool.o
+ag71xx-objs	:= ag71xx_main.o ag71xx_ethtool.o ag71xx_phy.o ag71xx_mdio.o
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
index 247667dc8e..230e4816d5 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
@@ -15,6 +15,7 @@
 #define __AG71XX_H
 
 #include <linux/kernel.h>
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
@@ -36,7 +37,7 @@
 #define ETH_FCS_LEN	4
 
 #define AG71XX_DRV_NAME		"ag71xx"
-#define AG71XX_DRV_VERSION	"0.3.10"
+#define AG71XX_DRV_VERSION	"0.4.0"
 
 #define AG71XX_NAPI_TX		1
 
@@ -67,8 +68,8 @@
 
 #define AG71XX_RX_RING_SIZE	128
 
-#undef DEBUG
-#ifdef DEBUG
+#undef AG71XX_DEBUG
+#ifdef AG71XX_DEBUG
 #define DBG(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
 #else
 #define DBG(fmt, args...)	do {} while (0)
@@ -104,8 +105,15 @@ struct ag71xx_ring {
 	unsigned int		size;
 };
 
+struct ag71xx_mdio {
+	struct mii_bus	mii_bus;
+	int		mii_irq[PHY_MAX_ADDR];
+	void __iomem	*mdio_base;
+};
+
 struct ag71xx {
 	void __iomem		*mac_base;
+	void __iomem		*mac_base2;
 	void __iomem		*mii_ctrl;
 
 	spinlock_t		lock;
@@ -116,8 +124,8 @@ struct ag71xx {
 	struct ag71xx_ring	rx_ring;
 	struct ag71xx_ring	tx_ring;
 
+	struct mii_bus		*mii_bus;
 	struct phy_device	*phy_dev;
-	struct mii_bus		mii_bus;
 
 	unsigned int		link;
 	unsigned int		speed;
@@ -126,42 +134,20 @@ struct ag71xx {
 
 extern struct ethtool_ops ag71xx_ethtool_ops;
 
-extern int ag71xx_mdio_init(struct ag71xx *ag, int id);
-extern void ag71xx_mdio_cleanup(struct ag71xx *ag);
-extern int ag71xx_mii_peek(struct ag71xx *ag);
-extern void ag71xx_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if);
-extern void ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, unsigned int speed);
-extern void ag71xx_link_update(struct ag71xx *ag);
+extern struct ag71xx_mdio *ag71xx_mdio_bus;
+extern int ag71xx_mdio_driver_init(void) __init;
+extern void ag71xx_mdio_driver_exit(void);
+
+extern int ag71xx_phy_connect(struct ag71xx *ag);
+extern void ag71xx_phy_disconnect(struct ag71xx *ag);
+extern void ag71xx_phy_start(struct ag71xx *ag);
+extern void ag71xx_phy_stop(struct ag71xx *ag);
 
 static inline struct ag71xx_platform_data *ag71xx_get_pdata(struct ag71xx *ag)
 {
 	return ag->pdev->dev.platform_data;
 }
 
-static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value)
-{
-	__raw_writel(value, ag->mac_base + reg);
-}
-
-static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg)
-{
-	return __raw_readl(ag->mac_base + reg);
-}
-
-static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask)
-{
-	void __iomem *r = ag->mac_base + reg;
-
-	__raw_writel(__raw_readl(r) | mask, r);
-}
-
-static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask)
-{
-	void __iomem *r = ag->mac_base + reg;
-
-	__raw_writel(__raw_readl(r) & ~mask, r);
-}
-
 static inline int ag71xx_desc_empty(struct ag71xx_desc *desc)
 {
 	return ((desc->ctrl & DESC_EMPTY) != 0);
@@ -242,6 +228,7 @@ static inline int ag71xx_desc_pktlen(struct ag71xx_desc *desc)
 #define MII_CFG_CLK_DIV_14	5
 #define MII_CFG_CLK_DIV_20	6
 #define MII_CFG_CLK_DIV_28	7
+#define MII_CFG_RESET		BIT(31)
 
 #define MII_CMD_WRITE		0x0
 #define MII_CMD_READ		0x1
@@ -263,12 +250,81 @@ static inline int ag71xx_desc_pktlen(struct ag71xx_desc *desc)
 
 #define FIFO_CFG5_BYTE_PER_CLK	BIT(19)
 
-#define MII_CTRL_SPEED_S	4
-#define MII_CTRL_SPEED_M	3
+#define MII_CTRL_IF_MASK	3
+#define MII_CTRL_SPEED_SHIFT	4
+#define MII_CTRL_SPEED_MASK	3
 #define MII_CTRL_SPEED_10	0
 #define MII_CTRL_SPEED_100	1
 #define MII_CTRL_SPEED_1000	2
 
+static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value)
+{
+	switch (reg) {
+	case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL:
+		__raw_writel(value, ag->mac_base + reg);
+		break;
+	case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS:
+		reg -= AG71XX_REG_MAC_IFCTL;
+		__raw_writel(value, ag->mac_base2 + reg);
+		break;
+	default:
+		BUG();
+	}
+}
+
+static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg)
+{
+	u32 ret;
+
+	switch (reg) {
+	case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL:
+		ret = __raw_readl(ag->mac_base + reg);
+		break;
+	case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS:
+		reg -= AG71XX_REG_MAC_IFCTL;
+		ret = __raw_readl(ag->mac_base2 + reg);
+		break;
+	}
+
+	return ret;
+}
+
+static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask)
+{
+	void __iomem *r;
+
+	switch (reg) {
+	case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL:
+		r = ag->mac_base + reg;
+		__raw_writel(__raw_readl(r) | mask, r);
+		break;
+	case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS:
+		r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL;
+		__raw_writel(__raw_readl(r) | mask, r);
+		break;
+	default:
+		BUG();
+	}
+}
+
+static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask)
+{
+	void __iomem *r;
+
+	switch (reg) {
+	case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL:
+		r = ag->mac_base + reg;
+		__raw_writel(__raw_readl(r) & ~mask, r);
+		break;
+	case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_INT_STATUS:
+		r = ag->mac_base2 + reg - AG71XX_REG_MAC_IFCTL;
+		__raw_writel(__raw_readl(r) & ~mask, r);
+		break;
+	default:
+		BUG();
+	}
+}
+
 static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints)
 {
 	ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints);
@@ -279,4 +335,36 @@ static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints)
 	ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints);
 }
 
+static inline void ag71xx_mii_ctrl_wr(struct ag71xx *ag, u32 value)
+{
+	__raw_writel(value, ag->mii_ctrl);
+}
+
+static inline u32 ag71xx_mii_ctrl_rr(struct ag71xx *ag)
+{
+	return __raw_readl(ag->mii_ctrl);
+}
+
+static void inline ag71xx_mii_ctrl_set_if(struct ag71xx *ag,
+					  unsigned int mii_if)
+{
+	u32 t;
+
+	t = ag71xx_mii_ctrl_rr(ag);
+	t &= ~(MII_CTRL_IF_MASK);
+	t |= (mii_if & MII_CTRL_IF_MASK);
+	ag71xx_mii_ctrl_wr(ag, t);
+}
+
+static void inline ag71xx_mii_ctrl_set_speed(struct ag71xx *ag,
+					     unsigned int speed)
+{
+	u32 t;
+
+	t = ag71xx_mii_ctrl_rr(ag);
+	t &= ~(MII_CTRL_SPEED_MASK << MII_CTRL_SPEED_SHIFT);
+	t |= (speed & MII_CTRL_SPEED_MASK) << MII_CTRL_SPEED_SHIFT;
+	ag71xx_mii_ctrl_wr(ag, t);
+}
+
 #endif /* _AG71XX_H */
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c
index 065f6033ab..88ed22a420 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c
@@ -22,16 +22,6 @@ static void ag71xx_dump_regs(struct ag71xx *ag)
 		ag71xx_rr(ag, AG71XX_REG_MAC_IPG),
 		ag71xx_rr(ag, AG71XX_REG_MAC_HDX),
 		ag71xx_rr(ag, AG71XX_REG_MAC_MFL));
-	DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n",
-		ag->dev->name,
-		ag71xx_rr(ag, AG71XX_REG_MII_CFG),
-		ag71xx_rr(ag, AG71XX_REG_MII_CMD),
-		ag71xx_rr(ag, AG71XX_REG_MII_ADDR));
-	DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n",
-		ag->dev->name,
-		ag71xx_rr(ag, AG71XX_REG_MII_CTRL),
-		ag71xx_rr(ag, AG71XX_REG_MII_STATUS),
-		ag71xx_rr(ag, AG71XX_REG_MII_IND));
 	DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n",
 		ag->dev->name,
 		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
@@ -286,8 +276,6 @@ static void ag71xx_hw_init(struct ag71xx *ag)
 
 	ag71xx_mii_ctrl_set_if(ag, pdata->mii_if);
 
-	ag71xx_wr(ag, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_28);
-
 	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000);
 	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff);
 	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, 0x0000ffff);
@@ -325,14 +313,7 @@ static int ag71xx_open(struct net_device *dev)
 	napi_enable(&ag->napi);
 
 	netif_carrier_off(dev);
-	if (ag->phy_dev) {
-		phy_start(ag->phy_dev);
-	} else {
-		ag->duplex = DUPLEX_FULL;
-		ag->speed = SPEED_100;
-		ag->link = 1;
-		ag71xx_link_update(ag);
-	}
+	ag71xx_phy_start(ag);
 
 	ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
 	ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma);
@@ -362,14 +343,7 @@ static int ag71xx_stop(struct net_device *dev)
 	ag71xx_hw_stop(ag);
 
 	netif_carrier_off(dev);
-	if (ag->phy_dev) {
-		phy_stop(ag->phy_dev);
-	} else {
-		ag->duplex = -1;
-		ag->link = 0;
-		ag->speed = 0;
-		ag71xx_link_update(ag);
-	}
+	ag71xx_phy_stop(ag);
 
 	napi_disable(&ag->napi);
 
@@ -717,6 +691,7 @@ static int __init ag71xx_probe(struct platform_device *pdev)
 	ag = netdev_priv(dev);
 	ag->pdev = pdev;
 	ag->dev = dev;
+	ag->mii_bus = &ag71xx_mdio_bus->mii_bus;
 	spin_lock_init(&ag->lock);
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base");
@@ -733,18 +708,32 @@ static int __init ag71xx_probe(struct platform_device *pdev)
 		goto err_free_dev;
 	}
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base2");
+	if (!res) {
+		dev_err(&pdev->dev, "no mac_base2 resource found\n");
+		err = -ENXIO;
+		goto err_unmap_base1;
+	}
+
+	ag->mac_base2 = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!ag->mac_base) {
+		dev_err(&pdev->dev, "unable to ioremap mac_base2\n");
+		err = -ENOMEM;
+		goto err_unmap_base1;
+	}
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mii_ctrl");
 	if (!res) {
 		dev_err(&pdev->dev, "no mii_ctrl resource found\n");
 		err = -ENXIO;
-		goto err_unmap_base;
+		goto err_unmap_base2;
 	}
 
 	ag->mii_ctrl = ioremap_nocache(res->start, res->end - res->start + 1);
 	if (!ag->mii_ctrl) {
 		dev_err(&pdev->dev, "unable to ioremap mii_ctrl\n");
 		err = -ENOMEM;
-		goto err_unmap_base;
+		goto err_unmap_base2;
 	}
 
 	dev->irq = platform_get_irq(pdev, 0);
@@ -790,7 +779,14 @@ static int __init ag71xx_probe(struct platform_device *pdev)
 
 	ag71xx_dump_regs(ag);
 
-	err = ag71xx_mdio_init(ag, pdev->id);
+	/* Reset the mdio bus explicitly */
+	if (ag->mii_bus) {
+		mutex_lock(&ag->mii_bus->mdio_lock);
+		ag->mii_bus->reset(ag->mii_bus);
+		mutex_unlock(&ag->mii_bus->mdio_lock);
+	}
+
+	err = ag71xx_phy_connect(ag);
 	if (err)
 		goto err_unregister_netdev;
 
@@ -804,7 +800,9 @@ err_free_irq:
 	free_irq(dev->irq, dev);
 err_unmap_mii_ctrl:
 	iounmap(ag->mii_ctrl);
-err_unmap_base:
+err_unmap_base2:
+	iounmap(ag->mac_base2);
+err_unmap_base1:
 	iounmap(ag->mac_base);
 err_free_dev:
 	kfree(dev);
@@ -820,12 +818,11 @@ static int __exit ag71xx_remove(struct platform_device *pdev)
 	if (dev) {
 		struct ag71xx *ag = netdev_priv(dev);
 
-		if (ag->phy_dev)
-			phy_disconnect(ag->phy_dev);
-		ag71xx_mdio_cleanup(ag);
+		ag71xx_phy_disconnect(ag);
 		unregister_netdev(dev);
 		free_irq(dev->irq, dev);
 		iounmap(ag->mii_ctrl);
+		iounmap(ag->mac_base2);
 		iounmap(ag->mac_base);
 		kfree(dev);
 		platform_set_drvdata(pdev, NULL);
@@ -844,7 +841,22 @@ static struct platform_driver ag71xx_driver = {
 
 static int __init ag71xx_module_init(void)
 {
-	return platform_driver_register(&ag71xx_driver);
+	int ret;
+
+	ret = ag71xx_mdio_driver_init();
+	if (ret)
+		goto err_out;
+
+	ret = platform_driver_register(&ag71xx_driver);
+	if (ret)
+		goto err_mdio_exit;
+
+	return 0;
+
+err_mdio_exit:
+	ag71xx_mdio_driver_exit();
+err_out:
+	return ret;
 }
 
 static void __exit ag71xx_module_exit(void)
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mdio.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mdio.c
new file mode 100644
index 0000000000..b58d3cb55e
--- /dev/null
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mdio.c
@@ -0,0 +1,226 @@
+/*
+ *  Atheros AR71xx built-in ethernet mac driver
+ *
+ *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Based on Atheros' AG7100 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include "ag71xx.h"
+
+#define AG71XX_MDIO_RETRY	1000
+#define AG71XX_MDIO_DELAY	5
+
+struct ag71xx_mdio *ag71xx_mdio_bus;
+
+static inline void ag71xx_mdio_wr(struct ag71xx_mdio *am, unsigned reg,
+				  u32 value)
+{
+	__raw_writel(value, am->mdio_base + reg - AG71XX_REG_MII_CFG);
+}
+
+static inline u32 ag71xx_mdio_rr(struct ag71xx_mdio *am, unsigned reg)
+{
+	return __raw_readl(am->mdio_base + reg - AG71XX_REG_MII_CFG);
+}
+
+static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am)
+{
+	DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n",
+		am->mii_bus.name,
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_CFG),
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_CMD),
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_ADDR));
+	DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n",
+		am->mii_bus.name,
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_CTRL),
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS),
+		ag71xx_mdio_rr(am, AG71XX_REG_MII_IND));
+}
+
+static int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg)
+{
+	int ret;
+	int i;
+
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
+			((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ);
+
+	i = AG71XX_MDIO_RETRY;
+	while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
+		if (i-- == 0) {
+			printk(KERN_ERR "%s: mii_read timed out\n",
+				am->mii_bus.name);
+			ret = 0xffff;
+			goto out;
+		}
+		udelay(AG71XX_MDIO_DELAY);
+	}
+
+	ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff;
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
+
+	DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
+
+out:
+	return ret;
+}
+
+static void ag71xx_mdio_mii_write(struct ag71xx_mdio *am,
+				  int addr, int reg, u16 val)
+{
+	int i;
+
+	DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
+
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
+			((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val);
+
+	i = AG71XX_MDIO_RETRY;
+	while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
+		if (i-- == 0) {
+			printk(KERN_ERR "%s: mii_write timed out\n",
+				am->mii_bus.name);
+			break;
+		}
+		udelay(AG71XX_MDIO_DELAY);
+	}
+}
+
+static int ag71xx_mdio_reset(struct mii_bus *bus)
+{
+	struct ag71xx_mdio *am = bus->priv;
+
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_RESET);
+	udelay(100);
+
+	ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_28);
+	udelay(100);
+
+	return 0;
+}
+
+static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
+{
+	struct ag71xx_mdio *am = bus->priv;
+
+	return ag71xx_mdio_mii_read(am, addr, reg);
+}
+
+static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+	struct ag71xx_mdio *am = bus->priv;
+
+	ag71xx_mdio_mii_write(am, addr, reg, val);
+	return 0;
+}
+
+static int __init ag71xx_mdio_probe(struct platform_device *pdev)
+{
+	struct ag71xx_mdio_platform_data *pdata;
+	struct ag71xx_mdio *am;
+	struct resource *res;
+	int i;
+	int err;
+
+	if (ag71xx_mdio_bus)
+		return -EBUSY;
+
+	am = kzalloc(sizeof(*am), GFP_KERNEL);
+	if (!am) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no iomem resource found\n");
+		err = -ENXIO;
+		goto err_out;
+	}
+
+	am->mdio_base = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!am->mdio_base) {
+		dev_err(&pdev->dev, "unable to ioremap registers\n");
+		err = -ENOMEM;
+		goto err_free_mdio;
+	}
+
+	am->mii_bus.name = "ag71xx_mdio";
+	am->mii_bus.read = ag71xx_mdio_read;
+	am->mii_bus.write = ag71xx_mdio_write;
+	am->mii_bus.reset = ag71xx_mdio_reset;
+	am->mii_bus.irq = am->mii_irq;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
+	am->mii_bus.id = 0;
+#else
+	snprintf(am->mii_bus.id, MII_BUS_ID_SIZE, "%x", 0);
+#endif
+	am->mii_bus.priv = am;
+	am->mii_bus.dev = &pdev->dev;
+
+	pdata = pdev->dev.platform_data;
+	if (pdata)
+		am->mii_bus.phy_mask = pdata->phy_mask;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		am->mii_irq[i] = PHY_POLL;
+
+	err = mdiobus_register(&am->mii_bus);
+	if (err)
+		goto err_iounmap;
+
+	ag71xx_mdio_dump_regs(am);
+
+	platform_set_drvdata(pdev, am);
+	ag71xx_mdio_bus = am;
+	return 0;
+
+err_iounmap:
+	iounmap(am->mdio_base);
+err_free_mdio:
+	kfree(am);
+err_out:
+	return err;
+}
+
+static int __exit ag71xx_mdio_remove(struct platform_device *pdev)
+{
+	struct ag71xx_mdio *am = platform_get_drvdata(pdev);
+
+	if (am) {
+		ag71xx_mdio_bus = NULL;
+		mdiobus_unregister(&am->mii_bus);
+		iounmap(am->mdio_base);
+		kfree(am);
+		platform_set_drvdata(pdev, NULL);
+	}
+
+	return 0;
+}
+
+static struct platform_driver ag71xx_mdio_driver = {
+	.probe		= ag71xx_mdio_probe,
+	.remove		= __exit_p(ag71xx_mdio_remove),
+	.driver = {
+		.name	= "ag71xx-mdio",
+	}
+};
+
+int ag71xx_mdio_driver_init(void)
+{
+	return platform_driver_register(&ag71xx_mdio_driver);
+}
+
+void ag71xx_mdio_driver_exit(void)
+{
+	platform_driver_unregister(&ag71xx_mdio_driver);
+}
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mii.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mii.c
deleted file mode 100644
index 65940d2017..0000000000
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_mii.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- *  Atheros AR71xx built-in ethernet mac driver
- *
- *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
- *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
- *
- *  Based on Atheros' AG7100 driver
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- */
-
-#include "ag71xx.h"
-
-#define AG71XX_MII_RETRY	1000
-#define AG71XX_MII_DELAY	5
-
-static inline void ag71xx_mii_ctrl_wr(struct ag71xx *ag, u32 value)
-{
-	__raw_writel(value, ag->mii_ctrl);
-}
-
-static inline u32 ag71xx_mii_ctrl_rr(struct ag71xx *ag)
-{
-	return __raw_readl(ag->mii_ctrl);
-}
-
-void ag71xx_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if)
-{
-	u32 t;
-
-	t = ag71xx_mii_ctrl_rr(ag);
-	t &= ~(0x3);
-	t |= (mii_if & 0x3);
-	ag71xx_mii_ctrl_wr(ag, t);
-}
-
-void ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, unsigned int speed)
-{
-	u32 t;
-
-	t = ag71xx_mii_ctrl_rr(ag);
-	t &= ~(0x3 << 4);
-	t |= (speed & 0x3) << 4;
-	ag71xx_mii_ctrl_wr(ag, t);
-}
-
-static int ag71xx_mii_read(struct ag71xx *ag, int addr, int reg)
-{
-	int ret;
-	int i;
-
-	ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
-	ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
-			((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
-	ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_READ);
-
-	i = AG71XX_MII_RETRY;
-	while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
-		if (i-- == 0) {
-			printk(KERN_ERR "%s: mii_read timed out\n",
-				ag->dev->name);
-			ret = 0xffff;
-			goto out;
-		}
-		udelay(AG71XX_MII_DELAY);
-	}
-
-	ret = ag71xx_rr(ag, AG71XX_REG_MII_STATUS) & 0xffff;
-	ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
-
-	DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
-
-out:
-	return ret;
-}
-
-static void ag71xx_mii_write(struct ag71xx *ag, int addr, int reg, u16 val)
-{
-	int i;
-
-	DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
-
-	ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
-			((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
-	ag71xx_wr(ag, AG71XX_REG_MII_CTRL, val);
-
-	i = AG71XX_MII_RETRY;
-	while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
-		if (i-- == 0) {
-			printk(KERN_ERR "%s: mii_write timed out\n",
-				ag->dev->name);
-			break;
-		}
-		udelay(AG71XX_MII_DELAY);
-	}
-}
-
-int ag71xx_mii_peek(struct ag71xx *ag)
-{
-	int cnt;
-	int i;
-
-	cnt = 0;
-	for (i = 0; i < PHY_MAX_ADDR; i++) {
-		u16 bmsr, id1, id2, bmcr, advert, lpa;
-
-		bmsr = ag71xx_mii_read(ag, i, MII_BMSR);
-		bmcr = ag71xx_mii_read(ag, i, MII_BMCR);
-		id1 = ag71xx_mii_read(ag, i, MII_PHYSID1);
-		id2 = ag71xx_mii_read(ag, i, MII_PHYSID2);
-		advert = ag71xx_mii_read(ag, i, MII_ADVERTISE);
-		lpa = ag71xx_mii_read(ag, i, MII_LPA);
-		DBG("%s: phy%02d bmsr=%04x, bmcr=%04x, "
-			"id=%04x.%04x, advertise=%04x, lpa=%04x\n",
-			ag->dev->name, i,
-			bmsr, bmcr, id1, id2, advert, lpa);
-
-		if ((bmsr | bmcr | id1 | id2 | advert | lpa) != 0)
-			cnt++;
-	}
-
-	return cnt;
-}
-
-#define PLL_SEC_CONFIG		0x18050004
-#define PLL_ETH0_INT_CLOCK	0x18050010
-#define PLL_ETH1_INT_CLOCK	0x18050014
-#define PLL_ETH_EXT_CLOCK	0x18050018
-
-#define ag7100_pll_shift(_ag)   (((_ag)->pdev->id) ? 19 : 17)
-#define ag7100_pll_offset(_ag)	(((_ag)->pdev->id) ? PLL_ETH1_INT_CLOCK \
-						   : PLL_ETH0_INT_CLOCK)
-
-static void ag71xx_set_pll(struct ag71xx *ag, u32 pll_val)
-{
-	void __iomem *pll_reg = ioremap_nocache(ag7100_pll_offset(ag), 4);
-	void __iomem *pll_cfg = ioremap_nocache(PLL_SEC_CONFIG, 4);
-	u32 s;
-	u32 t;
-
-	s = ag7100_pll_shift(ag);
-
-	t = __raw_readl(pll_cfg);
-	t &= ~(3 << s);
-	t |=  (2 << s);
-	__raw_writel(t, pll_cfg);
-	udelay(100);
-
-	__raw_writel(pll_val, pll_reg);
-
-	t |= (3 << s);
-	__raw_writel(t, pll_cfg);
-	udelay(100);
-
-	t &= ~(3 << s);
-	__raw_writel(t, pll_cfg);
-	udelay(100);
-	DBG("%s: pll_reg %#x: %#x\n", ag->dev->name,
-			(unsigned int)pll_reg, __raw_readl(pll_reg));
-
-	iounmap(pll_cfg);
-	iounmap(pll_reg);
-}
-
-static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
-{
-	switch (ag->speed) {
-	case SPEED_1000:
-		return "1000";
-	case SPEED_100:
-		return "100";
-	case SPEED_10:
-		return "10";
-	}
-
-	return "?";
-}
-
-#if 1
-#define PLL_VAL_1000	0x00110000
-#define PLL_VAL_100	0x00001099
-#define PLL_VAL_10	0x00991099
-#else
-#define PLL_VAL_1000	0x01111000
-#define PLL_VAL_100	0x09991000
-#define PLL_VAL_10	0x09991999
-#endif
-
-void ag71xx_link_update(struct ag71xx *ag)
-{
-	u32 cfg2;
-	u32 ifctl;
-	u32 pll;
-	u32 fifo5;
-	u32 mii_speed;
-
-	if (!ag->link) {
-		netif_carrier_off(ag->dev);
-		printk(KERN_INFO "%s: link down\n", ag->dev->name);
-		return;
-	}
-
-	cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
-	cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
-	cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
-
-	ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
-	ifctl &= ~(MAC_IFCTL_SPEED);
-
-	fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
-	fifo5 &= ~FIFO_CFG5_BYTE_PER_CLK;
-
-	switch (ag->speed) {
-	case SPEED_1000:
-		mii_speed =  MII_CTRL_SPEED_1000;
-		cfg2 |= MAC_CFG2_IF_1000;
-		pll = PLL_VAL_1000;
-		fifo5 |= FIFO_CFG5_BYTE_PER_CLK;
-		break;
-	case SPEED_100:
-		mii_speed = MII_CTRL_SPEED_100;
-		cfg2 |= MAC_CFG2_IF_10_100;
-		ifctl |= MAC_IFCTL_SPEED;
-		pll = PLL_VAL_100;
-		break;
-	case SPEED_10:
-		mii_speed = MII_CTRL_SPEED_10;
-		cfg2 |= MAC_CFG2_IF_10_100;
-		pll = PLL_VAL_10;
-		break;
-	default:
-		BUG();
-		return;
-	}
-
-	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);
-	ag71xx_set_pll(ag, pll);
-	ag71xx_mii_ctrl_set_speed(ag, mii_speed);
-
-	ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
-	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
-	ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
-
-	netif_carrier_on(ag->dev);
-	printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
-		ag->dev->name,
-		ag71xx_speed_str(ag),
-		(DUPLEX_FULL == ag->duplex) ? "Full" : "Half");
-
-	DBG("%s: fifo1=%#x, fifo2=%#x, fifo3=%#x, fifo4=%#x, fifo5=%#x\n",
-		ag->dev->name,
-		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
-		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2),
-		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
-		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
-		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
-
-	DBG("%s: mac_cfg2=%#x, ifctl=%#x, mii_ctrl=%#x\n",
-		ag->dev->name,
-		ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
-		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
-		ag71xx_mii_ctrl_rr(ag));
-}
-
-static void ag71xx_link_adjust(struct net_device *dev)
-{
-	struct ag71xx *ag = netdev_priv(dev);
-	struct phy_device *phydev = ag->phy_dev;
-	unsigned long flags;
-	int status_change = 0;
-
-	spin_lock_irqsave(&ag->lock, flags);
-
-	if (phydev->link) {
-		if (ag->duplex != phydev->duplex
-		    || ag->speed != phydev->speed) {
-			status_change = 1;
-		}
-	}
-
-	if (phydev->link != ag->link) {
-		if (phydev->link)
-			netif_schedule(dev);
-
-		status_change = 1;
-	}
-
-	ag->link = phydev->link;
-	ag->duplex = phydev->duplex;
-	ag->speed = phydev->speed;
-
-	if (status_change)
-		ag71xx_link_update(ag);
-
-	spin_unlock_irqrestore(&ag->lock, flags);
-}
-
-static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
-{
-	struct ag71xx *ag = bus->priv;
-
-	return ag71xx_mii_read(ag, addr, reg);
-}
-
-static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-	struct ag71xx *ag = bus->priv;
-
-	ag71xx_mii_write(ag, addr, reg, val);
-	return 0;
-}
-
-static int ag71xx_mdio_reset(struct mii_bus *bus)
-{
-	/* TODO */
-	return 0;
-}
-
-static int ag71xx_mdio_probe(struct ag71xx *ag)
-{
-	struct net_device *dev = ag->dev;
-	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
-	struct phy_device *phydev = NULL;
-	int phy_count = 0;
-	int phy_addr;
-
-	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
-		if (!(pdata->phy_mask & (1 << phy_addr)))
-			continue;
-
-		if (ag->mii_bus.phy_map[phy_addr] == NULL)
-			continue;
-
-		DBG("%s: PHY found at %s, uid=%08x\n",
-			dev->name,
-			ag->mii_bus.phy_map[phy_addr]->dev.bus_id,
-			ag->mii_bus.phy_map[phy_addr]->phy_id);
-
-		if (phydev == NULL)
-			phydev = ag->mii_bus.phy_map[phy_addr];
-
-		phy_count++;
-	}
-
-	switch (phy_count) {
-	case 0:
-		printk(KERN_ERR "%s: no PHY found\n", dev->name);
-		return -ENODEV;
-	case 1:
-		ag->phy_dev = phy_connect(dev, phydev->dev.bus_id,
-			&ag71xx_link_adjust, 0, pdata->phy_if_mode);
-
-		if (IS_ERR(ag->phy_dev)) {
-			printk(KERN_ERR "%s: could not connect to PHY at %s\n",
-				dev->name, phydev->dev.bus_id);
-			return PTR_ERR(ag->phy_dev);
-		}
-
-		/* mask with MAC supported features */
-		phydev->supported &= (SUPPORTED_10baseT_Half
-			| SUPPORTED_10baseT_Full
-			| SUPPORTED_100baseT_Half
-			| SUPPORTED_100baseT_Full
-			| SUPPORTED_Autoneg
-			| SUPPORTED_MII
-			| SUPPORTED_TP);
-
-		phydev->advertising = phydev->supported;
-
-		printk(KERN_DEBUG "%s: connected to PHY at %s "
-			"[uid=%08x, driver=%s]\n",
-			dev->name, phydev->dev.bus_id,
-			phydev->phy_id, phydev->drv->name);
-
-		ag->link = 0;
-		ag->speed = 0;
-		ag->duplex = -1;
-		break;
-	default:
-		ag->phy_dev = NULL;
-		printk(KERN_DEBUG "%s: connected to multiple PHYs (%d)\n",
-			dev->name, phy_count);
-		break;
-	}
-
-	return 0;
-}
-
-int ag71xx_mdio_init(struct ag71xx *ag, int id)
-{
-	int err;
-	int i;
-
-	ag->mii_bus.name = "ag71xx_mii";
-	ag->mii_bus.read = ag71xx_mdio_read;
-	ag->mii_bus.write = ag71xx_mdio_write;
-	ag->mii_bus.reset = ag71xx_mdio_reset;
-	ag->mii_bus.id = id;
-	ag->mii_bus.priv = ag;
-	ag->mii_bus.dev = &ag->dev->dev;
-
-	ag->mii_bus.irq = kmalloc(sizeof(*ag->mii_bus.irq) * PHY_MAX_ADDR,
-				  GFP_KERNEL);
-	if (!ag->mii_bus.irq) {
-		err = -ENOMEM;
-		goto err_out;
-	}
-
-	for (i = 0; i < PHY_MAX_ADDR; i++)
-		ag->mii_bus.irq[i] = PHY_POLL;
-
-	err = mdiobus_register(&ag->mii_bus);
-	if (err)
-		goto err_free_irqs;
-
-	err = ag71xx_mdio_probe(ag);
-	if (err)
-		goto err_unregister_bus;
-
-	return 0;
-
-err_unregister_bus:
-	mdiobus_unregister(&ag->mii_bus);
-err_free_irqs:
-	kfree(ag->mii_bus.irq);
-err_out:
-	return err;
-}
-
-void ag71xx_mdio_cleanup(struct ag71xx *ag)
-{
-	mdiobus_unregister(&ag->mii_bus);
-	kfree(ag->mii_bus.irq);
-}
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
new file mode 100644
index 0000000000..b1838d578b
--- /dev/null
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
@@ -0,0 +1,290 @@
+/*
+ *  Atheros AR71xx built-in ethernet mac driver
+ *
+ *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Based on Atheros' AG7100 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include "ag71xx.h"
+
+#define PLL_SEC_CONFIG		0x18050004
+#define PLL_ETH0_INT_CLOCK	0x18050010
+#define PLL_ETH1_INT_CLOCK	0x18050014
+#define PLL_ETH_EXT_CLOCK	0x18050018
+
+#define ag71xx_pll_shift(_ag)   (((_ag)->pdev->id) ? 19 : 17)
+#define ag71xx_pll_offset(_ag)	(((_ag)->pdev->id) ? PLL_ETH1_INT_CLOCK \
+						   : PLL_ETH0_INT_CLOCK)
+
+static void ag71xx_set_pll(struct ag71xx *ag, u32 pll_val)
+{
+	void __iomem *pll_reg = ioremap_nocache(ag71xx_pll_offset(ag), 4);
+	void __iomem *pll_cfg = ioremap_nocache(PLL_SEC_CONFIG, 4);
+	u32 s;
+	u32 t;
+
+	s = ag71xx_pll_shift(ag);
+
+	t = __raw_readl(pll_cfg);
+	t &= ~(3 << s);
+	t |=  (2 << s);
+	__raw_writel(t, pll_cfg);
+	udelay(100);
+
+	__raw_writel(pll_val, pll_reg);
+
+	t |= (3 << s);
+	__raw_writel(t, pll_cfg);
+	udelay(100);
+
+	t &= ~(3 << s);
+	__raw_writel(t, pll_cfg);
+	udelay(100);
+	DBG("%s: pll_reg %#x: %#x\n", ag->dev->name,
+			(unsigned int)pll_reg, __raw_readl(pll_reg));
+
+	iounmap(pll_cfg);
+	iounmap(pll_reg);
+}
+
+static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
+{
+	switch (ag->speed) {
+	case SPEED_1000:
+		return "1000";
+	case SPEED_100:
+		return "100";
+	case SPEED_10:
+		return "10";
+	}
+
+	return "?";
+}
+
+#if 1
+#define PLL_VAL_1000	0x00110000
+#define PLL_VAL_100	0x00001099
+#define PLL_VAL_10	0x00991099
+#else
+#define PLL_VAL_1000	0x01111000
+#define PLL_VAL_100	0x09991000
+#define PLL_VAL_10	0x09991999
+#endif
+
+static void ag71xx_phy_link_update(struct ag71xx *ag)
+{
+	u32 cfg2;
+	u32 ifctl;
+	u32 pll;
+	u32 fifo5;
+	u32 mii_speed;
+
+	if (!ag->link) {
+		netif_carrier_off(ag->dev);
+		printk(KERN_INFO "%s: link down\n", ag->dev->name);
+		return;
+	}
+
+	cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
+	cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
+	cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
+
+	ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
+	ifctl &= ~(MAC_IFCTL_SPEED);
+
+	fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
+	fifo5 &= ~FIFO_CFG5_BYTE_PER_CLK;
+
+	switch (ag->speed) {
+	case SPEED_1000:
+		mii_speed =  MII_CTRL_SPEED_1000;
+		cfg2 |= MAC_CFG2_IF_1000;
+		pll = PLL_VAL_1000;
+		fifo5 |= FIFO_CFG5_BYTE_PER_CLK;
+		break;
+	case SPEED_100:
+		mii_speed = MII_CTRL_SPEED_100;
+		cfg2 |= MAC_CFG2_IF_10_100;
+		ifctl |= MAC_IFCTL_SPEED;
+		pll = PLL_VAL_100;
+		break;
+	case SPEED_10:
+		mii_speed = MII_CTRL_SPEED_10;
+		cfg2 |= MAC_CFG2_IF_10_100;
+		pll = PLL_VAL_10;
+		break;
+	default:
+		BUG();
+		return;
+	}
+
+	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);
+	ag71xx_set_pll(ag, pll);
+	ag71xx_mii_ctrl_set_speed(ag, mii_speed);
+
+	ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
+	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
+	ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
+
+	netif_carrier_on(ag->dev);
+	printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
+		ag->dev->name,
+		ag71xx_speed_str(ag),
+		(DUPLEX_FULL == ag->duplex) ? "Full" : "Half");
+
+	DBG("%s: fifo1=%#x, fifo2=%#x, fifo3=%#x, fifo4=%#x, fifo5=%#x\n",
+		ag->dev->name,
+		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
+		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2),
+		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
+		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
+		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
+
+	DBG("%s: mac_cfg2=%#x, ifctl=%#x, mii_ctrl=%#x\n",
+		ag->dev->name,
+		ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
+		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
+		ag71xx_mii_ctrl_rr(ag));
+}
+
+static void ag71xx_phy_link_adjust(struct net_device *dev)
+{
+	struct ag71xx *ag = netdev_priv(dev);
+	struct phy_device *phydev = ag->phy_dev;
+	unsigned long flags;
+	int status_change = 0;
+
+	spin_lock_irqsave(&ag->lock, flags);
+
+	if (phydev->link) {
+		if (ag->duplex != phydev->duplex
+		    || ag->speed != phydev->speed) {
+			status_change = 1;
+		}
+	}
+
+	if (phydev->link != ag->link) {
+		if (phydev->link)
+			netif_schedule(dev);
+
+		status_change = 1;
+	}
+
+	ag->link = phydev->link;
+	ag->duplex = phydev->duplex;
+	ag->speed = phydev->speed;
+
+	if (status_change)
+		ag71xx_phy_link_update(ag);
+
+	spin_unlock_irqrestore(&ag->lock, flags);
+}
+
+void ag71xx_phy_start(struct ag71xx *ag)
+{
+	if (ag->phy_dev) {
+		phy_start(ag->phy_dev);
+	} else {
+		ag->duplex = DUPLEX_FULL;
+		ag->speed = SPEED_100;
+		ag->link = 1;
+		ag71xx_phy_link_update(ag);
+	}
+}
+
+void ag71xx_phy_stop(struct ag71xx *ag)
+{
+	if (ag->phy_dev) {
+		phy_stop(ag->phy_dev);
+	} else {
+		ag->duplex = -1;
+		ag->link = 0;
+		ag->speed = 0;
+		ag71xx_phy_link_update(ag);
+	}
+}
+
+int ag71xx_phy_connect(struct ag71xx *ag)
+{
+	struct net_device *dev = ag->dev;
+	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
+	struct phy_device *phydev = NULL;
+	int phy_count = 0;
+	int phy_addr;
+
+	if (ag->mii_bus) {
+		/* TODO: use mutex of the mdio bus */
+		for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+			if (!(pdata->phy_mask & (1 << phy_addr)))
+				continue;
+
+			if (ag->mii_bus->phy_map[phy_addr] == NULL)
+				continue;
+
+			DBG("%s: PHY found at %s, uid=%08x\n",
+				dev->name,
+				ag->mii_bus->phy_map[phy_addr]->dev.bus_id,
+				ag->mii_bus->phy_map[phy_addr]->phy_id);
+
+			if (phydev == NULL)
+				phydev = ag->mii_bus->phy_map[phy_addr];
+
+			phy_count++;
+		}
+	}
+
+	switch (phy_count) {
+	case 0:
+		printk(KERN_ERR "%s: no PHY found\n", dev->name);
+		return -ENODEV;
+	case 1:
+		ag->phy_dev = phy_connect(dev, phydev->dev.bus_id,
+			&ag71xx_phy_link_adjust, 0, pdata->phy_if_mode);
+
+		if (IS_ERR(ag->phy_dev)) {
+			printk(KERN_ERR "%s: could not connect to PHY at %s\n",
+				dev->name, phydev->dev.bus_id);
+			return PTR_ERR(ag->phy_dev);
+		}
+
+		/* mask with MAC supported features */
+		phydev->supported &= (SUPPORTED_10baseT_Half
+			| SUPPORTED_10baseT_Full
+			| SUPPORTED_100baseT_Half
+			| SUPPORTED_100baseT_Full
+			| SUPPORTED_Autoneg
+			| SUPPORTED_MII
+			| SUPPORTED_TP);
+
+		phydev->advertising = phydev->supported;
+
+		printk(KERN_DEBUG "%s: connected to PHY at %s "
+			"[uid=%08x, driver=%s]\n",
+			dev->name, phydev->dev.bus_id,
+			phydev->phy_id, phydev->drv->name);
+
+		ag->link = 0;
+		ag->speed = 0;
+		ag->duplex = -1;
+		break;
+	default:
+		ag->phy_dev = NULL;
+		printk(KERN_DEBUG "%s: connected to multiple PHYs (%d)\n",
+			dev->name, phy_count);
+		break;
+	}
+
+	return 0;
+}
+
+void ag71xx_phy_disconnect(struct ag71xx *ag)
+{
+	if (ag->phy_dev)
+		phy_disconnect(ag->phy_dev);
+}
diff --git a/target/linux/ar71xx/files/include/asm-mips/mach-ar71xx/platform.h b/target/linux/ar71xx/files/include/asm-mips/mach-ar71xx/platform.h
index f24ff800b5..820a1cb1d5 100644
--- a/target/linux/ar71xx/files/include/asm-mips/mach-ar71xx/platform.h
+++ b/target/linux/ar71xx/files/include/asm-mips/mach-ar71xx/platform.h
@@ -26,6 +26,10 @@ struct ag71xx_platform_data {
 	u8		mac_addr[ETH_ALEN];
 };
 
+struct ag71xx_mdio_platform_data {
+	u32		phy_mask;
+};
+
 struct ar71xx_spi_platform_data {
 	unsigned	bus_num;
 	unsigned	num_chipselect;
@@ -43,4 +47,6 @@ extern void ar71xx_set_mac_base(char *mac_str) __init;
 extern void ar71xx_add_device_eth(unsigned int id, phy_interface_t phy_if_mode,
 				u32 phy_mask) __init;
 
+extern void ar71xx_add_device_mdio(u32 phy_mask) __init;
+
 #endif /* __ASM_MACH_AR71XX_PLATFORM_H */
diff --git a/target/linux/ar71xx/patches-2.6.26/801-ag71xx_mii_bus_id_fix.patch b/target/linux/ar71xx/patches-2.6.26/801-ag71xx_mii_bus_id_fix.patch
deleted file mode 100644
index 60f2b7f96d..0000000000
--- a/target/linux/ar71xx/patches-2.6.26/801-ag71xx_mii_bus_id_fix.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/drivers/net/ag71xx/ag71xx_mii.c
-+++ b/drivers/net/ag71xx/ag71xx_mii.c
-@@ -397,7 +397,7 @@
- 	ag->mii_bus.read = ag71xx_mdio_read;
- 	ag->mii_bus.write = ag71xx_mdio_write;
- 	ag->mii_bus.reset = ag71xx_mdio_reset;
--	ag->mii_bus.id = id;
-+	snprintf(ag->mii_bus.id, MII_BUS_ID_SIZE, "%x", id);
- 	ag->mii_bus.priv = ag;
- 	ag->mii_bus.dev = &ag->dev->dev;
- 
-- 
cgit v1.2.3