summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/mdio-gpio.c13
-rw-r--r--drivers/net/phy/mdio_bus.c56
-rw-r--r--drivers/net/phy/phy.c41
-rw-r--r--drivers/net/phy/smsc.c31
4 files changed, 92 insertions, 49 deletions
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index af28ff7ae17..33984b73723 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -202,16 +202,21 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev,
{
struct device_node *np = NULL;
struct mdio_gpio_platform_data *pdata;
+ int ret;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- pdata->mdc = of_get_gpio(ofdev->node, 0);
- pdata->mdio = of_get_gpio(ofdev->node, 1);
-
- if (pdata->mdc < 0 || pdata->mdio < 0)
+ ret = of_get_gpio(ofdev->node, 0);
+ if (ret < 0)
goto out_free;
+ pdata->mdc = ret;
+
+ ret = of_get_gpio(ofdev->node, 1);
+ if (ret < 0)
+ goto out_free;
+ pdata->mdio = ret;
while ((np = of_get_next_child(ofdev->node, np)))
if (!strcmp(np->type, "ethernet-phy"))
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 811a637695c..b754020cbe7 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
@@ -98,7 +99,7 @@ int mdiobus_register(struct mii_bus *bus)
bus->dev.parent = bus->parent;
bus->dev.class = &mdio_bus_class;
bus->dev.groups = NULL;
- dev_set_name(&bus->dev, bus->id);
+ dev_set_name(&bus->dev, "%s", bus->id);
err = device_register(&bus->dev);
if (err) {
@@ -286,33 +287,58 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv)
(phydev->phy_id & phydrv->phy_id_mask));
}
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
+{
+ struct device_driver *drv = phydev->dev.driver;
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ struct net_device *netdev = phydev->attached_dev;
+
+ if (!drv || !phydrv->suspend)
+ return false;
+
+ /* PHY not attached? May suspend. */
+ if (!netdev)
+ return true;
+
+ /*
+ * Don't suspend PHY if the attched netdev parent may wakeup.
+ * The parent may point to a PCI device, as in tg3 driver.
+ */
+ if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
+ return false;
+
+ /*
+ * Also don't suspend PHY if the netdev itself may wakeup. This
+ * is the case for devices w/o underlaying pwr. mgmt. aware bus,
+ * e.g. SoC devices.
+ */
+ if (device_may_wakeup(&netdev->dev))
+ return false;
+
+ return true;
+}
+
/* Suspend and resume. Copied from platform_suspend and
* platform_resume
*/
static int mdio_bus_suspend(struct device * dev, pm_message_t state)
{
- int ret = 0;
- struct device_driver *drv = dev->driver;
- struct phy_driver *phydrv = to_phy_driver(drv);
+ struct phy_driver *phydrv = to_phy_driver(dev->driver);
struct phy_device *phydev = to_phy_device(dev);
- if (drv && phydrv->suspend && !device_may_wakeup(phydev->dev.parent))
- ret = phydrv->suspend(phydev);
-
- return ret;
+ if (!mdio_bus_phy_may_suspend(phydev))
+ return 0;
+ return phydrv->suspend(phydev);
}
static int mdio_bus_resume(struct device * dev)
{
- int ret = 0;
- struct device_driver *drv = dev->driver;
- struct phy_driver *phydrv = to_phy_driver(drv);
+ struct phy_driver *phydrv = to_phy_driver(dev->driver);
struct phy_device *phydev = to_phy_device(dev);
- if (drv && phydrv->resume && !device_may_wakeup(phydev->dev.parent))
- ret = phydrv->resume(phydev);
-
- return ret;
+ if (!mdio_bus_phy_may_suspend(phydev))
+ return 0;
+ return phydrv->resume(phydev);
}
struct bus_type mdio_bus_type = {
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index e4ede6080c9..58b73b08dde 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -414,7 +414,6 @@ EXPORT_SYMBOL(phy_start_aneg);
static void phy_change(struct work_struct *work);
static void phy_state_machine(struct work_struct *work);
-static void phy_timer(unsigned long data);
/**
* phy_start_machine - start PHY state machine tracking
@@ -434,11 +433,8 @@ void phy_start_machine(struct phy_device *phydev,
{
phydev->adjust_state = handler;
- INIT_WORK(&phydev->state_queue, phy_state_machine);
- init_timer(&phydev->phy_timer);
- phydev->phy_timer.function = &phy_timer;
- phydev->phy_timer.data = (unsigned long) phydev;
- mod_timer(&phydev->phy_timer, jiffies + HZ);
+ INIT_DELAYED_WORK(&phydev->state_queue, phy_state_machine);
+ schedule_delayed_work(&phydev->state_queue, jiffies + HZ);
}
/**
@@ -451,8 +447,7 @@ void phy_start_machine(struct phy_device *phydev,
*/
void phy_stop_machine(struct phy_device *phydev)
{
- del_timer_sync(&phydev->phy_timer);
- cancel_work_sync(&phydev->state_queue);
+ cancel_delayed_work_sync(&phydev->state_queue);
mutex_lock(&phydev->lock);
if (phydev->state > PHY_UP)
@@ -680,11 +675,9 @@ static void phy_change(struct work_struct *work)
if (err)
goto irq_enable_err;
- /* Stop timer and run the state queue now. The work function for
- * state_queue will start the timer up again.
- */
- del_timer(&phydev->phy_timer);
- schedule_work(&phydev->state_queue);
+ /* reschedule state queue work to run as soon as possible */
+ cancel_delayed_work_sync(&phydev->state_queue);
+ schedule_delayed_work(&phydev->state_queue, 0);
return;
@@ -761,14 +754,13 @@ EXPORT_SYMBOL(phy_start);
/**
* phy_state_machine - Handle the state machine
* @work: work_struct that describes the work to be done
- *
- * Description: Scheduled by the state_queue workqueue each time
- * phy_timer is triggered.
*/
static void phy_state_machine(struct work_struct *work)
{
+ struct delayed_work *dwork =
+ container_of(work, struct delayed_work, work);
struct phy_device *phydev =
- container_of(work, struct phy_device, state_queue);
+ container_of(dwork, struct phy_device, state_queue);
int needs_aneg = 0;
int err = 0;
@@ -946,17 +938,6 @@ static void phy_state_machine(struct work_struct *work)
if (err < 0)
phy_error(phydev);
- mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
-}
-
-/* PHY timer which schedules the state machine work */
-static void phy_timer(unsigned long data)
-{
- struct phy_device *phydev = (struct phy_device *)data;
-
- /*
- * PHY I/O operations can potentially sleep so we ensure that
- * it's done from a process context
- */
- schedule_work(&phydev->state_queue);
+ schedule_delayed_work(&phydev->state_queue,
+ jiffies + PHY_STATE_TIME * HZ);
}
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 1387187543e..5123bb954dd 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -159,6 +159,30 @@ static struct phy_driver lan911x_int_driver = {
.driver = { .owner = THIS_MODULE, }
};
+static struct phy_driver lan8710_driver = {
+ .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
+ .phy_id_mask = 0xfffffff0,
+ .name = "SMSC LAN8710/LAN8720",
+
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
+ | SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+
+ /* basic functions */
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .config_init = smsc_phy_config_init,
+
+ /* IRQ related */
+ .ack_interrupt = smsc_phy_ack_interrupt,
+ .config_intr = smsc_phy_config_intr,
+
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+
+ .driver = { .owner = THIS_MODULE, }
+};
+
static int __init smsc_init(void)
{
int ret;
@@ -179,8 +203,14 @@ static int __init smsc_init(void)
if (ret)
goto err4;
+ ret = phy_driver_register (&lan8710_driver);
+ if (ret)
+ goto err5;
+
return 0;
+err5:
+ phy_driver_unregister (&lan911x_int_driver);
err4:
phy_driver_unregister (&lan8700_driver);
err3:
@@ -193,6 +223,7 @@ err1:
static void __exit smsc_exit(void)
{
+ phy_driver_unregister (&lan8710_driver);
phy_driver_unregister (&lan911x_int_driver);
phy_driver_unregister (&lan8700_driver);
phy_driver_unregister (&lan8187_driver);