Commit 7c32f470 authored by Vitaly Bordug's avatar Vitaly Bordug Committed by David S. Miller

PHY fixed driver: rework release path and update phy_id notation

device_bind_driver() error code returning has been fixed.  release()
function has been written, so that to free resources in correct way; the
release path is now clean.

Before the rework, it used to cause
 Device 'fixed@100:1' does not have a release() function, it is broken
 and must be fixed.
 BUG: at drivers/base/core.c:104 device_release()

 Call Trace:
  [<ffffffff802ec380>] kobject_cleanup+0x53/0x7e
  [<ffffffff802ec3ab>] kobject_release+0x0/0x9
  [<ffffffff802ecf3f>] kref_put+0x74/0x81
  [<ffffffff8035493b>] fixed_mdio_register_device+0x230/0x265
  [<ffffffff80564d31>] fixed_init+0x1f/0x35
  [<ffffffff802071a4>] init+0x147/0x2fb
  [<ffffffff80223b6e>] schedule_tail+0x36/0x92
  [<ffffffff8020a678>] child_rip+0xa/0x12
  [<ffffffff80311714>] acpi_ds_init_one_object+0x0/0x83
  [<ffffffff8020705d>] init+0x0/0x2fb
  [<ffffffff8020a66e>] child_rip+0x0/0x12

Also changed the notation of the fixed phy definition on
mdio bus to the form of <speed>+<duplex> to make it able to be used by
gianfar and ucc_geth that define phy_id strictly as "%d:%d" and cleaned up
the whitespace issues.
Signed-off-by: default avatarVitaly Bordug <vitb@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent cdcc520d
...@@ -76,4 +76,18 @@ config FIXED_MII_100_FDX ...@@ -76,4 +76,18 @@ config FIXED_MII_100_FDX
bool "Emulation for 100M Fdx fixed PHY behavior" bool "Emulation for 100M Fdx fixed PHY behavior"
depends on FIXED_PHY depends on FIXED_PHY
config FIXED_MII_1000_FDX
bool "Emulation for 1000M Fdx fixed PHY behavior"
depends on FIXED_PHY
config FIXED_MII_AMNT
int "Number of emulated PHYs to allocate "
depends on FIXED_PHY
default "1"
---help---
Sometimes it is required to have several independent emulated
PHYs on the bus (in case of multi-eth but phy-less HW for instance).
This control will have specified number allocated for each fixed
PHY type enabled.
endif # PHYLIB endif # PHYLIB
...@@ -30,53 +30,31 @@ ...@@ -30,53 +30,31 @@
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#define MII_REGS_NUM 7 /* we need to track the allocated pointers in order to free them on exit */
static struct fixed_info *fixed_phy_ptrs[CONFIG_FIXED_MII_AMNT*MAX_PHY_AMNT];
/*
The idea is to emulate normal phy behavior by responding with
pre-defined values to mii BMCR read, so that read_status hook could
take all the needed info.
*/
struct fixed_phy_status {
u8 link;
u16 speed;
u8 duplex;
};
/*-----------------------------------------------------------------------------
* Private information hoder for mii_bus
*-----------------------------------------------------------------------------*/
struct fixed_info {
u16 *regs;
u8 regs_num;
struct fixed_phy_status phy_status;
struct phy_device *phydev; /* pointer to the container */
/* link & speed cb */
int(*link_update)(struct net_device*, struct fixed_phy_status*);
};
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* If something weird is required to be done with link/speed, * If something weird is required to be done with link/speed,
* network driver is able to assign a function to implement this. * network driver is able to assign a function to implement this.
* May be useful for PHY's that need to be software-driven. * May be useful for PHY's that need to be software-driven.
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
int fixed_mdio_set_link_update(struct phy_device* phydev, int fixed_mdio_set_link_update(struct phy_device *phydev,
int(*link_update)(struct net_device*, struct fixed_phy_status*)) int (*link_update) (struct net_device *,
struct fixed_phy_status *))
{ {
struct fixed_info *fixed; struct fixed_info *fixed;
if(link_update == NULL) if (link_update == NULL)
return -EINVAL; return -EINVAL;
if(phydev) { if (phydev) {
if(phydev->bus) { if (phydev->bus) {
fixed = phydev->bus->priv; fixed = phydev->bus->priv;
fixed->link_update = link_update; fixed->link_update = link_update;
return 0; return 0;
...@@ -84,30 +62,40 @@ int fixed_mdio_set_link_update(struct phy_device* phydev, ...@@ -84,30 +62,40 @@ int fixed_mdio_set_link_update(struct phy_device* phydev,
} }
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL(fixed_mdio_set_link_update); EXPORT_SYMBOL(fixed_mdio_set_link_update);
struct fixed_info *fixed_mdio_get_phydev (int phydev_ind)
{
if (phydev_ind >= MAX_PHY_AMNT)
return NULL;
return fixed_phy_ptrs[phydev_ind];
}
EXPORT_SYMBOL(fixed_mdio_get_phydev);
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* This is used for updating internal mii regs from the status * This is used for updating internal mii regs from the status
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) #if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX)
static int fixed_mdio_update_regs(struct fixed_info *fixed) static int fixed_mdio_update_regs(struct fixed_info *fixed)
{ {
u16 *regs = fixed->regs; u16 *regs = fixed->regs;
u16 bmsr = 0; u16 bmsr = 0;
u16 bmcr = 0; u16 bmcr = 0;
if(!regs) { if (!regs) {
printk(KERN_ERR "%s: regs not set up", __FUNCTION__); printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
return -EINVAL; return -EINVAL;
} }
if(fixed->phy_status.link) if (fixed->phy_status.link)
bmsr |= BMSR_LSTATUS; bmsr |= BMSR_LSTATUS;
if(fixed->phy_status.duplex) { if (fixed->phy_status.duplex) {
bmcr |= BMCR_FULLDPLX; bmcr |= BMCR_FULLDPLX;
switch ( fixed->phy_status.speed ) { switch (fixed->phy_status.speed) {
case 100: case 100:
bmsr |= BMSR_100FULL; bmsr |= BMSR_100FULL;
bmcr |= BMCR_SPEED100; bmcr |= BMCR_SPEED100;
...@@ -118,7 +106,7 @@ static int fixed_mdio_update_regs(struct fixed_info *fixed) ...@@ -118,7 +106,7 @@ static int fixed_mdio_update_regs(struct fixed_info *fixed)
break; break;
} }
} else { } else {
switch ( fixed->phy_status.speed ) { switch (fixed->phy_status.speed) {
case 100: case 100:
bmsr |= BMSR_100HALF; bmsr |= BMSR_100HALF;
bmcr |= BMCR_SPEED100; bmcr |= BMCR_SPEED100;
...@@ -131,7 +119,7 @@ static int fixed_mdio_update_regs(struct fixed_info *fixed) ...@@ -131,7 +119,7 @@ static int fixed_mdio_update_regs(struct fixed_info *fixed)
} }
regs[MII_BMCR] = bmcr; regs[MII_BMCR] = bmcr;
regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx*/ regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx */
return 0; return 0;
} }
...@@ -141,9 +129,9 @@ static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location) ...@@ -141,9 +129,9 @@ static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
struct fixed_info *fixed = bus->priv; struct fixed_info *fixed = bus->priv;
/* if user has registered link update callback, use it */ /* if user has registered link update callback, use it */
if(fixed->phydev) if (fixed->phydev)
if(fixed->phydev->attached_dev) { if (fixed->phydev->attached_dev) {
if(fixed->link_update) { if (fixed->link_update) {
fixed->link_update(fixed->phydev->attached_dev, fixed->link_update(fixed->phydev->attached_dev,
&fixed->phy_status); &fixed->phy_status);
fixed_mdio_update_regs(fixed); fixed_mdio_update_regs(fixed);
...@@ -155,15 +143,16 @@ static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location) ...@@ -155,15 +143,16 @@ static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
return fixed->regs[location]; return fixed->regs[location];
} }
static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val) static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location,
u16 val)
{ {
/* do nothing for now*/ /* do nothing for now */
return 0; return 0;
} }
static int fixed_mii_reset(struct mii_bus *bus) static int fixed_mii_reset(struct mii_bus *bus)
{ {
/*nothing here - no way/need to reset it*/ /*nothing here - no way/need to reset it */
return 0; return 0;
} }
#endif #endif
...@@ -183,58 +172,78 @@ static int fixed_config_aneg(struct phy_device *phydev) ...@@ -183,58 +172,78 @@ static int fixed_config_aneg(struct phy_device *phydev)
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
static struct phy_driver fixed_mdio_driver = { static struct phy_driver fixed_mdio_driver = {
.name = "Fixed PHY", .name = "Fixed PHY",
#ifdef CONFIG_FIXED_MII_1000_FDX
.features = PHY_GBIT_FEATURES,
#else
.features = PHY_BASIC_FEATURES, .features = PHY_BASIC_FEATURES,
#endif
.config_aneg = fixed_config_aneg, .config_aneg = fixed_config_aneg,
.read_status = genphy_read_status, .read_status = genphy_read_status,
.driver = { .owner = THIS_MODULE,}, .driver = { .owner = THIS_MODULE, },
}; };
static void fixed_mdio_release(struct device *dev)
{
struct phy_device *phydev = container_of(dev, struct phy_device, dev);
struct mii_bus *bus = phydev->bus;
struct fixed_info *fixed = bus->priv;
kfree(phydev);
kfree(bus->dev);
kfree(bus);
kfree(fixed->regs);
kfree(fixed);
}
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* This func is used to create all the necessary stuff, bind * This func is used to create all the necessary stuff, bind
* the fixed phy driver and register all it on the mdio_bus_type. * the fixed phy driver and register all it on the mdio_bus_type.
* speed is either 10 or 100, duplex is boolean. * speed is either 10 or 100 or 1000, duplex is boolean.
* number is used to create multiple fixed PHYs, so that several devices can * number is used to create multiple fixed PHYs, so that several devices can
* utilize them simultaneously. * utilize them simultaneously.
*
* The device on mdio bus will look like [bus_id]:[phy_id],
* bus_id = number
* phy_id = speed+duplex.
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) #if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX)
static int fixed_mdio_register_device(int number, int speed, int duplex) struct fixed_info *fixed_mdio_register_device(
int bus_id, int speed, int duplex, u8 phy_id)
{ {
struct mii_bus *new_bus; struct mii_bus *new_bus;
struct fixed_info *fixed; struct fixed_info *fixed;
struct phy_device *phydev; struct phy_device *phydev;
int err = 0; int err;
struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL); struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (NULL == dev) if (dev == NULL)
return -ENOMEM; goto err_dev_alloc;
new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
if (NULL == new_bus) { if (new_bus == NULL)
kfree(dev); goto err_bus_alloc;
return -ENOMEM;
}
fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL); fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
if (NULL == fixed) { if (fixed == NULL)
kfree(dev); goto err_fixed_alloc;
kfree(new_bus);
return -ENOMEM; fixed->regs = kzalloc(MII_REGS_NUM * sizeof(int), GFP_KERNEL);
} if (NULL == fixed->regs)
goto err_fixed_regs_alloc;
fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
fixed->regs_num = MII_REGS_NUM; fixed->regs_num = MII_REGS_NUM;
fixed->phy_status.speed = speed; fixed->phy_status.speed = speed;
fixed->phy_status.duplex = duplex; fixed->phy_status.duplex = duplex;
fixed->phy_status.link = 1; fixed->phy_status.link = 1;
new_bus->name = "Fixed MII Bus", new_bus->name = "Fixed MII Bus";
new_bus->read = &fixed_mii_read, new_bus->read = &fixed_mii_read;
new_bus->write = &fixed_mii_write, new_bus->write = &fixed_mii_write;
new_bus->reset = &fixed_mii_reset, new_bus->reset = &fixed_mii_reset;
/*set up workspace */
/*set up workspace*/
fixed_mdio_update_regs(fixed); fixed_mdio_update_regs(fixed);
new_bus->priv = fixed; new_bus->priv = fixed;
...@@ -243,119 +252,110 @@ static int fixed_mdio_register_device(int number, int speed, int duplex) ...@@ -243,119 +252,110 @@ static int fixed_mdio_register_device(int number, int speed, int duplex)
/* create phy_device and register it on the mdio bus */ /* create phy_device and register it on the mdio bus */
phydev = phy_device_create(new_bus, 0, 0); phydev = phy_device_create(new_bus, 0, 0);
if (phydev == NULL)
goto err_phy_dev_create;
/* /*
Put the phydev pointer into the fixed pack so that bus read/write code could * Put the phydev pointer into the fixed pack so that bus read/write
be able to access for instance attached netdev. Well it doesn't have to do * code could be able to access for instance attached netdev. Well it
so, only in case of utilizing user-specified link-update... * doesn't have to do so, only in case of utilizing user-specified
* link-update...
*/ */
fixed->phydev = phydev;
if(NULL == phydev) { fixed->phydev = phydev;
err = -ENOMEM; phydev->speed = speed;
goto device_create_fail; phydev->duplex = duplex;
}
phydev->irq = PHY_IGNORE_INTERRUPT; phydev->irq = PHY_IGNORE_INTERRUPT;
phydev->dev.bus = &mdio_bus_type; phydev->dev.bus = &mdio_bus_type;
if(number)
snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
"fixed_%d@%d:%d", number, speed, duplex);
else
snprintf(phydev->dev.bus_id, BUS_ID_SIZE, snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
"fixed@%d:%d", speed, duplex); PHY_ID_FMT, bus_id, phy_id);
phydev->bus = new_bus; phydev->bus = new_bus;
phydev->dev.driver = &fixed_mdio_driver.driver;
phydev->dev.release = fixed_mdio_release;
err = phydev->dev.driver->probe(&phydev->dev);
if (err < 0) {
printk(KERN_ERR "Phy %s: problems with fixed driver\n",
phydev->dev.bus_id);
goto err_out;
}
err = device_register(&phydev->dev); err = device_register(&phydev->dev);
if(err) { if (err) {
printk(KERN_ERR "Phy %s failed to register\n", printk(KERN_ERR "Phy %s failed to register\n",
phydev->dev.bus_id); phydev->dev.bus_id);
goto bus_register_fail; goto err_out;
}
/*
the mdio bus has phy_id match... In order not to do it
artificially, we are binding the driver here by hand;
it will be the same for all the fixed phys anyway.
*/
phydev->dev.driver = &fixed_mdio_driver.driver;
err = phydev->dev.driver->probe(&phydev->dev);
if(err < 0) {
printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
goto probe_fail;
} }
//phydev->state = PHY_RUNNING; /* make phy go up quick, but in 10Mbit/HDX
return fixed;
err = device_bind_driver(&phydev->dev); err_out:
if (err)
goto probe_fail;
return 0;
probe_fail:
device_unregister(&phydev->dev);
bus_register_fail:
kfree(phydev); kfree(phydev);
device_create_fail: err_phy_dev_create:
kfree(dev); kfree(fixed->regs);
kfree(new_bus); err_fixed_regs_alloc:
kfree(fixed); kfree(fixed);
err_fixed_alloc:
kfree(new_bus);
err_bus_alloc:
kfree(dev);
err_dev_alloc:
return NULL;
return err;
} }
#endif #endif
MODULE_DESCRIPTION("Fixed PHY device & driver for PAL"); MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
MODULE_AUTHOR("Vitaly Bordug"); MODULE_AUTHOR("Vitaly Bordug");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int __init fixed_init(void) static int __init fixed_init(void)
{ {
#if 0 int cnt = 0;
int ret; int i;
int duplex = 0; /* register on the bus... Not expected to be matched
#endif * with anything there...
*
/* register on the bus... Not expected to be matched with anything there... */ */
phy_driver_register(&fixed_mdio_driver); phy_driver_register(&fixed_mdio_driver);
/* So let the fun begin... /* We will create several mdio devices here, and will bound the upper
We will create several mdio devices here, and will bound the upper * driver to them.
driver to them. *
* Then the external software can lookup the phy bus by searching
Then the external software can lookup the phy bus by searching * for 0:101, to be connected to the virtual 100M Fdx phy.
fixed@speed:duplex, e.g. fixed@100:1, to be connected to the *
virtual 100M Fdx phy. * In case several virtual PHYs required, the bus_id will be in form
* [num]:[duplex]+[speed], which make it able even to define
In case several virtual PHYs required, the bus_id will be in form * driver-specific link control callback, if for instance PHY is
fixed_<num>@<speed>:<duplex>, which make it able even to define * completely SW-driven.
driver-specific link control callback, if for instance PHY is completely
SW-driven.
*/ */
for (i=1; i <= CONFIG_FIXED_MII_AMNT; i++) {
#ifdef CONFIG_FIXED_MII_DUPLEX #ifdef CONFIG_FIXED_MII_1000_FDX
#if 0 fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(0, 1000, 1, i);
duplex = 1;
#endif #endif
#endif
#ifdef CONFIG_FIXED_MII_100_FDX #ifdef CONFIG_FIXED_MII_100_FDX
fixed_mdio_register_device(0, 100, 1); fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(1, 100, 1, i);
#endif #endif
#ifdef CONFIG_FIXED_MII_10_FDX #ifdef CONFIG_FIXED_MII_10_FDX
fixed_mdio_register_device(0, 10, 1); fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(2, 10, 1, i);
#endif #endif
}
return 0; return 0;
} }
static void __exit fixed_exit(void) static void __exit fixed_exit(void)
{ {
int i;
phy_driver_unregister(&fixed_mdio_driver); phy_driver_unregister(&fixed_mdio_driver);
/* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */ for (i=0; i < MAX_PHY_AMNT; i++)
if ( fixed_phy_ptrs[i] )
device_unregister(&fixed_phy_ptrs[i]->phydev->dev);
} }
module_init(fixed_init); module_init(fixed_init);
......
#ifndef __PHY_FIXED_H
#define __PHY_FIXED_H
#define MII_REGS_NUM 29
/* max number of virtual phy stuff */
#define MAX_PHY_AMNT 10
/*
The idea is to emulate normal phy behavior by responding with
pre-defined values to mii BMCR read, so that read_status hook could
take all the needed info.
*/
struct fixed_phy_status {
u8 link;
u16 speed;
u8 duplex;
};
/*-----------------------------------------------------------------------------
* Private information hoder for mii_bus
*-----------------------------------------------------------------------------*/
struct fixed_info {
u16 *regs;
u8 regs_num;
struct fixed_phy_status phy_status;
struct phy_device *phydev; /* pointer to the container */
/* link & speed cb */
int (*link_update) (struct net_device *, struct fixed_phy_status *);
};
int fixed_mdio_set_link_update(struct phy_device *,
int (*link_update) (struct net_device *, struct fixed_phy_status *));
struct fixed_info *fixed_mdio_get_phydev (int phydev_ind);
#endif /* __PHY_FIXED_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment