9
0
Fork 0

net/eth: fix link handling

Check link status on eth device open time and then periodically
every 5 seconds.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Sascha Hauer 2012-09-26 17:53:29 +02:00
parent 2263e27814
commit 0dc9de2efd
3 changed files with 85 additions and 28 deletions

View File

@ -29,6 +29,30 @@
static int genphy_config_init(struct phy_device *phydev); static int genphy_config_init(struct phy_device *phydev);
int phy_update_status(struct phy_device *dev)
{
struct phy_driver *drv = to_phy_driver(dev->dev.driver);
struct eth_device *edev = dev->attached_dev;
int ret;
int oldspeed = dev->speed, oldduplex = dev->duplex;
ret = drv->read_status(dev);
if (ret)
return ret;
if (dev->speed == oldspeed && dev->duplex == oldduplex)
return 0;
if (dev->adjust_link)
dev->adjust_link(edev);
if (dev->link)
printf("%dMbps %s duplex link detected\n", dev->speed,
dev->duplex ? "full" : "half");
return 0;
}
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id)
{ {
struct phy_device *dev; struct phy_device *dev;
@ -172,16 +196,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr,
drv->config_aneg(dev); drv->config_aneg(dev);
ret = drv->read_status(dev); dev->adjust_link = adjust_link;
if (ret < 0)
return ret;
if (dev->link)
printf("%dMbps %s duplex link detected\n", dev->speed,
dev->duplex ? "full" : "half");
if (adjust_link)
adjust_link(edev);
return 0; return 0;

View File

@ -164,6 +164,7 @@ struct phy_device {
void *priv; void *priv;
struct eth_device *attached_dev; struct eth_device *attached_dev;
void (*adjust_link)(struct eth_device *dev);
struct cdev cdev; struct cdev cdev;
}; };
@ -253,6 +254,8 @@ int phy_device_connect(struct eth_device *dev, struct mii_bus *bus, int addr,
void (*adjust_link) (struct eth_device *edev), void (*adjust_link) (struct eth_device *edev),
u32 flags, phy_interface_t interface); u32 flags, phy_interface_t interface);
int phy_update_status(struct phy_device *phydev);
/* Generic PHY support and helper functions */ /* Generic PHY support and helper functions */
int genphy_restart_aneg(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev);
int genphy_config_aneg(struct phy_device *phydev); int genphy_config_aneg(struct phy_device *phydev);

View File

@ -32,6 +32,7 @@
#include <malloc.h> #include <malloc.h>
static struct eth_device *eth_current; static struct eth_device *eth_current;
static uint64_t last_link_check;
static LIST_HEAD(netdev_list); static LIST_HEAD(netdev_list);
@ -128,23 +129,63 @@ int eth_complete(struct string_list *sl, char *instr)
} }
#endif #endif
int eth_send(void *packet, int length) /*
* Check for link if we haven't done so for longer.
*/
static int eth_carrier_check(int force)
{
int ret;
if (!IS_ENABLED(CONFIG_PHYLIB))
return 0;
if (!eth_current->phydev)
return 0;
if (force || is_timeout(last_link_check, 5 * SECOND)) {
ret = phy_update_status(eth_current->phydev);
if (ret)
return ret;
last_link_check = get_time_ns();
}
return eth_current->phydev->link ? 0 : -ENETDOWN;
}
/*
* Check if we have a current ethernet device and
* eventually open it if we have to.
*/
static int eth_check_open(void)
{ {
int ret; int ret;
if (!eth_current) if (!eth_current)
return -ENODEV; return -ENODEV;
if (!eth_current->active) { if (eth_current->active)
ret = eth_current->open(eth_current); return 0;
if (ret)
return ret;
if (eth_current->phydev) ret = eth_current->open(eth_current);
eth_current->active = eth_current->phydev->link; if (ret)
else return ret;
eth_current->active = 1;
} eth_current->active = 1;
return eth_carrier_check(1);
}
int eth_send(void *packet, int length)
{
int ret;
ret = eth_check_open();
if (ret)
return ret;
ret = eth_carrier_check(0);
if (ret)
return ret;
led_trigger_network(LED_TRIGGER_NET_TX); led_trigger_network(LED_TRIGGER_NET_TX);
@ -155,15 +196,13 @@ int eth_rx(void)
{ {
int ret; int ret;
if (!eth_current) ret = eth_check_open();
return -ENODEV; if (ret)
return ret;
if (!eth_current->active) { ret = eth_carrier_check(0);
ret = eth_current->open(eth_current); if (ret)
if (ret) return ret;
return ret;
eth_current->active = 1;
}
return eth_current->recv(eth_current); return eth_current->recv(eth_current);
} }