From: Felix Fietkau Date: Sun, 20 Apr 2008 08:29:01 +0000 (+0000) Subject: Add support for the ultra-crappy Marvell 88E6060, which is used in Fonera+ and the... X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;h=f7c23c7a96ff478be34eab821d0db032d255dd17;hp=53e02390125712bc63c7146797e9e94629aec056 Add support for the ultra-crappy Marvell 88E6060, which is used in Fonera+ and the upcoming Fonera 2.0 SVN-Revision: 10876 --- diff --git a/target/linux/atheros/config-2.6.23 b/target/linux/atheros/config-2.6.23 index 2f69da0ba7..86824273e3 100644 --- a/target/linux/atheros/config-2.6.23 +++ b/target/linux/atheros/config-2.6.23 @@ -134,6 +134,7 @@ CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_ROM is not set # CONFIG_MTD_SLRAM is not set CONFIG_MTD_SPIFLASH=y +CONFIG_MVSWITCH_PHY=y CONFIG_NEW_GPIO=y # CONFIG_NO_IOPORT is not set # CONFIG_PAGE_SIZE_16KB is not set diff --git a/target/linux/atheros/patches-2.6.23/200-ar2313_enable_mvswitch.patch b/target/linux/atheros/patches-2.6.23/200-ar2313_enable_mvswitch.patch new file mode 100644 index 0000000000..b209aee0c8 --- /dev/null +++ b/target/linux/atheros/patches-2.6.23/200-ar2313_enable_mvswitch.patch @@ -0,0 +1,38 @@ +Index: linux-2.6.23.16/drivers/net/ar2313/ar2313.c +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/ar2313/ar2313.c 2008-04-20 10:26:15.000000000 +0200 ++++ linux-2.6.23.16/drivers/net/ar2313/ar2313.c 2008-04-20 10:26:16.000000000 +0200 +@@ -955,7 +955,7 @@ + dev->stats.rx_bytes += skb->len; + skb->protocol = eth_type_trans(skb, dev); + /* pass the packet to upper layers */ +- netif_rx(skb); ++ sp->rx(skb); + + skb_new->dev = dev; + /* 16 bit align */ +@@ -1370,6 +1370,11 @@ + return PTR_ERR(phydev); + } + ++ if (phydev->netif_rx) ++ sp->rx = phydev->netif_rx; ++ else ++ sp->rx = netif_rx; ++ + /* mask with MAC supported features */ + phydev->supported &= (SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full +Index: linux-2.6.23.16/drivers/net/ar2313/ar2313.h +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/ar2313/ar2313.h 2008-04-20 10:26:15.000000000 +0200 ++++ linux-2.6.23.16/drivers/net/ar2313/ar2313.h 2008-04-20 10:26:16.000000000 +0200 +@@ -107,6 +107,8 @@ + */ + struct ar2313_private { + struct net_device *dev; ++ int (*rx)(struct sk_buff *skb); ++ + int version; + u32 mb[2]; + diff --git a/target/linux/generic-2.6/config-2.6.23 b/target/linux/generic-2.6/config-2.6.23 index baac771313..20efb3ce90 100644 --- a/target/linux/generic-2.6/config-2.6.23 +++ b/target/linux/generic-2.6/config-2.6.23 @@ -734,6 +734,7 @@ CONFIG_MSDOS_PARTITION=y CONFIG_MTD_ROOTFS_ROOT_DEV=y CONFIG_MTD_ROOTFS_SPLIT=y # CONFIG_MTD_UBI is not set +# CONFIG_MVSWITCH_PHY is not set # CONFIG_MWAVE is not set # CONFIG_MYRI10GE is not set # CONFIG_NCP_FS is not set diff --git a/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c new file mode 100644 index 0000000000..3e644c2232 --- /dev/null +++ b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c @@ -0,0 +1,405 @@ +/* + * Marvell 88E6060 switch driver + * Copyright (c) 2008 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "mvswitch.h" + +MODULE_DESCRIPTION("Marvell 88E6060 Switch driver"); +MODULE_AUTHOR("Felix Fietkau"); +MODULE_LICENSE("GPL"); + +struct mvswitch_priv { + /* the driver's tx function */ + int (*hardstart)(struct sk_buff *skb, struct net_device *dev); + struct vlan_group *grp; + u8 vlans[16]; +}; + +#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv) + +static inline u16 +r16(struct phy_device *phydev, int addr, int reg) +{ + return phydev->bus->read(phydev->bus, addr, reg); +} + +static inline void +w16(struct phy_device *phydev, int addr, int reg, u16 val) +{ + phydev->bus->write(phydev->bus, addr, reg, val); +} + +static int +mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct mvswitch_priv *priv; + struct vlan_ethhdr *eh; + char *buf = NULL; + u16 vid; + + priv = dev->phy_ptr; + if (unlikely(!priv)) + goto error; + + if (unlikely(skb->len < 16)) + goto error; + + eh = (struct vlan_ethhdr *) skb->data; + if (be16_to_cpu(eh->h_vlan_proto) != 0x8100) + goto error; + + vid = be16_to_cpu(eh->h_vlan_TCI) & VLAN_VID_MASK; + if (unlikely((vid > 15 || !priv->vlans[vid]))) + goto error; + + if (skb->len <= 64) { + if (pskb_expand_head(skb, 0, 68 - skb->len, GFP_ATOMIC)) { + if (net_ratelimit()) + printk("%s: failed to expand/update skb for the switch\n", dev->name); + goto error; + } + + buf = skb->data + 64; + skb->len = 68; + } else { + if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) { + if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC)) { + if (net_ratelimit()) + printk("%s: failed to expand/update skb for the switch\n", dev->name); + goto error; + } + } + buf = skb_put(skb, 4); + } + + /* move the ethernet header 4 bytes forward, overwriting the vlan tag */ + memmove(skb->data + 4, skb->data, 12); + skb->data += 4; + skb->len -= 4; + skb->mac_header += 4; + + if (!buf) + goto error; + + /* append the tag */ + *((u32 *) buf) = ( + (0x80 << 24) | + ((priv->vlans[vid] & 0x1f) << 16) + ); + + return priv->hardstart(skb, dev); + +error: + /* any errors? drop the packet! */ + dev_kfree_skb_any(skb); + return 0; +} + +static int +mvswitch_mangle_rx(struct sk_buff *skb, int napi) +{ + struct mvswitch_priv *priv; + struct net_device *dev; + int vlan = -1; + unsigned char *buf; + int i; + + dev = skb->dev; + if (!dev) + goto error; + + priv = dev->phy_ptr; + if (!priv) + goto error; + + if (!priv->grp) + goto error; + + buf = skb->data + skb->len - 4; + if (buf[0] != 0x80) + goto error; + + /* look for the vlan matching the incoming port */ + for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) { + if ((1 << buf[1]) & priv->vlans[i]) + vlan = i; + } + + if (vlan == -1) + goto error; + + if (napi) + return vlan_hwaccel_receive_skb(skb, priv->grp, vlan); + else + return vlan_hwaccel_rx(skb, priv->grp, vlan); + +error: + /* no vlan? eat the packet! */ + dev_kfree_skb_any(skb); + return 0; +} + + +static int +mvswitch_netif_rx(struct sk_buff *skb) +{ + return mvswitch_mangle_rx(skb, 0); +} + +static int +mvswitch_netif_receive_skb(struct sk_buff *skb) +{ + return mvswitch_mangle_rx(skb, 1); +} + + +static void +mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct mvswitch_priv *priv = dev->phy_ptr; + priv->grp = grp; +} + + +static int +mvswitch_config_init(struct phy_device *pdev) +{ + struct mvswitch_priv *priv = to_mvsw(pdev); + struct net_device *dev = pdev->attached_dev; + u8 vlmap = 0; + int i; + + if (!dev) + return -EINVAL; + + printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name); + pdev->supported = ADVERTISED_100baseT_Full; + pdev->advertising = ADVERTISED_100baseT_Full; + dev->phy_ptr = priv; + + /* initialize default vlans */ + for (i = 0; i < MV_PORTS; i++) + priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i); + + /* before entering reset, disable all ports */ + for (i = 0; i < MV_PORTS; i++) + w16(pdev, MV_PORTREG(CONTROL, i), 0x00); + + msleep(2); /* wait for the status change to settle in */ + + /* put the device in reset and set ATU flags */ + w16(pdev, MV_SWITCHREG(ATU_CTRL), + MV_ATUCTL_RESET | + MV_ATUCTL_ATU_1K | + MV_ATUCTL_AGETIME(4080) /* maximum */ + ); + + i = 100; /* timeout */ + do { + if (!(r16(pdev, MV_SWITCHREG(ATU_CTRL)) & MV_ATUCTL_RESET)) + break; + msleep(1); + } while (--i > 0); + + if (!i) { + printk("%s: Timeout waiting for the switch to reset.\n", dev->name); + return -ETIMEDOUT; + } + + /* initialize the cpu port */ + w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT), + MV_PORTCTRL_ENABLED | + MV_PORTCTRL_VLANTUN | + MV_PORTCTRL_RXTR | + MV_PORTCTRL_TXTR + ); + /* wait for the phy change to settle in */ + msleep(2); + for (i = 0; i < MV_PORTS; i++) { + u8 pvid = 0; + int j; + + vlmap = 0; + + /* look for the matching vlan */ + for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) { + if (priv->vlans[j] & (1 << i)) { + vlmap = priv->vlans[j]; + pvid = j; + } + } + /* leave port unconfigured if it's not part of a vlan */ + if (!vlmap) + break; + + /* add the cpu port to the allowed destinations list */ + vlmap |= (1 << MV_CPUPORT); + + /* take port out of its own vlan destination map */ + vlmap &= ~(1 << i); + + /* apply vlan settings */ + w16(pdev, MV_PORTREG(VLANMAP, i), + MV_PORTVLAN_PORTS(vlmap) | + MV_PORTVLAN_ID(pvid) + ); + + /* re-enable port */ + w16(pdev, MV_PORTREG(CONTROL, i), MV_PORTCTRL_ENABLED); + } + + /* build the target list for the cpu port */ + for (i = 0, vlmap = 0; i < ARRAY_SIZE(priv->vlans); i++) + vlmap |= priv->vlans[i]; + + w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT), + MV_PORTVLAN_PORTS(vlmap) + ); + + /* set the port association vector */ + for (i = 0; i <= MV_PORTS; i++) { + w16(pdev, MV_PORTREG(ASSOC, i), + MV_PORTASSOC_PORTS(1 << i) + ); + } + + /* hook into the tx function */ + priv->hardstart = dev->hard_start_xmit; + pdev->netif_receive_skb = mvswitch_netif_receive_skb; + pdev->netif_rx = mvswitch_netif_rx; + dev->hard_start_xmit = mvswitch_mangle_tx; + dev->vlan_rx_register = mvswitch_vlan_rx_register; + dev->features |= NETIF_F_HW_VLAN_RX; + + return 0; +} + +static int +mvswitch_read_status(struct phy_device *phydev) +{ + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + phydev->state = PHY_UP; + return 0; +} + +static int +mvswitch_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static void +mvswitch_remove(struct phy_device *pdev) +{ + struct mvswitch_priv *priv = to_mvsw(pdev); + struct net_device *dev = pdev->attached_dev; + + /* restore old xmit handler */ + if (priv->hardstart && dev) + dev->hard_start_xmit = priv->hardstart; + dev->vlan_rx_register = NULL; + dev->vlan_rx_kill_vid = NULL; + dev->phy_ptr = NULL; + dev->features &= ~NETIF_F_HW_VLAN_RX; + kfree(priv); +} + +static bool +mvswitch_detect(struct mii_bus *bus, int addr) +{ + u16 reg; + int i; + + /* we attach to phy id 31 to make sure that the late probe works */ + if (addr != 31) + return false; + + /* look for the switch on the bus */ + reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; + if (reg != MV_IDENT_VALUE) + return false; + + /* + * Now that we've established that the switch actually exists, let's + * get rid of the competition :) + */ + for (i = 0; i < 31; i++) { + if (!bus->phy_map[i]) + continue; + + device_unregister(&bus->phy_map[i]->dev); + kfree(bus->phy_map[i]); + bus->phy_map[i] = NULL; + } + + return true; +} + +static int +mvswitch_probe(struct phy_device *pdev) +{ + struct mvswitch_priv *priv; + + priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + pdev->priv = priv; + + return 0; +} + + +static struct phy_driver mvswitch_driver = { + .name = "Marvell 88E6060", + .features = PHY_BASIC_FEATURES, + .detect = &mvswitch_detect, + .probe = &mvswitch_probe, + .remove = &mvswitch_remove, + .config_init = &mvswitch_config_init, + .config_aneg = &mvswitch_config_aneg, + .read_status = &mvswitch_read_status, + .driver = { .owner = THIS_MODULE,}, +}; + +static int __init +mvswitch_init(void) +{ + return phy_driver_register(&mvswitch_driver); +} + +static void __exit +mvswitch_exit(void) +{ + phy_driver_unregister(&mvswitch_driver); +} + +module_init(mvswitch_init); +module_exit(mvswitch_exit); diff --git a/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.h b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.h new file mode 100644 index 0000000000..b51e84a731 --- /dev/null +++ b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.h @@ -0,0 +1,104 @@ +/* + * Marvell 88E6060 switch driver + * Copyright (c) 2008 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation + */ +#ifndef __MVSWITCH_H +#define __MVSWITCH_H + +#define MV_PORTS 5 +#define MV_WANPORT 4 +#define MV_CPUPORT 5 + +#define MV_BASE 0x10 + +#define MV_PHYPORT_BASE (MV_BASE + 0x0) +#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n)) +#define MV_SWITCHPORT_BASE (MV_BASE + 0x8) +#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n)) +#define MV_SWITCHREGS (MV_BASE + 0xf) + +enum { + MV_PHY_CONTROL = 0x00, + MV_PHY_STATUS = 0x01, + MV_PHY_IDENT0 = 0x02, + MV_PHY_IDENT1 = 0x03, + MV_PHY_ANEG = 0x04, + MV_PHY_LINK_ABILITY = 0x05, + MV_PHY_ANEG_EXPAND = 0x06, + MV_PHY_XMIT_NEXTP = 0x07, + MV_PHY_LINK_NEXTP = 0x08, + MV_PHY_CONTROL1 = 0x10, + MV_PHY_STATUS1 = 0x11, + MV_PHY_INTR_EN = 0x12, + MV_PHY_INTR_STATUS = 0x13, + MV_PHY_INTR_PORT = 0x14, + MV_PHY_RECV_COUNTER = 0x15, + MV_PHY_LED_PARALLEL = 0x16, + MV_PHY_LED_STREAM = 0x17, + MV_PHY_LED_CTRL = 0x18, + MV_PHY_LED_OVERRIDE = 0x19, + MV_PHY_VCT_CTRL = 0x1a, + MV_PHY_VCT_STATUS = 0x1b, + MV_PHY_CONTROL2 = 0x1e +}; +#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type + +enum { + MV_PORT_STATUS = 0x00, + MV_PORT_IDENT = 0x03, + MV_PORT_CONTROL = 0x04, + MV_PORT_VLANMAP = 0x06, + MV_PORT_ASSOC = 0x0b, + MV_PORT_RXCOUNT = 0x10, + MV_PORT_TXCOUNT = 0x11, +}; +#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type + +enum { + MV_PORTCTRL_BLOCK = (1 << 0), + MV_PORTCTRL_LEARN = (2 << 0), + MV_PORTCTRL_ENABLED = (3 << 0), + MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */ + MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */ + MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */ + MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */ +}; + +#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12) +#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f) + +#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f) +#define MV_PORTASSOC_MONITOR (1 << 15) + +enum { + MV_SWITCH_MAC0 = 0x01, + MV_SWITCH_MAC1 = 0x02, + MV_SWITCH_MAC2 = 0x03, + MV_SWITCH_CTRL = 0x04, + MV_SWITCH_ATU_CTRL = 0x0a, + MV_SWITCH_ATU_OP = 0x0b, + MV_SWITCH_ATU_DATA = 0x0c, + MV_SWITCH_ATU_MAC0 = 0x0d, + MV_SWITCH_ATU_MAC1 = 0x0e, + MV_SWITCH_ATU_MAC2 = 0x0f, +}; +#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type + +enum { +#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4) + MV_ATUCTL_ATU_256 = (0 << 12), + MV_ATUCTL_ATU_512 = (1 << 12), + MV_ATUCTL_ATU_1K = (2 << 12), + MV_ATUCTL_ATUMASK = (3 << 12), + MV_ATUCTL_NO_LEARN = (1 << 14), + MV_ATUCTL_RESET = (1 << 15), +} + +#define MV_IDENT_MASK 0xfff0 +#define MV_IDENT_VALUE 0x0600 + +#endif diff --git a/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch b/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch new file mode 100644 index 0000000000..5d7f97195f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch @@ -0,0 +1,47 @@ +Index: linux-2.6.23.16/drivers/net/phy/phy_device.c +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/phy/phy_device.c 2008-02-11 07:06:32.000000000 +0100 ++++ linux-2.6.23.16/drivers/net/phy/phy_device.c 2008-04-20 05:42:28.000000000 +0200 +@@ -67,6 +67,8 @@ + dev->bus = bus; + + dev->state = PHY_DOWN; ++ dev->netif_receive_skb = &netif_receive_skb; ++ dev->netif_rx = &netif_rx; + + spin_lock_init(&dev->lock); + +Index: linux-2.6.23.16/include/linux/phy.h +=================================================================== +--- linux-2.6.23.16.orig/include/linux/phy.h 2008-04-20 04:40:31.000000000 +0200 ++++ linux-2.6.23.16/include/linux/phy.h 2008-04-20 05:53:21.000000000 +0200 +@@ -289,6 +289,17 @@ + void (*adjust_link)(struct net_device *dev); + + void (*adjust_state)(struct net_device *dev); ++ ++ /* ++ * By default these point to the original functions ++ * with the same name. adding them to the phy_device ++ * allows the phy driver to override them for packet ++ * mangling if the ethernet driver supports it ++ * This is required to support some really horrible ++ * switches such as the Marvell 88E6060 ++ */ ++ int (*netif_receive_skb)(struct sk_buff *skb); ++ int (*netif_rx)(struct sk_buff *skb); + }; + #define to_phy_device(d) container_of(d, struct phy_device, dev) + +Index: linux-2.6.23.16/include/linux/netdevice.h +=================================================================== +--- linux-2.6.23.16.orig/include/linux/netdevice.h 2008-02-11 07:06:32.000000000 +0100 ++++ linux-2.6.23.16/include/linux/netdevice.h 2008-04-20 06:33:25.000000000 +0200 +@@ -426,6 +426,7 @@ + void *ax25_ptr; /* AX.25 specific data */ + struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, + assign before registering */ ++ void *phy_ptr; /* PHY device specific data */ + + /* + * Cache line mostly used on receive path (including eth_type_trans()) diff --git a/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch b/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch new file mode 100644 index 0000000000..15c6e481ff --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch @@ -0,0 +1,52 @@ +Index: linux-2.6.23.16/drivers/net/phy/Kconfig +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/phy/Kconfig 2008-04-20 07:31:20.000000000 +0200 ++++ linux-2.6.23.16/drivers/net/phy/Kconfig 2008-04-20 08:57:26.000000000 +0200 +@@ -65,6 +65,12 @@ + ---help--- + Currently supports the ADM6996F switch + ++config MVSWITCH_PHY ++ tristate "Driver for Marvell switches" ++ select VLAN_8021Q ++ ---help--- ++ Currently supports the Marvell 88E6060 switch. ++ + config FIXED_PHY + tristate "Drivers for PHY emulation on fixed speed/link" + ---help--- +Index: linux-2.6.23.16/drivers/net/phy/Makefile +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/phy/Makefile 2008-04-20 07:31:20.000000000 +0200 ++++ linux-2.6.23.16/drivers/net/phy/Makefile 2008-04-20 07:31:34.000000000 +0200 +@@ -13,4 +13,5 @@ + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o + obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o + obj-$(CONFIG_FIXED_PHY) += fixed.o +Index: linux-2.6.23.16/drivers/net/phy/mdio_bus.c +=================================================================== +--- linux-2.6.23.16.orig/drivers/net/phy/mdio_bus.c 2008-04-20 04:40:31.000000000 +0200 ++++ linux-2.6.23.16/drivers/net/phy/mdio_bus.c 2008-04-20 07:34:08.000000000 +0200 +@@ -35,6 +35,12 @@ + #include + #include + ++static void mdio_dev_release(struct device *dev) ++{ ++ /* nothing to do */ ++} ++ ++ + /** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus +@@ -85,6 +91,7 @@ + + phydev->dev.parent = bus->dev; + phydev->dev.bus = &mdio_bus_type; ++ phydev->dev.release = mdio_dev_release; + snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i); + + phydev->bus = bus;