drivers: ethernet: eth_sam_gmac: Detect and report link status

This commit adds the "monitor task" to detect and report any changes
in the PHY link status to the operating system.

The monitor task is perodically executed to poll the link status from
the PHY and call `net_eth_carrier_{off,on}` based on the detected
link status change.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
This commit is contained in:
Stephanos Ioannidis 2020-03-11 23:24:56 +09:00 committed by Jukka Rissanen
commit aa85756bf5
3 changed files with 60 additions and 11 deletions

View file

@ -77,6 +77,14 @@ config ETH_SAM_GMAC_IRQ_PRI
help
IRQ priority of Ethernet device
config ETH_SAM_GMAC_MONITOR_PERIOD
int "Monitor task execution period"
default 1000
help
Monitor task execution period in milliseconds. The monitor task is
periodically executed to detect and report any changes in the PHY
link status to the operating system.
choice ETH_SAM_GMAC_MAC_SELECT
prompt "MAC address"
help

View file

@ -1141,8 +1141,6 @@ static void link_configure(Gmac *gmac, u32_t flags)
{
u32_t val;
gmac->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN);
val = gmac->GMAC_NCFGR;
val &= ~(GMAC_NCFGR_FD | GMAC_NCFGR_SPD);
@ -1806,6 +1804,49 @@ static void generate_mac(u8_t mac_addr[6])
#endif
}
static void monitor_work_handler(struct k_work *work)
{
struct eth_sam_dev_data *const dev_data =
CONTAINER_OF(work, struct eth_sam_dev_data, monitor_work);
struct device *const dev = net_if_get_device(dev_data->iface);
const struct eth_sam_dev_cfg *const cfg = DEV_CFG(dev);
bool link_status;
u32_t link_config;
int result;
/* Poll PHY link status */
link_status = phy_sam_gmac_link_status_get(&cfg->phy);
if (link_status && !dev_data->link_up) {
LOG_INF("Link up");
/* Announce link up status */
dev_data->link_up = true;
net_eth_carrier_on(dev_data->iface);
/* PHY auto-negotiate link parameters */
result = phy_sam_gmac_auto_negotiate(&cfg->phy, &link_config);
if (result < 0) {
LOG_ERR("ETH PHY auto-negotiate sequence failed");
goto finally;
}
/* Set up link parameters */
link_configure(cfg->regs, link_config);
} else if (!link_status && dev_data->link_up) {
LOG_INF("Link down");
/* Announce link down status */
dev_data->link_up = false;
net_eth_carrier_off(dev_data->iface);
}
finally:
/* Submit delayed work */
k_delayed_work_submit(&dev_data->monitor_work,
CONFIG_ETH_SAM_GMAC_MONITOR_PERIOD);
}
static void eth0_iface_init(struct net_if *iface)
{
struct device *const dev = net_if_get_device(iface);
@ -1813,7 +1854,6 @@ static void eth0_iface_init(struct net_if *iface)
const struct eth_sam_dev_cfg *const cfg = DEV_CFG(dev);
static bool init_done;
u32_t gmac_ncfgr_val;
u32_t link_status;
int result;
int i;
@ -1918,15 +1958,14 @@ static void eth0_iface_init(struct net_if *iface)
LOG_ERR("ETH PHY Initialization Error");
return;
}
/* PHY auto-negotiate link parameters */
result = phy_sam_gmac_auto_negotiate(&cfg->phy, &link_status);
if (result < 0) {
LOG_ERR("ETH PHY auto-negotiate sequence failed");
return;
}
/* Set up link parameters */
link_configure(cfg->regs, link_status);
/* Initialise monitor */
k_delayed_work_init(&dev_data->monitor_work, monitor_work_handler);
k_delayed_work_submit(&dev_data->monitor_work,
CONFIG_ETH_SAM_GMAC_MONITOR_PERIOD);
/* Do not start the interface until PHY link is up */
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
init_done = true;
}

View file

@ -257,6 +257,8 @@ struct eth_sam_dev_data {
struct device *ptp_clock;
#endif
u8_t mac_addr[6];
struct k_delayed_work monitor_work;
bool link_up;
struct gmac_queue queue_list[GMAC_QUEUE_NUM];
};