From 87dc8df4d6344a5328205f7e1a1b4a6421485923 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 14 May 2015 19:15:41 +0100 Subject: [PATCH] jme: link management changes from OOT version 1.0.8.5 This is entirely untested, so don't come crying to me. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/jme.c | 252 ++++++++++++++++++++++++++++++++++++++++++++- drivers/net/ethernet/jme.h | 14 +++ 2 files changed, 263 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 6e9a792..daf2fbd 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -48,6 +48,7 @@ static int force_pseudohp = -1; static int no_pseudohp = -1; static int no_extplug = -1; +static int delay_time = 11; module_param(force_pseudohp, int, 0); MODULE_PARM_DESC(force_pseudohp, "Enable pseudo hot-plug feature manually by driver instead of BIOS."); @@ -56,6 +57,9 @@ MODULE_PARM_DESC(no_pseudohp, "Disable pseudo hot-plug feature."); module_param(no_extplug, int, 0); MODULE_PARM_DESC(no_extplug, "Do not use external plug signal for pseudo hot-plug."); +module_param(delay_time, uint, 0); +MODULE_PARM_DESC(delay_time, + "Seconds to delay before switching lower speed; default = 11 seconds(3 trials)"); static int jme_mdio_read(struct net_device *netdev, int phy, int reg) @@ -1292,6 +1296,223 @@ jme_stop_shutdown_timer(struct jme_adapter *jme) } static void +jme_set_physpeed_capability(struct jme_adapter *jme, u16 speed) +{ + u32 advert, advert2; + +// spin_lock_bh(&jme->phy_lock); + advert = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_ADVERTISE); + advert2 = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_CTRL1000); + switch (speed) { + case SPEED_1000: + advert = (advert|ADVERTISE_100HALF|ADVERTISE_100FULL); + advert2 = (advert2|ADVERTISE_1000HALF|ADVERTISE_1000FULL); + break; + case SPEED_100: + advert = (advert|ADVERTISE_100HALF|ADVERTISE_100FULL); + advert2 = advert2 & ~(ADVERTISE_1000HALF|ADVERTISE_1000FULL); + break; + default: + advert = advert & ~(ADVERTISE_100HALF|ADVERTISE_100FULL); + advert2 = advert2 & ~(ADVERTISE_1000HALF|ADVERTISE_1000FULL); + } + jme_mdio_write(jme->dev, jme->mii_if.phy_id, MII_ADVERTISE, advert); + jme_mdio_write(jme->dev, jme->mii_if.phy_id, MII_CTRL1000, advert2); +// spin_unlock_bh(&jme->phy_lock); + return; +} + +/* + PHY reg: MII_FCSCOUNTER is read and clear, we have to + continuing read until RJ45 is attached, then cache + this result. +*/ +static int +jme_check_ANcomplete(struct jme_adapter *jme) +{ + u32 val; + + val = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_FCSCOUNTER); + return ((val&(PHY_SPEC_STATUS_AN_FAIL|PHY_SPEC_STATUS_AN_COMPLETE)) + == PHY_SPEC_STATUS_AN_COMPLETE) ? true : false; +} + +static int +jme_media_connected(struct jme_adapter *jme) +{ + if (jme->media_cnct == true) + return true; + + jme->media_cnct = jme_check_ANcomplete(jme); + return jme->media_cnct; +} + +static inline void +jme_slowspeed_tune(struct jme_adapter *jme, u16 speed) +{ + if (jme_check_ANcomplete(jme)) + jme_set_physpeed_capability(jme, speed); + else { + jme->media_cnct = false; + jme->media_cnct_sec = 0; + } +} + +static void +asd_polling_func(unsigned long data) +{ + struct jme_adapter *jme = (struct jme_adapter *)data; + unsigned long flags; + /* + check condition term by term. + 1. link is up() + ==> reset all thing, exit the process. + 2. there is no RJ45 cable attached + ==> do nothing but polling + 3. RJ45 cable attached. but link is down + ==> downspeed if the timeing is over 3.5 second. + */ + spin_lock_irqsave(&jme->asd_lock, flags); + if (jme->flag_run_asd == true) { + if (jme_media_connected(jme)) { + jme->media_cnct_sec++; + if (jme->media_cnct_sec == (delay_time*3-5)) { + /* Unable to link anyway, it can NOT be resolved + by slower speed, restore the capability */ + jme_set_physpeed_capability(jme, SPEED_1000); + jme->media_cnct = false; + jme->media_cnct_sec = 0; + } else if (jme->media_cnct_sec == (delay_time*2-5)) + jme_slowspeed_tune(jme, SPEED_10); + else if (jme->media_cnct_sec == delay_time-5) + jme_slowspeed_tune(jme, SPEED_100); + } + mod_timer(&jme->asd_timer, jiffies+HZ); + spin_unlock_irqrestore(&jme->asd_lock, flags); + return ; + } + jme->media_cnct = false; + jme->media_cnct_sec = 0; + spin_unlock_irqrestore(&jme->asd_lock, flags); + return; +} + +static int jme_check_linkup(struct jme_adapter *jme) +{ + u32 phylink; + + if (jme->fpgaver) + phylink = jme_linkstat_from_phy(jme); + else + phylink = jread32(jme, JME_PHY_LINK); + + return (phylink & PHY_LINK_UP) ? true : false; +} + +static void jme_LC_task(struct work_struct *work) +{ + struct jme_adapter *jme; + struct net_device *netdev; + int rc; + unsigned long flags; + + jme = container_of(work, struct jme_adapter, LC_task); + netdev = jme->dev; + + + msleep(500); + while (!atomic_dec_and_test(&jme->link_changing)) { + atomic_inc(&jme->link_changing); + netif_info(jme, intr, jme->dev, "Get link change lock failed\n"); + while (atomic_read(&jme->link_changing) != 1) + netif_info(jme, intr, jme->dev, "Waiting link change lock\n"); + } + spin_lock_irqsave(&jme->asd_lock, flags); + if (jme_check_linkup(jme)) { + if (jme->flag_run_asd) { + jme->flag_run_asd = false; + del_timer_sync(&jme->asd_timer); + } + } else { + if (!jme->flag_run_asd) { + jme_set_physpeed_capability(jme, SPEED_1000); + jme_check_ANcomplete(jme); + jme->media_cnct = false; + jme->flag_run_asd = true; + jme->media_cnct_sec = 0; + jme->asd_timer.expires = jiffies + 4*HZ; + jme->asd_timer.function = &asd_polling_func; + jme->asd_timer.data = (unsigned long)jme; + add_timer(&jme->asd_timer); + } + } + spin_unlock_irqrestore(&jme->asd_lock, flags); + if (jme_check_link(netdev, 1) && jme->old_mtu == netdev->mtu) + goto out; + + jme->old_mtu = netdev->mtu; + netif_stop_queue(netdev); + if (jme_pseudo_hotplug_enabled(jme)) + jme_stop_shutdown_timer(jme); + + jme_stop_pcc_timer(jme); + tasklet_disable(&jme->txclean_task); + tasklet_disable(&jme->rxclean_task); + tasklet_disable(&jme->rxempty_task); + + if (netif_carrier_ok(netdev)) { + jme_disable_rx_engine(jme); + jme_disable_tx_engine(jme); + jme_reset_mac_processor(jme); + jme_free_rx_resources(jme); + jme_free_tx_resources(jme); + + if (test_bit(JME_FLAG_POLL, &jme->flags)) + jme_polling_mode(jme); + + netif_carrier_off(netdev); + } + + jme_check_link(netdev, 0); + if (netif_carrier_ok(netdev)) { + rc = jme_setup_rx_resources(jme); + if (rc) { + pr_err("Allocating resources for RX error, Device STOPPED!\n"); + goto out_enable_tasklet; + } + + rc = jme_setup_tx_resources(jme); + if (rc) { + pr_err("Allocating resources for TX error, Device STOPPED!\n"); + goto err_out_free_rx_resources; + } + + jme_enable_rx_engine(jme); + jme_enable_tx_engine(jme); + + netif_start_queue(netdev); + + if (test_bit(JME_FLAG_POLL, &jme->flags)) + jme_interrupt_mode(jme); + + jme_start_pcc_timer(jme); + } else if (jme_pseudo_hotplug_enabled(jme)) { + jme_start_shutdown_timer(jme); + } + + goto out_enable_tasklet; + +err_out_free_rx_resources: + jme_free_rx_resources(jme); +out_enable_tasklet: + tasklet_enable(&jme->txclean_task); + tasklet_enable(&jme->rxclean_task); + tasklet_enable(&jme->rxempty_task); +out: + atomic_inc(&jme->link_changing); +} + +static void jme_link_change_tasklet(unsigned long arg) { struct jme_adapter *jme = (struct jme_adapter *)arg; @@ -1538,7 +1759,7 @@ jme_intr_msi(struct jme_adapter *jme, u32 intrstat) * all other events are ignored */ jwrite32(jme, JME_IEVE, intrstat); - tasklet_schedule(&jme->linkch_task); + schedule_work(&jme->LC_task); goto out_reenable; } @@ -3112,6 +3333,9 @@ jme_init_one(struct pci_dev *pdev, tasklet_init(&jme->pcc_task, jme_pcc_tasklet, (unsigned long) jme); + + INIT_WORK(&jme->LC_task, jme_LC_task); + jme->dpi.cur = PCC_P1; jme->reg_ghc = 0; @@ -3207,6 +3431,11 @@ jme_init_one(struct pci_dev *pdev, goto err_out_unmap; } + init_timer(&(jme->asd_timer)); + jme->media_cnct_sec = 0; + jme->flag_run_asd = false; + jme->media_cnct = false; + netif_info(jme, probe, jme->dev, "%s%s chiprev:%x pcirev:%x macaddr:%pM\n", (jme->pdev->device == PCI_DEVICE_ID_JMICRON_JMC250) ? "JMC250 Gigabit Ethernet" : @@ -3236,6 +3465,8 @@ jme_remove_one(struct pci_dev *pdev) struct net_device *netdev = pci_get_drvdata(pdev); struct jme_adapter *jme = netdev_priv(netdev); + del_timer_sync(&jme->asd_timer); + cancel_work_sync(&jme->LC_task); unregister_netdev(netdev); iounmap(jme->regs); free_netdev(netdev); @@ -3261,6 +3492,7 @@ jme_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct jme_adapter *jme = netdev_priv(netdev); + unsigned long flags; if (!netif_running(netdev)) return 0; @@ -3270,7 +3502,10 @@ jme_suspend(struct device *dev) netif_device_detach(netdev); netif_stop_queue(netdev); jme_stop_irq(jme); - + spin_lock_irqsave(&jme->asd_lock, flags); + if (jme->flag_run_asd) + del_timer_sync(&jme->asd_timer); + spin_unlock_irqrestore(&jme->asd_lock, flags); tasklet_disable(&jme->txclean_task); tasklet_disable(&jme->rxclean_task); tasklet_disable(&jme->rxempty_task); @@ -3294,6 +3529,8 @@ jme_suspend(struct device *dev) tasklet_enable(&jme->rxempty_task); jme_powersave_phy(jme); + jme->media_cnct_sec = 0; + jme->media_cnct = false; return 0; } @@ -3304,6 +3541,7 @@ jme_resume(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct jme_adapter *jme = netdev_priv(netdev); + unsigned long flags; if (!netif_running(netdev)) return 0; @@ -3322,7 +3560,15 @@ jme_resume(struct device *dev) atomic_inc(&jme->link_changing); jme_reset_link(jme); - + spin_lock_irqsave(&jme->asd_lock, flags); + if (jme->flag_run_asd) { + jme_check_ANcomplete(jme); + jme->asd_timer.expires = jiffies + 4*HZ; + jme->asd_timer.function = &asd_polling_func; + jme->asd_timer.data = (unsigned long)jme; + add_timer(&jme->asd_timer); + } + spin_unlock_irqrestore(&jme->asd_lock, flags); return 0; } diff --git a/drivers/net/ethernet/jme.h b/drivers/net/ethernet/jme.h index 58cd67c..0ce4a3c 100644 --- a/drivers/net/ethernet/jme.h +++ b/drivers/net/ethernet/jme.h @@ -426,6 +426,7 @@ struct jme_adapter { struct tasklet_struct txclean_task; struct tasklet_struct linkch_task; struct tasklet_struct pcc_task; + struct work_struct LC_task; unsigned long flags; u32 reg_txcs; u32 reg_txpfc; @@ -456,6 +457,11 @@ struct jme_adapter { atomic_t rx_cleaning; atomic_t rx_empty; int (*jme_rx)(struct sk_buff *skb); + spinlock_t asd_lock; + u8 flag_run_asd; + u32 media_cnct_sec; + u8 media_cnct; + struct timer_list asd_timer; DECLARE_NAPI_STRUCT DECLARE_NET_DEVICE_STATS }; @@ -907,6 +913,14 @@ enum jme_phy_pwr_bit_masks { }; /* + * False carrier Counter + */ +enum jme_phy_an_status { + PHY_SPEC_STATUS_AN_COMPLETE = 0x00000800, + PHY_SPEC_STATUS_AN_FAIL = 0x00008000, +}; + +/* * Giga PHY Status Registers */ enum jme_phy_link_bit_mask {